1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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"
12 #include "ActiveLayerTracker.h"
13 #include "DisplayItemClip.h"
14 #include "gfx2DGlue.h"
15 #include "gfxContext.h"
16 #include "gfxDrawable.h"
18 #include "gfxMatrix.h"
19 #include "gfxPlatform.h"
23 #include "ImageContainer.h"
25 #include "ImageRegion.h"
26 #include "imgIContainer.h"
27 #include "imgIRequest.h"
28 #include "LayoutLogging.h"
29 #include "MobileViewportManager.h"
30 #include "mozilla/AccessibleCaretEventHub.h"
31 #include "mozilla/ArrayUtils.h"
32 #include "mozilla/Baseline.h"
33 #include "mozilla/BasicEvents.h"
34 #include "mozilla/ClearOnShutdown.h"
35 #include "mozilla/DisplayPortUtils.h"
36 #include "mozilla/glean/GleanMetrics.h"
37 #include "mozilla/dom/AnonymousContent.h"
38 #include "mozilla/dom/BrowserChild.h"
39 #include "mozilla/dom/CanvasUtils.h"
40 #include "mozilla/dom/Document.h"
41 #include "mozilla/dom/DocumentInlines.h"
42 #include "mozilla/dom/DOMRect.h"
43 #include "mozilla/dom/DOMStringList.h"
44 #include "mozilla/dom/Element.h"
45 #include "mozilla/dom/HTMLBodyElement.h"
46 #include "mozilla/dom/HTMLCanvasElement.h"
47 #include "mozilla/dom/HTMLImageElement.h"
48 #include "mozilla/dom/HTMLMediaElementBinding.h"
49 #include "mozilla/dom/HTMLVideoElement.h"
50 #include "mozilla/dom/InspectorFontFace.h"
51 #include "mozilla/dom/ImageBitmap.h"
52 #include "mozilla/dom/KeyframeEffect.h"
53 #include "mozilla/dom/SVGViewportElement.h"
54 #include "mozilla/dom/UIEvent.h"
55 #include "mozilla/dom/VideoFrame.h"
56 #include "mozilla/dom/VideoFrameBinding.h"
57 #include "mozilla/intl/BidiEmbeddingLevel.h"
58 #include "mozilla/EffectCompositor.h"
59 #include "mozilla/EffectSet.h"
60 #include "mozilla/EventDispatcher.h"
61 #include "mozilla/EventStateManager.h"
62 #include "mozilla/FloatingPoint.h"
63 #include "mozilla/gfx/2D.h"
64 #include "mozilla/gfx/gfxVars.h"
65 #include "mozilla/gfx/PathHelpers.h"
66 #include "mozilla/gfx/DataSurfaceHelpers.h"
67 #include "mozilla/IntegerRange.h"
68 #include "mozilla/layers/APZCCallbackHelper.h"
69 #include "mozilla/layers/APZPublicUtils.h" // for apz::CalculatePendingDisplayPort
70 #include "mozilla/layers/CompositorBridgeChild.h"
71 #include "mozilla/layers/PAPZ.h"
72 #include "mozilla/layers/StackingContextHelper.h"
73 #include "mozilla/layers/WebRenderLayerManager.h"
74 #include "mozilla/Likely.h"
75 #include "mozilla/LookAndFeel.h"
76 #include "mozilla/Maybe.h"
77 #include "mozilla/MemoryReporting.h"
78 #include "mozilla/PerfStats.h"
79 #include "mozilla/Preferences.h"
80 #include "mozilla/PresShell.h"
81 #include "mozilla/ProfilerLabels.h"
82 #include "mozilla/ProfilerMarkers.h"
83 #include "mozilla/RestyleManager.h"
84 #include "mozilla/ScopeExit.h"
85 #include "mozilla/ScrollOrigin.h"
86 #include "mozilla/ServoStyleSet.h"
87 #include "mozilla/ServoStyleSetInlines.h"
88 #include "mozilla/StaticPrefs_apz.h"
89 #include "mozilla/StaticPrefs_browser.h"
90 #include "mozilla/StaticPrefs_dom.h"
91 #include "mozilla/StaticPrefs_font.h"
92 #include "mozilla/StaticPrefs_general.h"
93 #include "mozilla/StaticPrefs_gfx.h"
94 #include "mozilla/StaticPrefs_image.h"
95 #include "mozilla/StaticPrefs_layers.h"
96 #include "mozilla/StaticPrefs_layout.h"
97 #include "mozilla/StaticPtr.h"
98 #include "mozilla/StyleAnimationValue.h"
99 #include "mozilla/SVGImageContext.h"
100 #include "mozilla/SVGIntegrationUtils.h"
101 #include "mozilla/SVGTextFrame.h"
102 #include "mozilla/SVGUtils.h"
103 #include "mozilla/Telemetry.h"
104 #include "mozilla/ToString.h"
105 #include "mozilla/Unused.h"
106 #include "mozilla/ViewportFrame.h"
107 #include "mozilla/ViewportUtils.h"
108 #include "mozilla/WheelHandlingHelper.h" // for WheelHandlingUtils
109 #include "nsAnimationManager.h"
111 #include "nsBidiPresUtils.h"
112 #include "nsBlockFrame.h"
113 #include "nsCanvasFrame.h"
115 #include "nsCharTraits.h"
116 #include "nsCOMPtr.h"
117 #include "nsComputedDOMStyle.h"
118 #include "nsContentUtils.h"
119 #include "nsCSSAnonBoxes.h"
120 #include "nsCSSColorUtils.h"
121 #include "nsCSSFrameConstructor.h"
122 #include "nsCSSProps.h"
123 #include "nsCSSPseudoElements.h"
124 #include "nsCSSRendering.h"
125 #include "nsDisplayList.h"
126 #include "nsFieldSetFrame.h"
127 #include "nsFlexContainerFrame.h"
128 #include "nsFontInflationData.h"
129 #include "nsFontMetrics.h"
130 #include "nsFrameList.h"
131 #include "nsFrameSelection.h"
132 #include "nsGenericHTMLElement.h"
133 #include "nsGkAtoms.h"
134 #include "nsICanvasRenderingContextInternal.h"
135 #include "nsIContent.h"
136 #include "nsIContentInlines.h"
137 #include "nsIDocShell.h"
138 #include "nsIDocumentViewer.h"
139 #include "nsIFrameInlines.h"
140 #include "nsIImageLoadingContent.h"
141 #include "nsIInterfaceRequestorUtils.h"
142 #include "nsIScrollableFrame.h"
143 #include "nsIWidget.h"
144 #include "nsListControlFrame.h"
145 #include "nsPIDOMWindow.h"
146 #include "nsPlaceholderFrame.h"
147 #include "nsPresContext.h"
148 #include "nsPresContextInlines.h"
149 #include "nsRefreshDriver.h"
150 #include "nsRegion.h"
151 #include "nsStyleConsts.h"
152 #include "nsStyleStructInlines.h"
153 #include "nsStyleTransformMatrix.h"
154 #include "nsSubDocumentFrame.h"
155 #include "nsTableWrapperFrame.h"
156 #include "nsTArray.h"
157 #include "nsTextFragment.h"
158 #include "nsTextFrame.h"
159 #include "nsTHashMap.h"
160 #include "nsTransitionManager.h"
162 #include "nsViewManager.h"
164 #include "RegionBuilder.h"
165 #include "RetainedDisplayListBuilder.h"
166 #include "TextDrawTarget.h"
167 #include "UnitTransforms.h"
168 #include "ViewportFrame.h"
170 #include "nsXULPopupManager.h"
172 // Make sure getpid() works.
174 # include <process.h>
175 # define getpid _getpid
180 using namespace mozilla
;
181 using namespace mozilla::dom
;
182 using namespace mozilla::image
;
183 using namespace mozilla::layers
;
184 using namespace mozilla::layout
;
185 using namespace mozilla::gfx
;
186 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA
;
187 using mozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING
;
189 typedef ScrollableLayerGuid::ViewID ViewID
;
190 typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox
;
192 static ViewID sScrollIdCounter
= ScrollableLayerGuid::START_SCROLL_ID
;
194 typedef nsTHashMap
<nsUint64HashKey
, nsIContent
*> ContentMap
;
195 static StaticAutoPtr
<ContentMap
> sContentMap
;
197 static ContentMap
& GetContentMap() {
199 sContentMap
= new ContentMap();
204 template <typename TestType
>
205 static bool HasMatchingAnimations(EffectSet
& aEffects
, TestType
&& aTest
) {
206 for (KeyframeEffect
* effect
: aEffects
) {
207 if (!effect
->GetAnimation() || !effect
->GetAnimation()->IsRelevant()) {
211 if (aTest(*effect
, aEffects
)) {
219 template <typename TestType
>
220 static bool HasMatchingAnimations(const nsIFrame
* aFrame
,
221 const nsCSSPropertyIDSet
& aPropertySet
,
225 if (aPropertySet
.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties()) &&
226 !aFrame
->MayHaveOpacityAnimation()) {
230 if (aPropertySet
.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties()) &&
231 !aFrame
->MayHaveTransformAnimation()) {
235 EffectSet
* effectSet
= EffectSet::GetForFrame(aFrame
, aPropertySet
);
240 return HasMatchingAnimations(*effectSet
, aTest
);
244 bool nsLayoutUtils::HasAnimationOfPropertySet(
245 const nsIFrame
* aFrame
, const nsCSSPropertyIDSet
& aPropertySet
) {
246 return HasMatchingAnimations(
247 aFrame
, aPropertySet
,
248 [&aPropertySet
](KeyframeEffect
& aEffect
, const EffectSet
&) {
249 return aEffect
.HasAnimationOfPropertySet(aPropertySet
);
254 bool nsLayoutUtils::HasAnimationOfPropertySet(
255 const nsIFrame
* aFrame
, const nsCSSPropertyIDSet
& aPropertySet
,
256 EffectSet
* aEffectSet
) {
258 !aEffectSet
|| EffectSet::GetForFrame(aFrame
, aPropertySet
) == aEffectSet
,
259 "The EffectSet, if supplied, should match what we would otherwise fetch");
262 return nsLayoutUtils::HasAnimationOfPropertySet(aFrame
, aPropertySet
);
265 if (aPropertySet
.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties()) &&
266 !aEffectSet
->MayHaveTransformAnimation()) {
270 if (aPropertySet
.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties()) &&
271 !aEffectSet
->MayHaveOpacityAnimation()) {
275 return HasMatchingAnimations(
277 [&aPropertySet
](KeyframeEffect
& aEffect
, const EffectSet
& aEffectSet
) {
278 return aEffect
.HasAnimationOfPropertySet(aPropertySet
);
283 bool nsLayoutUtils::HasAnimationOfTransformAndMotionPath(
284 const nsIFrame
* aFrame
) {
285 return nsLayoutUtils::HasAnimationOfPropertySet(
287 nsCSSPropertyIDSet
{eCSSProperty_transform
, eCSSProperty_translate
,
288 eCSSProperty_rotate
, eCSSProperty_scale
,
289 eCSSProperty_offset_path
}) ||
290 (!aFrame
->StyleDisplay()->mOffsetPath
.IsNone() &&
291 nsLayoutUtils::HasAnimationOfPropertySet(
292 aFrame
, nsCSSPropertyIDSet::MotionPathProperties()));
296 bool nsLayoutUtils::HasEffectiveAnimation(
297 const nsIFrame
* aFrame
, const nsCSSPropertyIDSet
& aPropertySet
) {
298 return HasMatchingAnimations(
299 aFrame
, aPropertySet
,
300 [&aPropertySet
](KeyframeEffect
& aEffect
, const EffectSet
& aEffectSet
) {
301 return aEffect
.HasEffectiveAnimationOfPropertySet(aPropertySet
,
307 nsCSSPropertyIDSet
nsLayoutUtils::GetAnimationPropertiesForCompositor(
308 const nsIFrame
* aStyleFrame
) {
309 nsCSSPropertyIDSet properties
;
311 // We fetch the effects for the style frame here since this method is called
312 // by RestyleManager::AddLayerChangesForAnimation which takes care to apply
313 // the relevant hints to the primary frame as needed.
314 EffectSet
* effects
= EffectSet::GetForStyleFrame(aStyleFrame
);
319 AnimationPerformanceWarning::Type warning
;
320 if (!EffectCompositor::AllowCompositorAnimationsOnFrame(aStyleFrame
,
325 for (const KeyframeEffect
* effect
: *effects
) {
326 properties
|= effect
->GetPropertiesForCompositor(*effects
, aStyleFrame
);
329 // If properties only have motion-path properties, we have to make sure they
330 // have effects. i.e. offset-path is not none or we have offset-path
332 if (properties
.IsSubsetOf(nsCSSPropertyIDSet::MotionPathProperties()) &&
333 !properties
.HasProperty(eCSSProperty_offset_path
) &&
334 aStyleFrame
->StyleDisplay()->mOffsetPath
.IsNone()) {
341 static float GetSuitableScale(float aMaxScale
, float aMinScale
,
342 nscoord aVisibleDimension
,
343 nscoord aDisplayDimension
) {
344 float displayVisibleRatio
=
345 float(aDisplayDimension
) / float(aVisibleDimension
);
346 // We want to rasterize based on the largest scale used during the
347 // transform animation, unless that would make us rasterize something
348 // larger than the screen. But we never want to go smaller than the
349 // minimum scale over the animation.
350 if (FuzzyEqualsMultiplicative(displayVisibleRatio
, aMaxScale
, .01f
)) {
351 // Using aMaxScale may make us rasterize something a fraction larger than
352 // the screen. However, if aMaxScale happens to be the final scale of a
353 // transform animation it is better to use aMaxScale so that for the
354 // fraction of a second before we delayerize the composited texture it has
355 // a better chance of being pixel aligned and composited without resampling
356 // (avoiding visually clunky delayerization).
359 return std::max(std::min(aMaxScale
, displayVisibleRatio
), aMinScale
);
362 // The first value in this pair is the min scale, and the second one is the max
364 using MinAndMaxScale
= std::pair
<MatrixScales
, MatrixScales
>;
366 static inline void UpdateMinMaxScale(const nsIFrame
* aFrame
,
367 const AnimationValue
& aValue
,
368 MinAndMaxScale
& aMinAndMaxScale
) {
369 MatrixScales size
= aValue
.GetScaleValue(aFrame
);
370 MatrixScales
& minScale
= aMinAndMaxScale
.first
;
371 MatrixScales
& maxScale
= aMinAndMaxScale
.second
;
373 minScale
= Min(minScale
, size
);
374 maxScale
= Max(maxScale
, size
);
377 // The final transform matrix is calculated by merging the final results of each
378 // transform-like properties, so do the scale factors. In other words, the
379 // potential min/max scales could be gotten by multiplying the max/min scales of
382 // For example, there is an animation:
383 // from { "transform: scale(1, 1)", "scale: 3, 3" };
384 // to { "transform: scale(2, 2)", "scale: 1, 1" };
386 // the min scale is (1, 1) * (1, 1) = (1, 1), and
387 // The max scale is (2, 2) * (3, 3) = (6, 6).
388 // This means we multiply the min/max scale factor of transform property and the
389 // min/max scale factor of scale property to get the final max/min scale factor.
390 static Array
<MinAndMaxScale
, 2> GetMinAndMaxScaleForAnimationProperty(
391 const nsIFrame
* aFrame
,
392 const nsTArray
<RefPtr
<dom::Animation
>>& aAnimations
) {
393 // We use a fixed array to store the min/max scales for each property.
394 // The first element in the array is for eCSSProperty_transform, and the
395 // second one is for eCSSProperty_scale.
396 const MinAndMaxScale defaultValue
=
397 std::make_pair(MatrixScales(std::numeric_limits
<float>::max(),
398 std::numeric_limits
<float>::max()),
399 MatrixScales(std::numeric_limits
<float>::min(),
400 std::numeric_limits
<float>::min()));
401 Array
<MinAndMaxScale
, 2> minAndMaxScales(defaultValue
, defaultValue
);
403 for (dom::Animation
* anim
: aAnimations
) {
404 // This method is only expected to be passed animations that are running on
405 // the compositor and we only pass playing animations to the compositor,
406 // which are, by definition, "relevant" animations (animations that are
407 // not yet finished or which are filling forwards).
408 MOZ_ASSERT(anim
->IsRelevant());
410 const dom::KeyframeEffect
* effect
=
411 anim
->GetEffect() ? anim
->GetEffect()->AsKeyframeEffect() : nullptr;
412 MOZ_ASSERT(effect
, "A playing animation should have a keyframe effect");
413 for (const AnimationProperty
& prop
: effect
->Properties()) {
414 if (prop
.mProperty
.mID
!= eCSSProperty_transform
&&
415 prop
.mProperty
.mID
!= eCSSProperty_scale
) {
419 // 0: eCSSProperty_transform.
420 // 1: eCSSProperty_scale.
421 MinAndMaxScale
& scales
=
422 minAndMaxScales
[prop
.mProperty
.mID
== eCSSProperty_transform
? 0 : 1];
424 // We need to factor in the scale of the base style if the base style
425 // will be used on the compositor.
426 const AnimationValue
& baseStyle
= effect
->BaseStyle(prop
.mProperty
);
427 if (!baseStyle
.IsNull()) {
428 UpdateMinMaxScale(aFrame
, baseStyle
, scales
);
431 for (const AnimationPropertySegment
& segment
: prop
.mSegments
) {
432 // In case of add or accumulate composite, StyleAnimationValue does
433 // not have a valid value.
434 if (segment
.HasReplaceableFromValue()) {
435 UpdateMinMaxScale(aFrame
, segment
.mFromValue
, scales
);
438 if (segment
.HasReplaceableToValue()) {
439 UpdateMinMaxScale(aFrame
, segment
.mToValue
, scales
);
445 return minAndMaxScales
;
448 MatrixScales
nsLayoutUtils::ComputeSuitableScaleForAnimation(
449 const nsIFrame
* aFrame
, const nsSize
& aVisibleSize
,
450 const nsSize
& aDisplaySize
) {
451 const nsTArray
<RefPtr
<dom::Animation
>> compositorAnimations
=
452 EffectCompositor::GetAnimationsForCompositor(
454 nsCSSPropertyIDSet
{eCSSProperty_transform
, eCSSProperty_scale
});
456 if (compositorAnimations
.IsEmpty()) {
457 return MatrixScales();
460 const Array
<MinAndMaxScale
, 2> minAndMaxScales
=
461 GetMinAndMaxScaleForAnimationProperty(aFrame
, compositorAnimations
);
463 // This might cause an issue if users use std::numeric_limits<float>::min()
464 // (or max()) as the scale value. However, in this case, we may render an
465 // extreme small (or large) element, so this may not be a problem. If so,
467 MatrixScales
maxScale(std::numeric_limits
<float>::min(),
468 std::numeric_limits
<float>::min());
469 MatrixScales
minScale(std::numeric_limits
<float>::max(),
470 std::numeric_limits
<float>::max());
472 auto isUnset
= [](const MatrixScales
& aMax
, const MatrixScales
& aMin
) {
473 return aMax
.xScale
== std::numeric_limits
<float>::min() &&
474 aMax
.yScale
== std::numeric_limits
<float>::min() &&
475 aMin
.xScale
== std::numeric_limits
<float>::max() &&
476 aMin
.yScale
== std::numeric_limits
<float>::max();
479 // Iterate the slots to get the final scale value.
480 for (const auto& pair
: minAndMaxScales
) {
481 const MatrixScales
& currMinScale
= pair
.first
;
482 const MatrixScales
& currMaxScale
= pair
.second
;
484 if (isUnset(currMaxScale
, currMinScale
)) {
485 // We don't have this animation property, so skip.
489 if (isUnset(maxScale
, minScale
)) {
490 // Initialize maxScale and minScale.
491 maxScale
= currMaxScale
;
492 minScale
= currMinScale
;
494 // The scale factors of each transform-like property should be multiplied
495 // by others because we merge their sampled values as a final matrix by
496 // matrix multiplication, so here we multiply the scale factors by the
497 // previous one to get the possible max and min scale factors.
498 maxScale
= maxScale
* currMaxScale
;
499 minScale
= minScale
* currMinScale
;
503 if (isUnset(maxScale
, minScale
)) {
504 // We didn't encounter any transform-like property.
505 return MatrixScales();
509 GetSuitableScale(maxScale
.xScale
, minScale
.xScale
, aVisibleSize
.width
,
511 GetSuitableScale(maxScale
.yScale
, minScale
.yScale
, aVisibleSize
.height
,
512 aDisplaySize
.height
));
515 bool nsLayoutUtils::AreAsyncAnimationsEnabled() {
516 return StaticPrefs::layers_offmainthreadcomposition_async_animations() &&
517 gfxPlatform::OffMainThreadCompositingEnabled();
520 bool nsLayoutUtils::AreRetainedDisplayListsEnabled() {
521 #ifdef MOZ_WIDGET_ANDROID
522 return StaticPrefs::layout_display_list_retain();
524 if (XRE_IsContentProcess()) {
525 return StaticPrefs::layout_display_list_retain();
528 if (XRE_IsE10sParentProcess()) {
529 return StaticPrefs::layout_display_list_retain_chrome();
532 // Retained display lists require e10s.
537 bool nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame
* aFrame
) {
538 return GetRetainedDisplayListBuilder(aFrame
) != nullptr;
541 RetainedDisplayListBuilder
* nsLayoutUtils::GetRetainedDisplayListBuilder(
544 MOZ_ASSERT(aFrame
->PresShell());
546 // Use the pres shell root frame to get the display root frame. This skips
547 // the early exit in |nsLayoutUtils::GetDisplayRootFrame()| for popup frames.
548 const nsIFrame
* rootFrame
= aFrame
->PresShell()->GetRootFrame();
553 const nsIFrame
* displayRootFrame
= GetDisplayRootFrame(rootFrame
);
554 MOZ_ASSERT(displayRootFrame
);
556 return displayRootFrame
->GetProperty(RetainedDisplayListBuilder::Cached());
559 bool nsLayoutUtils::GPUImageScalingEnabled() {
560 static bool sGPUImageScalingEnabled
;
561 static bool sGPUImageScalingPrefInitialised
= false;
563 if (!sGPUImageScalingPrefInitialised
) {
564 sGPUImageScalingPrefInitialised
= true;
565 sGPUImageScalingEnabled
=
566 Preferences::GetBool("layout.gpu-image-scaling.enabled", false);
569 return sGPUImageScalingEnabled
;
572 void nsLayoutUtils::UnionChildOverflow(nsIFrame
* aFrame
,
573 OverflowAreas
& aOverflowAreas
,
574 FrameChildListIDs aSkipChildLists
) {
575 // Iterate over all children except pop-ups.
576 FrameChildListIDs
skip(aSkipChildLists
);
577 skip
+= FrameChildListID::Popup
;
579 for (const auto& [list
, listID
] : aFrame
->ChildLists()) {
580 if (skip
.contains(listID
)) {
583 for (nsIFrame
* child
: list
) {
584 aOverflowAreas
.UnionWith(
585 child
->GetActualAndNormalOverflowAreasRelativeToParent());
590 static void DestroyViewID(void* aObject
, nsAtom
* aPropertyName
,
591 void* aPropertyValue
, void* aData
) {
592 ViewID
* id
= static_cast<ViewID
*>(aPropertyValue
);
593 GetContentMap().Remove(*id
);
598 * A namespace class for static layout utilities.
601 bool nsLayoutUtils::FindIDFor(const nsIContent
* aContent
, ViewID
* aOutViewId
) {
602 void* scrollIdProperty
= aContent
->GetProperty(nsGkAtoms::RemoteId
);
603 if (scrollIdProperty
) {
604 *aOutViewId
= *static_cast<ViewID
*>(scrollIdProperty
);
610 ViewID
nsLayoutUtils::FindOrCreateIDFor(nsIContent
* aContent
) {
613 if (!FindIDFor(aContent
, &scrollId
)) {
614 scrollId
= sScrollIdCounter
++;
615 aContent
->SetProperty(nsGkAtoms::RemoteId
, new ViewID(scrollId
),
617 GetContentMap().InsertOrUpdate(scrollId
, aContent
);
623 nsIContent
* nsLayoutUtils::FindContentFor(ViewID aId
) {
624 MOZ_ASSERT(aId
!= ScrollableLayerGuid::NULL_SCROLL_ID
,
625 "Cannot find a content element in map for null IDs.");
627 bool exists
= GetContentMap().Get(aId
, &content
);
636 nsIFrame
* nsLayoutUtils::GetScrollFrameFromContent(nsIContent
* aContent
) {
637 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
638 if (aContent
->OwnerDoc()->GetRootElement() == aContent
) {
639 PresShell
* presShell
= frame
? frame
->PresShell() : nullptr;
641 presShell
= aContent
->OwnerDoc()->GetPresShell();
643 // We want the scroll frame, the root scroll frame differs from all
644 // others in that the primary frame is not the scroll frame.
645 nsIFrame
* rootScrollFrame
=
646 presShell
? presShell
->GetRootScrollFrame() : nullptr;
647 if (rootScrollFrame
) {
648 frame
= rootScrollFrame
;
654 nsIScrollableFrame
* nsLayoutUtils::FindScrollableFrameFor(
655 nsIContent
* aContent
) {
656 nsIFrame
* scrollFrame
= GetScrollFrameFromContent(aContent
);
657 return scrollFrame
? scrollFrame
->GetScrollTargetFrame() : nullptr;
660 nsIScrollableFrame
* nsLayoutUtils::FindScrollableFrameFor(ViewID aId
) {
661 nsIContent
* content
= FindContentFor(aId
);
666 return FindScrollableFrameFor(content
);
669 ViewID
nsLayoutUtils::FindIDForScrollableFrame(
670 nsIScrollableFrame
* aScrollable
) {
672 return ScrollableLayerGuid::NULL_SCROLL_ID
;
675 nsIFrame
* scrollFrame
= do_QueryFrame(aScrollable
);
676 nsIContent
* scrollContent
= scrollFrame
->GetContent();
678 ScrollableLayerGuid::ViewID scrollId
;
679 if (scrollContent
&& nsLayoutUtils::FindIDFor(scrollContent
, &scrollId
)) {
683 return ScrollableLayerGuid::NULL_SCROLL_ID
;
686 bool nsLayoutUtils::UsesAsyncScrolling(nsIFrame
* aFrame
) {
687 #ifdef MOZ_WIDGET_ANDROID
688 // We always have async scrolling for android
692 return AsyncPanZoomEnabled(aFrame
);
695 bool nsLayoutUtils::AsyncPanZoomEnabled(const nsIFrame
* aFrame
) {
696 // We use this as a shortcut, since if the compositor will never use APZ,
697 // no widget will either.
698 if (!gfxPlatform::AsyncPanZoomEnabled()) {
702 const nsIFrame
* frame
= nsLayoutUtils::GetDisplayRootFrame(aFrame
);
703 nsIWidget
* widget
= frame
->GetNearestWidget();
707 return widget
->AsyncPanZoomEnabled();
710 bool nsLayoutUtils::AllowZoomingForDocument(
711 const mozilla::dom::Document
* aDocument
) {
712 if (aDocument
->GetPresShell() &&
713 !aDocument
->GetPresShell()->AsyncPanZoomEnabled()) {
716 // True if we allow zooming for all documents on this platform, or if we are
718 BrowsingContext
* bc
= aDocument
->GetBrowsingContext();
719 return StaticPrefs::apz_allow_zooming() || (bc
&& bc
->InRDMPane());
722 static bool HasVisibleAnonymousContents(Document
* aDoc
) {
723 for (RefPtr
<AnonymousContent
>& ac
: aDoc
->GetAnonymousContents()) {
724 // We check to see if the anonymous content node has a frame. If it doesn't,
725 // that means that's not visible to the user because e.g. it's display:none.
726 // For now we assume that if it has a frame, it is visible. We might be able
727 // to refine this further by adding complexity if it turns out this
728 // condition results in a lot of false positives.
729 if (ac
->Host()->GetPrimaryFrame()) {
736 bool nsLayoutUtils::ShouldDisableApzForElement(nsIContent
* aContent
) {
741 if (aContent
->GetProperty(nsGkAtoms::apzDisabled
)) {
745 Document
* doc
= aContent
->GetComposedDoc();
746 if (PresShell
* rootPresShell
=
747 APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
749 if (Document
* rootDoc
= rootPresShell
->GetDocument()) {
750 nsIContent
* rootContent
=
751 rootPresShell
->GetRootScrollFrame()
752 ? rootPresShell
->GetRootScrollFrame()->GetContent()
753 : rootDoc
->GetDocumentElement();
754 // For the AccessibleCaret and other anonymous contents: disable APZ on
755 // any scrollable subframes that are not the root scrollframe of a
756 // document, if the document has any visible anonymous contents.
758 // If we find this is triggering in too many scenarios then we might
759 // want to tighten this check further. The main use cases for which we
760 // want to disable APZ as of this writing are listed in bug 1316318.
761 if (aContent
!= rootContent
&& HasVisibleAnonymousContents(rootDoc
)) {
771 if (PresShell
* presShell
= doc
->GetPresShell()) {
772 if (RefPtr
<AccessibleCaretEventHub
> eventHub
=
773 presShell
->GetAccessibleCaretEventHub()) {
774 // Disable APZ for all elements if AccessibleCaret tells us to do so.
775 if (eventHub
->ShouldDisableApz()) {
781 return StaticPrefs::apz_disable_for_scroll_linked_effects() &&
782 doc
->HasScrollLinkedEffect();
785 void nsLayoutUtils::NotifyPaintSkipTransaction(ViewID aScrollId
) {
786 if (nsIScrollableFrame
* scrollFrame
=
787 nsLayoutUtils::FindScrollableFrameFor(aScrollId
)) {
789 nsIFrame
* f
= do_QueryFrame(scrollFrame
);
790 MOZ_ASSERT(f
&& f
->PresShell() && !f
->PresShell()->IsResolutionUpdated());
792 scrollFrame
->NotifyApzTransaction();
796 nsContainerFrame
* nsLayoutUtils::LastContinuationWithChild(
797 nsContainerFrame
* aFrame
) {
798 MOZ_ASSERT(aFrame
, "NULL frame pointer");
799 for (auto f
= aFrame
->LastContinuation(); f
; f
= f
->GetPrevContinuation()) {
800 for (const auto& childList
: f
->ChildLists()) {
801 if (MOZ_LIKELY(!childList
.mList
.IsEmpty())) {
802 return static_cast<nsContainerFrame
*>(f
);
810 FrameChildListID
nsLayoutUtils::GetChildListNameFor(nsIFrame
* aChildFrame
) {
811 FrameChildListID id
= FrameChildListID::Principal
;
813 MOZ_DIAGNOSTIC_ASSERT(!aChildFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
));
815 if (aChildFrame
->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER
)) {
816 nsIFrame
* pif
= aChildFrame
->GetPrevInFlow();
817 if (pif
->GetParent() == aChildFrame
->GetParent()) {
818 id
= FrameChildListID::ExcessOverflowContainers
;
820 id
= FrameChildListID::OverflowContainers
;
823 LayoutFrameType childType
= aChildFrame
->Type();
824 if (LayoutFrameType::TableColGroup
== childType
) {
825 id
= FrameChildListID::ColGroup
;
826 } else if (aChildFrame
->IsTableCaption()) {
827 id
= FrameChildListID::Caption
;
829 id
= FrameChildListID::Principal
;
834 // Verify that the frame is actually in that child list or in the
835 // corresponding overflow list.
836 nsContainerFrame
* parent
= aChildFrame
->GetParent();
837 bool found
= parent
->GetChildList(id
).ContainsFrame(aChildFrame
);
839 found
= parent
->GetChildList(FrameChildListID::Overflow
)
840 .ContainsFrame(aChildFrame
);
841 MOZ_ASSERT(found
, "not in child list");
848 static Element
* GetPseudo(const nsIContent
* aContent
, nsAtom
* aPseudoProperty
) {
849 MOZ_ASSERT(aPseudoProperty
== nsGkAtoms::beforePseudoProperty
||
850 aPseudoProperty
== nsGkAtoms::afterPseudoProperty
||
851 aPseudoProperty
== nsGkAtoms::markerPseudoProperty
);
852 if (!aContent
->MayHaveAnonymousChildren()) {
855 return static_cast<Element
*>(aContent
->GetProperty(aPseudoProperty
));
859 Element
* nsLayoutUtils::GetBeforePseudo(const nsIContent
* aContent
) {
860 return GetPseudo(aContent
, nsGkAtoms::beforePseudoProperty
);
864 nsIFrame
* nsLayoutUtils::GetBeforeFrame(const nsIContent
* aContent
) {
865 Element
* pseudo
= GetBeforePseudo(aContent
);
866 return pseudo
? pseudo
->GetPrimaryFrame() : nullptr;
870 Element
* nsLayoutUtils::GetAfterPseudo(const nsIContent
* aContent
) {
871 return GetPseudo(aContent
, nsGkAtoms::afterPseudoProperty
);
875 nsIFrame
* nsLayoutUtils::GetAfterFrame(const nsIContent
* aContent
) {
876 Element
* pseudo
= GetAfterPseudo(aContent
);
877 return pseudo
? pseudo
->GetPrimaryFrame() : nullptr;
881 Element
* nsLayoutUtils::GetMarkerPseudo(const nsIContent
* aContent
) {
882 return GetPseudo(aContent
, nsGkAtoms::markerPseudoProperty
);
886 nsIFrame
* nsLayoutUtils::GetMarkerFrame(const nsIContent
* aContent
) {
887 Element
* pseudo
= GetMarkerPseudo(aContent
);
888 return pseudo
? pseudo
->GetPrimaryFrame() : nullptr;
892 void nsLayoutUtils::GetMarkerSpokenText(const nsIContent
* aContent
,
894 MOZ_ASSERT(aContent
&& aContent
->IsGeneratedContentContainerForMarker());
898 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
903 if (frame
->StyleContent()->ContentCount() > 0) {
904 for (nsIFrame
* child
: frame
->PrincipalChildList()) {
905 nsIFrame::RenderedText text
= child
->GetRenderedText();
906 aText
+= text
.mString
;
911 if (!frame
->StyleList()->mListStyleImage
.IsNone()) {
912 // ::marker is an image, so use default bullet character.
913 static const char16_t kDiscMarkerString
[] = {0x2022, ' ', 0};
914 aText
.AssignLiteral(kDiscMarkerString
);
920 ->GetContainStyleScopeManager()
921 .GetSpokenCounterText(frame
, aText
);
926 nsIFrame
* nsLayoutUtils::GetClosestFrameOfType(nsIFrame
* aFrame
,
927 LayoutFrameType aFrameType
,
929 for (nsIFrame
* frame
= aFrame
; frame
; frame
= frame
->GetParent()) {
930 if (frame
->Type() == aFrameType
) {
933 if (frame
== aStopAt
) {
941 nsIFrame
* nsLayoutUtils::GetPageFrame(nsIFrame
* aFrame
) {
942 return GetClosestFrameOfType(aFrame
, LayoutFrameType::Page
);
946 nsIFrame
* nsLayoutUtils::GetStyleFrame(nsIFrame
* aPrimaryFrame
) {
947 MOZ_ASSERT(aPrimaryFrame
);
948 if (aPrimaryFrame
->IsTableWrapperFrame()) {
949 nsIFrame
* inner
= aPrimaryFrame
->PrincipalChildList().FirstChild();
950 // inner may be null, if aPrimaryFrame is mid-destruction
954 return aPrimaryFrame
;
957 const nsIFrame
* nsLayoutUtils::GetStyleFrame(const nsIFrame
* aPrimaryFrame
) {
958 return nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame
*>(aPrimaryFrame
));
961 nsIFrame
* nsLayoutUtils::GetStyleFrame(const nsIContent
* aContent
) {
962 nsIFrame
* frame
= aContent
->GetPrimaryFrame();
967 return nsLayoutUtils::GetStyleFrame(frame
);
970 CSSIntCoord
nsLayoutUtils::UnthemedScrollbarSize(StyleScrollbarWidth aWidth
) {
972 case StyleScrollbarWidth::Auto
:
974 case StyleScrollbarWidth::Thin
:
976 case StyleScrollbarWidth::None
:
983 nsIFrame
* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(nsIFrame
* aStyleFrame
) {
984 nsIFrame
* parent
= aStyleFrame
->GetParent();
985 return parent
&& parent
->IsTableWrapperFrame() ? parent
: aStyleFrame
;
989 const nsIFrame
* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
990 const nsIFrame
* aStyleFrame
) {
991 return nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
992 const_cast<nsIFrame
*>(aStyleFrame
));
996 bool nsLayoutUtils::IsPrimaryStyleFrame(const nsIFrame
* aFrame
) {
997 if (aFrame
->IsTableWrapperFrame()) {
1001 const nsIFrame
* parent
= aFrame
->GetParent();
1002 if (parent
&& parent
->IsTableWrapperFrame()) {
1003 return parent
->PrincipalChildList().FirstChild() == aFrame
;
1006 return aFrame
->IsPrimaryFrame();
1009 nsIFrame
* nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame
* aFrame
) {
1010 NS_ASSERTION(aFrame
->IsPlaceholderFrame(), "Must have a placeholder here");
1011 if (aFrame
->HasAnyStateBits(PLACEHOLDER_FOR_FLOAT
)) {
1012 nsIFrame
* outOfFlowFrame
=
1013 nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame
);
1014 NS_ASSERTION(outOfFlowFrame
&& outOfFlowFrame
->IsFloating(),
1015 "How did that happen?");
1016 return outOfFlowFrame
;
1023 nsIFrame
* nsLayoutUtils::GetCrossDocParentFrameInProcess(
1024 const nsIFrame
* aFrame
, nsPoint
* aCrossDocOffset
) {
1025 nsIFrame
* p
= aFrame
->GetParent();
1030 nsView
* v
= aFrame
->GetView();
1034 v
= v
->GetParent(); // anonymous inner view
1038 v
= v
->GetParent(); // subdocumentframe's view
1044 if (p
&& aCrossDocOffset
) {
1045 nsSubDocumentFrame
* subdocumentFrame
= do_QueryFrame(p
);
1046 MOZ_ASSERT(subdocumentFrame
);
1047 *aCrossDocOffset
+= subdocumentFrame
->GetExtraOffset();
1054 nsIFrame
* nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame
* aFrame
,
1055 nsPoint
* aCrossDocOffset
) {
1056 return GetCrossDocParentFrameInProcess(aFrame
, aCrossDocOffset
);
1060 bool nsLayoutUtils::IsProperAncestorFrameCrossDoc(
1061 const nsIFrame
* aAncestorFrame
, const nsIFrame
* aFrame
,
1062 const nsIFrame
* aCommonAncestor
) {
1063 if (aFrame
== aAncestorFrame
) return false;
1064 return IsAncestorFrameCrossDoc(aAncestorFrame
, aFrame
, aCommonAncestor
);
1068 bool nsLayoutUtils::IsProperAncestorFrameCrossDocInProcess(
1069 const nsIFrame
* aAncestorFrame
, const nsIFrame
* aFrame
,
1070 const nsIFrame
* aCommonAncestor
) {
1071 if (aFrame
== aAncestorFrame
) return false;
1072 return IsAncestorFrameCrossDocInProcess(aAncestorFrame
, aFrame
,
1077 bool nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame
* aAncestorFrame
,
1078 const nsIFrame
* aFrame
,
1079 const nsIFrame
* aCommonAncestor
) {
1080 for (const nsIFrame
* f
= aFrame
; f
!= aCommonAncestor
;
1081 f
= GetCrossDocParentFrameInProcess(f
)) {
1082 if (f
== aAncestorFrame
) return true;
1084 return aCommonAncestor
== aAncestorFrame
;
1088 bool nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
1089 const nsIFrame
* aAncestorFrame
, const nsIFrame
* aFrame
,
1090 const nsIFrame
* aCommonAncestor
) {
1091 for (const nsIFrame
* f
= aFrame
; f
!= aCommonAncestor
;
1092 f
= GetCrossDocParentFrameInProcess(f
)) {
1093 if (f
== aAncestorFrame
) return true;
1095 return aCommonAncestor
== aAncestorFrame
;
1099 bool nsLayoutUtils::IsProperAncestorFrame(const nsIFrame
* aAncestorFrame
,
1100 const nsIFrame
* aFrame
,
1101 const nsIFrame
* aCommonAncestor
) {
1102 if (aFrame
== aAncestorFrame
) return false;
1103 for (const nsIFrame
* f
= aFrame
; f
!= aCommonAncestor
; f
= f
->GetParent()) {
1104 if (f
== aAncestorFrame
) return true;
1106 return aCommonAncestor
== aAncestorFrame
;
1110 nsIFrame
* nsLayoutUtils::FillAncestors(nsIFrame
* aFrame
,
1111 nsIFrame
* aStopAtAncestor
,
1112 nsTArray
<nsIFrame
*>* aAncestors
) {
1113 while (aFrame
&& aFrame
!= aStopAtAncestor
) {
1114 aAncestors
->AppendElement(aFrame
);
1115 aFrame
= nsLayoutUtils::GetParentOrPlaceholderFor(aFrame
);
1120 // Return true if aFrame1 is after aFrame2
1121 static bool IsFrameAfter(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
) {
1122 nsIFrame
* f
= aFrame2
;
1124 f
= f
->GetNextSibling();
1125 if (f
== aFrame1
) return true;
1131 int32_t nsLayoutUtils::DoCompareTreePosition(nsIFrame
* aFrame1
,
1133 nsIFrame
* aCommonAncestor
) {
1134 MOZ_ASSERT(aFrame1
, "aFrame1 must not be null");
1135 MOZ_ASSERT(aFrame2
, "aFrame2 must not be null");
1137 AutoTArray
<nsIFrame
*, 20> frame2Ancestors
;
1138 nsIFrame
* nonCommonAncestor
=
1139 FillAncestors(aFrame2
, aCommonAncestor
, &frame2Ancestors
);
1140 return DoCompareTreePosition(aFrame1
, aFrame2
, frame2Ancestors
,
1141 nonCommonAncestor
? aCommonAncestor
: nullptr);
1145 int32_t nsLayoutUtils::DoCompareTreePosition(
1146 nsIFrame
* aFrame1
, nsIFrame
* aFrame2
, nsTArray
<nsIFrame
*>& aFrame2Ancestors
,
1147 nsIFrame
* aCommonAncestor
) {
1148 MOZ_ASSERT(aFrame1
, "aFrame1 must not be null");
1149 MOZ_ASSERT(aFrame2
, "aFrame2 must not be null");
1151 nsPresContext
* presContext
= aFrame1
->PresContext();
1152 if (presContext
!= aFrame2
->PresContext()) {
1153 NS_ERROR("no common ancestor at all, different documents");
1157 AutoTArray
<nsIFrame
*, 20> frame1Ancestors
;
1158 if (aCommonAncestor
&&
1159 !FillAncestors(aFrame1
, aCommonAncestor
, &frame1Ancestors
)) {
1160 // We reached the root of the frame tree ... if aCommonAncestor was set,
1162 return DoCompareTreePosition(aFrame1
, aFrame2
, nullptr);
1165 int32_t last1
= int32_t(frame1Ancestors
.Length()) - 1;
1166 int32_t last2
= int32_t(aFrame2Ancestors
.Length()) - 1;
1167 while (last1
>= 0 && last2
>= 0 &&
1168 frame1Ancestors
[last1
] == aFrame2Ancestors
[last2
]) {
1175 NS_ASSERTION(aFrame1
== aFrame2
, "internal error?");
1178 // aFrame1 is an ancestor of aFrame2
1183 // aFrame2 is an ancestor of aFrame1
1187 nsIFrame
* ancestor1
= frame1Ancestors
[last1
];
1188 nsIFrame
* ancestor2
= aFrame2Ancestors
[last2
];
1189 // Now we should be able to walk sibling chains to find which one is first
1190 if (IsFrameAfter(ancestor2
, ancestor1
)) {
1193 if (IsFrameAfter(ancestor1
, ancestor2
)) {
1196 NS_WARNING("Frames were in different child lists???");
1201 nsIFrame
* nsLayoutUtils::GetLastSibling(nsIFrame
* aFrame
) {
1207 while ((next
= aFrame
->GetNextSibling()) != nullptr) {
1214 nsView
* nsLayoutUtils::FindSiblingViewFor(nsView
* aParentView
,
1216 nsIFrame
* parentViewFrame
= aParentView
->GetFrame();
1217 nsIContent
* parentViewContent
=
1218 parentViewFrame
? parentViewFrame
->GetContent() : nullptr;
1219 for (nsView
* insertBefore
= aParentView
->GetFirstChild(); insertBefore
;
1220 insertBefore
= insertBefore
->GetNextSibling()) {
1221 nsIFrame
* f
= insertBefore
->GetFrame();
1223 // this view could be some anonymous view attached to a meaningful parent
1224 for (nsView
* searchView
= insertBefore
->GetParent(); searchView
;
1225 searchView
= searchView
->GetParent()) {
1226 f
= searchView
->GetFrame();
1231 NS_ASSERTION(f
, "Can't find a frame anywhere!");
1233 if (!f
|| !aFrame
->GetContent() || !f
->GetContent() ||
1234 nsContentUtils::CompareTreePosition
<TreeKind::Flat
>(
1235 aFrame
->GetContent(), f
->GetContent(), parentViewContent
) > 0) {
1236 // aFrame's content is after f's content (or we just don't know),
1237 // so put our view before f's view
1238 return insertBefore
;
1245 nsIScrollableFrame
* nsLayoutUtils::GetScrollableFrameFor(
1246 const nsIFrame
* aScrolledFrame
) {
1247 nsIFrame
* frame
= aScrolledFrame
->GetParent();
1248 nsIScrollableFrame
* sf
= do_QueryFrame(frame
);
1249 return (sf
&& sf
->GetScrolledFrame() == aScrolledFrame
) ? sf
: nullptr;
1253 SideBits
nsLayoutUtils::GetSideBitsForFixedPositionContent(
1254 const nsIFrame
* aFixedPosFrame
) {
1255 SideBits sides
= SideBits::eNone
;
1256 if (aFixedPosFrame
) {
1257 const nsStylePosition
* position
= aFixedPosFrame
->StylePosition();
1258 if (!position
->mOffset
.Get(eSideRight
).IsAuto()) {
1259 sides
|= SideBits::eRight
;
1261 if (!position
->mOffset
.Get(eSideLeft
).IsAuto()) {
1262 sides
|= SideBits::eLeft
;
1264 if (!position
->mOffset
.Get(eSideBottom
).IsAuto()) {
1265 sides
|= SideBits::eBottom
;
1267 if (!position
->mOffset
.Get(eSideTop
).IsAuto()) {
1268 sides
|= SideBits::eTop
;
1274 ScrollableLayerGuid::ViewID
nsLayoutUtils::ScrollIdForRootScrollFrame(
1275 nsPresContext
* aPresContext
) {
1276 ViewID id
= ScrollableLayerGuid::NULL_SCROLL_ID
;
1277 if (nsIFrame
* rootScrollFrame
=
1278 aPresContext
->PresShell()->GetRootScrollFrame()) {
1279 if (nsIContent
* content
= rootScrollFrame
->GetContent()) {
1280 id
= FindOrCreateIDFor(content
);
1287 nsIScrollableFrame
* nsLayoutUtils::GetNearestScrollableFrameForDirection(
1288 nsIFrame
* aFrame
, ScrollDirections aDirections
) {
1290 aFrame
, "GetNearestScrollableFrameForDirection expects a non-null frame");
1291 // FIXME Bug 1714720 : This nearest scroll target is not going to work over
1292 // process boundaries, in such cases we need to hand over in APZ side.
1293 for (nsIFrame
* f
= aFrame
; f
;
1294 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
1295 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(f
);
1296 if (scrollableFrame
) {
1297 ScrollDirections directions
=
1298 scrollableFrame
->GetAvailableScrollingDirectionsForUserInputEvents();
1299 if (aDirections
.contains(ScrollDirection::eVertical
)) {
1300 if (directions
.contains(ScrollDirection::eVertical
)) {
1301 return scrollableFrame
;
1304 if (aDirections
.contains(ScrollDirection::eHorizontal
)) {
1305 if (directions
.contains(ScrollDirection::eHorizontal
)) {
1306 return scrollableFrame
;
1314 static nsIFrame
* GetNearestScrollableOrOverflowClipFrame(
1315 nsIFrame
* aFrame
, uint32_t aFlags
,
1316 const std::function
<bool(const nsIFrame
* aCurrentFrame
)>& aClipFrameCheck
=
1320 "GetNearestScrollableOrOverflowClipFrame expects a non-null frame");
1322 auto GetNextFrame
= [aFlags
](const nsIFrame
* aFrame
) -> nsIFrame
* {
1323 return (aFlags
& nsLayoutUtils::SCROLLABLE_SAME_DOC
)
1324 ? aFrame
->GetParent()
1325 : nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame
);
1328 for (nsIFrame
* f
= aFrame
; f
; f
= GetNextFrame(f
)) {
1329 if (aClipFrameCheck
&& aClipFrameCheck(f
)) {
1333 if ((aFlags
& nsLayoutUtils::SCROLLABLE_STOP_AT_PAGE
) && f
->IsPageFrame()) {
1336 if (nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(f
)) {
1337 if (aFlags
& nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE
) {
1338 if (scrollableFrame
->WantAsyncScroll()) {
1342 ScrollStyles ss
= scrollableFrame
->GetScrollStyles();
1343 if ((aFlags
& nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
) ||
1344 ss
.mVertical
!= StyleOverflow::Hidden
||
1345 ss
.mHorizontal
!= StyleOverflow::Hidden
) {
1349 if (aFlags
& nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
) {
1350 PresShell
* presShell
= f
->PresShell();
1351 if (presShell
->GetRootScrollFrame() == f
&& presShell
->GetDocument() &&
1352 presShell
->GetDocument()->IsRootDisplayDocument()) {
1357 if ((aFlags
& nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT
) &&
1358 f
->StyleDisplay()->mPosition
== StylePositionProperty::Fixed
&&
1359 nsLayoutUtils::IsReallyFixedPos(f
)) {
1360 return f
->PresShell()->GetRootScrollFrame();
1367 nsIScrollableFrame
* nsLayoutUtils::GetNearestScrollableFrame(nsIFrame
* aFrame
,
1369 nsIFrame
* found
= GetNearestScrollableOrOverflowClipFrame(aFrame
, aFlags
);
1374 return do_QueryFrame(found
);
1378 nsIFrame
* nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame
* aFrame
) {
1379 return GetNearestScrollableOrOverflowClipFrame(
1380 aFrame
, SCROLLABLE_SAME_DOC
| SCROLLABLE_INCLUDE_HIDDEN
,
1381 [](const nsIFrame
* currentFrame
) -> bool {
1382 // In cases of SVG Inner/Outer frames it basically clips descendants
1383 // unless overflow: visible is explicitly specified.
1384 LayoutFrameType type
= currentFrame
->Type();
1385 return ((type
== LayoutFrameType::SVGOuterSVG
||
1386 type
== LayoutFrameType::SVGInnerSVG
) &&
1387 (currentFrame
->StyleDisplay()->mOverflowX
!=
1388 StyleOverflow::Visible
&&
1389 currentFrame
->StyleDisplay()->mOverflowY
!=
1390 StyleOverflow::Visible
));
1395 nsRect
nsLayoutUtils::GetScrolledRect(nsIFrame
* aScrolledFrame
,
1396 const nsRect
& aScrolledFrameOverflowArea
,
1397 const nsSize
& aScrollPortSize
,
1398 StyleDirection aDirection
) {
1399 WritingMode wm
= aScrolledFrame
->GetWritingMode();
1400 // Potentially override the frame's direction to use the direction found
1401 // by nsHTMLScrollFrame::GetScrolledFrameDir()
1402 wm
.SetDirectionFromBidiLevel(aDirection
== StyleDirection::Rtl
1403 ? mozilla::intl::BidiEmbeddingLevel::RTL()
1404 : mozilla::intl::BidiEmbeddingLevel::LTR());
1406 nscoord x1
= aScrolledFrameOverflowArea
.x
,
1407 x2
= aScrolledFrameOverflowArea
.XMost(),
1408 y1
= aScrolledFrameOverflowArea
.y
,
1409 y2
= aScrolledFrameOverflowArea
.YMost();
1411 const bool isHorizontalWM
= !wm
.IsVertical();
1412 const bool isVerticalWM
= wm
.IsVertical();
1413 bool isInlineFlowFromTopOrLeft
= !wm
.IsInlineReversed();
1414 bool isBlockFlowFromTopOrLeft
= isHorizontalWM
|| wm
.IsVerticalLR();
1416 if (aScrolledFrame
->IsFlexContainerFrame()) {
1417 // In a flex container, the children flow (and overflow) along the flex
1418 // container's main axis and cross axis. These are analogous to the
1419 // inline/block axes, and by default they correspond exactly to those axes;
1420 // but the flex container's CSS (e.g. flex-direction: column-reverse) may
1421 // have swapped and/or reversed them, and we need to account for that here.
1422 FlexboxAxisInfo
info(aScrolledFrame
);
1423 if (info
.mIsRowOriented
) {
1424 // The flex container's inline axis is the main axis.
1425 isInlineFlowFromTopOrLeft
=
1426 isInlineFlowFromTopOrLeft
== !info
.mIsMainAxisReversed
;
1427 isBlockFlowFromTopOrLeft
=
1428 isBlockFlowFromTopOrLeft
== !info
.mIsCrossAxisReversed
;
1430 // The flex container's block axis is the main axis.
1431 isBlockFlowFromTopOrLeft
=
1432 isBlockFlowFromTopOrLeft
== !info
.mIsMainAxisReversed
;
1433 isInlineFlowFromTopOrLeft
=
1434 isInlineFlowFromTopOrLeft
== !info
.mIsCrossAxisReversed
;
1438 // Clamp the horizontal start-edge (x1 or x2, depending whether the logical
1439 // axis that corresponds to horizontal progresses from left-to-right or
1441 if ((isHorizontalWM
&& isInlineFlowFromTopOrLeft
) ||
1442 (isVerticalWM
&& isBlockFlowFromTopOrLeft
)) {
1447 if (x2
> aScrollPortSize
.width
) {
1448 x2
= aScrollPortSize
.width
;
1450 // When the scrolled frame chooses a size larger than its available width
1451 // (because its padding alone is larger than the available width), we need
1452 // to keep the start-edge of the scroll frame anchored to the start-edge of
1454 // When the scrolled frame is RTL, this means moving it in our left-based
1455 // coordinate system, so we need to compensate for its extra width here by
1456 // effectively repositioning the frame.
1457 nscoord extraWidth
=
1458 std::max(0, aScrolledFrame
->GetSize().width
- aScrollPortSize
.width
);
1462 // Similarly, clamp the vertical start-edge (y1 or y2, depending whether the
1463 // logical axis that corresponds to vertical progresses from top-to-bottom or
1465 if ((isHorizontalWM
&& isBlockFlowFromTopOrLeft
) ||
1466 (isVerticalWM
&& isInlineFlowFromTopOrLeft
)) {
1471 if (y2
> aScrollPortSize
.height
) {
1472 y2
= aScrollPortSize
.height
;
1474 nscoord extraHeight
=
1475 std::max(0, aScrolledFrame
->GetSize().height
- aScrollPortSize
.height
);
1479 return nsRect(x1
, y1
, x2
- x1
, y2
- y1
);
1483 bool nsLayoutUtils::HasPseudoStyle(nsIContent
* aContent
,
1484 ComputedStyle
* aComputedStyle
,
1485 PseudoStyleType aPseudoElement
,
1486 nsPresContext
* aPresContext
) {
1487 MOZ_ASSERT(aPresContext
, "Must have a prescontext");
1489 RefPtr
<ComputedStyle
> pseudoContext
;
1491 pseudoContext
= aPresContext
->StyleSet()->ProbePseudoElementStyle(
1492 *aContent
->AsElement(), aPseudoElement
, nullptr, aComputedStyle
);
1494 return pseudoContext
!= nullptr;
1497 nsPoint
nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(Event
* aDOMEvent
,
1499 if (!aDOMEvent
) return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1500 WidgetEvent
* event
= aDOMEvent
->WidgetEventPtr();
1501 if (!event
) return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1502 return GetEventCoordinatesRelativeTo(event
, RelativeTo
{aFrame
});
1505 static bool IsValidCoordinateTypeEvent(const WidgetEvent
* aEvent
) {
1509 return aEvent
->mClass
== eMouseEventClass
||
1510 aEvent
->mClass
== eMouseScrollEventClass
||
1511 aEvent
->mClass
== eWheelEventClass
||
1512 aEvent
->mClass
== eDragEventClass
||
1513 aEvent
->mClass
== eSimpleGestureEventClass
||
1514 aEvent
->mClass
== ePointerEventClass
||
1515 aEvent
->mClass
== eGestureNotifyEventClass
||
1516 aEvent
->mClass
== eTouchEventClass
||
1517 aEvent
->mClass
== eQueryContentEventClass
;
1520 nsPoint
nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent
* aEvent
,
1521 RelativeTo aFrame
) {
1522 if (!IsValidCoordinateTypeEvent(aEvent
)) {
1523 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1526 return GetEventCoordinatesRelativeTo(aEvent
, aEvent
->AsGUIEvent()->mRefPoint
,
1530 nsPoint
nsLayoutUtils::GetEventCoordinatesRelativeTo(
1531 const WidgetEvent
* aEvent
, const LayoutDeviceIntPoint
& aPoint
,
1532 RelativeTo aFrame
) {
1533 if (!aFrame
.mFrame
) {
1534 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1537 nsIWidget
* widget
= aEvent
->AsGUIEvent()->mWidget
;
1539 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1542 return GetEventCoordinatesRelativeTo(widget
, aPoint
, aFrame
);
1545 nsPoint
GetEventCoordinatesRelativeTo(nsIWidget
* aWidget
,
1546 const LayoutDeviceIntPoint
& aPoint
,
1547 RelativeTo aFrame
) {
1548 const nsIFrame
* frame
= aFrame
.mFrame
;
1549 if (!frame
|| !aWidget
) {
1550 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1553 nsView
* view
= frame
->GetView();
1555 nsIWidget
* frameWidget
= view
->GetWidget();
1556 if (frameWidget
&& frameWidget
== aWidget
) {
1557 // Special case this cause it happens a lot.
1558 // This also fixes bug 664707, events in the extra-special case of select
1559 // dropdown popups that are transformed.
1560 nsPresContext
* presContext
= frame
->PresContext();
1561 nsPoint
pt(presContext
->DevPixelsToAppUnits(aPoint
.x
),
1562 presContext
->DevPixelsToAppUnits(aPoint
.y
));
1563 return pt
- view
->ViewToWidgetOffset();
1567 /* If we walk up the frame tree and discover that any of the frames are
1568 * transformed, we need to do extra work to convert from the global
1569 * space to the local space.
1571 const nsIFrame
* rootFrame
= frame
;
1572 bool transformFound
= false;
1573 for (const nsIFrame
* f
= frame
; f
;
1574 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
1575 if (f
->IsTransformed() || ViewportUtils::IsZoomedContentRoot(f
)) {
1576 transformFound
= true;
1582 nsView
* rootView
= rootFrame
->GetView();
1584 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1587 nsPoint widgetToView
= nsLayoutUtils::TranslateWidgetToView(
1588 rootFrame
->PresContext(), aWidget
, aPoint
, rootView
);
1590 if (widgetToView
== nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
)) {
1591 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
1594 // Convert from root document app units to app units of the document aFrame
1596 int32_t rootAPD
= rootFrame
->PresContext()->AppUnitsPerDevPixel();
1597 int32_t localAPD
= frame
->PresContext()->AppUnitsPerDevPixel();
1598 widgetToView
= widgetToView
.ScaleToOtherAppUnits(rootAPD
, localAPD
);
1600 /* If we encountered a transform, we can't do simple arithmetic to figure
1601 * out how to convert back to aFrame's coordinates and must use the CTM.
1603 if (transformFound
|| frame
->IsInSVGTextSubtree()) {
1604 return nsLayoutUtils::TransformRootPointToFrame(ViewportType::Visual
,
1605 aFrame
, widgetToView
);
1608 /* Otherwise, all coordinate systems are translations of one another,
1609 * so we can just subtract out the difference.
1611 return widgetToView
- frame
->GetOffsetToCrossDoc(rootFrame
);
1614 nsPoint
nsLayoutUtils::GetEventCoordinatesRelativeTo(
1615 nsIWidget
* aWidget
, const LayoutDeviceIntPoint
& aPoint
, RelativeTo aFrame
) {
1616 nsPoint result
= ::GetEventCoordinatesRelativeTo(aWidget
, aPoint
, aFrame
);
1617 if (aFrame
.mViewportType
== ViewportType::Layout
&& aFrame
.mFrame
&&
1618 aFrame
.mFrame
->Type() == LayoutFrameType::Viewport
&&
1619 aFrame
.mFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
1620 result
= ViewportUtils::VisualToLayout(result
, aFrame
.mFrame
->PresShell());
1625 nsIFrame
* nsLayoutUtils::GetPopupFrameForEventCoordinates(
1626 nsPresContext
* aRootPresContext
, const WidgetEvent
* aEvent
) {
1627 if (!IsValidCoordinateTypeEvent(aEvent
)) {
1631 const auto* guiEvent
= aEvent
->AsGUIEvent();
1632 return GetPopupFrameForPoint(aRootPresContext
, guiEvent
->mWidget
,
1633 guiEvent
->mRefPoint
);
1636 nsIFrame
* nsLayoutUtils::GetPopupFrameForPoint(
1637 nsPresContext
* aRootPresContext
, nsIWidget
* aWidget
,
1638 const mozilla::LayoutDeviceIntPoint
& aPoint
,
1639 GetPopupFrameForPointFlags aFlags
/* = GetPopupFrameForPointFlags(0) */) {
1640 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1644 nsTArray
<nsIFrame
*> popups
;
1645 pm
->GetVisiblePopups(popups
);
1646 // Search from top to bottom
1647 for (nsIFrame
* popup
: popups
) {
1648 if (popup
->PresContext()->GetRootPresContext() != aRootPresContext
) {
1651 if (!popup
->ScrollableOverflowRect().Contains(GetEventCoordinatesRelativeTo(
1652 aWidget
, aPoint
, RelativeTo
{popup
}))) {
1655 if (aFlags
& GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets
) {
1656 if (!popup
->HasView() || !popup
->GetView()->HasWidget()) {
1665 void nsLayoutUtils::GetContainerAndOffsetAtEvent(PresShell
* aPresShell
,
1666 const WidgetEvent
* aEvent
,
1667 nsIContent
** aContainer
,
1669 MOZ_ASSERT(aContainer
|| aOffset
);
1672 *aContainer
= nullptr;
1682 aPresShell
->FlushPendingNotifications(FlushType::Layout
);
1684 RefPtr
<nsPresContext
> presContext
= aPresShell
->GetPresContext();
1689 nsIFrame
* targetFrame
= presContext
->EventStateManager()->GetEventTarget();
1694 WidgetEvent
* openingEvent
= nullptr;
1695 // For popupshowing events, redirect via the original mouse event
1696 // that triggered the popup to open.
1697 if (aEvent
->mMessage
== eXULPopupShowing
) {
1698 if (auto* pm
= nsXULPopupManager::GetInstance()) {
1699 if (Event
* openingPopupEvent
= pm
->GetOpeningPopupEvent()) {
1700 openingEvent
= openingPopupEvent
->WidgetEventPtr();
1705 nsPoint point
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
1706 openingEvent
? openingEvent
: aEvent
, RelativeTo
{targetFrame
});
1709 // TODO: This result may be useful to change to Selection. However, this
1710 // may return improper node (e.g., native anonymous node) for the
1711 // Selection. Perhaps, this should take Selection optionally and
1712 // if it's specified, needs to check if it's proper for the
1714 nsCOMPtr
<nsIContent
> container
=
1715 targetFrame
->GetContentOffsetsFromPoint(point
).content
;
1716 if (container
&& (!container
->ChromeOnlyAccess() ||
1717 nsContentUtils::CanAccessNativeAnon())) {
1718 container
.forget(aContainer
);
1722 *aOffset
= targetFrame
->GetContentOffsetsFromPoint(point
).offset
;
1726 void nsLayoutUtils::ConstrainToCoordValues(float& aStart
, float& aSize
) {
1727 MOZ_ASSERT(aSize
>= 0);
1729 // Here we try to make sure that the resulting nsRect will continue to cover
1730 // as much of the area that was covered by the original gfx Rect as possible.
1732 // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
1733 // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this
1735 float end
= aStart
+ aSize
;
1736 aStart
= clamped(aStart
, float(nscoord_MIN
), float(nscoord_MAX
));
1737 end
= clamped(end
, float(nscoord_MIN
), float(nscoord_MAX
));
1739 aSize
= end
- aStart
;
1741 // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
1742 // can't return a value greater than nscoord_MAX. If aSize is greater than
1743 // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
1745 if (MOZ_UNLIKELY(std::isnan(aSize
))) {
1746 // Can happen if aStart is -inf and aSize is +inf for example.
1748 aSize
= float(nscoord_MAX
);
1749 } else if (aSize
> float(nscoord_MAX
)) {
1750 float excess
= aSize
- float(nscoord_MAX
);
1753 aSize
= float(nscoord_MAX
);
1758 * Given a gfxFloat, constrains its value to be between nscoord_MIN and
1761 * @param aVal The value to constrain (in/out)
1763 static void ConstrainToCoordValues(gfxFloat
& aVal
) {
1764 if (aVal
<= nscoord_MIN
)
1766 else if (aVal
>= nscoord_MAX
)
1770 void nsLayoutUtils::ConstrainToCoordValues(gfxFloat
& aStart
, gfxFloat
& aSize
) {
1771 gfxFloat max
= aStart
+ aSize
;
1773 // Clamp the end points to within nscoord range
1774 ::ConstrainToCoordValues(aStart
);
1775 ::ConstrainToCoordValues(max
);
1777 aSize
= max
- aStart
;
1778 // If the width if still greater than the max nscoord, then bring both
1779 // endpoints in by the same amount until it fits.
1780 if (MOZ_UNLIKELY(std::isnan(aSize
))) {
1781 // Can happen if aStart is -inf and aSize is +inf for example.
1783 aSize
= nscoord_MAX
;
1784 } else if (aSize
> nscoord_MAX
) {
1785 gfxFloat excess
= aSize
- nscoord_MAX
;
1789 aSize
= nscoord_MAX
;
1790 } else if (aSize
< nscoord_MIN
) {
1791 gfxFloat excess
= aSize
- nscoord_MIN
;
1795 aSize
= nscoord_MIN
;
1799 nsRegion
nsLayoutUtils::RoundedRectIntersectRect(const nsRect
& aRoundedRect
,
1800 const nscoord aRadii
[8],
1801 const nsRect
& aContainedRect
) {
1802 // rectFullHeight and rectFullWidth together will approximately contain
1803 // the total area of the frame minus the rounded corners.
1804 nsRect rectFullHeight
= aRoundedRect
;
1805 nscoord xDiff
= std::max(aRadii
[eCornerTopLeftX
], aRadii
[eCornerBottomLeftX
]);
1806 rectFullHeight
.x
+= xDiff
;
1807 rectFullHeight
.width
-=
1808 std::max(aRadii
[eCornerTopRightX
], aRadii
[eCornerBottomRightX
]) + xDiff
;
1810 r1
.IntersectRect(rectFullHeight
, aContainedRect
);
1812 nsRect rectFullWidth
= aRoundedRect
;
1813 nscoord yDiff
= std::max(aRadii
[eCornerTopLeftY
], aRadii
[eCornerTopRightY
]);
1814 rectFullWidth
.y
+= yDiff
;
1815 rectFullWidth
.height
-=
1816 std::max(aRadii
[eCornerBottomLeftY
], aRadii
[eCornerBottomRightY
]) + yDiff
;
1818 r2
.IntersectRect(rectFullWidth
, aContainedRect
);
1825 nsIntRegion
nsLayoutUtils::RoundedRectIntersectIntRect(
1826 const nsIntRect
& aRoundedRect
, const RectCornerRadii
& aCornerRadii
,
1827 const nsIntRect
& aContainedRect
) {
1828 // rectFullHeight and rectFullWidth together will approximately contain
1829 // the total area of the frame minus the rounded corners.
1830 nsIntRect rectFullHeight
= aRoundedRect
;
1832 std::max(aCornerRadii
.TopLeft().width
, aCornerRadii
.BottomLeft().width
);
1833 rectFullHeight
.x
+= xDiff
;
1834 rectFullHeight
.width
-= std::max(aCornerRadii
.TopRight().width
,
1835 aCornerRadii
.BottomRight().width
) +
1838 r1
.IntersectRect(rectFullHeight
, aContainedRect
);
1840 nsIntRect rectFullWidth
= aRoundedRect
;
1842 std::max(aCornerRadii
.TopLeft().height
, aCornerRadii
.TopRight().height
);
1843 rectFullWidth
.y
+= yDiff
;
1844 rectFullWidth
.height
-= std::max(aCornerRadii
.BottomLeft().height
,
1845 aCornerRadii
.BottomRight().height
) +
1848 r2
.IntersectRect(rectFullWidth
, aContainedRect
);
1855 // Helper for RoundedRectIntersectsRect.
1856 static bool CheckCorner(nscoord aXOffset
, nscoord aYOffset
, nscoord aXRadius
,
1858 MOZ_ASSERT(aXOffset
> 0 && aYOffset
> 0,
1859 "must not pass nonpositives to CheckCorner");
1860 MOZ_ASSERT(aXRadius
>= 0 && aYRadius
>= 0,
1861 "must not pass negatives to CheckCorner");
1863 // Avoid floating point math unless we're either (1) within the
1864 // quarter-ellipse area at the rounded corner or (2) outside the
1866 if (aXOffset
>= aXRadius
|| aYOffset
>= aYRadius
) return true;
1868 // Convert coordinates to a unit circle with (0,0) as the center of
1869 // curvature, and see if we're inside the circle or outside.
1870 float scaledX
= float(aXRadius
- aXOffset
) / float(aXRadius
);
1871 float scaledY
= float(aYRadius
- aYOffset
) / float(aYRadius
);
1872 return scaledX
* scaledX
+ scaledY
* scaledY
< 1.0f
;
1875 bool nsLayoutUtils::RoundedRectIntersectsRect(const nsRect
& aRoundedRect
,
1876 const nscoord aRadii
[8],
1877 const nsRect
& aTestRect
) {
1878 if (!aTestRect
.Intersects(aRoundedRect
)) return false;
1880 // distances from this edge of aRoundedRect to opposite edge of aTestRect,
1881 // which we know are positive due to the Intersects check above.
1883 insets
.top
= aTestRect
.YMost() - aRoundedRect
.y
;
1884 insets
.right
= aRoundedRect
.XMost() - aTestRect
.x
;
1885 insets
.bottom
= aRoundedRect
.YMost() - aTestRect
.y
;
1886 insets
.left
= aTestRect
.XMost() - aRoundedRect
.x
;
1888 // Check whether the bottom-right corner of aTestRect is inside the
1889 // top left corner of aBounds when rounded by aRadii, etc. If any
1890 // corner is not, then fail; otherwise succeed.
1891 return CheckCorner(insets
.left
, insets
.top
, aRadii
[eCornerTopLeftX
],
1892 aRadii
[eCornerTopLeftY
]) &&
1893 CheckCorner(insets
.right
, insets
.top
, aRadii
[eCornerTopRightX
],
1894 aRadii
[eCornerTopRightY
]) &&
1895 CheckCorner(insets
.right
, insets
.bottom
, aRadii
[eCornerBottomRightX
],
1896 aRadii
[eCornerBottomRightY
]) &&
1897 CheckCorner(insets
.left
, insets
.bottom
, aRadii
[eCornerBottomLeftX
],
1898 aRadii
[eCornerBottomLeftY
]);
1901 nsRect
nsLayoutUtils::MatrixTransformRect(const nsRect
& aBounds
,
1902 const Matrix4x4
& aMatrix
,
1905 RectDouble(NSAppUnitsToDoublePixels(aBounds
.x
, aFactor
),
1906 NSAppUnitsToDoublePixels(aBounds
.y
, aFactor
),
1907 NSAppUnitsToDoublePixels(aBounds
.width
, aFactor
),
1908 NSAppUnitsToDoublePixels(aBounds
.height
, aFactor
));
1910 RectDouble maxBounds
= RectDouble(
1911 double(nscoord_MIN
) / aFactor
* 0.5, double(nscoord_MIN
) / aFactor
* 0.5,
1912 double(nscoord_MAX
) / aFactor
, double(nscoord_MAX
) / aFactor
);
1914 image
= aMatrix
.TransformAndClipBounds(image
, maxBounds
);
1916 return RoundGfxRectToAppRect(ThebesRect(image
), aFactor
);
1919 nsRect
nsLayoutUtils::MatrixTransformRect(const nsRect
& aBounds
,
1920 const Matrix4x4Flagged
& aMatrix
,
1923 RectDouble(NSAppUnitsToDoublePixels(aBounds
.x
, aFactor
),
1924 NSAppUnitsToDoublePixels(aBounds
.y
, aFactor
),
1925 NSAppUnitsToDoublePixels(aBounds
.width
, aFactor
),
1926 NSAppUnitsToDoublePixels(aBounds
.height
, aFactor
));
1928 RectDouble maxBounds
= RectDouble(
1929 double(nscoord_MIN
) / aFactor
* 0.5, double(nscoord_MIN
) / aFactor
* 0.5,
1930 double(nscoord_MAX
) / aFactor
, double(nscoord_MAX
) / aFactor
);
1932 image
= aMatrix
.TransformAndClipBounds(image
, maxBounds
);
1934 return RoundGfxRectToAppRect(ThebesRect(image
), aFactor
);
1937 nsPoint
nsLayoutUtils::MatrixTransformPoint(const nsPoint
& aPoint
,
1938 const Matrix4x4
& aMatrix
,
1940 gfxPoint image
= gfxPoint(NSAppUnitsToFloatPixels(aPoint
.x
, aFactor
),
1941 NSAppUnitsToFloatPixels(aPoint
.y
, aFactor
));
1942 image
= aMatrix
.TransformPoint(image
);
1943 return nsPoint(NSFloatPixelsToAppUnits(float(image
.x
), aFactor
),
1944 NSFloatPixelsToAppUnits(float(image
.y
), aFactor
));
1947 void nsLayoutUtils::PostTranslate(Matrix4x4
& aTransform
, const nsPoint
& aOrigin
,
1948 float aAppUnitsPerPixel
, bool aRounded
) {
1950 Point3D(NSAppUnitsToFloatPixels(aOrigin
.x
, aAppUnitsPerPixel
),
1951 NSAppUnitsToFloatPixels(aOrigin
.y
, aAppUnitsPerPixel
), 0.0f
);
1953 gfxOrigin
.x
= NS_round(gfxOrigin
.x
);
1954 gfxOrigin
.y
= NS_round(gfxOrigin
.y
);
1956 aTransform
.PostTranslate(gfxOrigin
);
1959 bool nsLayoutUtils::ShouldSnapToGrid(const nsIFrame
* aFrame
) {
1960 return !aFrame
|| !aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
) ||
1961 aFrame
->IsSVGOuterSVGAnonChildFrame();
1964 Matrix4x4Flagged
nsLayoutUtils::GetTransformToAncestor(
1965 RelativeTo aFrame
, RelativeTo aAncestor
, uint32_t aFlags
,
1966 nsIFrame
** aOutAncestor
) {
1968 Matrix4x4Flagged ctm
;
1969 // Make sure we don't get an invalid combination of source and destination
1970 // RelativeTo values.
1971 MOZ_ASSERT(!(aFrame
.mViewportType
== ViewportType::Visual
&&
1972 aAncestor
.mViewportType
== ViewportType::Layout
));
1973 if (aFrame
== aAncestor
) {
1976 ctm
= aFrame
.mFrame
->GetTransformMatrix(aFrame
.mViewportType
, aAncestor
,
1978 if (!aFrame
.mFrame
->Combines3DTransformWithAncestors()) {
1981 while (parent
&& parent
!= aAncestor
.mFrame
&&
1982 (!(aFlags
& nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT
) ||
1983 (!parent
->IsStackingContext() &&
1984 !DisplayPortUtils::FrameHasDisplayPort(parent
)))) {
1985 nsIFrame
* cur
= parent
;
1986 ctm
= ctm
* cur
->GetTransformMatrix(aFrame
.mViewportType
, aAncestor
,
1988 if (!cur
->Combines3DTransformWithAncestors()) {
1993 *aOutAncestor
= parent
;
1998 MatrixScales
nsLayoutUtils::GetTransformToAncestorScale(
1999 const nsIFrame
* aFrame
) {
2000 Matrix4x4Flagged transform
= GetTransformToAncestor(
2002 RelativeTo
{nsLayoutUtils::GetDisplayRootFrame(aFrame
)});
2004 if (transform
.CanDraw2D(&transform2D
)) {
2005 return ThebesMatrix(transform2D
).ScaleFactors().ConvertTo
<float>();
2007 return MatrixScales();
2010 static Matrix4x4Flagged
GetTransformToAncestorExcludingAnimated(
2011 nsIFrame
* aFrame
, const nsIFrame
* aAncestor
) {
2013 Matrix4x4Flagged ctm
;
2014 if (aFrame
== aAncestor
) {
2017 if (ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame
)) {
2020 ctm
= aFrame
->GetTransformMatrix(ViewportType::Layout
, RelativeTo
{aAncestor
},
2022 while (parent
&& parent
!= aAncestor
) {
2023 if (ActiveLayerTracker::IsScaleSubjectToAnimation(parent
)) {
2024 return Matrix4x4Flagged();
2026 if (!parent
->Extend3DContext()) {
2029 ctm
= ctm
* parent
->GetTransformMatrix(ViewportType::Layout
,
2030 RelativeTo
{aAncestor
}, &parent
);
2035 MatrixScales
nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(
2037 Matrix4x4Flagged transform
= GetTransformToAncestorExcludingAnimated(
2038 aFrame
, nsLayoutUtils::GetDisplayRootFrame(aFrame
));
2040 if (transform
.Is2D(&transform2D
)) {
2041 return ThebesMatrix(transform2D
).ScaleFactors().ConvertTo
<float>();
2043 return MatrixScales();
2046 const nsIFrame
* nsLayoutUtils::FindNearestCommonAncestorFrame(
2047 const nsIFrame
* aFrame1
, const nsIFrame
* aFrame2
) {
2048 AutoTArray
<const nsIFrame
*, 100> ancestors1
;
2049 AutoTArray
<const nsIFrame
*, 100> ancestors2
;
2050 const nsIFrame
* commonAncestor
= nullptr;
2051 if (aFrame1
->PresContext() == aFrame2
->PresContext()) {
2052 commonAncestor
= aFrame1
->PresShell()->GetRootFrame();
2054 for (const nsIFrame
* f
= aFrame1
; f
!= commonAncestor
;
2055 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
2056 ancestors1
.AppendElement(f
);
2058 for (const nsIFrame
* f
= aFrame2
; f
!= commonAncestor
;
2059 f
= nsLayoutUtils::GetCrossDocParentFrameInProcess(f
)) {
2060 ancestors2
.AppendElement(f
);
2062 uint32_t minLengths
= std::min(ancestors1
.Length(), ancestors2
.Length());
2063 for (uint32_t i
= 1; i
<= minLengths
; ++i
) {
2064 if (ancestors1
[ancestors1
.Length() - i
] ==
2065 ancestors2
[ancestors2
.Length() - i
]) {
2066 commonAncestor
= ancestors1
[ancestors1
.Length() - i
];
2071 return commonAncestor
;
2074 const nsIFrame
* nsLayoutUtils::FindNearestCommonAncestorFrameWithinBlock(
2075 const nsTextFrame
* aFrame1
, const nsTextFrame
* aFrame2
) {
2076 MOZ_ASSERT(aFrame1
);
2077 MOZ_ASSERT(aFrame2
);
2079 const nsIFrame
* f1
= aFrame1
;
2080 const nsIFrame
* f2
= aFrame2
;
2085 for (auto f
= f1
->GetParent();;) {
2086 NS_ASSERTION(f
, "All text frames should have a block ancestor");
2090 if (f
->IsBlockFrameOrSubclass()) {
2097 for (auto f
= f2
->GetParent();;) {
2098 NS_ASSERTION(f
, "All text frames should have a block ancestor");
2102 if (f
->IsBlockFrameOrSubclass()) {
2115 f2
= f2
->GetParent();
2123 f1
= f1
->GetParent();
2124 f2
= f2
->GetParent();
2131 bool nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(
2132 StyleAppearance aAppearance
) {
2133 return aAppearance
== StyleAppearance::NumberInput
||
2134 aAppearance
== StyleAppearance::Button
||
2135 aAppearance
== StyleAppearance::Textfield
||
2136 aAppearance
== StyleAppearance::Textarea
||
2137 aAppearance
== StyleAppearance::Listbox
||
2138 aAppearance
== StyleAppearance::Menulist
||
2139 aAppearance
== StyleAppearance::MenulistButton
;
2142 static SVGTextFrame
* GetContainingSVGTextFrame(const nsIFrame
* aFrame
) {
2143 if (!aFrame
->IsInSVGTextSubtree()) {
2147 return static_cast<SVGTextFrame
*>(nsLayoutUtils::GetClosestFrameOfType(
2148 aFrame
->GetParent(), LayoutFrameType::SVGText
));
2151 static bool TransformGfxPointFromAncestor(RelativeTo aFrame
,
2152 const Point
& aPoint
,
2153 RelativeTo aAncestor
,
2154 Maybe
<Matrix4x4Flagged
>& aMatrixCache
,
2156 SVGTextFrame
* text
= GetContainingSVGTextFrame(aFrame
.mFrame
);
2158 if (!aMatrixCache
) {
2159 auto matrix
= nsLayoutUtils::GetTransformToAncestor(
2160 RelativeTo
{text
? text
: aFrame
.mFrame
, aFrame
.mViewportType
},
2162 if (matrix
.IsSingular()) {
2166 aMatrixCache
.emplace(matrix
);
2169 const Matrix4x4Flagged
& ctm
= *aMatrixCache
;
2170 Point4D point
= ctm
.ProjectPoint(aPoint
);
2171 if (!point
.HasPositiveWCoord()) {
2175 *aOut
= point
.As2DPoint();
2178 *aOut
= text
->TransformFramePointToTextChild(*aOut
, aFrame
.mFrame
);
2184 static Point
TransformGfxPointToAncestor(
2185 RelativeTo aFrame
, const Point
& aPoint
, RelativeTo aAncestor
,
2186 Maybe
<Matrix4x4Flagged
>& aMatrixCache
) {
2187 if (SVGTextFrame
* text
= GetContainingSVGTextFrame(aFrame
.mFrame
)) {
2189 text
->TransformFramePointFromTextChild(aPoint
, aFrame
.mFrame
);
2190 return TransformGfxPointToAncestor(RelativeTo
{text
}, result
, aAncestor
,
2193 if (!aMatrixCache
) {
2194 aMatrixCache
.emplace(
2195 nsLayoutUtils::GetTransformToAncestor(aFrame
, aAncestor
));
2197 return aMatrixCache
->ProjectPoint(aPoint
).As2DPoint();
2200 static Rect
TransformGfxRectToAncestor(
2201 RelativeTo aFrame
, const Rect
& aRect
, RelativeTo aAncestor
,
2202 bool* aPreservesAxisAlignedRectangles
= nullptr,
2203 Maybe
<Matrix4x4Flagged
>* aMatrixCache
= nullptr,
2204 bool aStopAtStackingContextAndDisplayPortAndOOFFrame
= false,
2205 nsIFrame
** aOutAncestor
= nullptr) {
2207 Matrix4x4Flagged ctm
;
2208 if (SVGTextFrame
* text
= GetContainingSVGTextFrame(aFrame
.mFrame
)) {
2209 result
= text
->TransformFrameRectFromTextChild(aRect
, aFrame
.mFrame
);
2211 result
= TransformGfxRectToAncestor(
2212 RelativeTo
{text
}, result
, aAncestor
, nullptr, aMatrixCache
,
2213 aStopAtStackingContextAndDisplayPortAndOOFFrame
, aOutAncestor
);
2214 if (aPreservesAxisAlignedRectangles
) {
2215 // TransformFrameRectFromTextChild could involve any kind of transform, we
2216 // could drill down into it to get an answer out of it but we don't yet.
2217 *aPreservesAxisAlignedRectangles
= false;
2221 if (aMatrixCache
&& *aMatrixCache
) {
2222 // We are given a matrix to use, so use it
2223 ctm
= aMatrixCache
->value();
2227 if (aStopAtStackingContextAndDisplayPortAndOOFFrame
) {
2228 flags
|= nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT
;
2230 ctm
= nsLayoutUtils::GetTransformToAncestor(aFrame
, aAncestor
, flags
,
2233 // and put it in the cache, if provided
2234 *aMatrixCache
= Some(ctm
);
2237 // Fill out the axis-alignment flag
2238 if (aPreservesAxisAlignedRectangles
) {
2239 // TransformFrameRectFromTextChild could involve any kind of transform, we
2240 // could drill down into it to get an answer out of it but we don't yet.
2242 *aPreservesAxisAlignedRectangles
=
2243 ctm
.Is2D(&matrix2d
) && matrix2d
.PreservesAxisAlignedRectangles();
2245 const nsIFrame
* ancestor
= aOutAncestor
? *aOutAncestor
: aAncestor
.mFrame
;
2246 float factor
= ancestor
->PresContext()->AppUnitsPerDevPixel();
2248 Rect(float(nscoord_MIN
) / factor
* 0.5, float(nscoord_MIN
) / factor
* 0.5,
2249 float(nscoord_MAX
) / factor
, float(nscoord_MAX
) / factor
);
2250 return ctm
.TransformAndClipBounds(aRect
, maxBounds
);
2253 nsLayoutUtils::TransformResult
nsLayoutUtils::TransformPoints(
2254 RelativeTo aFromFrame
, RelativeTo aToFrame
, uint32_t aPointCount
,
2255 CSSPoint
* aPoints
) {
2256 // Conceptually, {ViewportFrame, Visual} is an ancestor of
2257 // {ViewportFrame, Layout}, so factor that into the nearest ancestor
2259 RelativeTo nearestCommonAncestor
{
2260 FindNearestCommonAncestorFrame(aFromFrame
.mFrame
, aToFrame
.mFrame
),
2261 aFromFrame
.mViewportType
== ViewportType::Visual
||
2262 aToFrame
.mViewportType
== ViewportType::Visual
2263 ? ViewportType::Visual
2264 : ViewportType::Layout
};
2265 if (!nearestCommonAncestor
.mFrame
) {
2266 return NO_COMMON_ANCESTOR
;
2268 CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame
=
2269 aFromFrame
.mFrame
->PresContext()->CSSToDevPixelScale();
2270 CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame
=
2271 aToFrame
.mFrame
->PresContext()->CSSToDevPixelScale();
2272 Maybe
<Matrix4x4Flagged
> cacheTo
;
2273 Maybe
<Matrix4x4Flagged
> cacheFrom
;
2274 for (uint32_t i
= 0; i
< aPointCount
; ++i
) {
2275 LayoutDevicePoint devPixels
= aPoints
[i
] * devPixelsPerCSSPixelFromFrame
;
2276 // What should the behaviour be if some of the points aren't invertible
2277 // and others are? Just assume all points are for now.
2279 TransformGfxPointToAncestor(aFromFrame
, Point(devPixels
.x
, devPixels
.y
),
2280 nearestCommonAncestor
, cacheTo
);
2282 if (!TransformGfxPointFromAncestor(
2283 aToFrame
, toDevPixels
, nearestCommonAncestor
, cacheFrom
, &result
)) {
2284 return NONINVERTIBLE_TRANSFORM
;
2286 // Divide here so that when the devPixelsPerCSSPixels are the same, we get
2287 // the correct answer instead of some inaccuracy multiplying a number by its
2290 LayoutDevicePoint(result
.x
, result
.y
) / devPixelsPerCSSPixelToFrame
;
2292 return TRANSFORM_SUCCEEDED
;
2295 nsLayoutUtils::TransformResult
nsLayoutUtils::TransformPoint(
2296 RelativeTo aFromFrame
, RelativeTo aToFrame
, nsPoint
& aPoint
) {
2297 CSSPoint point
= CSSPoint::FromAppUnits(aPoint
);
2298 auto result
= TransformPoints(aFromFrame
, aToFrame
, 1, &point
);
2299 if (result
== TRANSFORM_SUCCEEDED
) {
2300 aPoint
= CSSPoint::ToAppUnits(point
);
2305 nsLayoutUtils::TransformResult
nsLayoutUtils::TransformRect(
2306 const nsIFrame
* aFromFrame
, const nsIFrame
* aToFrame
, nsRect
& aRect
) {
2307 const nsIFrame
* nearestCommonAncestor
=
2308 FindNearestCommonAncestorFrame(aFromFrame
, aToFrame
);
2309 if (!nearestCommonAncestor
) {
2310 return NO_COMMON_ANCESTOR
;
2312 Matrix4x4Flagged downToDest
= GetTransformToAncestor(
2313 RelativeTo
{aToFrame
}, RelativeTo
{nearestCommonAncestor
});
2314 if (downToDest
.IsSingular()) {
2315 return NONINVERTIBLE_TRANSFORM
;
2317 downToDest
.Invert();
2318 aRect
= TransformFrameRectToAncestor(aFromFrame
, aRect
,
2319 RelativeTo
{nearestCommonAncestor
});
2321 float devPixelsPerAppUnitFromFrame
=
2322 1.0f
/ nearestCommonAncestor
->PresContext()->AppUnitsPerDevPixel();
2323 float devPixelsPerAppUnitToFrame
=
2324 1.0f
/ aToFrame
->PresContext()->AppUnitsPerDevPixel();
2325 gfx::Rect toDevPixels
= downToDest
.ProjectRectBounds(
2326 gfx::Rect(aRect
.x
* devPixelsPerAppUnitFromFrame
,
2327 aRect
.y
* devPixelsPerAppUnitFromFrame
,
2328 aRect
.width
* devPixelsPerAppUnitFromFrame
,
2329 aRect
.height
* devPixelsPerAppUnitFromFrame
),
2330 Rect(-std::numeric_limits
<Float
>::max() * devPixelsPerAppUnitFromFrame
*
2332 -std::numeric_limits
<Float
>::max() * devPixelsPerAppUnitFromFrame
*
2334 std::numeric_limits
<Float
>::max() * devPixelsPerAppUnitFromFrame
,
2335 std::numeric_limits
<Float
>::max() * devPixelsPerAppUnitFromFrame
));
2336 aRect
.x
= NSToCoordRoundWithClamp(toDevPixels
.x
/ devPixelsPerAppUnitToFrame
);
2337 aRect
.y
= NSToCoordRoundWithClamp(toDevPixels
.y
/ devPixelsPerAppUnitToFrame
);
2339 NSToCoordRoundWithClamp(toDevPixels
.width
/ devPixelsPerAppUnitToFrame
);
2341 NSToCoordRoundWithClamp(toDevPixels
.height
/ devPixelsPerAppUnitToFrame
);
2342 return TRANSFORM_SUCCEEDED
;
2345 nsRect
nsLayoutUtils::GetRectRelativeToFrame(Element
* aElement
,
2347 if (!aElement
|| !aFrame
) {
2351 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
2356 nsRect rect
= frame
->GetRectRelativeToSelf();
2357 nsLayoutUtils::TransformResult rv
=
2358 nsLayoutUtils::TransformRect(frame
, aFrame
, rect
);
2359 if (rv
!= nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
2366 bool nsLayoutUtils::ContainsPoint(const nsRect
& aRect
, const nsPoint
& aPoint
,
2367 nscoord aInflateSize
) {
2368 nsRect rect
= aRect
;
2369 rect
.Inflate(aInflateSize
);
2370 return rect
.Contains(aPoint
);
2373 nsRect
nsLayoutUtils::ClampRectToScrollFrames(nsIFrame
* aFrame
,
2374 const nsRect
& aRect
) {
2375 nsIFrame
* closestScrollFrame
=
2376 nsLayoutUtils::GetClosestFrameOfType(aFrame
, LayoutFrameType::Scroll
);
2378 nsRect resultRect
= aRect
;
2380 while (closestScrollFrame
) {
2381 nsIScrollableFrame
* sf
= do_QueryFrame(closestScrollFrame
);
2383 nsRect scrollPortRect
= sf
->GetScrollPortRect();
2384 nsLayoutUtils::TransformRect(closestScrollFrame
, aFrame
, scrollPortRect
);
2386 resultRect
= resultRect
.Intersect(scrollPortRect
);
2388 // Check whether aRect is visible in the scroll frame or not.
2389 if (resultRect
.IsEmpty()) {
2393 // Get next ancestor scroll frame.
2394 closestScrollFrame
= nsLayoutUtils::GetClosestFrameOfType(
2395 closestScrollFrame
->GetParent(), LayoutFrameType::Scroll
);
2401 nsPoint
nsLayoutUtils::TransformAncestorPointToFrame(RelativeTo aFrame
,
2402 const nsPoint
& aPoint
,
2403 RelativeTo aAncestor
) {
2404 float factor
= aFrame
.mFrame
->PresContext()->AppUnitsPerDevPixel();
2405 Point
result(NSAppUnitsToFloatPixels(aPoint
.x
, factor
),
2406 NSAppUnitsToFloatPixels(aPoint
.y
, factor
));
2408 Maybe
<Matrix4x4Flagged
> matrixCache
;
2409 if (!TransformGfxPointFromAncestor(aFrame
, result
, aAncestor
, matrixCache
,
2411 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2414 return nsPoint(NSFloatPixelsToAppUnits(float(result
.x
), factor
),
2415 NSFloatPixelsToAppUnits(float(result
.y
), factor
));
2418 nsRect
nsLayoutUtils::TransformFrameRectToAncestor(
2419 const nsIFrame
* aFrame
, const nsRect
& aRect
, RelativeTo aAncestor
,
2420 bool* aPreservesAxisAlignedRectangles
/* = nullptr */,
2421 Maybe
<Matrix4x4Flagged
>* aMatrixCache
/* = nullptr */,
2422 bool aStopAtStackingContextAndDisplayPortAndOOFFrame
/* = false */,
2423 nsIFrame
** aOutAncestor
/* = nullptr */) {
2424 MOZ_ASSERT(IsAncestorFrameCrossDocInProcess(aAncestor
.mFrame
, aFrame
),
2426 float srcAppUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
2427 Rect
result(NSAppUnitsToFloatPixels(aRect
.x
, srcAppUnitsPerDevPixel
),
2428 NSAppUnitsToFloatPixels(aRect
.y
, srcAppUnitsPerDevPixel
),
2429 NSAppUnitsToFloatPixels(aRect
.width
, srcAppUnitsPerDevPixel
),
2430 NSAppUnitsToFloatPixels(aRect
.height
, srcAppUnitsPerDevPixel
));
2431 result
= TransformGfxRectToAncestor(
2432 RelativeTo
{aFrame
}, result
, aAncestor
, aPreservesAxisAlignedRectangles
,
2433 aMatrixCache
, aStopAtStackingContextAndDisplayPortAndOOFFrame
,
2436 float destAppUnitsPerDevPixel
=
2437 aAncestor
.mFrame
->PresContext()->AppUnitsPerDevPixel();
2439 NSFloatPixelsToAppUnits(float(result
.x
), destAppUnitsPerDevPixel
),
2440 NSFloatPixelsToAppUnits(float(result
.y
), destAppUnitsPerDevPixel
),
2441 NSFloatPixelsToAppUnits(float(result
.width
), destAppUnitsPerDevPixel
),
2442 NSFloatPixelsToAppUnits(float(result
.height
), destAppUnitsPerDevPixel
));
2445 static LayoutDeviceIntPoint
GetWidgetOffset(nsIWidget
* aWidget
,
2446 nsIWidget
*& aRootWidget
) {
2447 LayoutDeviceIntPoint
offset(0, 0);
2448 while (aWidget
->GetWindowType() == widget::WindowType::Child
) {
2449 nsIWidget
* parent
= aWidget
->GetParent();
2453 LayoutDeviceIntRect bounds
= aWidget
->GetBounds();
2454 offset
+= bounds
.TopLeft();
2457 aRootWidget
= aWidget
;
2461 LayoutDeviceIntPoint
nsLayoutUtils::WidgetToWidgetOffset(nsIWidget
* aFrom
,
2463 nsIWidget
* fromRoot
;
2464 LayoutDeviceIntPoint fromOffset
= GetWidgetOffset(aFrom
, fromRoot
);
2466 LayoutDeviceIntPoint toOffset
= GetWidgetOffset(aTo
, toRoot
);
2468 if (fromRoot
!= toRoot
) {
2469 fromOffset
= aFrom
->WidgetToScreenOffset();
2470 toOffset
= aTo
->WidgetToScreenOffset();
2472 return fromOffset
- toOffset
;
2475 nsPoint
nsLayoutUtils::TranslateWidgetToView(nsPresContext
* aPresContext
,
2477 const LayoutDeviceIntPoint
& aPt
,
2480 nsIWidget
* viewWidget
= aView
->GetNearestWidget(&viewOffset
);
2482 return nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2485 LayoutDeviceIntPoint widgetPoint
=
2486 aPt
+ WidgetToWidgetOffset(aWidget
, viewWidget
);
2487 nsPoint
widgetAppUnits(aPresContext
->DevPixelsToAppUnits(widgetPoint
.x
),
2488 aPresContext
->DevPixelsToAppUnits(widgetPoint
.y
));
2489 return widgetAppUnits
- viewOffset
;
2492 LayoutDeviceIntPoint
nsLayoutUtils::TranslateViewToWidget(
2493 nsPresContext
* aPresContext
, nsView
* aView
, nsPoint aPt
,
2494 ViewportType aViewportType
, nsIWidget
* aWidget
) {
2496 nsIWidget
* viewWidget
= aView
->GetNearestWidget(&viewOffset
);
2498 return LayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
2501 nsPoint pt
= (aPt
+ viewOffset
);
2502 // The target coordinates are visual, so perform a layout-to-visual
2503 // conversion if the incoming coordinates are layout.
2504 if (aViewportType
== ViewportType::Layout
&& aPresContext
->GetPresShell()) {
2505 pt
= ViewportUtils::LayoutToVisual(pt
, aPresContext
->GetPresShell());
2507 LayoutDeviceIntPoint
relativeToViewWidget(
2508 aPresContext
->AppUnitsToDevPixels(pt
.x
),
2509 aPresContext
->AppUnitsToDevPixels(pt
.y
));
2510 return relativeToViewWidget
+ WidgetToWidgetOffset(viewWidget
, aWidget
);
2513 StyleClear
nsLayoutUtils::CombineClearType(StyleClear aOrigClearType
,
2514 StyleClear aNewClearType
) {
2515 StyleClear clearType
= aOrigClearType
;
2516 switch (clearType
) {
2517 case StyleClear::Left
:
2518 if (StyleClear::Right
== aNewClearType
||
2519 StyleClear::Both
== aNewClearType
) {
2520 clearType
= StyleClear::Both
;
2523 case StyleClear::Right
:
2524 if (StyleClear::Left
== aNewClearType
||
2525 StyleClear::Both
== aNewClearType
) {
2526 clearType
= StyleClear::Both
;
2529 case StyleClear::None
:
2530 if (StyleClear::Left
== aNewClearType
||
2531 StyleClear::Right
== aNewClearType
||
2532 StyleClear::Both
== aNewClearType
) {
2533 clearType
= aNewClearType
;
2536 case StyleClear::Both
:
2543 #ifdef MOZ_DUMP_PAINTING
2546 static bool gDumpEventList
= false;
2548 // nsLayoutUtils::PaintFrame() can call itself recursively, so rather than
2549 // maintaining a single paint count, we need a stack.
2550 StaticAutoPtr
<nsTArray
<int>> gPaintCountStack
;
2552 struct AutoNestedPaintCount
{
2553 AutoNestedPaintCount() { gPaintCountStack
->AppendElement(0); }
2554 ~AutoNestedPaintCount() { gPaintCountStack
->RemoveLastElement(); }
2559 nsIFrame
* nsLayoutUtils::GetFrameForPoint(
2560 RelativeTo aRelativeTo
, nsPoint aPt
, const FrameForPointOptions
& aOptions
) {
2561 AUTO_PROFILER_LABEL("nsLayoutUtils::GetFrameForPoint", LAYOUT
);
2564 AutoTArray
<nsIFrame
*, 8> outFrames
;
2565 rv
= GetFramesForArea(aRelativeTo
, nsRect(aPt
, nsSize(1, 1)), outFrames
,
2567 NS_ENSURE_SUCCESS(rv
, nullptr);
2568 return outFrames
.SafeElementAt(0);
2571 nsresult
nsLayoutUtils::GetFramesForArea(RelativeTo aRelativeTo
,
2572 const nsRect
& aRect
,
2573 nsTArray
<nsIFrame
*>& aOutFrames
,
2574 const FrameForPointOptions
& aOptions
) {
2575 AUTO_PROFILER_LABEL("nsLayoutUtils::GetFramesForArea", LAYOUT
);
2577 nsIFrame
* frame
= const_cast<nsIFrame
*>(aRelativeTo
.mFrame
);
2579 nsDisplayListBuilder
builder(frame
, nsDisplayListBuilderMode::EventDelivery
,
2581 builder
.BeginFrame();
2582 nsDisplayList
list(&builder
);
2584 if (aOptions
.mBits
.contains(FrameForPointOption::IgnorePaintSuppression
)) {
2585 builder
.IgnorePaintSuppression();
2587 if (aOptions
.mBits
.contains(FrameForPointOption::IgnoreRootScrollFrame
)) {
2588 nsIFrame
* rootScrollFrame
= frame
->PresShell()->GetRootScrollFrame();
2589 if (rootScrollFrame
) {
2590 builder
.SetIgnoreScrollFrame(rootScrollFrame
);
2593 if (aRelativeTo
.mViewportType
== ViewportType::Layout
) {
2594 builder
.SetIsRelativeToLayoutViewport();
2596 if (aOptions
.mBits
.contains(FrameForPointOption::IgnoreCrossDoc
)) {
2597 builder
.SetDescendIntoSubdocuments(false);
2600 if (aOptions
.mBits
.contains(FrameForPointOption::OnlyVisible
)) {
2601 builder
.SetHitTestIsForVisibility(aOptions
.mVisibleThreshold
);
2604 builder
.EnterPresShell(frame
);
2606 builder
.SetVisibleRect(aRect
);
2607 builder
.SetDirtyRect(aRect
);
2609 frame
->BuildDisplayListForStackingContext(&builder
, &list
);
2610 builder
.LeavePresShell(frame
, nullptr);
2612 #ifdef MOZ_DUMP_PAINTING
2613 if (gDumpEventList
) {
2614 fprintf_stderr(stderr
, "Event handling --- (%d,%d):\n", aRect
.x
, aRect
.y
);
2616 std::stringstream ss
;
2617 nsIFrame::PrintDisplayList(&builder
, list
, ss
);
2622 nsDisplayItem::HitTestState hitTestState
;
2623 list
.HitTest(&builder
, aRect
, &hitTestState
, &aOutFrames
);
2624 list
.DeleteAll(&builder
);
2629 mozilla::ParentLayerToScreenScale2D
2630 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
2631 const nsIFrame
* aFrame
) {
2632 ParentLayerToScreenScale2D transformToAncestorScale
=
2633 ViewAs
<ParentLayerToScreenScale2D
>(
2634 nsLayoutUtils::GetTransformToAncestorScale(aFrame
));
2636 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(aFrame
->PresShell())) {
2637 transformToAncestorScale
=
2638 ViewTargetAs
<ParentLayerPixel
>(
2639 transformToAncestorScale
,
2640 PixelCastJustification::PropagatingToChildProcess
) *
2641 browserChild
->GetEffectsInfo().mTransformToAncestorScale
;
2644 return transformToAncestorScale
;
2647 // aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrame
2648 FrameMetrics
nsLayoutUtils::CalculateBasicFrameMetrics(
2649 nsIScrollableFrame
* aScrollFrame
) {
2650 nsIFrame
* frame
= do_QueryFrame(aScrollFrame
);
2653 // Calculate the metrics necessary for calculating the displayport.
2654 // This code has a lot in common with the code in ComputeFrameMetrics();
2655 // we may want to refactor this at some point.
2656 FrameMetrics metrics
;
2657 nsPresContext
* presContext
= frame
->PresContext();
2658 PresShell
* presShell
= presContext
->PresShell();
2659 CSSToLayoutDeviceScale deviceScale
= presContext
->CSSToDevPixelScale();
2660 float resolution
= 1.0f
;
2661 bool isRcdRsf
= aScrollFrame
->IsRootScrollFrameOfDocument() &&
2662 presContext
->IsRootContentDocumentCrossProcess();
2663 metrics
.SetIsRootContent(isRcdRsf
);
2665 // Only the root content document's root scrollable frame should pick up
2666 // the presShell's resolution. All the other frames are 1.0.
2667 resolution
= presShell
->GetResolution();
2669 LayoutDeviceToLayerScale
cumulativeResolution(
2670 LayoutDeviceToLayerScale(presShell
->GetCumulativeResolution()));
2672 LayerToParentLayerScale
layerToParentLayerScale(1.0f
);
2673 metrics
.SetDevPixelsPerCSSPixel(deviceScale
);
2674 metrics
.SetPresShellResolution(resolution
);
2676 metrics
.SetTransformToAncestorScale(
2677 GetTransformToAncestorScaleCrossProcessForFrameMetrics(frame
));
2678 metrics
.SetCumulativeResolution(cumulativeResolution
);
2679 metrics
.SetZoom(deviceScale
* cumulativeResolution
* layerToParentLayerScale
);
2681 // Only the size of the composition bounds is relevant to the
2682 // displayport calculation, not its origin.
2683 nsSize compositionSize
=
2684 nsLayoutUtils::CalculateCompositionSizeForFrame(frame
);
2685 LayoutDeviceToParentLayerScale compBoundsScale
;
2686 if (frame
== presShell
->GetRootScrollFrame() &&
2687 presContext
->IsRootContentDocumentCrossProcess()) {
2688 if (presContext
->GetParentPresContext()) {
2689 float res
= presContext
->GetParentPresContext()
2691 ->GetCumulativeResolution();
2692 compBoundsScale
= LayoutDeviceToParentLayerScale(res
);
2695 compBoundsScale
= cumulativeResolution
* layerToParentLayerScale
;
2697 metrics
.SetCompositionBounds(
2698 LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize
),
2699 presContext
->AppUnitsPerDevPixel()) *
2702 metrics
.SetBoundingCompositionSize(
2703 nsLayoutUtils::CalculateBoundingCompositionSize(frame
, false, metrics
));
2705 metrics
.SetLayoutViewport(
2706 CSSRect::FromAppUnits(nsRect(aScrollFrame
->GetScrollPosition(),
2707 aScrollFrame
->GetScrollPortRect().Size())));
2708 metrics
.SetVisualScrollOffset(
2709 isRcdRsf
? CSSPoint::FromAppUnits(presShell
->GetVisualViewportOffset())
2710 : metrics
.GetLayoutViewport().TopLeft());
2712 metrics
.SetScrollableRect(CSSRect::FromAppUnits(
2713 nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrame
, nullptr)));
2718 nsIScrollableFrame
* nsLayoutUtils::GetAsyncScrollableAncestorFrame(
2719 nsIFrame
* aTarget
) {
2720 uint32_t flags
= nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
|
2721 nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE
|
2722 nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT
;
2723 return nsLayoutUtils::GetNearestScrollableFrame(aTarget
, flags
);
2726 void nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder
* aBuilder
,
2727 nsDisplayList
* aList
,
2729 const nsRect
& aCanvasArea
,
2730 const nsRegion
& aVisibleRegion
,
2731 nscolor aBackstop
) {
2732 if (aFrame
->IsPageFrame()) {
2733 // For printing, this function is first called on an nsPageFrame, which
2734 // creates a display list with a PageContent item. The PageContent item's
2735 // paint function calls this function on the nsPageFrame's child which is an
2736 // nsPageContentFrame. We only want to add the canvas background color item
2737 // once, for the nsPageContentFrame.
2740 // Add the canvas background color to the bottom of the list. This
2741 // happens after we've built the list so that AddCanvasBackgroundColorItem
2742 // can monkey with the contents if necessary.
2743 nsRect canvasArea
= aVisibleRegion
.GetBounds();
2744 canvasArea
.IntersectRect(aCanvasArea
, canvasArea
);
2745 nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(
2746 aBuilder
, aFrame
, canvasArea
, canvasArea
);
2747 aFrame
->PresShell()->AddCanvasBackgroundColorItem(aBuilder
, aList
, aFrame
,
2748 canvasArea
, aBackstop
);
2751 // #define PRINT_HITTESTINFO_STATS
2752 #ifdef PRINT_HITTESTINFO_STATS
2753 void PrintHitTestInfoStatsInternal(nsDisplayList
* aList
, int& aTotal
,
2754 int& aHitTest
, int& aVisible
,
2756 for (nsDisplayItem
* i
: *aList
) {
2759 if (i
->GetChildren()) {
2760 PrintHitTestInfoStatsInternal(i
->GetChildren(), aTotal
, aHitTest
,
2761 aVisible
, aSpecial
);
2764 if (i
->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO
) {
2767 const auto& hitTestInfo
= static_cast<nsDisplayCompositorHitTestInfo
*>(i
)
2771 if (hitTestInfo
.size() > 1) {
2776 if (hitTestInfo
== CompositorHitTestFlags::eVisibleToHitTest
) {
2786 void PrintHitTestInfoStats(nsDisplayList
* aList
) {
2792 PrintHitTestInfoStatsInternal(aList
, total
, hitTest
, visible
, special
);
2794 double ratio
= (double)hitTest
/ (double)total
;
2797 "List %p: total items: %d, hit test items: %d, ratio: %f, visible: %d, "
2799 aList
, total
, hitTest
, ratio
, visible
, special
);
2803 // Apply a batch of effects updates generated during a paint to their
2804 // respective remote browsers.
2805 static void ApplyEffectsUpdates(
2806 const nsTHashMap
<nsPtrHashKey
<RemoteBrowser
>, EffectsInfo
>& aUpdates
) {
2807 for (const auto& entry
: aUpdates
) {
2808 auto* browser
= entry
.GetKey();
2809 const auto& update
= entry
.GetData();
2810 browser
->UpdateEffects(update
);
2814 static void DumpBeforePaintDisplayList(UniquePtr
<std::stringstream
>& aStream
,
2815 nsDisplayListBuilder
* aBuilder
,
2816 nsDisplayList
* aList
,
2817 const nsRect
& aVisibleRect
) {
2818 #ifdef MOZ_DUMP_PAINTING
2819 if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
2820 nsCString
string("dump-");
2821 // Include the process ID in the dump file name, to make sure that in an
2822 // e10s setup different processes don't clobber each other's dump files.
2823 string
.AppendInt(getpid());
2824 for (int paintCount
: *gPaintCountStack
) {
2825 string
.AppendLiteral("-");
2826 string
.AppendInt(paintCount
);
2828 string
.AppendLiteral(".html");
2829 gfxUtils::sDumpPaintFile
= fopen(string
.BeginReading(), "w");
2831 gfxUtils::sDumpPaintFile
= stderr
;
2833 if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
2834 *aStream
<< "<html><head><script>\n"
2836 "function ViewImage(index) { \n"
2837 " var image = document.getElementById(index);\n"
2838 " if (image.src) {\n"
2839 " image.removeAttribute('src');\n"
2841 " image.src = array[index];\n"
2843 "}</script></head><body>";
2846 *aStream
<< nsPrintfCString(
2847 "Painting --- before optimization (dirty %d,%d,%d,%d):\n",
2848 aVisibleRect
.x
, aVisibleRect
.y
, aVisibleRect
.width
,
2849 aVisibleRect
.height
)
2851 nsIFrame::PrintDisplayList(aBuilder
, *aList
, *aStream
,
2852 gfxEnv::MOZ_DUMP_PAINT_TO_FILE());
2854 if (gfxEnv::MOZ_DUMP_PAINT() || gfxEnv::MOZ_DUMP_PAINT_ITEMS()) {
2855 // Flush stream now to avoid reordering dump output relative to
2856 // messages dumped by PaintRoot below.
2857 fprint_stderr(gfxUtils::sDumpPaintFile
, *aStream
);
2858 aStream
= MakeUnique
<std::stringstream
>();
2862 static void DumpAfterPaintDisplayList(UniquePtr
<std::stringstream
>& aStream
,
2863 nsDisplayListBuilder
* aBuilder
,
2864 nsDisplayList
* aList
) {
2865 *aStream
<< "Painting --- after optimization:\n";
2866 nsIFrame::PrintDisplayList(aBuilder
, *aList
, *aStream
,
2867 gfxEnv::MOZ_DUMP_PAINT_TO_FILE());
2869 fprint_stderr(gfxUtils::sDumpPaintFile
, *aStream
);
2871 #ifdef MOZ_DUMP_PAINTING
2872 if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
2873 *aStream
<< "</body></html>";
2875 if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
2876 fclose(gfxUtils::sDumpPaintFile
);
2880 std::stringstream lsStream
;
2881 nsIFrame::PrintDisplayList(aBuilder
, *aList
, lsStream
);
2884 struct TemporaryDisplayListBuilder
{
2885 TemporaryDisplayListBuilder(nsIFrame
* aFrame
,
2886 nsDisplayListBuilderMode aBuilderMode
,
2887 const bool aBuildCaret
)
2888 : mBuilder(aFrame
, aBuilderMode
, aBuildCaret
), mList(&mBuilder
) {}
2890 ~TemporaryDisplayListBuilder() { mList
.DeleteAll(&mBuilder
); }
2892 nsDisplayListBuilder mBuilder
;
2893 nsDisplayList mList
;
2894 RetainedDisplayListMetrics mMetrics
;
2897 void nsLayoutUtils::PaintFrame(gfxContext
* aRenderingContext
, nsIFrame
* aFrame
,
2898 const nsRegion
& aDirtyRegion
, nscolor aBackstop
,
2899 nsDisplayListBuilderMode aBuilderMode
,
2900 PaintFrameFlags aFlags
) {
2901 AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS
);
2903 // Create a static storage counter that is incremented on eacy entry to
2904 // PaintFrame and decremented on exit. We can use this later to determine if
2905 // this is a top-level paint.
2906 static uint32_t paintFrameDepth
= 0;
2909 #ifdef MOZ_DUMP_PAINTING
2910 if (!gPaintCountStack
) {
2911 gPaintCountStack
= new nsTArray
<int>();
2912 ClearOnShutdown(&gPaintCountStack
);
2914 gPaintCountStack
->AppendElement(0);
2916 ++gPaintCountStack
->LastElement();
2917 AutoNestedPaintCount nestedPaintCount
;
2920 nsIFrame
* displayRoot
= GetDisplayRootFrame(aFrame
);
2922 if (aFlags
& PaintFrameFlags::WidgetLayers
) {
2923 nsView
* view
= aFrame
->GetView();
2924 if (!(view
&& view
->GetWidget() && displayRoot
== aFrame
)) {
2925 aFlags
&= ~PaintFrameFlags::WidgetLayers
;
2926 NS_ASSERTION(aRenderingContext
, "need a rendering context");
2930 nsPresContext
* presContext
= aFrame
->PresContext();
2931 PresShell
* presShell
= presContext
->PresShell();
2933 TimeStamp startBuildDisplayList
= TimeStamp::Now();
2934 auto dlTimerId
= mozilla::glean::paint::build_displaylist_time
.Start();
2936 const bool buildCaret
= !(aFlags
& PaintFrameFlags::HideCaret
);
2938 // Note that isForPainting here does not include the PaintForPrinting builder
2939 // mode; that's OK because there is no point in using retained display lists
2940 // for a print destination.
2941 const bool isForPainting
= (aFlags
& PaintFrameFlags::WidgetLayers
) &&
2942 aBuilderMode
== nsDisplayListBuilderMode::Painting
;
2944 // Only allow retaining for painting when preffed on, and for root frames
2945 // (since the modified frame tracking is per-root-frame).
2946 const bool retainDisplayList
=
2947 isForPainting
&& AreRetainedDisplayListsEnabled() && !aFrame
->GetParent();
2949 RetainedDisplayListBuilder
* retainedBuilder
= nullptr;
2950 Maybe
<TemporaryDisplayListBuilder
> temporaryBuilder
;
2952 nsDisplayListBuilder
* builder
= nullptr;
2953 nsDisplayList
* list
= nullptr;
2954 RetainedDisplayListMetrics
* metrics
= nullptr;
2956 if (retainDisplayList
) {
2957 MOZ_ASSERT(aFrame
== displayRoot
);
2958 retainedBuilder
= aFrame
->GetProperty(RetainedDisplayListBuilder::Cached());
2959 if (!retainedBuilder
) {
2961 new RetainedDisplayListBuilder(aFrame
, aBuilderMode
, buildCaret
);
2962 aFrame
->SetProperty(RetainedDisplayListBuilder::Cached(),
2966 builder
= retainedBuilder
->Builder();
2967 list
= retainedBuilder
->List();
2968 metrics
= retainedBuilder
->Metrics();
2970 temporaryBuilder
.emplace(aFrame
, aBuilderMode
, buildCaret
);
2971 builder
= &temporaryBuilder
->mBuilder
;
2972 list
= &temporaryBuilder
->mList
;
2973 metrics
= &temporaryBuilder
->mMetrics
;
2976 MOZ_ASSERT(builder
&& list
&& metrics
);
2979 if (MOZ_LOG_TEST(GetLoggerByProcess(), LogLevel::Info
) ||
2980 MOZ_UNLIKELY(gfxUtils::DumpDisplayList()) ||
2981 MOZ_UNLIKELY(gfxEnv::MOZ_DUMP_PAINT())) {
2982 if (Document
* doc
= presContext
->Document()) {
2983 Unused
<< doc
->GetDocumentURI(uri
);
2987 nsAutoString frameName
, displayRootName
;
2988 #ifdef DEBUG_FRAME_DUMP
2989 if (MOZ_LOG_TEST(GetLoggerByProcess(), LogLevel::Info
)) {
2990 aFrame
->GetFrameName(frameName
);
2991 displayRoot
->GetFrameName(displayRootName
);
2995 DL_LOGI("PaintFrame: %p (%s), DisplayRoot: %p (%s), Builder: %p, URI: %s",
2996 aFrame
, NS_ConvertUTF16toUTF8(frameName
).get(), displayRoot
,
2997 NS_ConvertUTF16toUTF8(displayRootName
).get(), retainedBuilder
,
2998 NS_ConvertUTF16toUTF8(uri
).get());
3001 metrics
->StartBuild();
3003 builder
->BeginFrame();
3005 MOZ_ASSERT(paintFrameDepth
>= 1);
3006 // If this is a top-level paint, increment the paint sequence number.
3007 if (paintFrameDepth
== 1) {
3008 // Increment the paint sequence number for the display list builder.
3009 nsDisplayListBuilder::IncrementPaintSequenceNumber();
3012 if (aFlags
& PaintFrameFlags::InTransform
) {
3013 builder
->SetInTransform(true);
3015 if (aFlags
& PaintFrameFlags::SyncDecodeImages
) {
3016 builder
->SetSyncDecodeImages(true);
3018 if (aFlags
& (PaintFrameFlags::WidgetLayers
| PaintFrameFlags::ToWindow
)) {
3019 builder
->SetPaintingToWindow(true);
3021 if (aFlags
& PaintFrameFlags::UseHighQualityScaling
) {
3022 builder
->SetUseHighQualityScaling(true);
3024 if (aFlags
& PaintFrameFlags::ForWebRender
) {
3025 builder
->SetPaintingForWebRender(true);
3027 if (aFlags
& PaintFrameFlags::IgnoreSuppression
) {
3028 builder
->IgnorePaintSuppression();
3031 if (BrowsingContext
* bc
= presContext
->Document()->GetBrowsingContext()) {
3032 builder
->SetInActiveDocShell(bc
->IsActive());
3035 nsRect rootInkOverflow
= aFrame
->InkOverflowRectRelativeToSelf();
3037 // If the dynamic toolbar is completely collapsed, the visible rect should
3038 // be expanded to include this area.
3039 const bool hasDynamicToolbar
=
3040 presContext
->IsRootContentDocumentCrossProcess() &&
3041 presContext
->HasDynamicToolbar();
3042 if (hasDynamicToolbar
) {
3043 rootInkOverflow
.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
3044 presContext
, rootInkOverflow
.Size()));
3047 // If we are in a remote browser, then apply clipping from ancestor browsers
3048 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(presShell
)) {
3049 if (!browserChild
->IsTopLevel()) {
3050 const nsRect unscaledVisibleRect
=
3051 browserChild
->GetVisibleRect().valueOr(nsRect());
3052 rootInkOverflow
.IntersectRect(rootInkOverflow
, unscaledVisibleRect
);
3056 builder
->ClearHaveScrollableDisplayPort();
3057 if (builder
->IsPaintingToWindow() &&
3058 nsLayoutUtils::AsyncPanZoomEnabled(aFrame
)) {
3059 DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
3063 nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame();
3064 if (rootScrollFrame
&& !aFrame
->GetParent()) {
3065 nsIScrollableFrame
* rootScrollableFrame
=
3066 presShell
->GetRootScrollFrameAsScrollable();
3067 MOZ_ASSERT(rootScrollableFrame
);
3068 nsRect displayPortBase
= rootInkOverflow
;
3069 nsRect temp
= displayPortBase
;
3070 Unused
<< rootScrollableFrame
->DecideScrollableLayer(
3071 builder
, &displayPortBase
, &temp
,
3072 /* aSetBase = */ true);
3075 nsRegion visibleRegion
;
3076 if (aFlags
& PaintFrameFlags::WidgetLayers
) {
3077 // This layer tree will be reused, so we'll need to calculate it
3078 // for the whole "visible" area of the window
3080 // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
3081 // document-rendering state. We rely on PresShell to flush
3082 // retained layers as needed when that persistent state changes.
3083 visibleRegion
= rootInkOverflow
;
3085 visibleRegion
= aDirtyRegion
;
3088 Maybe
<nsPoint
> originalScrollPosition
;
3089 auto maybeResetScrollPosition
= MakeScopeExit([&]() {
3090 if (originalScrollPosition
&& rootScrollFrame
) {
3091 nsIScrollableFrame
* rootScrollableFrame
=
3092 presShell
->GetRootScrollFrameAsScrollable();
3093 MOZ_ASSERT(rootScrollableFrame
->GetScrolledFrame()->GetPosition() ==
3095 rootScrollableFrame
->GetScrolledFrame()->SetPosition(
3096 *originalScrollPosition
);
3100 nsRect
canvasArea(nsPoint(0, 0),
3101 aFrame
->InkOverflowRectRelativeToSelf().Size());
3102 bool ignoreViewportScrolling
=
3103 !aFrame
->GetParent() && presShell
->IgnoringViewportScrolling();
3105 if (!aFrame
->GetParent() && hasDynamicToolbar
) {
3106 canvasArea
.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
3107 presContext
, canvasArea
.Size()));
3110 if (ignoreViewportScrolling
&& rootScrollFrame
) {
3111 nsIScrollableFrame
* rootScrollableFrame
=
3112 presShell
->GetRootScrollFrameAsScrollable();
3113 if (aFlags
& PaintFrameFlags::ResetViewportScrolling
) {
3114 // Temporarily scroll the root scroll frame to 0,0 so that position:fixed
3115 // elements will appear fixed to the top-left of the document. We manually
3116 // set the position of the scrolled frame instead of using ScrollTo, since
3117 // the latter fires scroll listeners, which we don't want.
3118 originalScrollPosition
.emplace(
3119 rootScrollableFrame
->GetScrolledFrame()->GetPosition());
3120 rootScrollableFrame
->GetScrolledFrame()->SetPosition(nsPoint());
3122 if (aFlags
& PaintFrameFlags::DocumentRelative
) {
3123 // Make visibleRegion and aRenderingContext relative to the
3124 // scrolled frame instead of the root frame.
3125 nsPoint pos
= rootScrollableFrame
->GetScrollPosition();
3126 visibleRegion
.MoveBy(-pos
);
3127 if (aRenderingContext
) {
3128 gfxPoint devPixelOffset
= nsLayoutUtils::PointToGfxPoint(
3129 pos
, presContext
->AppUnitsPerDevPixel());
3130 aRenderingContext
->SetMatrixDouble(
3131 aRenderingContext
->CurrentMatrixDouble().PreTranslate(
3135 builder
->SetIgnoreScrollFrame(rootScrollFrame
);
3137 nsCanvasFrame
* canvasFrame
=
3138 do_QueryFrame(rootScrollableFrame
->GetScrolledFrame());
3140 // Use UnionRect here to ensure that areas where the scrollbars
3141 // were are still filled with the background color.
3142 canvasArea
.UnionRect(
3144 canvasFrame
->CanvasArea() + builder
->ToReferenceFrame(canvasFrame
));
3148 nsRect visibleRect
= visibleRegion
.GetBounds();
3149 PartialUpdateResult updateState
= PartialUpdateResult::Failed
;
3152 AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_DisplayListBuilding
);
3153 AUTO_PROFILER_TRACING_MARKER("Paint", "DisplayList", GRAPHICS
);
3154 PerfStats::AutoMetricRecording
<PerfStats::Metric::DisplayListBuilding
>
3157 ViewID id
= ScrollableLayerGuid::NULL_SCROLL_ID
;
3158 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter
asrSetter(
3161 if (presShell
->GetDocument() &&
3162 presShell
->GetDocument()->IsRootDisplayDocument() &&
3163 !presShell
->GetRootScrollFrame()) {
3164 // In cases where the root document is a XUL document, we want to take
3165 // the ViewID from the root element, as that will be the ViewID of the
3166 // root APZC in the tree. Skip doing this in cases where we know
3167 // nsGfxScrollFrame::BuilDisplayList will do it instead.
3168 if (dom::Element
* element
=
3169 presShell
->GetDocument()->GetDocumentElement()) {
3170 id
= nsLayoutUtils::FindOrCreateIDFor(element
);
3172 // In some cases we get a root document here on an APZ-enabled window
3173 // that doesn't have the root displayport initialized yet, even though
3174 // the ChromeProcessController is supposed to do it when the widget is
3175 // created. This can happen simply because the ChromeProcessController
3176 // does it on the next spin of the event loop, and we can trigger a
3177 // paint synchronously after window creation but before that runs. In
3178 // that case we should initialize the root displayport here before we do
3180 } else if (XRE_IsParentProcess() && presContext
->IsRoot() &&
3181 presShell
->GetDocument() != nullptr &&
3182 presShell
->GetRootScrollFrame() != nullptr &&
3183 nsLayoutUtils::UsesAsyncScrolling(
3184 presShell
->GetRootScrollFrame())) {
3185 if (dom::Element
* element
=
3186 presShell
->GetDocument()->GetDocumentElement()) {
3187 if (!DisplayPortUtils::HasNonMinimalDisplayPort(element
)) {
3188 APZCCallbackHelper::InitializeRootDisplayport(presShell
);
3193 asrSetter
.SetCurrentScrollParentId(id
);
3195 builder
->SetVisibleRect(visibleRect
);
3196 builder
->SetIsBuilding(true);
3197 builder
->SetAncestorHasApzAwareEventHandler(
3198 gfxPlatform::AsyncPanZoomEnabled() &&
3199 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell
));
3201 // If a pref is toggled that adds or removes display list items,
3202 // we need to rebuild the display list. The pref may be toggled
3203 // manually by the user, or during test setup.
3204 if (retainDisplayList
&&
3205 !builder
->ShouldRebuildDisplayListDueToPrefChange()) {
3206 // Attempt to do a partial build and merge into the existing list.
3207 // This calls BuildDisplayListForStacking context on a subset of the
3209 updateState
= retainedBuilder
->AttemptPartialUpdate(aBackstop
);
3210 metrics
->EndPartialBuild(updateState
);
3212 // Partial updates are disabled.
3213 DL_LOGI("Partial updates are disabled");
3214 metrics
->mPartialUpdateResult
= PartialUpdateResult::Failed
;
3215 metrics
->mPartialUpdateFailReason
= PartialUpdateFailReason::Disabled
;
3218 // Rebuild the full display list if the partial display list build failed.
3219 bool doFullRebuild
= updateState
== PartialUpdateResult::Failed
;
3221 if (StaticPrefs::layout_display_list_build_twice()) {
3222 // Build display list twice to compare partial and full display list
3224 metrics
->StartBuild();
3225 doFullRebuild
= true;
3228 if (doFullRebuild
) {
3229 if (retainDisplayList
) {
3230 retainedBuilder
->ClearRetainedData();
3232 mozilla::RDLUtils::AssertFrameSubtreeUnmodified(
3233 builder
->RootReferenceFrame());
3237 list
->DeleteAll(builder
);
3239 builder
->ClearRetainedWindowRegions();
3240 builder
->ClearWillChangeBudgets();
3242 builder
->EnterPresShell(aFrame
);
3243 builder
->SetDirtyRect(visibleRect
);
3245 DL_LOGI("Starting full display list build, root frame: %p",
3246 builder
->RootReferenceFrame());
3248 aFrame
->BuildDisplayListForStackingContext(builder
, list
);
3249 AddExtraBackgroundItems(builder
, list
, aFrame
, canvasArea
, visibleRegion
,
3252 builder
->LeavePresShell(aFrame
, list
);
3253 metrics
->EndFullBuild();
3255 DL_LOGI("Finished full display list build");
3256 updateState
= PartialUpdateResult::Updated
;
3259 builder
->SetIsBuilding(false);
3260 builder
->IncrementPresShellPaintCount(presShell
);
3263 MOZ_ASSERT(updateState
!= PartialUpdateResult::Failed
);
3266 const double geckoDLBuildTime
=
3267 (TimeStamp::Now() - startBuildDisplayList
).ToMilliseconds();
3268 mozilla::glean::paint::build_displaylist_time
.StopAndAccumulate(
3269 std::move(dlTimerId
));
3271 bool consoleNeedsDisplayList
=
3272 (gfxUtils::DumpDisplayList() || gfxEnv::MOZ_DUMP_PAINT()) &&
3273 builder
->IsInActiveDocShell();
3274 #ifdef MOZ_DUMP_PAINTING
3275 FILE* savedDumpFile
= gfxUtils::sDumpPaintFile
;
3278 UniquePtr
<std::stringstream
> ss
;
3279 if (consoleNeedsDisplayList
) {
3280 ss
= MakeUnique
<std::stringstream
>();
3281 *ss
<< "Display list for " << uri
<< "\n";
3282 DumpBeforePaintDisplayList(ss
, builder
, list
, visibleRect
);
3285 uint32_t flags
= nsDisplayList::PAINT_DEFAULT
;
3286 if (aFlags
& PaintFrameFlags::WidgetLayers
) {
3287 flags
|= nsDisplayList::PAINT_USE_WIDGET_LAYERS
;
3288 if (!(aFlags
& PaintFrameFlags::DocumentRelative
)) {
3289 nsIWidget
* widget
= aFrame
->GetNearestWidget();
3291 // If we're finished building display list items for painting of the
3292 // outermost pres shell, notify the widget about any toolbars we've
3294 widget
->UpdateThemeGeometries(builder
->GetThemeGeometries());
3298 if (aFlags
& PaintFrameFlags::ExistingTransaction
) {
3299 flags
|= nsDisplayList::PAINT_EXISTING_TRANSACTION
;
3301 if (updateState
== PartialUpdateResult::NoChange
&& !aRenderingContext
) {
3302 flags
|= nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST
;
3305 #ifdef PRINT_HITTESTINFO_STATS
3306 if (XRE_IsContentProcess()) {
3307 PrintHitTestInfoStats(list
);
3311 TimeStamp paintStart
= TimeStamp::Now();
3312 list
->PaintRoot(builder
, aRenderingContext
, flags
, Some(geckoDLBuildTime
));
3313 Telemetry::AccumulateTimeDelta(Telemetry::PAINT_RASTERIZE_TIME
, paintStart
);
3315 if (builder
->IsPaintingToWindow()) {
3316 presShell
->EndPaint();
3320 if (consoleNeedsDisplayList
) {
3321 DumpAfterPaintDisplayList(ss
, builder
, list
);
3324 #ifdef MOZ_DUMP_PAINTING
3325 gfxUtils::sDumpPaintFile
= savedDumpFile
;
3328 // Update the widget's opaque region information. This sets
3329 // glass boundaries on Windows. Also set up the window dragging region.
3330 if ((aFlags
& PaintFrameFlags::WidgetLayers
) &&
3331 !(aFlags
& PaintFrameFlags::DocumentRelative
)) {
3332 if (nsIWidget
* widget
= aFrame
->GetNearestWidget()) {
3333 const nsRegion
& opaqueRegion
= builder
->GetWindowOpaqueRegion();
3334 widget
->UpdateOpaqueRegion(LayoutDeviceIntRegion::FromUnknownRegion(
3335 opaqueRegion
.ToNearestPixels(presContext
->AppUnitsPerDevPixel())));
3336 widget
->UpdateWindowDraggingRegion(builder
->GetWindowDraggingRegion());
3340 // Apply effects updates if we were actually painting
3341 if (isForPainting
) {
3342 ApplyEffectsUpdates(builder
->GetEffectUpdates());
3348 AUTO_PROFILER_TRACING_MARKER("Paint", "DisplayListResources", GRAPHICS
);
3350 builder
->EndFrame();
3352 if (temporaryBuilder
) {
3353 temporaryBuilder
.reset();
3359 if (XRE_IsParentProcess()) {
3360 if (metrics
->mPartialUpdateResult
== PartialUpdateResult::Failed
) {
3361 printf("DL partial update failed: %s, Frame: %p\n",
3362 metrics
->FailReasonString(), aFrame
);
3365 "DL partial build success!"
3366 " new: %d, reused: %d, rebuilt: %d, removed: %d, total: %d\n",
3367 metrics
->mNewItems
, metrics
->mReusedItems
, metrics
->mRebuiltItems
,
3368 metrics
->mRemovedItems
, metrics
->mTotalItems
);
3375 * Uses a binary search for find where the cursor falls in the line of text
3376 * It also keeps track of the part of the string that has already been measured
3377 * so it doesn't have to keep measuring the same text over and over
3379 * @param "aBaseWidth" contains the width in twips of the portion
3380 * of the text that has already been measured, and aBaseInx contains
3381 * the index of the text that has already been measured.
3383 * @param aTextWidth returns the (in twips) the length of the text that falls
3384 * before the cursor aIndex contains the index of the text where the cursor
3387 bool nsLayoutUtils::BinarySearchForPosition(
3388 DrawTarget
* aDrawTarget
, nsFontMetrics
& aFontMetrics
, const char16_t
* aText
,
3389 int32_t aBaseWidth
, int32_t aBaseInx
, int32_t aStartInx
, int32_t aEndInx
,
3390 int32_t aCursorPos
, int32_t& aIndex
, int32_t& aTextWidth
) {
3391 int32_t range
= aEndInx
- aStartInx
;
3392 if ((range
== 1) || (range
== 2 && NS_IS_HIGH_SURROGATE(aText
[aStartInx
]))) {
3393 aIndex
= aStartInx
+ aBaseInx
;
3394 aTextWidth
= nsLayoutUtils::AppUnitWidthOfString(aText
, aIndex
,
3395 aFontMetrics
, aDrawTarget
);
3399 int32_t inx
= aStartInx
+ (range
/ 2);
3401 // Make sure we don't leave a dangling low surrogate
3402 if (NS_IS_HIGH_SURROGATE(aText
[inx
- 1])) inx
++;
3404 int32_t textWidth
= nsLayoutUtils::AppUnitWidthOfString(
3405 aText
, inx
, aFontMetrics
, aDrawTarget
);
3407 int32_t fullWidth
= aBaseWidth
+ textWidth
;
3408 if (fullWidth
== aCursorPos
) {
3409 aTextWidth
= textWidth
;
3412 } else if (aCursorPos
< fullWidth
) {
3413 aTextWidth
= aBaseWidth
;
3414 if (BinarySearchForPosition(aDrawTarget
, aFontMetrics
, aText
, aBaseWidth
,
3415 aBaseInx
, aStartInx
, inx
, aCursorPos
, aIndex
,
3420 aTextWidth
= fullWidth
;
3421 if (BinarySearchForPosition(aDrawTarget
, aFontMetrics
, aText
, aBaseWidth
,
3422 aBaseInx
, inx
, aEndInx
, aCursorPos
, aIndex
,
3430 void nsLayoutUtils::AddBoxesForFrame(nsIFrame
* aFrame
,
3431 nsLayoutUtils::BoxCallback
* aCallback
) {
3432 auto pseudoType
= aFrame
->Style()->GetPseudoType();
3434 if (pseudoType
== PseudoStyleType::tableWrapper
) {
3435 AddBoxesForFrame(aFrame
->PrincipalChildList().FirstChild(), aCallback
);
3436 if (aCallback
->mIncludeCaptionBoxForTable
) {
3438 aFrame
->GetChildList(FrameChildListID::Caption
).FirstChild();
3440 AddBoxesForFrame(kid
, aCallback
);
3443 } else if (pseudoType
== PseudoStyleType::mozBlockInsideInlineWrapper
||
3444 pseudoType
== PseudoStyleType::mozMathMLAnonymousBlock
) {
3445 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
3446 AddBoxesForFrame(kid
, aCallback
);
3449 aCallback
->AddBox(aFrame
);
3453 void nsLayoutUtils::GetAllInFlowBoxes(nsIFrame
* aFrame
,
3454 BoxCallback
* aCallback
) {
3455 aCallback
->mInTargetContinuation
= false;
3457 AddBoxesForFrame(aFrame
, aCallback
);
3458 aFrame
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame
);
3459 aCallback
->mInTargetContinuation
= true;
3463 nsIFrame
* nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame
* aFrame
) {
3465 auto pseudoType
= aFrame
->Style()->GetPseudoType();
3467 if (pseudoType
== PseudoStyleType::tableWrapper
) {
3469 GetFirstNonAnonymousFrame(aFrame
->PrincipalChildList().FirstChild());
3474 aFrame
->GetChildList(FrameChildListID::Caption
).FirstChild();
3476 f
= GetFirstNonAnonymousFrame(kid
);
3481 } else if (pseudoType
== PseudoStyleType::mozBlockInsideInlineWrapper
||
3482 pseudoType
== PseudoStyleType::mozMathMLAnonymousBlock
) {
3483 for (nsIFrame
* kid
: aFrame
->PrincipalChildList()) {
3484 nsIFrame
* f
= GetFirstNonAnonymousFrame(kid
);
3493 aFrame
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame
);
3498 struct BoxToRect
: public nsLayoutUtils::BoxCallback
{
3499 const nsIFrame
* mRelativeTo
;
3500 RectCallback
* mCallback
;
3502 // If the frame we're measuring relative to is the root, we know all frames
3503 // are descendants of it, so we don't need to compute the common ancestor
3504 // between a frame and mRelativeTo.
3505 bool mRelativeToIsRoot
;
3506 // For the same reason, if the frame we're measuring relative to is the target
3507 // (this is useful for IntersectionObserver), we know all frames are
3508 // descendants of it except if we're in a continuation or ib-split-sibling of
3510 bool mRelativeToIsTarget
;
3512 BoxToRect(const nsIFrame
* aTargetFrame
, const nsIFrame
* aRelativeTo
,
3513 RectCallback
* aCallback
, uint32_t aFlags
)
3514 : mRelativeTo(aRelativeTo
),
3515 mCallback(aCallback
),
3517 mRelativeToIsRoot(!aRelativeTo
->GetParent()),
3518 mRelativeToIsTarget(aRelativeTo
== aTargetFrame
) {}
3520 void AddBox(nsIFrame
* aFrame
) override
{
3522 nsIFrame
* outer
= SVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame
, &r
);
3523 const bool usingSVGOuterFrame
= !!outer
;
3526 switch (mFlags
& nsLayoutUtils::RECTS_WHICH_BOX_MASK
) {
3527 case nsLayoutUtils::RECTS_USE_CONTENT_BOX
:
3528 r
= aFrame
->GetContentRectRelativeToSelf();
3530 case nsLayoutUtils::RECTS_USE_PADDING_BOX
:
3531 r
= aFrame
->GetPaddingRectRelativeToSelf();
3533 case nsLayoutUtils::RECTS_USE_MARGIN_BOX
:
3534 r
= aFrame
->GetMarginRectRelativeToSelf();
3536 default: // Use the border box
3537 r
= aFrame
->GetRectRelativeToSelf();
3540 if (mFlags
& nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
) {
3541 const bool isAncestorKnown
= [&] {
3542 if (mRelativeToIsRoot
) {
3545 if (mRelativeToIsTarget
&& !mInTargetContinuation
) {
3546 return !usingSVGOuterFrame
;
3550 if (isAncestorKnown
) {
3551 r
= nsLayoutUtils::TransformFrameRectToAncestor(outer
, r
, mRelativeTo
);
3553 nsLayoutUtils::TransformRect(outer
, mRelativeTo
, r
);
3556 if (aFrame
->PresContext() != mRelativeTo
->PresContext()) {
3557 r
+= outer
->GetOffsetToCrossDoc(mRelativeTo
);
3559 r
+= outer
->GetOffsetTo(mRelativeTo
);
3562 mCallback
->AddRect(r
);
3566 struct MOZ_RAII BoxToRectAndText
: public BoxToRect
{
3567 Sequence
<nsString
>* mTextList
;
3569 BoxToRectAndText(const nsIFrame
* aTargetFrame
, const nsIFrame
* aRelativeTo
,
3570 RectCallback
* aCallback
, Sequence
<nsString
>* aTextList
,
3572 : BoxToRect(aTargetFrame
, aRelativeTo
, aCallback
, aFlags
),
3573 mTextList(aTextList
) {}
3575 static void AccumulateText(nsIFrame
* aFrame
, nsAString
& aResult
) {
3578 // Get all the text in aFrame and child frames, while respecting
3579 // the content offsets in each of the nsTextFrames.
3580 if (aFrame
->IsTextFrame()) {
3581 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(aFrame
);
3583 nsIFrame::RenderedText renderedText
= textFrame
->GetRenderedText(
3584 textFrame
->GetContentOffset(),
3585 textFrame
->GetContentOffset() + textFrame
->GetContentLength(),
3586 nsIFrame::TextOffsetType::OffsetsInContentText
,
3587 nsIFrame::TrailingWhitespace::DontTrim
);
3589 aResult
.Append(renderedText
.mString
);
3592 for (nsIFrame
* child
= aFrame
->PrincipalChildList().FirstChild(); child
;
3593 child
= child
->GetNextSibling()) {
3594 AccumulateText(child
, aResult
);
3598 void AddBox(nsIFrame
* aFrame
) override
{
3599 BoxToRect::AddBox(aFrame
);
3601 nsString
* textForFrame
= mTextList
->AppendElement(fallible
);
3603 AccumulateText(aFrame
, *textForFrame
);
3609 void nsLayoutUtils::GetAllInFlowRects(nsIFrame
* aFrame
,
3610 const nsIFrame
* aRelativeTo
,
3611 RectCallback
* aCallback
,
3613 BoxToRect
converter(aFrame
, aRelativeTo
, aCallback
, aFlags
);
3614 GetAllInFlowBoxes(aFrame
, &converter
);
3617 void nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame
* aFrame
,
3618 const nsIFrame
* aRelativeTo
,
3619 RectCallback
* aCallback
,
3620 Sequence
<nsString
>* aTextList
,
3622 BoxToRectAndText
converter(aFrame
, aRelativeTo
, aCallback
, aTextList
, aFlags
);
3623 GetAllInFlowBoxes(aFrame
, &converter
);
3626 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
3628 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect
& aRect
) {
3629 mResultRect
.UnionRect(mResultRect
, aRect
);
3630 if (!mSeenFirstRect
) {
3631 mSeenFirstRect
= true;
3636 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList
* aList
)
3637 : mRectList(aList
) {}
3639 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect
& aRect
) {
3640 RefPtr
<DOMRect
> rect
= new DOMRect(mRectList
);
3642 rect
->SetLayoutRect(aRect
);
3643 mRectList
->Append(rect
);
3646 nsIFrame
* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame
* aFrame
) {
3647 return aFrame
->PresShell()->GetRootFrame();
3650 nsRect
nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame
* aFrame
,
3651 const nsIFrame
* aRelativeTo
,
3653 RectAccumulator accumulator
;
3654 GetAllInFlowRects(aFrame
, aRelativeTo
, &accumulator
, aFlags
);
3655 return accumulator
.mResultRect
.IsEmpty() ? accumulator
.mFirstRect
3656 : accumulator
.mResultRect
;
3659 nsRect
nsLayoutUtils::GetTextShadowRectsUnion(
3660 const nsRect
& aTextAndDecorationsRect
, nsIFrame
* aFrame
, uint32_t aFlags
) {
3661 const nsStyleText
* textStyle
= aFrame
->StyleText();
3662 auto shadows
= textStyle
->mTextShadow
.AsSpan();
3663 if (shadows
.IsEmpty()) {
3664 return aTextAndDecorationsRect
;
3667 nsRect resultRect
= aTextAndDecorationsRect
;
3668 int32_t A2D
= aFrame
->PresContext()->AppUnitsPerDevPixel();
3669 for (auto& shadow
: shadows
) {
3671 nsContextBoxBlur::GetBlurRadiusMargin(shadow
.blur
.ToAppUnits(), A2D
);
3672 if ((aFlags
& EXCLUDE_BLUR_SHADOWS
) && blur
!= nsMargin(0, 0, 0, 0))
3675 nsRect
tmpRect(aTextAndDecorationsRect
);
3678 nsPoint(shadow
.horizontal
.ToAppUnits(), shadow
.vertical
.ToAppUnits()));
3679 tmpRect
.Inflate(blur
);
3681 resultRect
.UnionRect(resultRect
, tmpRect
);
3686 enum ObjectDimensionType
{ eWidth
, eHeight
};
3687 static nscoord
ComputeMissingDimension(
3688 const nsSize
& aDefaultObjectSize
, const AspectRatio
& aIntrinsicRatio
,
3689 const Maybe
<nscoord
>& aSpecifiedWidth
,
3690 const Maybe
<nscoord
>& aSpecifiedHeight
,
3691 ObjectDimensionType aDimensionToCompute
) {
3692 // The "default sizing algorithm" computes the missing dimension as follows:
3693 // (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )
3695 // 1. "If the object has an intrinsic aspect ratio, the missing dimension of
3696 // the concrete object size is calculated using the intrinsic aspect
3697 // ratio and the present dimension."
3698 if (aIntrinsicRatio
) {
3699 // Fill in the missing dimension using the intrinsic aspect ratio.
3700 if (aDimensionToCompute
== eWidth
) {
3701 return aIntrinsicRatio
.ApplyTo(*aSpecifiedHeight
);
3703 return aIntrinsicRatio
.Inverted().ApplyTo(*aSpecifiedWidth
);
3706 // 2. "Otherwise, if the missing dimension is present in the object's
3707 // intrinsic dimensions, [...]"
3708 // NOTE: *Skipping* this case, because we already know it's not true -- we're
3709 // in this function because the missing dimension is *not* present in
3710 // the object's intrinsic dimensions.
3712 // 3. "Otherwise, the missing dimension of the concrete object size is taken
3713 // from the default object size. "
3714 return (aDimensionToCompute
== eWidth
) ? aDefaultObjectSize
.width
3715 : aDefaultObjectSize
.height
;
3719 * This computes & returns the concrete object size of replaced content, if
3720 * that content were to be rendered with "object-fit: none". (Or, if the
3721 * element has neither an intrinsic height nor width, this method returns an
3722 * empty Maybe<> object.)
3725 * http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none
3726 * ..we use "the default sizing algorithm with no specified size,
3727 * and a default object size equal to the replaced element's used width and
3730 * The default sizing algorithm is described here:
3731 * http://dev.w3.org/csswg/css-images-3/#default-sizing
3732 * Quotes in the function-impl are taken from that ^ spec-text.
3734 * Per its final bulleted section: since there's no specified size,
3735 * we run the default sizing algorithm using the object's intrinsic size in
3736 * place of the specified size. But if the object has neither an intrinsic
3737 * height nor an intrinsic width, then we instead return without populating our
3738 * outparam, and we let the caller figure out the size (using a contain
3741 static Maybe
<nsSize
> MaybeComputeObjectFitNoneSize(
3742 const nsSize
& aDefaultObjectSize
, const IntrinsicSize
& aIntrinsicSize
,
3743 const AspectRatio
& aIntrinsicRatio
) {
3744 // "If the object has an intrinsic height or width, its size is resolved as
3745 // if its intrinsic dimensions were given as the specified size."
3747 // So, first we check if we have an intrinsic height and/or width:
3748 const Maybe
<nscoord
>& specifiedWidth
= aIntrinsicSize
.width
;
3749 const Maybe
<nscoord
>& specifiedHeight
= aIntrinsicSize
.height
;
3751 Maybe
<nsSize
> noneSize
; // (the value we'll return)
3752 if (specifiedWidth
|| specifiedHeight
) {
3753 // We have at least one specified dimension; use whichever dimension is
3754 // specified, and compute the other one using our intrinsic ratio, or (if
3755 // no valid ratio) using the default object size.
3761 : ComputeMissingDimension(aDefaultObjectSize
, aIntrinsicRatio
,
3762 specifiedWidth
, specifiedHeight
, eWidth
);
3767 : ComputeMissingDimension(aDefaultObjectSize
, aIntrinsicRatio
,
3768 specifiedWidth
, specifiedHeight
, eHeight
);
3770 // [else:] "Otherwise [if there's neither an intrinsic height nor width], its
3771 // size is resolved as a contain constraint against the default object size."
3772 // We'll let our caller do that, to share code & avoid redundant
3773 // computations; so, we return w/out populating noneSize.
3777 // Computes the concrete object size to render into, as described at
3778 // http://dev.w3.org/csswg/css-images-3/#concrete-size-resolution
3779 static nsSize
ComputeConcreteObjectSize(const nsSize
& aConstraintSize
,
3780 const IntrinsicSize
& aIntrinsicSize
,
3781 const AspectRatio
& aIntrinsicRatio
,
3782 StyleObjectFit aObjectFit
) {
3783 // Handle default behavior (filling the container) w/ fast early return.
3784 // (Also: if there's no valid intrinsic ratio, then we have the "fill"
3785 // behavior & just use the constraint size.)
3786 if (MOZ_LIKELY(aObjectFit
== StyleObjectFit::Fill
) || !aIntrinsicRatio
) {
3787 return aConstraintSize
;
3790 // The type of constraint to compute (cover/contain), if needed:
3791 Maybe
<nsImageRenderer::FitType
> fitType
;
3793 Maybe
<nsSize
> noneSize
;
3794 if (aObjectFit
== StyleObjectFit::None
||
3795 aObjectFit
== StyleObjectFit::ScaleDown
) {
3796 noneSize
= MaybeComputeObjectFitNoneSize(aConstraintSize
, aIntrinsicSize
,
3798 if (!noneSize
|| aObjectFit
== StyleObjectFit::ScaleDown
) {
3799 // Need to compute a 'CONTAIN' constraint (either for the 'none' size
3800 // itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)
3801 fitType
.emplace(nsImageRenderer::CONTAIN
);
3803 } else if (aObjectFit
== StyleObjectFit::Cover
) {
3804 fitType
.emplace(nsImageRenderer::COVER
);
3805 } else if (aObjectFit
== StyleObjectFit::Contain
) {
3806 fitType
.emplace(nsImageRenderer::CONTAIN
);
3809 Maybe
<nsSize
> constrainedSize
;
3811 constrainedSize
.emplace(nsImageRenderer::ComputeConstrainedSize(
3812 aConstraintSize
, aIntrinsicRatio
, *fitType
));
3815 // Now, we should have all the sizing information that we need.
3816 switch (aObjectFit
) {
3817 // skipping StyleObjectFit::Fill; we handled it w/ early-return.
3818 case StyleObjectFit::Contain
:
3819 case StyleObjectFit::Cover
:
3820 MOZ_ASSERT(constrainedSize
);
3821 return *constrainedSize
;
3823 case StyleObjectFit::None
:
3827 MOZ_ASSERT(constrainedSize
);
3828 return *constrainedSize
;
3830 case StyleObjectFit::ScaleDown
:
3831 MOZ_ASSERT(constrainedSize
);
3833 constrainedSize
->width
=
3834 std::min(constrainedSize
->width
, noneSize
->width
);
3835 constrainedSize
->height
=
3836 std::min(constrainedSize
->height
, noneSize
->height
);
3838 return *constrainedSize
;
3841 MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");
3842 return aConstraintSize
; // fall back to (default) 'fill' behavior
3846 // (Helper for HasInitialObjectFitAndPosition, to check
3847 // each "object-position" coord.)
3848 static bool IsCoord50Pct(const LengthPercentage
& aCoord
) {
3849 return aCoord
.ConvertsToPercentage() && aCoord
.ToPercentage() == 0.5f
;
3852 // Indicates whether the given nsStylePosition has the initial values
3853 // for the "object-fit" and "object-position" properties.
3854 static bool HasInitialObjectFitAndPosition(const nsStylePosition
* aStylePos
) {
3855 const Position
& objectPos
= aStylePos
->mObjectPosition
;
3857 return aStylePos
->mObjectFit
== StyleObjectFit::Fill
&&
3858 IsCoord50Pct(objectPos
.horizontal
) && IsCoord50Pct(objectPos
.vertical
);
3862 nsRect
nsLayoutUtils::ComputeObjectDestRect(const nsRect
& aConstraintRect
,
3863 const IntrinsicSize
& aIntrinsicSize
,
3864 const AspectRatio
& aIntrinsicRatio
,
3865 const nsStylePosition
* aStylePos
,
3866 nsPoint
* aAnchorPoint
) {
3867 // Step 1: Figure out our "concrete object size"
3868 // (the size of the region we'll actually draw our image's pixels into).
3869 nsSize concreteObjectSize
=
3870 ComputeConcreteObjectSize(aConstraintRect
.Size(), aIntrinsicSize
,
3871 aIntrinsicRatio
, aStylePos
->mObjectFit
);
3873 // Step 2: Figure out how to align that region in the element's content-box.
3874 nsPoint imageTopLeftPt
, imageAnchorPt
;
3875 nsImageRenderer::ComputeObjectAnchorPoint(
3876 aStylePos
->mObjectPosition
, aConstraintRect
.Size(), concreteObjectSize
,
3877 &imageTopLeftPt
, &imageAnchorPt
);
3878 // Right now, we're with respect to aConstraintRect's top-left point. We add
3879 // that point here, to convert to the same broader coordinate space that
3880 // aConstraintRect is in.
3881 imageTopLeftPt
+= aConstraintRect
.TopLeft();
3882 imageAnchorPt
+= aConstraintRect
.TopLeft();
3885 // Special-case: if our "object-fit" and "object-position" properties have
3886 // their default values ("object-fit: fill; object-position:50% 50%"), then
3887 // we'll override the calculated imageAnchorPt, and instead use the
3888 // object's top-left corner.
3890 // This special case is partly for backwards compatibility (since
3891 // traditionally we've pixel-aligned the top-left corner of e.g. <img>
3892 // elements), and partly because ComputeSnappedDrawingParameters produces
3893 // less error if the anchor point is at the top-left corner. So, all other
3894 // things being equal, we prefer that code path with less error.
3895 if (HasInitialObjectFitAndPosition(aStylePos
)) {
3896 *aAnchorPoint
= imageTopLeftPt
;
3898 *aAnchorPoint
= imageAnchorPt
;
3901 return nsRect(imageTopLeftPt
, concreteObjectSize
);
3904 already_AddRefed
<nsFontMetrics
> nsLayoutUtils::GetFontMetricsForFrame(
3905 const nsIFrame
* aFrame
, float aInflation
) {
3906 ComputedStyle
* computedStyle
= aFrame
->Style();
3907 uint8_t variantWidth
= NS_FONT_VARIANT_WIDTH_NORMAL
;
3908 if (computedStyle
->IsTextCombined()) {
3909 MOZ_ASSERT(aFrame
->IsTextFrame());
3910 auto textFrame
= static_cast<const nsTextFrame
*>(aFrame
);
3911 auto clusters
= textFrame
->CountGraphemeClusters();
3912 if (clusters
== 2) {
3913 variantWidth
= NS_FONT_VARIANT_WIDTH_HALF
;
3914 } else if (clusters
== 3) {
3915 variantWidth
= NS_FONT_VARIANT_WIDTH_THIRD
;
3916 } else if (clusters
== 4) {
3917 variantWidth
= NS_FONT_VARIANT_WIDTH_QUARTER
;
3920 return GetFontMetricsForComputedStyle(computedStyle
, aFrame
->PresContext(),
3921 aInflation
, variantWidth
);
3924 already_AddRefed
<nsFontMetrics
> nsLayoutUtils::GetFontMetricsForComputedStyle(
3925 const ComputedStyle
* aComputedStyle
, nsPresContext
* aPresContext
,
3926 float aInflation
, uint8_t aVariantWidth
) {
3927 WritingMode
wm(aComputedStyle
);
3928 const nsStyleFont
* styleFont
= aComputedStyle
->StyleFont();
3929 nsFontMetrics::Params params
;
3930 params
.language
= styleFont
->mLanguage
;
3931 params
.explicitLanguage
= styleFont
->mExplicitLanguage
;
3932 params
.orientation
= wm
.IsVertical() && !wm
.IsSideways()
3933 ? nsFontMetrics::eVertical
3934 : nsFontMetrics::eHorizontal
;
3935 // pass the user font set object into the device context to
3936 // pass along to CreateFontGroup
3937 params
.userFontSet
= aPresContext
->GetUserFontSet();
3938 params
.textPerf
= aPresContext
->GetTextPerfMetrics();
3939 params
.featureValueLookup
= aPresContext
->GetFontFeatureValuesLookup();
3941 // When aInflation is 1.0 and we don't require width variant, avoid
3942 // making a local copy of the nsFont.
3943 // This also avoids running font.size through floats when it is large,
3944 // which would be lossy. Fortunately, in such cases, aInflation is
3945 // guaranteed to be 1.0f.
3946 if (aInflation
== 1.0f
&& aVariantWidth
== NS_FONT_VARIANT_WIDTH_NORMAL
) {
3947 return aPresContext
->GetMetricsFor(styleFont
->mFont
, params
);
3950 nsFont font
= styleFont
->mFont
;
3951 MOZ_ASSERT(!std::isnan(float(font
.size
.ToCSSPixels())),
3952 "Style font should never be NaN");
3953 font
.size
.ScaleBy(aInflation
);
3954 if (MOZ_UNLIKELY(std::isnan(float(font
.size
.ToCSSPixels())))) {
3957 font
.variantWidth
= aVariantWidth
;
3958 return aPresContext
->GetMetricsFor(font
, params
);
3961 nsIFrame
* nsLayoutUtils::FindChildContainingDescendant(
3962 nsIFrame
* aParent
, nsIFrame
* aDescendantFrame
) {
3963 nsIFrame
* result
= aDescendantFrame
;
3966 nsIFrame
* parent
= result
->GetParent();
3967 if (parent
== aParent
) {
3971 // The frame is not an immediate child of aParent so walk up another level
3978 nsBlockFrame
* nsLayoutUtils::FindNearestBlockAncestor(nsIFrame
* aFrame
) {
3979 nsIFrame
* nextAncestor
;
3980 for (nextAncestor
= aFrame
->GetParent(); nextAncestor
;
3981 nextAncestor
= nextAncestor
->GetParent()) {
3982 nsBlockFrame
* block
= do_QueryFrame(nextAncestor
);
3983 if (block
) return block
;
3988 nsIFrame
* nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame
* aFrame
) {
3989 if (!aFrame
->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT
)) return aFrame
;
3991 nsIFrame
* f
= aFrame
;
3993 f
= GetParentOrPlaceholderFor(f
);
3994 } while (f
->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT
));
3998 nsIFrame
* nsLayoutUtils::GetParentOrPlaceholderFor(const nsIFrame
* aFrame
) {
3999 // This condition must match the condition in FindContainingBlocks in
4000 // RetainedDisplayListBuider.cpp, MarkFrameForDisplayIfVisible and
4001 // UnmarkFrameForDisplayIfVisible in nsDisplayList.cpp
4002 if (aFrame
->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW
) &&
4003 !aFrame
->GetPrevInFlow()) {
4004 return aFrame
->GetProperty(nsIFrame::PlaceholderFrameProperty());
4006 return aFrame
->GetParent();
4009 nsIFrame
* nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(
4010 const nsIFrame
* aFrame
) {
4011 nsIFrame
* f
= GetParentOrPlaceholderFor(aFrame
);
4013 return GetCrossDocParentFrameInProcess(aFrame
);
4016 nsIFrame
* nsLayoutUtils::GetDisplayListParent(nsIFrame
* aFrame
) {
4017 if (aFrame
->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT
)) {
4018 return aFrame
->GetParent();
4020 return nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(aFrame
);
4023 nsIFrame
* nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(
4024 const nsIFrame
* aFrame
) {
4025 if (nsIFrame
* result
= aFrame
->GetPrevContinuation()) {
4029 if (aFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4030 // We are the first frame in the continuation chain. Get the ib-split prev
4031 // sibling property stored in us.
4032 return aFrame
->GetProperty(nsIFrame::IBSplitPrevSibling());
4038 nsIFrame
* nsLayoutUtils::GetNextContinuationOrIBSplitSibling(
4039 const nsIFrame
* aFrame
) {
4040 if (nsIFrame
* result
= aFrame
->GetNextContinuation()) {
4044 if (aFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4045 // We only store the ib-split sibling annotation with the first frame in the
4046 // continuation chain.
4047 return aFrame
->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
4053 nsIFrame
* nsLayoutUtils::FirstContinuationOrIBSplitSibling(
4054 const nsIFrame
* aFrame
) {
4055 nsIFrame
* result
= aFrame
->FirstContinuation();
4057 if (result
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4058 while (auto* f
= result
->GetProperty(nsIFrame::IBSplitPrevSibling())) {
4066 nsIFrame
* nsLayoutUtils::LastContinuationOrIBSplitSibling(
4067 const nsIFrame
* aFrame
) {
4068 nsIFrame
* result
= aFrame
->FirstContinuation();
4070 if (result
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
)) {
4071 while (auto* f
= result
->GetProperty(nsIFrame::IBSplitSibling())) {
4076 return result
->LastContinuation();
4079 bool nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(
4080 const nsIFrame
* aFrame
) {
4081 if (aFrame
->GetPrevContinuation()) {
4084 if (aFrame
->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
) &&
4085 aFrame
->GetProperty(nsIFrame::IBSplitPrevSibling())) {
4092 bool nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame
* aFrame
) {
4093 if (!aFrame
) return false;
4095 nsIFrame
* rootScrollFrame
= aFrame
->PresShell()->GetRootScrollFrame();
4096 if (!rootScrollFrame
) return false;
4098 nsIScrollableFrame
* rootScrollableFrame
= do_QueryFrame(rootScrollFrame
);
4099 NS_ASSERTION(rootScrollableFrame
, "The root scorollable frame is null");
4101 if (!IsProperAncestorFrame(rootScrollFrame
, aFrame
)) return false;
4103 nsIFrame
* rootScrolledFrame
= rootScrollableFrame
->GetScrolledFrame();
4104 return !(rootScrolledFrame
== aFrame
||
4105 IsProperAncestorFrame(rootScrolledFrame
, aFrame
));
4109 * Use only for paddings / widths / heights, since it clamps negative calc() to
4112 template <typename LengthPercentageLike
>
4113 static bool GetAbsoluteCoord(const LengthPercentageLike
& aStyle
,
4115 if (!aStyle
.ConvertsToLength()) {
4118 aResult
= std::max(0, aStyle
.ToLength());
4122 static nscoord
GetBSizePercentBasisAdjustment(StyleBoxSizing aBoxSizing
,
4124 bool aHorizontalAxis
,
4125 bool aResolvesAgainstPaddingBox
);
4127 static bool GetPercentBSize(const LengthPercentage
& aStyle
, nsIFrame
* aFrame
,
4128 bool aHorizontalAxis
, nscoord
& aResult
);
4130 // Only call on style coords for which GetAbsoluteCoord returned false.
4131 template <typename SizeOrMaxSize
>
4132 static bool GetPercentBSize(const SizeOrMaxSize
& aStyle
, nsIFrame
* aFrame
,
4133 bool aHorizontalAxis
, nscoord
& aResult
) {
4134 if (!aStyle
.IsLengthPercentage()) {
4137 return GetPercentBSize(aStyle
.AsLengthPercentage(), aFrame
, aHorizontalAxis
,
4141 static bool GetPercentBSize(const LengthPercentage
& aStyle
, nsIFrame
* aFrame
,
4142 bool aHorizontalAxis
, nscoord
& aResult
) {
4143 if (!aStyle
.HasPercent()) {
4147 MOZ_ASSERT(!aStyle
.ConvertsToLength(),
4148 "GetAbsoluteCoord should have handled this");
4150 // During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses
4151 // SetComputedHeight on the reflow input for its child to propagate its
4152 // computed height to the scrolled content. So here we skip to the scroll
4153 // frame that contains this scrolled content in order to get the same
4154 // behavior as layout when computing percentage heights.
4155 nsIFrame
* f
= aFrame
->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME
);
4157 MOZ_ASSERT_UNREACHABLE("top of frame tree not a containing block");
4161 WritingMode wm
= f
->GetWritingMode();
4163 const nsStylePosition
* pos
= f
->StylePosition();
4164 const auto& bSizeCoord
= pos
->BSize(wm
);
4166 if (!GetAbsoluteCoord(bSizeCoord
, h
) &&
4167 !GetPercentBSize(bSizeCoord
, f
, aHorizontalAxis
, h
)) {
4168 LayoutFrameType fType
= f
->Type();
4169 if (fType
!= LayoutFrameType::Viewport
&&
4170 fType
!= LayoutFrameType::Canvas
&&
4171 fType
!= LayoutFrameType::PageContent
) {
4172 // There's no basis for the percentage height, so it acts like auto.
4173 // Should we consider a max-height < min-height pair a basis for
4174 // percentage heights? The spec is somewhat unclear, and not doing
4175 // so is simpler and avoids troubling discontinuities in behavior,
4176 // so I'll choose not to. -LDB
4179 // For the viewport, canvas, and page-content kids, the percentage
4180 // basis is just the parent block-size.
4182 if (h
== NS_UNCONSTRAINEDSIZE
) {
4183 // We don't have a percentage basis after all
4188 const auto& maxBSizeCoord
= pos
->MaxBSize(wm
);
4191 if (GetAbsoluteCoord(maxBSizeCoord
, maxh
) ||
4192 GetPercentBSize(maxBSizeCoord
, f
, aHorizontalAxis
, maxh
)) {
4193 if (maxh
< h
) h
= maxh
;
4196 const auto& minBSizeCoord
= pos
->MinBSize(wm
);
4199 if (GetAbsoluteCoord(minBSizeCoord
, minh
) ||
4200 GetPercentBSize(minBSizeCoord
, f
, aHorizontalAxis
, minh
)) {
4206 // If we're an abspos box, percentages in that case resolve against the
4209 // TODO: This could conceivably cause some problems with fieldsets (which are
4210 // the other place that wants to ignore padding), but solving that here
4211 // without hardcoding a check for f being a fieldset-content frame is a bit of
4213 const bool resolvesAgainstPaddingBox
= aFrame
->IsAbsolutelyPositioned();
4214 h
+= GetBSizePercentBasisAdjustment(pos
->mBoxSizing
, f
, aHorizontalAxis
,
4215 resolvesAgainstPaddingBox
);
4217 aResult
= std::max(aStyle
.Resolve(std::max(h
, 0)), 0);
4221 // Return true if aStyle can be resolved to a definite value and if so
4222 // return that value in aResult.
4223 static bool GetDefiniteSize(const LengthPercentage
& aStyle
, nsIFrame
* aFrame
,
4225 const Maybe
<LogicalSize
>& aPercentageBasis
,
4227 if (aStyle
.ConvertsToLength()) {
4228 *aResult
= aStyle
.ToLength();
4232 if (!aPercentageBasis
) {
4236 auto wm
= aFrame
->GetWritingMode();
4237 nscoord pb
= aIsInlineAxis
? aPercentageBasis
.value().ISize(wm
)
4238 : aPercentageBasis
.value().BSize(wm
);
4239 if (pb
== NS_UNCONSTRAINEDSIZE
) {
4242 *aResult
= std::max(0, aStyle
.Resolve(pb
));
4246 // Return true if aStyle can be resolved to a definite value and if so
4247 // return that value in aResult.
4248 template <typename SizeOrMaxSize
>
4249 static bool GetDefiniteSize(const SizeOrMaxSize
& aStyle
, nsIFrame
* aFrame
,
4251 const Maybe
<LogicalSize
>& aPercentageBasis
,
4253 if (!aStyle
.IsLengthPercentage()) {
4256 return GetDefiniteSize(aStyle
.AsLengthPercentage(), aFrame
, aIsInlineAxis
,
4257 aPercentageBasis
, aResult
);
4260 // NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug
4261 // 1363918). Please do not add new uses of this function.
4263 // Get the amount of space to add or subtract out of aFrame's 'block-size' or
4264 // property value due its borders and paddings, given the box-sizing value in
4267 // aHorizontalAxis is true if our inline direction is horizontal and our block
4268 // direction is vertical. aResolvesAgainstPaddingBox is true if padding should
4269 // be added or not removed.
4270 static nscoord
GetBSizePercentBasisAdjustment(StyleBoxSizing aBoxSizing
,
4272 bool aHorizontalAxis
,
4273 bool aResolvesAgainstPaddingBox
) {
4274 nscoord adjustment
= 0;
4275 if (aBoxSizing
== StyleBoxSizing::Border
) {
4276 const auto& border
= aFrame
->StyleBorder()->GetComputedBorder();
4277 adjustment
-= aHorizontalAxis
? border
.TopBottom() : border
.LeftRight();
4279 if ((aBoxSizing
== StyleBoxSizing::Border
) == !aResolvesAgainstPaddingBox
) {
4280 const auto& stylePadding
= aFrame
->StylePadding()->mPadding
;
4281 const LengthPercentage
& paddingStart
=
4282 stylePadding
.Get(aHorizontalAxis
? eSideTop
: eSideLeft
);
4283 const LengthPercentage
& paddingEnd
=
4284 stylePadding
.Get(aHorizontalAxis
? eSideBottom
: eSideRight
);
4286 // XXXbz Calling GetPercentBSize on padding values looks bogus, since
4287 // percent padding is always a percentage of the inline-size of the
4288 // containing block. We should perhaps just treat non-absolute paddings
4289 // here as 0 instead, except that in some cases the width may in fact be
4290 // known. See bug 1231059.
4291 if (GetAbsoluteCoord(paddingStart
, pad
) ||
4292 GetPercentBSize(paddingStart
, aFrame
, aHorizontalAxis
, pad
)) {
4293 adjustment
+= aResolvesAgainstPaddingBox
? pad
: -pad
;
4295 if (GetAbsoluteCoord(paddingEnd
, pad
) ||
4296 GetPercentBSize(paddingEnd
, aFrame
, aHorizontalAxis
, pad
)) {
4297 adjustment
+= aResolvesAgainstPaddingBox
? pad
: -pad
;
4303 // Get the amount of space taken out of aFrame's content area due to its
4304 // borders and paddings given the box-sizing value in aBoxSizing. We don't
4305 // get aBoxSizing from the frame because some callers want to compute this for
4306 // specific box-sizing values.
4307 // aIsInlineAxis is true if we're computing for aFrame's inline axis.
4308 // aIgnorePadding is true if padding should be ignored.
4309 static nscoord
GetDefiniteSizeTakenByBoxSizing(
4310 StyleBoxSizing aBoxSizing
, nsIFrame
* aFrame
, bool aIsInlineAxis
,
4311 bool aIgnorePadding
, const Maybe
<LogicalSize
>& aPercentageBasis
) {
4312 nscoord sizeTakenByBoxSizing
= 0;
4313 if (MOZ_UNLIKELY(aBoxSizing
== StyleBoxSizing::Border
)) {
4314 const bool isHorizontalAxis
=
4315 aIsInlineAxis
== !aFrame
->GetWritingMode().IsVertical();
4316 const nsStyleBorder
* styleBorder
= aFrame
->StyleBorder();
4317 sizeTakenByBoxSizing
= isHorizontalAxis
4318 ? styleBorder
->GetComputedBorder().LeftRight()
4319 : styleBorder
->GetComputedBorder().TopBottom();
4320 if (!aIgnorePadding
) {
4321 const auto& stylePadding
= aFrame
->StylePadding()->mPadding
;
4322 const LengthPercentage
& pStart
=
4323 stylePadding
.Get(isHorizontalAxis
? eSideLeft
: eSideTop
);
4324 const LengthPercentage
& pEnd
=
4325 stylePadding
.Get(isHorizontalAxis
? eSideRight
: eSideBottom
);
4327 // XXXbz Calling GetPercentBSize on padding values looks bogus, since
4328 // percent padding is always a percentage of the inline-size of the
4329 // containing block. We should perhaps just treat non-absolute paddings
4330 // here as 0 instead, except that in some cases the width may in fact be
4331 // known. See bug 1231059.
4332 if (GetDefiniteSize(pStart
, aFrame
, aIsInlineAxis
, aPercentageBasis
,
4334 (aPercentageBasis
.isNothing() &&
4335 GetPercentBSize(pStart
, aFrame
, isHorizontalAxis
, pad
))) {
4336 sizeTakenByBoxSizing
+= pad
;
4338 if (GetDefiniteSize(pEnd
, aFrame
, aIsInlineAxis
, aPercentageBasis
,
4340 (aPercentageBasis
.isNothing() &&
4341 GetPercentBSize(pEnd
, aFrame
, isHorizontalAxis
, pad
))) {
4342 sizeTakenByBoxSizing
+= pad
;
4346 return sizeTakenByBoxSizing
;
4350 * Handles only max-content and min-content, and
4351 * -moz-fit-content for min-width and max-width, since the others
4352 * (-moz-fit-content for width, and -moz-available) have no effect on
4355 static bool GetIntrinsicCoord(nsIFrame::ExtremumLength aStyle
,
4356 gfxContext
* aRenderingContext
, nsIFrame
* aFrame
,
4357 Maybe
<nscoord
> aInlineSizeFromAspectRatio
,
4358 nsIFrame::SizeProperty aProperty
,
4359 nscoord aContentBoxToBoxSizingDiff
,
4361 if (aStyle
== nsIFrame::ExtremumLength::MozAvailable
) {
4365 if (aStyle
== nsIFrame::ExtremumLength::FitContentFunction
) {
4366 // fit-content() should be handled by the caller.
4370 if (aStyle
== nsIFrame::ExtremumLength::FitContent
) {
4371 switch (aProperty
) {
4372 case nsIFrame::SizeProperty::Size
:
4373 // handle like 'width: auto'
4375 case nsIFrame::SizeProperty::MaxSize
:
4376 // constrain large 'width' values down to max-content
4377 aStyle
= nsIFrame::ExtremumLength::MaxContent
;
4379 case nsIFrame::SizeProperty::MinSize
:
4380 // constrain small 'width' or 'max-width' values up to min-content
4381 aStyle
= nsIFrame::ExtremumLength::MinContent
;
4386 NS_ASSERTION(aStyle
== nsIFrame::ExtremumLength::MinContent
||
4387 aStyle
== nsIFrame::ExtremumLength::MaxContent
,
4388 "should have reduced everything remaining to one of these");
4390 // If aFrame is a container for font size inflation, then shrink
4391 // wrapping inside of it should not apply font size inflation.
4392 AutoMaybeDisableFontInflation
an(aFrame
);
4394 if (aInlineSizeFromAspectRatio
) {
4395 aResult
= *aInlineSizeFromAspectRatio
;
4396 } else if (aStyle
== nsIFrame::ExtremumLength::MaxContent
) {
4397 aResult
= aFrame
->GetPrefISize(aRenderingContext
);
4399 aResult
= aFrame
->GetMinISize(aRenderingContext
);
4402 aResult
+= aContentBoxToBoxSizingDiff
;
4407 template <typename SizeOrMaxSize
>
4408 static bool GetIntrinsicCoord(const SizeOrMaxSize
& aStyle
,
4409 gfxContext
* aRenderingContext
, nsIFrame
* aFrame
,
4410 Maybe
<nscoord
> aInlineSizeFromAspectRatio
,
4411 nsIFrame::SizeProperty aProperty
,
4412 nscoord aContentBoxToBoxSizingDiff
,
4414 auto length
= nsIFrame::ToExtremumLength(aStyle
);
4418 return GetIntrinsicCoord(*length
, aRenderingContext
, aFrame
,
4419 aInlineSizeFromAspectRatio
, aProperty
,
4420 aContentBoxToBoxSizingDiff
, aResult
);
4423 #undef DEBUG_INTRINSIC_WIDTH
4425 #ifdef DEBUG_INTRINSIC_WIDTH
4426 static int32_t gNoiseIndent
= 0;
4429 static nscoord
GetFitContentSizeForMaxOrPreferredSize(
4430 const IntrinsicISizeType aType
, const nsIFrame::SizeProperty aProperty
,
4431 const nsIFrame
* aFrame
, const LengthPercentage
& aStyleSize
,
4432 const nscoord aInitialValue
, const nscoord aMinContentSize
,
4433 const nscoord aMaxContentSize
) {
4434 MOZ_ASSERT(aProperty
!= nsIFrame::SizeProperty::MinSize
);
4436 nscoord size
= NS_UNCONSTRAINEDSIZE
;
4437 // 1. Treat fit-content()'s arg as a plain LengthPercentage
4438 // However, we have to handle the cyclic percentage contribution first.
4440 // https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
4441 if (aType
== IntrinsicISizeType::MinISize
&&
4442 aFrame
->IsPercentageResolvedAgainstZero(aStyleSize
, aProperty
)) {
4443 // Case (c) in the spec.
4444 // FIXME: This doesn't follow the spec for calc(). We should fix this in
4447 } else if (!GetAbsoluteCoord(aStyleSize
, size
)) {
4448 // As initial value. Case (a) and (b) in the spec.
4449 size
= aInitialValue
;
4452 // 2. Clamp size by min-content and max-content.
4453 return std::max(aMinContentSize
, std::min(aMaxContentSize
, size
));
4457 * Add aOffsets which describes what to add on outside of the content box
4458 * aContentSize (controlled by 'box-sizing') and apply min/max properties.
4459 * We have to account for these properties after getting all the offsets
4460 * (margin, border, padding) because percentages do not operate linearly.
4461 * Doing this is ok because although percentages aren't handled linearly,
4462 * they are handled monotonically.
4464 * @param aContentSize the content size calculated so far
4465 (@see IntrinsicForContainer)
4466 * @param aContentMinSize ditto min content size
4467 * @param aStyleSize a 'width' or 'height' property value
4468 * @param aFixedMinSize if aStyleMinSize is a definite size then this points to
4469 * the value, otherwise nullptr
4470 * @param aStyleMinSize a 'min-width' or 'min-height' property value
4471 * @param aFixedMaxSize if aStyleMaxSize is a definite size then this points to
4472 * the value, otherwise nullptr
4473 * @param aStyleMaxSize a 'max-width' or 'max-height' property value
4474 * @param aInlineSizeFromAspectRatio the content-box inline size computed from
4475 * aspect-ratio and the definite block size.
4476 * We use this value to resolve
4477 * {min|max}-content.
4478 * @param aFlags same as for IntrinsicForContainer
4479 * @param aContainerWM the container's WM
4481 static nscoord
AddIntrinsicSizeOffset(
4482 gfxContext
* aRenderingContext
, nsIFrame
* aFrame
,
4483 const nsIFrame::IntrinsicSizeOffsetData
& aOffsets
, IntrinsicISizeType aType
,
4484 StyleBoxSizing aBoxSizing
, nscoord aContentSize
, nscoord aContentMinSize
,
4485 const StyleSize
& aStyleSize
, const nscoord
* aFixedMinSize
,
4486 const StyleSize
& aStyleMinSize
, const nscoord
* aFixedMaxSize
,
4487 const StyleMaxSize
& aStyleMaxSize
,
4488 Maybe
<nscoord
> aInlineSizeFromAspectRatio
, uint32_t aFlags
,
4489 PhysicalAxis aAxis
) {
4490 nscoord result
= aContentSize
;
4491 nscoord min
= aContentMinSize
;
4492 nscoord coordOutsideSize
= 0;
4493 nscoord contentBoxToBoxSizingDiff
= 0;
4495 if (!(aFlags
& nsLayoutUtils::IGNORE_PADDING
)) {
4496 coordOutsideSize
+= aOffsets
.padding
;
4499 coordOutsideSize
+= aOffsets
.border
;
4501 if (aBoxSizing
== StyleBoxSizing::Border
) {
4502 // Store the padding of the box so we can pass it into
4503 // functions that works with content box-sizing.
4504 contentBoxToBoxSizingDiff
= coordOutsideSize
;
4506 min
+= coordOutsideSize
;
4507 result
= NSCoordSaturatingAdd(result
, coordOutsideSize
);
4509 coordOutsideSize
= 0;
4512 coordOutsideSize
+= aOffsets
.margin
;
4514 min
+= coordOutsideSize
;
4516 // Compute min-content/max-content for fit-content().
4517 nscoord minContent
= 0;
4518 nscoord maxContent
= NS_UNCONSTRAINEDSIZE
;
4519 if (aStyleSize
.IsFitContentFunction() ||
4520 aStyleMaxSize
.IsFitContentFunction() ||
4521 aStyleMinSize
.IsFitContentFunction()) {
4522 if (aInlineSizeFromAspectRatio
) {
4523 minContent
= maxContent
= *aInlineSizeFromAspectRatio
;
4525 minContent
= aFrame
->GetMinISize(aRenderingContext
);
4526 maxContent
= aFrame
->GetPrefISize(aRenderingContext
);
4528 minContent
+= contentBoxToBoxSizingDiff
;
4529 maxContent
+= contentBoxToBoxSizingDiff
;
4533 nscoord size
= NS_UNCONSTRAINEDSIZE
;
4534 if (aType
== IntrinsicISizeType::MinISize
&&
4535 aFrame
->IsPercentageResolvedAgainstZero(aStyleSize
, aStyleMaxSize
)) {
4536 // XXX bug 1463700: this doesn't handle calc() according to spec
4537 result
= 0; // let |min| handle padding/border/margin
4538 } else if (GetAbsoluteCoord(aStyleSize
, size
) ||
4539 GetIntrinsicCoord(aStyleSize
, aRenderingContext
, aFrame
,
4540 aInlineSizeFromAspectRatio
,
4541 nsIFrame::SizeProperty::Size
,
4542 contentBoxToBoxSizingDiff
, size
)) {
4543 result
= size
+ coordOutsideSize
;
4544 } else if (aStyleSize
.IsFitContentFunction()) {
4545 // |result| here is the content size or border size, depends on
4546 // StyleBoxSizing. We use it as the initial value when handling the cyclic
4548 nscoord initial
= result
;
4549 nscoord fitContentFuncSize
= GetFitContentSizeForMaxOrPreferredSize(
4550 aType
, nsIFrame::SizeProperty::Size
, aFrame
,
4551 aStyleSize
.AsFitContentFunction(), initial
, minContent
, maxContent
);
4552 // Add border and padding.
4553 result
= NSCoordSaturatingAdd(fitContentFuncSize
, coordOutsideSize
);
4555 result
= NSCoordSaturatingAdd(result
, coordOutsideSize
);
4558 // Compute max-size.
4559 nscoord maxSize
= aFixedMaxSize
? *aFixedMaxSize
: 0;
4560 if (aFixedMaxSize
|| GetIntrinsicCoord(aStyleMaxSize
, aRenderingContext
,
4561 aFrame
, aInlineSizeFromAspectRatio
,
4562 nsIFrame::SizeProperty::MaxSize
,
4563 contentBoxToBoxSizingDiff
, maxSize
)) {
4564 maxSize
+= coordOutsideSize
;
4565 if (result
> maxSize
) {
4568 } else if (aStyleMaxSize
.IsFitContentFunction()) {
4569 nscoord fitContentFuncSize
= GetFitContentSizeForMaxOrPreferredSize(
4570 aType
, nsIFrame::SizeProperty::MaxSize
, aFrame
,
4571 aStyleMaxSize
.AsFitContentFunction(), NS_UNCONSTRAINEDSIZE
, minContent
,
4573 maxSize
= NSCoordSaturatingAdd(fitContentFuncSize
, coordOutsideSize
);
4574 if (result
> maxSize
) {
4579 // Compute min-size.
4580 nscoord minSize
= aFixedMinSize
? *aFixedMinSize
: 0;
4581 if (aFixedMinSize
|| GetIntrinsicCoord(aStyleMinSize
, aRenderingContext
,
4582 aFrame
, aInlineSizeFromAspectRatio
,
4583 nsIFrame::SizeProperty::MinSize
,
4584 contentBoxToBoxSizingDiff
, minSize
)) {
4585 minSize
+= coordOutsideSize
;
4586 if (result
< minSize
) {
4589 } else if (aStyleMinSize
.IsFitContentFunction()) {
4590 if (!GetAbsoluteCoord(aStyleMinSize
.AsFitContentFunction(), minSize
)) {
4591 // FIXME: Bug 1463700, we should resolve only the percentage part to 0
4592 // such as min-width: fit-content(calc(50% + 50px)).
4595 nscoord fitContentFuncSize
=
4596 std::max(minContent
, std::min(maxContent
, minSize
));
4597 minSize
= NSCoordSaturatingAdd(fitContentFuncSize
, coordOutsideSize
);
4598 if (result
< minSize
) {
4607 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
4608 if (aFrame
->IsThemed(disp
)) {
4609 nsPresContext
* pc
= aFrame
->PresContext();
4610 LayoutDeviceIntSize devSize
= pc
->Theme()->GetMinimumWidgetSize(
4611 pc
, aFrame
, disp
->EffectiveAppearance());
4612 nscoord themeSize
= pc
->DevPixelsToAppUnits(
4613 aAxis
== PhysicalAxis::Vertical
? devSize
.height
: devSize
.width
);
4614 // GetMinimumWidgetSize() returns a border-box width.
4615 themeSize
+= aOffsets
.margin
;
4616 if (themeSize
> result
) {
4623 static void AddStateBitToAncestors(nsIFrame
* aFrame
, nsFrameState aBit
) {
4624 for (nsIFrame
* f
= aFrame
; f
; f
= f
->GetParent()) {
4625 if (f
->HasAnyStateBits(aBit
)) {
4628 f
->AddStateBits(aBit
);
4633 nscoord
nsLayoutUtils::IntrinsicForAxis(
4634 PhysicalAxis aAxis
, gfxContext
* aRenderingContext
, nsIFrame
* aFrame
,
4635 IntrinsicISizeType aType
, const Maybe
<LogicalSize
>& aPercentageBasis
,
4636 uint32_t aFlags
, nscoord aMarginBoxMinSizeClamp
) {
4637 MOZ_ASSERT(aFrame
, "null frame");
4638 MOZ_ASSERT(aFrame
->GetParent(),
4639 "IntrinsicForAxis called on frame not in tree");
4640 MOZ_ASSERT(aFrame
->GetParent()->Type() != LayoutFrameType::GridContainer
||
4641 aPercentageBasis
.isSome(),
4642 "grid layout should always pass a percentage basis");
4644 const bool horizontalAxis
= MOZ_LIKELY(aAxis
== PhysicalAxis::Horizontal
);
4645 #ifdef DEBUG_INTRINSIC_WIDTH
4646 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
4647 aFrame
->ListTag(stderr
);
4648 printf_stderr(" %s %s intrinsic size for container:\n",
4649 aType
== IntrinsicISizeType::MinISize
? "min" : "pref",
4650 horizontalAxis
? "horizontal" : "vertical");
4653 // If aFrame is a container for font size inflation, then shrink
4654 // wrapping inside of it should not apply font size inflation.
4655 AutoMaybeDisableFontInflation
an(aFrame
);
4657 // We want the size this frame will contribute to the parent's inline-size,
4658 // so we work in the parent's writing mode; but if aFrame is orthogonal to
4659 // its parent, we'll need to look at its BSize instead of min/pref-ISize.
4660 const nsStylePosition
* stylePos
= aFrame
->StylePosition();
4661 StyleBoxSizing boxSizing
= stylePos
->mBoxSizing
;
4663 StyleSize styleMinISize
=
4664 horizontalAxis
? stylePos
->mMinWidth
: stylePos
->mMinHeight
;
4665 StyleSize styleISize
=
4666 (aFlags
& MIN_INTRINSIC_ISIZE
)
4668 : (horizontalAxis
? stylePos
->mWidth
: stylePos
->mHeight
);
4669 MOZ_ASSERT(!(aFlags
& MIN_INTRINSIC_ISIZE
) || styleISize
.IsAuto() ||
4670 nsIFrame::ToExtremumLength(styleISize
),
4671 "should only use MIN_INTRINSIC_ISIZE for intrinsic values");
4672 StyleMaxSize styleMaxISize
=
4673 horizontalAxis
? stylePos
->mMaxWidth
: stylePos
->mMaxHeight
;
4675 PhysicalAxis ourInlineAxis
=
4676 aFrame
->GetWritingMode().PhysicalAxis(LogicalAxis::Inline
);
4677 const bool isInlineAxis
= aAxis
== ourInlineAxis
;
4679 auto resetIfKeywords
= [](StyleSize
& aSize
, StyleSize
& aMinSize
,
4680 StyleMaxSize
& aMaxSize
) {
4681 if (!aSize
.IsLengthPercentage()) {
4682 aSize
= StyleSize::Auto();
4684 if (!aMinSize
.IsLengthPercentage()) {
4685 aMinSize
= StyleSize::Auto();
4687 if (!aMaxSize
.IsLengthPercentage()) {
4688 aMaxSize
= StyleMaxSize::None();
4691 // According to the spec, max-content and min-content should behave as the
4692 // property's initial values in block axis.
4693 // It also make senses to use the initial values for -moz-fit-content and
4694 // -moz-available for intrinsic size in block axis. Therefore, we reset them
4696 if (!isInlineAxis
) {
4697 resetIfKeywords(styleISize
, styleMinISize
, styleMaxISize
);
4700 // We build up two values starting with the content box, and then
4701 // adding padding, border and margin. The result is normally
4702 // |result|. Then, when we handle 'width', 'min-width', and
4703 // 'max-width', we use the results we've been building in |min| as a
4704 // minimum, overriding 'min-width'. This ensures two things:
4705 // * that we don't let a value of 'box-sizing' specifying a width
4706 // smaller than the padding/border inside the box-sizing box give
4707 // a content width less than zero
4708 // * that we prevent tables from becoming smaller than their
4709 // intrinsic minimum width
4710 nscoord result
= 0, min
= 0;
4713 bool haveFixedMaxISize
= GetAbsoluteCoord(styleMaxISize
, maxISize
);
4716 // Treat "min-width: auto" as 0.
4717 bool haveFixedMinISize
;
4718 if (styleMinISize
.IsAuto()) {
4719 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
4720 // flex items. However, we don't need to worry about that here, because
4721 // flex items' min-sizes are intentionally ignored until the flex
4722 // container explicitly considers them during space distribution.
4724 haveFixedMinISize
= true;
4726 haveFixedMinISize
= GetAbsoluteCoord(styleMinISize
, minISize
);
4729 auto childWM
= aFrame
->GetWritingMode();
4730 nscoord pmPercentageBasis
= NS_UNCONSTRAINEDSIZE
;
4731 if (aPercentageBasis
.isSome()) {
4732 // The padding/margin percentage basis is the inline-size in the parent's
4735 aFrame
->GetParent()->GetWritingMode().IsOrthogonalTo(childWM
)
4736 ? aPercentageBasis
->BSize(childWM
)
4737 : aPercentageBasis
->ISize(childWM
);
4739 nsIFrame::IntrinsicSizeOffsetData offsets
=
4740 MOZ_LIKELY(isInlineAxis
)
4741 ? aFrame
->IntrinsicISizeOffsets(pmPercentageBasis
)
4742 : aFrame
->IntrinsicBSizeOffsets(pmPercentageBasis
);
4744 auto getContentBoxSizeToBoxSizingAdjust
=
4745 [childWM
, &offsets
, &aFrame
, isInlineAxis
,
4746 pmPercentageBasis
](const StyleBoxSizing aBoxSizing
) {
4747 return aBoxSizing
== StyleBoxSizing::Border
4748 ? LogicalSize(childWM
,
4749 (isInlineAxis
? offsets
4750 : aFrame
->IntrinsicISizeOffsets(
4753 (!isInlineAxis
? offsets
4754 : aFrame
->IntrinsicBSizeOffsets(
4757 : LogicalSize(childWM
);
4760 Maybe
<nscoord
> inlineSizeFromAspectRatio
;
4761 Maybe
<LogicalSize
> contentBoxSizeToBoxSizingAdjust
;
4763 const bool ignorePadding
=
4764 (aFlags
& IGNORE_PADDING
) || aFrame
->IsAbsolutelyPositioned();
4766 // If we have a specified width (or a specified 'min-width' greater
4767 // than the specified 'max-width', which works out to the same thing),
4768 // don't even bother getting the frame's intrinsic width, because in
4769 // this case GetAbsoluteCoord(styleISize, w) will always succeed, so
4770 // we'll never need the intrinsic dimensions.
4771 if (styleISize
.IsMaxContent() || styleISize
.IsMinContent()) {
4772 MOZ_ASSERT(isInlineAxis
);
4773 // -moz-fit-content and -moz-available enumerated widths compute intrinsic
4774 // widths just like auto.
4775 // For max-content and min-content, we handle them like
4776 // specified widths, but ignore box-sizing.
4777 boxSizing
= StyleBoxSizing::Content
;
4778 } else if (!styleISize
.ConvertsToLength() &&
4779 !(styleISize
.IsFitContentFunction() &&
4780 styleISize
.AsFitContentFunction().ConvertsToLength()) &&
4781 !(haveFixedMinISize
&& haveFixedMaxISize
&&
4782 maxISize
<= minISize
)) {
4783 #ifdef DEBUG_INTRINSIC_WIDTH
4786 if (MOZ_UNLIKELY(!isInlineAxis
)) {
4787 IntrinsicSize intrinsicSize
= aFrame
->GetIntrinsicSize();
4788 const auto& intrinsicBSize
=
4789 horizontalAxis
? intrinsicSize
.width
: intrinsicSize
.height
;
4790 if (intrinsicBSize
) {
4791 result
= *intrinsicBSize
;
4793 // We don't have an intrinsic bsize and we need aFrame's block-dir size.
4794 if (aFlags
& BAIL_IF_REFLOW_NEEDED
) {
4795 return NS_INTRINSIC_ISIZE_UNKNOWN
;
4797 // XXX Unfortunately, we probably don't know this yet, so this is
4798 // wrong... but it's not clear what we should do. If aFrame's inline
4799 // size hasn't been determined yet, we can't necessarily figure out its
4800 // block size either. For now, authors who put orthogonal elements into
4801 // things like buttons or table cells may have to explicitly provide
4802 // sizes rather than expecting intrinsic sizing to work "perfectly" in
4803 // underspecified cases.
4804 result
= aFrame
->BSize();
4807 result
= aType
== IntrinsicISizeType::MinISize
4808 ? aFrame
->GetMinISize(aRenderingContext
)
4809 : aFrame
->GetPrefISize(aRenderingContext
);
4811 #ifdef DEBUG_INTRINSIC_WIDTH
4813 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
4814 aFrame
->ListTag(stderr
);
4815 printf_stderr(" %s %s intrinsic size from frame is %d.\n",
4816 aType
== IntrinsicISizeType::MinISize
? "min" : "pref",
4817 horizontalAxis
? "horizontal" : "vertical", result
);
4820 // Handle elements with an intrinsic ratio (or size) and a specified
4821 // height, min-height, or max-height.
4823 // 1. We treat "min-height:auto" as "0" for the purpose of this code,
4824 // since that's what it means in all cases except for on flex items -- and
4825 // even there, we're supposed to ignore it (i.e. treat it as 0) until the
4826 // flex container explicitly considers it.
4827 // 2. The 'B' in |styleBSize|, |styleMinBSize|, and |styleMaxBSize|
4828 // represents the ratio-determining axis of |aFrame|. It could be the inline
4829 // axis or the block axis of |aFrame|. (So we are calculating the size
4830 // along the ratio-dependent axis in this if-branch.)
4831 StyleSize styleBSize
=
4832 horizontalAxis
? stylePos
->mHeight
: stylePos
->mWidth
;
4833 StyleSize styleMinBSize
=
4834 horizontalAxis
? stylePos
->mMinHeight
: stylePos
->mMinWidth
;
4835 StyleMaxSize styleMaxBSize
=
4836 horizontalAxis
? stylePos
->mMaxHeight
: stylePos
->mMaxWidth
;
4838 // According to the spec, max-content and min-content should behave as the
4839 // property's initial values in block axis.
4840 // It also make senses to use the initial values for -moz-fit-content and
4841 // -moz-available for intrinsic size in block axis. Therefore, we reset them
4844 resetIfKeywords(styleBSize
, styleMinBSize
, styleMaxBSize
);
4847 // If our BSize or min/max-BSize properties are set to values that we can
4848 // resolve and that will impose a constraint when transferred through our
4849 // aspect ratio (if we have one), then compute and apply that constraint.
4851 // (Note: This helper-bool & lambda just let us optimize away the actual
4852 // transferring-and-clamping arithmetic, for the common case where we can
4853 // tell that none of the block-axis size properties establish a meaningful
4854 // transferred constraint.)
4855 const bool mightHaveBlockAxisConstraintToTransfer
= [&] {
4856 if (!styleBSize
.BehavesLikeInitialValueOnBlockAxis()) {
4857 return true; // BSize property might have a constraint to transfer.
4859 // Check for min-BSize values that would obviously produce zero in the
4860 // transferring logic that follows; zero is trivially-ignorable as a
4861 // transferred lower-bound. (These include the the property's initial
4862 // value, explicit 0, and values that are equivalent to these.)
4863 bool minBSizeHasNoConstraintToTransfer
=
4864 styleMinBSize
.BehavesLikeInitialValueOnBlockAxis() ||
4865 (styleMinBSize
.IsLengthPercentage() &&
4866 styleMinBSize
.AsLengthPercentage().IsDefinitelyZero());
4867 if (!minBSizeHasNoConstraintToTransfer
) {
4868 return true; // min-BSize property might have a constraint to transfer.
4870 if (!styleMaxBSize
.BehavesLikeInitialValueOnBlockAxis()) {
4871 return true; // max-BSize property might have a constraint to transfer.
4875 if (mightHaveBlockAxisConstraintToTransfer
) {
4876 if (AspectRatio ratio
= aFrame
->GetAspectRatio()) {
4877 AddStateBitToAncestors(
4878 aFrame
, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE
);
4880 nscoord bSizeTakenByBoxSizing
= GetDefiniteSizeTakenByBoxSizing(
4881 boxSizing
, aFrame
, !isInlineAxis
, ignorePadding
, aPercentageBasis
);
4882 contentBoxSizeToBoxSizingAdjust
.emplace(
4883 getContentBoxSizeToBoxSizingAdjust(boxSizing
));
4884 // NOTE: This is only the minContentSize if we've been passed
4885 // MIN_INTRINSIC_ISIZE (which is fine, because this should only be used
4886 // inside a check for that flag).
4887 nscoord minContentSize
= result
;
4889 if (GetDefiniteSize(styleBSize
, aFrame
, !isInlineAxis
, aPercentageBasis
,
4891 (aPercentageBasis
.isNothing() &&
4892 GetPercentBSize(styleBSize
, aFrame
, horizontalAxis
, h
))) {
4893 h
= std::max(0, h
- bSizeTakenByBoxSizing
);
4894 // We are computing the size of |aFrame|, so we use the inline & block
4895 // dimensions of |aFrame|.
4896 result
= ratio
.ComputeRatioDependentSize(
4897 isInlineAxis
? LogicalAxis::Inline
: LogicalAxis::Block
, childWM
,
4898 h
, *contentBoxSizeToBoxSizingAdjust
);
4899 // We have get the inlineSizeForAspectRatio value, so we don't have to
4900 // recompute this again below.
4901 inlineSizeFromAspectRatio
.emplace(result
);
4904 if (GetDefiniteSize(styleMaxBSize
, aFrame
, !isInlineAxis
,
4905 aPercentageBasis
, &h
) ||
4906 (aPercentageBasis
.isNothing() &&
4907 GetPercentBSize(styleMaxBSize
, aFrame
, horizontalAxis
, h
))) {
4908 h
= std::max(0, h
- bSizeTakenByBoxSizing
);
4909 nscoord maxISize
= ratio
.ComputeRatioDependentSize(
4910 isInlineAxis
? LogicalAxis::Inline
: LogicalAxis::Block
, childWM
,
4911 h
, *contentBoxSizeToBoxSizingAdjust
);
4912 if (maxISize
< result
) {
4915 if (maxISize
< minContentSize
) {
4916 minContentSize
= maxISize
;
4920 if (GetDefiniteSize(styleMinBSize
, aFrame
, !isInlineAxis
,
4921 aPercentageBasis
, &h
) ||
4922 (aPercentageBasis
.isNothing() &&
4923 GetPercentBSize(styleMinBSize
, aFrame
, horizontalAxis
, h
))) {
4924 h
= std::max(0, h
- bSizeTakenByBoxSizing
);
4925 nscoord minISize
= ratio
.ComputeRatioDependentSize(
4926 isInlineAxis
? LogicalAxis::Inline
: LogicalAxis::Block
, childWM
,
4927 h
, *contentBoxSizeToBoxSizingAdjust
);
4928 if (minISize
> result
) {
4931 if (minISize
> minContentSize
) {
4932 minContentSize
= minISize
;
4936 if (MOZ_UNLIKELY(aFlags
& nsLayoutUtils::MIN_INTRINSIC_ISIZE
) &&
4937 // FIXME: Bug 1715681. Should we use HasReplacedSizing instead
4938 // because IsReplaced is set on some other frames which are
4939 // non-replaced elements, e.g. <select>?
4940 aFrame
->IsReplaced()) {
4941 // This is the 'min-width/height:auto' "transferred size" piece of:
4942 // https://drafts.csswg.org/css-flexbox-1/#min-size-auto
4943 // https://drafts.csswg.org/css-grid/#min-size-auto
4944 // Per spec, we handle it only for replaced elements.
4945 result
= std::min(result
, minContentSize
);
4951 if (aFrame
->IsTableFrame()) {
4952 // Tables can't shrink smaller than their intrinsic minimum width,
4954 min
= aFrame
->GetMinISize(aRenderingContext
);
4957 // If we have an aspect-ratio and a definite block size of |aFrame|, we
4958 // resolve the {min|max}-content size by the aspect-ratio and the block size.
4959 // If |aAxis| is not the inline axis of |aFrame|, {min|max}-content should
4960 // behaves as auto, so we don't need this.
4962 // FIXME(emilio): For -moz-available it seems we shouldn't need this.
4964 // https://github.com/w3c/csswg-drafts/issues/5032
4965 // FIXME: Bug 1670151: Use GetAspectRatio() to cover replaced elements (and
4966 // then we can drop the check of eSupportsAspectRatio).
4967 const AspectRatio ar
= stylePos
->mAspectRatio
.ToLayoutRatio();
4968 if (isInlineAxis
&& ar
&& nsIFrame::ToExtremumLength(styleISize
) &&
4969 aFrame
->SupportsAspectRatio() && !inlineSizeFromAspectRatio
) {
4970 // This 'B' in |styleBSize| means the block size of |aFrame|. We go into
4971 // this branch only if |aAxis| is the inline axis of |aFrame|.
4972 const StyleSize
& styleBSize
=
4973 horizontalAxis
? stylePos
->mHeight
: stylePos
->mWidth
;
4975 if (GetDefiniteSize(styleBSize
, aFrame
, !isInlineAxis
, aPercentageBasis
,
4977 (aPercentageBasis
.isNothing() &&
4978 GetPercentBSize(styleBSize
, aFrame
, horizontalAxis
, bSize
))) {
4979 // We cannot reuse |boxSizing| because it may be updated to content-box
4980 // in the above if-branch.
4981 const StyleBoxSizing boxSizingForAR
= stylePos
->mBoxSizing
;
4982 if (!contentBoxSizeToBoxSizingAdjust
) {
4983 contentBoxSizeToBoxSizingAdjust
.emplace(
4984 getContentBoxSizeToBoxSizingAdjust(boxSizingForAR
));
4986 nscoord bSizeTakenByBoxSizing
=
4987 GetDefiniteSizeTakenByBoxSizing(boxSizingForAR
, aFrame
, !isInlineAxis
,
4988 ignorePadding
, aPercentageBasis
);
4989 bSize
-= bSizeTakenByBoxSizing
;
4990 inlineSizeFromAspectRatio
.emplace(
4991 ar
.ComputeRatioDependentSize(LogicalAxis::Inline
, childWM
, bSize
,
4992 *contentBoxSizeToBoxSizingAdjust
));
4996 nscoord contentBoxSize
= result
;
4997 result
= AddIntrinsicSizeOffset(
4998 aRenderingContext
, aFrame
, offsets
, aType
, boxSizing
, result
, min
,
4999 styleISize
, haveFixedMinISize
? &minISize
: nullptr, styleMinISize
,
5000 haveFixedMaxISize
? &maxISize
: nullptr, styleMaxISize
,
5001 inlineSizeFromAspectRatio
, aFlags
, aAxis
);
5002 nscoord overflow
= result
- aMarginBoxMinSizeClamp
;
5003 if (MOZ_UNLIKELY(overflow
> 0)) {
5004 nscoord newContentBoxSize
= std::max(nscoord(0), contentBoxSize
- overflow
);
5005 result
-= contentBoxSize
- newContentBoxSize
;
5008 #ifdef DEBUG_INTRINSIC_WIDTH
5009 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
5010 aFrame
->ListTag(stderr
);
5011 printf_stderr(" %s %s intrinsic size for container is %d twips.\n",
5012 aType
== IntrinsicISizeType::MinISize
? "min" : "pref",
5013 horizontalAxis
? "horizontal" : "vertical", result
);
5020 nscoord
nsLayoutUtils::IntrinsicForContainer(gfxContext
* aRenderingContext
,
5022 IntrinsicISizeType aType
,
5024 MOZ_ASSERT(aFrame
&& aFrame
->GetParent());
5025 // We want the size aFrame will contribute to its parent's inline-size.
5027 aFrame
->GetParent()->GetWritingMode().PhysicalAxis(LogicalAxis::Inline
);
5028 return IntrinsicForAxis(axis
, aRenderingContext
, aFrame
, aType
, Nothing(),
5033 nscoord
nsLayoutUtils::MinSizeContributionForAxis(
5034 PhysicalAxis aAxis
, gfxContext
* aRC
, nsIFrame
* aFrame
,
5035 IntrinsicISizeType aType
, const LogicalSize
& aPercentageBasis
,
5038 MOZ_ASSERT(aFrame
->IsFlexOrGridItem(),
5039 "only grid/flex items have this behavior currently");
5041 #ifdef DEBUG_INTRINSIC_WIDTH
5042 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
5043 aFrame
->ListTag(stderr
);
5044 printf_stderr(" %s min-isize for %s WM:\n",
5045 aType
== IntrinsicISizeType::MinISize
? "min" : "pref",
5046 aAxis
== eAxisVertical
? "vertical" : "horizontal");
5049 // Note: this method is only meant for grid/flex items.
5050 const nsStylePosition
* const stylePos
= aFrame
->StylePosition();
5051 StyleSize size
= aAxis
== PhysicalAxis::Horizontal
? stylePos
->mMinWidth
5052 : stylePos
->mMinHeight
;
5053 StyleMaxSize maxSize
= aAxis
== PhysicalAxis::Horizontal
5054 ? stylePos
->mMaxWidth
5055 : stylePos
->mMaxHeight
;
5056 auto childWM
= aFrame
->GetWritingMode();
5057 PhysicalAxis ourInlineAxis
= childWM
.PhysicalAxis(LogicalAxis::Inline
);
5058 // According to the spec, max-content and min-content should behave as the
5059 // property's initial values in block axis.
5060 // It also make senses to use the initial values for -moz-fit-content and
5061 // -moz-available for intrinsic size in block axis. Therefore, we reset them
5063 if (aAxis
!= ourInlineAxis
) {
5064 if (size
.BehavesLikeInitialValueOnBlockAxis()) {
5065 size
= StyleSize::Auto();
5067 if (maxSize
.BehavesLikeInitialValueOnBlockAxis()) {
5068 maxSize
= StyleMaxSize::None();
5073 nscoord
* fixedMinSize
= nullptr;
5074 if (size
.IsAuto()) {
5075 if (aFrame
->StyleDisplay()->IsScrollableOverflow()) {
5076 // min-[width|height]:auto with scrollable overflow computes to
5079 fixedMinSize
= &minSize
;
5081 size
= aAxis
== PhysicalAxis::Horizontal
? stylePos
->mWidth
5082 : stylePos
->mHeight
;
5083 // This is same as above: keywords should behaves as property's initial
5084 // values in block axis.
5085 if (aAxis
!= ourInlineAxis
&& size
.BehavesLikeInitialValueOnBlockAxis()) {
5086 size
= StyleSize::Auto();
5089 if (GetAbsoluteCoord(size
, minSize
)) {
5090 // We have a definite width/height. This is the "specified size" in:
5091 // https://drafts.csswg.org/css-grid/#min-size-auto
5092 fixedMinSize
= &minSize
;
5093 } else if (aFrame
->IsPercentageResolvedAgainstZero(size
, maxSize
)) {
5094 // XXX bug 1463700: this doesn't handle calc() according to spec
5096 fixedMinSize
= &minSize
;
5098 // fall through - the caller will have to deal with "transferred size"
5100 } else if (GetAbsoluteCoord(size
, minSize
)) {
5101 fixedMinSize
= &minSize
;
5102 } else if (size
.IsLengthPercentage()) {
5103 MOZ_ASSERT(size
.HasPercent());
5105 fixedMinSize
= &minSize
;
5108 if (!fixedMinSize
) {
5109 // Let the caller deal with the "content size" cases.
5110 #ifdef DEBUG_INTRINSIC_WIDTH
5111 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
5112 aFrame
->ListTag(stderr
);
5113 printf_stderr(" %s min-isize is indefinite.\n",
5114 aType
== IntrinsicISizeType::MinISize
? "min" : "pref");
5116 return NS_UNCONSTRAINEDSIZE
;
5119 // If aFrame is a container for font size inflation, then shrink
5120 // wrapping inside of it should not apply font size inflation.
5121 AutoMaybeDisableFontInflation
an(aFrame
);
5123 // The padding/margin percentage basis is the inline-size in the parent's
5125 nscoord pmPercentageBasis
=
5126 aFrame
->GetParent()->GetWritingMode().IsOrthogonalTo(childWM
)
5127 ? aPercentageBasis
.BSize(childWM
)
5128 : aPercentageBasis
.ISize(childWM
);
5129 nsIFrame::IntrinsicSizeOffsetData offsets
=
5130 ourInlineAxis
== aAxis
? aFrame
->IntrinsicISizeOffsets(pmPercentageBasis
)
5131 : aFrame
->IntrinsicBSizeOffsets(pmPercentageBasis
);
5134 // Note: aInlineSizeFromAspectRatio is Nothing() here because we don't handle
5135 // "content size" cases here (i.e. |fixedMinSize| is false here).
5136 result
= AddIntrinsicSizeOffset(
5137 aRC
, aFrame
, offsets
, aType
, stylePos
->mBoxSizing
, result
, min
, size
,
5138 fixedMinSize
, size
, nullptr, maxSize
, Nothing(), aFlags
, aAxis
);
5140 #ifdef DEBUG_INTRINSIC_WIDTH
5141 nsIFrame::IndentBy(stderr
, gNoiseIndent
);
5142 aFrame
->ListTag(stderr
);
5143 printf_stderr(" %s min-isize is %d twips.\n",
5144 aType
== IntrinsicISizeType::MinISize
? "min" : "pref", result
);
5151 nscoord
nsLayoutUtils::ComputeBSizeDependentValue(
5152 nscoord aContainingBlockBSize
, const LengthPercentageOrAuto
& aCoord
) {
5153 // XXXldb Some callers explicitly check aContainingBlockBSize
5154 // against NS_UNCONSTRAINEDSIZE *and* unit against eStyleUnit_Percent or
5155 // calc()s containing percents before calling this function.
5156 // However, it would be much more likely to catch problems without
5157 // the unit conditions.
5158 // XXXldb Many callers pass a non-'auto' containing block height when
5159 // according to CSS2.1 they should be passing 'auto'.
5161 NS_UNCONSTRAINEDSIZE
!= aContainingBlockBSize
|| !aCoord
.HasPercent(),
5162 "unexpected containing block block-size");
5164 if (aCoord
.IsAuto()) {
5168 return aCoord
.AsLengthPercentage().Resolve(aContainingBlockBSize
);
5172 void nsLayoutUtils::MarkDescendantsDirty(nsIFrame
* aSubtreeRoot
) {
5173 AutoTArray
<nsIFrame
*, 4> subtrees
;
5174 subtrees
.AppendElement(aSubtreeRoot
);
5176 // dirty descendants, iterating over subtrees that may include
5177 // additional subtrees associated with placeholders
5179 nsIFrame
* subtreeRoot
= subtrees
.PopLastElement();
5181 // Mark all descendants dirty (using an nsTArray stack rather than
5183 // Note that ReflowInput::InitResizeFlags has some similar
5184 // code; see comments there for how and why it differs.
5185 AutoTArray
<nsIFrame
*, 32> stack
;
5186 stack
.AppendElement(subtreeRoot
);
5189 nsIFrame
* f
= stack
.PopLastElement();
5191 f
->MarkIntrinsicISizesDirty();
5193 if (f
->IsPlaceholderFrame()) {
5194 nsIFrame
* oof
= nsPlaceholderFrame::GetRealFrameForPlaceholder(f
);
5195 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot
, oof
)) {
5196 // We have another distinct subtree we need to mark.
5197 subtrees
.AppendElement(oof
);
5201 for (const auto& childList
: f
->ChildLists()) {
5202 for (nsIFrame
* kid
: childList
.mList
) {
5203 stack
.AppendElement(kid
);
5206 } while (stack
.Length() != 0);
5207 } while (subtrees
.Length() != 0);
5211 void nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
5213 AutoTArray
<nsIFrame
*, 32> stack
;
5214 stack
.AppendElement(aFrame
);
5217 nsIFrame
* f
= stack
.PopLastElement();
5219 if (!f
->HasAnyStateBits(
5220 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE
)) {
5223 f
->MarkIntrinsicISizesDirty();
5225 for (const auto& childList
: f
->ChildLists()) {
5226 for (nsIFrame
* kid
: childList
.mList
) {
5227 stack
.AppendElement(kid
);
5230 } while (stack
.Length() != 0);
5233 nsSize
nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(
5234 nscoord minWidth
, nscoord minHeight
, nscoord maxWidth
, nscoord maxHeight
,
5235 nscoord tentWidth
, nscoord tentHeight
) {
5236 // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
5238 if (minWidth
> maxWidth
) maxWidth
= minWidth
;
5239 if (minHeight
> maxHeight
) maxHeight
= minHeight
;
5241 nscoord heightAtMaxWidth
, heightAtMinWidth
, widthAtMaxHeight
,
5244 if (tentWidth
> 0) {
5245 heightAtMaxWidth
= NSCoordMulDiv(maxWidth
, tentHeight
, tentWidth
);
5246 if (heightAtMaxWidth
< minHeight
) heightAtMaxWidth
= minHeight
;
5247 heightAtMinWidth
= NSCoordMulDiv(minWidth
, tentHeight
, tentWidth
);
5248 if (heightAtMinWidth
> maxHeight
) heightAtMinWidth
= maxHeight
;
5250 heightAtMaxWidth
= heightAtMinWidth
=
5251 NS_CSS_MINMAX(tentHeight
, minHeight
, maxHeight
);
5254 if (tentHeight
> 0) {
5255 widthAtMaxHeight
= NSCoordMulDiv(maxHeight
, tentWidth
, tentHeight
);
5256 if (widthAtMaxHeight
< minWidth
) widthAtMaxHeight
= minWidth
;
5257 widthAtMinHeight
= NSCoordMulDiv(minHeight
, tentWidth
, tentHeight
);
5258 if (widthAtMinHeight
> maxWidth
) widthAtMinHeight
= maxWidth
;
5260 widthAtMaxHeight
= widthAtMinHeight
=
5261 NS_CSS_MINMAX(tentWidth
, minWidth
, maxWidth
);
5264 // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
5266 nscoord width
, height
;
5268 if (tentWidth
> maxWidth
) {
5269 if (tentHeight
> maxHeight
) {
5270 if (int64_t(maxWidth
) * int64_t(tentHeight
) <=
5271 int64_t(maxHeight
) * int64_t(tentWidth
)) {
5273 height
= heightAtMaxWidth
;
5275 width
= widthAtMaxHeight
;
5279 // This also covers "(w > max-width) and (h < min-height)" since in
5280 // that case (max-width/w < 1), and with (h < min-height):
5281 // max(max-width * h/w, min-height) == min-height
5283 height
= heightAtMaxWidth
;
5285 } else if (tentWidth
< minWidth
) {
5286 if (tentHeight
< minHeight
) {
5287 if (int64_t(minWidth
) * int64_t(tentHeight
) <=
5288 int64_t(minHeight
) * int64_t(tentWidth
)) {
5289 width
= widthAtMinHeight
;
5293 height
= heightAtMinWidth
;
5296 // This also covers "(w < min-width) and (h > max-height)" since in
5297 // that case (min-width/w > 1), and with (h > max-height):
5298 // min(min-width * h/w, max-height) == max-height
5300 height
= heightAtMinWidth
;
5303 if (tentHeight
> maxHeight
) {
5304 width
= widthAtMaxHeight
;
5306 } else if (tentHeight
< minHeight
) {
5307 width
= widthAtMinHeight
;
5311 height
= tentHeight
;
5315 return nsSize(width
, height
);
5319 nscoord
nsLayoutUtils::MinISizeFromInline(nsIFrame
* aFrame
,
5320 gfxContext
* aRenderingContext
) {
5321 NS_ASSERTION(!aFrame
->IsContainerForFontSizeInflation(),
5322 "should not be container for font size inflation");
5324 nsIFrame::InlineMinISizeData data
;
5325 aFrame
->AddInlineMinISize(aRenderingContext
, &data
);
5327 return data
.mPrevLines
;
5331 nscoord
nsLayoutUtils::PrefISizeFromInline(nsIFrame
* aFrame
,
5332 gfxContext
* aRenderingContext
) {
5333 NS_ASSERTION(!aFrame
->IsContainerForFontSizeInflation(),
5334 "should not be container for font size inflation");
5336 nsIFrame::InlinePrefISizeData data
;
5337 aFrame
->AddInlinePrefISize(aRenderingContext
, &data
);
5339 return data
.mPrevLines
;
5342 static nscolor
DarkenColor(nscolor aColor
) {
5343 uint16_t hue
, sat
, value
;
5346 // convert the RBG to HSV so we can get the lightness (which is the v)
5347 NS_RGB2HSV(aColor
, hue
, sat
, value
, alpha
);
5349 // The goal here is to send white to black while letting colored
5350 // stuff stay colored... So we adopt the following approach.
5351 // Something with sat = 0 should end up with value = 0. Something
5352 // with a high sat can end up with a high value and it's ok.... At
5353 // the same time, we don't want to make things lighter. Do
5354 // something simple, since it seems to work.
5357 // convert this color back into the RGB color space.
5358 NS_HSV2RGB(aColor
, hue
, sat
, value
, alpha
);
5363 // Check whether we should darken text/decoration colors. We need to do this if
5364 // background images and colors are being suppressed, because that means
5365 // light text will not be visible against the (presumed light-colored)
5367 static bool ShouldDarkenColors(nsIFrame
* aFrame
) {
5368 nsPresContext
* pc
= aFrame
->PresContext();
5369 if (pc
->GetBackgroundColorDraw() || pc
->GetBackgroundImageDraw()) {
5372 return aFrame
->StyleVisibility()->mPrintColorAdjust
!=
5373 StylePrintColorAdjust::Exact
;
5376 nscolor
nsLayoutUtils::DarkenColorIfNeeded(nsIFrame
* aFrame
, nscolor aColor
) {
5377 return ShouldDarkenColors(aFrame
) ? DarkenColor(aColor
) : aColor
;
5380 gfxFloat
nsLayoutUtils::GetSnappedBaselineY(nsIFrame
* aFrame
,
5381 gfxContext
* aContext
, nscoord aY
,
5383 gfxFloat appUnitsPerDevUnit
= aFrame
->PresContext()->AppUnitsPerDevPixel();
5384 gfxFloat baseline
= gfxFloat(aY
) + aAscent
;
5385 gfxRect
putativeRect(0, baseline
/ appUnitsPerDevUnit
, 1, 1);
5386 if (!aContext
->UserToDevicePixelSnapped(
5387 putativeRect
, gfxContext::SnapOption::IgnoreScale
)) {
5390 return aContext
->DeviceToUser(putativeRect
.TopLeft()).y
* appUnitsPerDevUnit
;
5393 gfxFloat
nsLayoutUtils::GetSnappedBaselineX(nsIFrame
* aFrame
,
5394 gfxContext
* aContext
, nscoord aX
,
5396 gfxFloat appUnitsPerDevUnit
= aFrame
->PresContext()->AppUnitsPerDevPixel();
5397 gfxFloat baseline
= gfxFloat(aX
) + aAscent
;
5398 gfxRect
putativeRect(baseline
/ appUnitsPerDevUnit
, 0, 1, 1);
5399 if (!aContext
->UserToDevicePixelSnapped(
5400 putativeRect
, gfxContext::SnapOption::IgnoreScale
)) {
5403 return aContext
->DeviceToUser(putativeRect
.TopLeft()).x
* appUnitsPerDevUnit
;
5406 // Hard limit substring lengths to 8000 characters ... this lets us statically
5407 // size the cluster buffer array in FindSafeLength
5408 #define MAX_GFX_TEXT_BUF_SIZE 8000
5410 static int32_t FindSafeLength(const char16_t
* aString
, uint32_t aLength
,
5411 uint32_t aMaxChunkLength
) {
5412 if (aLength
<= aMaxChunkLength
) return aLength
;
5414 int32_t len
= aMaxChunkLength
;
5416 // Ensure that we don't break inside a surrogate pair
5417 while (len
> 0 && NS_IS_LOW_SURROGATE(aString
[len
])) {
5421 // We don't want our caller to go into an infinite loop, so don't
5422 // return zero. It's hard to imagine how we could actually get here
5423 // unless there are languages that allow clusters of arbitrary size.
5424 // If there are and someone feeds us a 500+ character cluster, too
5426 return aMaxChunkLength
;
5431 static int32_t GetMaxChunkLength(nsFontMetrics
& aFontMetrics
) {
5432 return std::min(aFontMetrics
.GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE
);
5435 nscoord
nsLayoutUtils::AppUnitWidthOfString(const char16_t
* aString
,
5437 nsFontMetrics
& aFontMetrics
,
5438 DrawTarget
* aDrawTarget
) {
5439 uint32_t maxChunkLength
= GetMaxChunkLength(aFontMetrics
);
5441 while (aLength
> 0) {
5442 int32_t len
= FindSafeLength(aString
, aLength
, maxChunkLength
);
5443 width
+= aFontMetrics
.GetWidth(aString
, len
, aDrawTarget
);
5450 nscoord
nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t
* aString
,
5452 const nsIFrame
* aFrame
,
5453 nsFontMetrics
& aFontMetrics
,
5454 gfxContext
& aContext
) {
5455 nsPresContext
* presContext
= aFrame
->PresContext();
5456 if (presContext
->BidiEnabled()) {
5457 mozilla::intl::BidiEmbeddingLevel level
=
5458 nsBidiPresUtils::BidiLevelFromStyle(aFrame
->Style());
5459 return nsBidiPresUtils::MeasureTextWidth(
5460 aString
, aLength
, level
, presContext
, aContext
, aFontMetrics
);
5462 aFontMetrics
.SetTextRunRTL(false);
5463 aFontMetrics
.SetVertical(aFrame
->GetWritingMode().IsVertical());
5464 aFontMetrics
.SetTextOrientation(aFrame
->StyleVisibility()->mTextOrientation
);
5465 return nsLayoutUtils::AppUnitWidthOfString(aString
, aLength
, aFontMetrics
,
5466 aContext
.GetDrawTarget());
5469 bool nsLayoutUtils::StringWidthIsGreaterThan(const nsString
& aString
,
5470 nsFontMetrics
& aFontMetrics
,
5471 DrawTarget
* aDrawTarget
,
5473 const char16_t
* string
= aString
.get();
5474 uint32_t length
= aString
.Length();
5475 uint32_t maxChunkLength
= GetMaxChunkLength(aFontMetrics
);
5477 while (length
> 0) {
5478 int32_t len
= FindSafeLength(string
, length
, maxChunkLength
);
5479 width
+= aFontMetrics
.GetWidth(string
, len
, aDrawTarget
);
5480 if (width
> aWidth
) {
5489 nsBoundingMetrics
nsLayoutUtils::AppUnitBoundsOfString(
5490 const char16_t
* aString
, uint32_t aLength
, nsFontMetrics
& aFontMetrics
,
5491 DrawTarget
* aDrawTarget
) {
5492 uint32_t maxChunkLength
= GetMaxChunkLength(aFontMetrics
);
5493 int32_t len
= FindSafeLength(aString
, aLength
, maxChunkLength
);
5494 // Assign directly in the first iteration. This ensures that
5495 // negative ascent/descent can be returned and the left bearing
5496 // is properly initialized.
5497 nsBoundingMetrics totalMetrics
=
5498 aFontMetrics
.GetBoundingMetrics(aString
, len
, aDrawTarget
);
5502 while (aLength
> 0) {
5503 len
= FindSafeLength(aString
, aLength
, maxChunkLength
);
5504 nsBoundingMetrics metrics
=
5505 aFontMetrics
.GetBoundingMetrics(aString
, len
, aDrawTarget
);
5506 totalMetrics
+= metrics
;
5510 return totalMetrics
;
5513 void nsLayoutUtils::DrawString(const nsIFrame
* aFrame
,
5514 nsFontMetrics
& aFontMetrics
,
5515 gfxContext
* aContext
, const char16_t
* aString
,
5516 int32_t aLength
, nsPoint aPoint
,
5517 ComputedStyle
* aComputedStyle
,
5518 DrawStringFlags aFlags
) {
5519 nsresult rv
= NS_ERROR_FAILURE
;
5521 // If caller didn't pass a style, use the frame's.
5522 if (!aComputedStyle
) {
5523 aComputedStyle
= aFrame
->Style();
5526 if (aFlags
& DrawStringFlags::ForceHorizontal
) {
5527 aFontMetrics
.SetVertical(false);
5529 aFontMetrics
.SetVertical(WritingMode(aComputedStyle
).IsVertical());
5532 aFontMetrics
.SetTextOrientation(
5533 aComputedStyle
->StyleVisibility()->mTextOrientation
);
5535 nsPresContext
* presContext
= aFrame
->PresContext();
5536 if (presContext
->BidiEnabled()) {
5537 mozilla::intl::BidiEmbeddingLevel level
=
5538 nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle
);
5539 rv
= nsBidiPresUtils::RenderText(aString
, aLength
, level
, presContext
,
5540 *aContext
, aContext
->GetDrawTarget(),
5541 aFontMetrics
, aPoint
.x
, aPoint
.y
);
5543 if (NS_FAILED(rv
)) {
5544 aFontMetrics
.SetTextRunRTL(false);
5545 DrawUniDirString(aString
, aLength
, aPoint
, aFontMetrics
, *aContext
);
5549 void nsLayoutUtils::DrawUniDirString(const char16_t
* aString
, uint32_t aLength
,
5550 const nsPoint
& aPoint
,
5551 nsFontMetrics
& aFontMetrics
,
5552 gfxContext
& aContext
) {
5553 nscoord x
= aPoint
.x
;
5554 nscoord y
= aPoint
.y
;
5556 uint32_t maxChunkLength
= GetMaxChunkLength(aFontMetrics
);
5557 if (aLength
<= maxChunkLength
) {
5558 aFontMetrics
.DrawString(aString
, aLength
, x
, y
, &aContext
,
5559 aContext
.GetDrawTarget());
5563 bool isRTL
= aFontMetrics
.GetTextRunRTL();
5565 // If we're drawing right to left, we must start at the end.
5567 x
+= nsLayoutUtils::AppUnitWidthOfString(aString
, aLength
, aFontMetrics
,
5568 aContext
.GetDrawTarget());
5571 while (aLength
> 0) {
5572 int32_t len
= FindSafeLength(aString
, aLength
, maxChunkLength
);
5574 aFontMetrics
.GetWidth(aString
, len
, aContext
.GetDrawTarget());
5578 aFontMetrics
.DrawString(aString
, len
, x
, y
, &aContext
,
5579 aContext
.GetDrawTarget());
5589 void nsLayoutUtils::PaintTextShadow(
5590 const nsIFrame
* aFrame
, gfxContext
* aContext
, const nsRect
& aTextRect
,
5591 const nsRect
& aDirtyRect
, const nscolor
& aForegroundColor
,
5592 TextShadowCallback aCallback
, void* aCallbackData
) {
5593 const nsStyleText
* textStyle
= aFrame
->StyleText();
5594 auto shadows
= textStyle
->mTextShadow
.AsSpan();
5595 if (shadows
.IsEmpty()) {
5599 // Text shadow happens with the last value being painted at the back,
5600 // ie. it is painted first.
5601 gfxContext
* aDestCtx
= aContext
;
5602 for (auto& shadow
: Reversed(shadows
)) {
5603 nsPoint
shadowOffset(shadow
.horizontal
.ToAppUnits(),
5604 shadow
.vertical
.ToAppUnits());
5605 nscoord blurRadius
= std::max(shadow
.blur
.ToAppUnits(), 0);
5607 nsRect
shadowRect(aTextRect
);
5608 shadowRect
.MoveBy(shadowOffset
);
5610 nsPresContext
* presCtx
= aFrame
->PresContext();
5611 nsContextBoxBlur contextBoxBlur
;
5613 nscolor shadowColor
= shadow
.color
.CalcColor(aForegroundColor
);
5615 // Webrender just needs the shadow details
5616 if (auto* textDrawer
= aContext
->GetTextDrawer()) {
5617 wr::Shadow wrShadow
;
5620 presCtx
->AppUnitsToFloatDevPixels(shadow
.horizontal
.ToAppUnits()),
5621 presCtx
->AppUnitsToFloatDevPixels(shadow
.vertical
.ToAppUnits())};
5623 wrShadow
.blur_radius
= presCtx
->AppUnitsToFloatDevPixels(blurRadius
);
5624 wrShadow
.color
= wr::ToColorF(ToDeviceColor(shadowColor
));
5626 // Gecko already inflates the bounding rect of text shadows,
5627 // so tell WR not to inflate again.
5628 bool inflate
= false;
5629 textDrawer
->AppendShadow(wrShadow
, inflate
);
5633 gfxContext
* shadowContext
= contextBoxBlur
.Init(
5634 shadowRect
, 0, blurRadius
, presCtx
->AppUnitsPerDevPixel(), aDestCtx
,
5635 aDirtyRect
, nullptr,
5636 nsContextBoxBlur::DISABLE_HARDWARE_ACCELERATION_BLUR
);
5637 if (!shadowContext
) continue;
5640 aDestCtx
->NewPath();
5641 aDestCtx
->SetColor(sRGBColor::FromABGR(shadowColor
));
5643 // The callback will draw whatever we want to blur as a shadow.
5644 aCallback(shadowContext
, shadowOffset
, shadowColor
, aCallbackData
);
5646 contextBoxBlur
.DoPaint();
5647 aDestCtx
->Restore();
5652 nscoord
nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics
* aFontMetrics
,
5653 nscoord aLineHeight
,
5655 nscoord fontAscent
=
5656 aIsInverted
? aFontMetrics
->MaxDescent() : aFontMetrics
->MaxAscent();
5657 nscoord fontHeight
= aFontMetrics
->MaxHeight();
5659 nscoord leading
= aLineHeight
- fontHeight
;
5660 return fontAscent
+ leading
/ 2;
5664 bool nsLayoutUtils::GetFirstLineBaseline(WritingMode aWritingMode
,
5665 const nsIFrame
* aFrame
,
5667 LinePosition position
;
5668 if (!GetFirstLinePosition(aWritingMode
, aFrame
, &position
)) return false;
5669 *aResult
= position
.mBaseline
;
5674 bool nsLayoutUtils::GetFirstLinePosition(WritingMode aWM
,
5675 const nsIFrame
* aFrame
,
5676 LinePosition
* aResult
) {
5677 if (aFrame
->StyleDisplay()->IsContainLayout()) {
5680 const nsBlockFrame
* block
= do_QueryFrame(aFrame
);
5682 // For the first-line baseline we also have to check for a table, and if
5683 // so, use the baseline of its first row.
5684 LayoutFrameType fType
= aFrame
->Type();
5685 if (fType
== LayoutFrameType::TableWrapper
||
5686 fType
== LayoutFrameType::FlexContainer
||
5687 fType
== LayoutFrameType::GridContainer
) {
5688 if ((fType
== LayoutFrameType::GridContainer
&&
5689 aFrame
->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE
)) ||
5690 (fType
== LayoutFrameType::FlexContainer
&&
5691 aFrame
->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE
)) ||
5692 (fType
== LayoutFrameType::TableWrapper
&&
5693 static_cast<const nsTableWrapperFrame
*>(aFrame
)->GetRowCount() ==
5695 // empty grid/flex/table container
5696 aResult
->mBStart
= 0;
5697 aResult
->mBaseline
= Baseline::SynthesizeBOffsetFromBorderBox(
5698 aFrame
, aWM
, BaselineSharingGroup::First
);
5699 aResult
->mBEnd
= aFrame
->BSize(aWM
);
5702 if (fType
== LayoutFrameType::TableWrapper
&&
5703 aFrame
->GetWritingMode().IsOrthogonalTo(aWM
)) {
5704 // For tables, the upcoming GetLogicalBaseline call would determine the
5705 // table's baseline from its first row that has a baseline. However:
5706 // this doesn't make sense for an orthogonal writing mode, so in that
5707 // case we report no baseline instead. The table wrapper and its rows
5708 // should flow the same way, so we can bail out early, but this logic
5709 // wouldn't be correct to transplant into other places in the codebase
5710 // (Depending on how bug 1786633 is resolved).
5713 aResult
->mBStart
= 0;
5714 aResult
->mBaseline
= aFrame
->GetLogicalBaseline(aWM
);
5715 // This is what we want for the list bullet caller; not sure if
5716 // other future callers will want the same.
5717 aResult
->mBEnd
= aFrame
->BSize(aWM
);
5721 // For first-line baselines, we have to consider scroll frames.
5722 if (nsIScrollableFrame
* sFrame
=
5723 do_QueryFrame(const_cast<nsIFrame
*>(aFrame
))) {
5724 LinePosition kidPosition
;
5725 if (GetFirstLinePosition(aWM
, sFrame
->GetScrolledFrame(), &kidPosition
)) {
5726 // Consider only the border (Padding is ignored, since
5727 // `-moz-scrolled-content` inherits and handles the padding) that
5728 // contributes to the kid's position, not the scrolling, so we get the
5729 // initial position.
5730 *aResult
= kidPosition
+ aFrame
->GetLogicalUsedBorder(aWM
).BStart(aWM
);
5731 // Don't want to move the line's block positioning, but the baseline
5732 // needs to be clamped (See bug 1791069).
5733 aResult
->mBaseline
= std::clamp(aResult
->mBaseline
, 0,
5734 aFrame
->GetLogicalSize(aWM
).BSize(aWM
));
5740 if (fType
== LayoutFrameType::FieldSet
) {
5741 LinePosition kidPosition
;
5742 // Get the first baseline from the fieldset content, not from the legend.
5743 nsIFrame
* kid
= static_cast<const nsFieldSetFrame
*>(aFrame
)->GetInner();
5744 if (kid
&& GetFirstLinePosition(aWM
, kid
, &kidPosition
)) {
5745 *aResult
= kidPosition
+
5746 kid
->GetLogicalNormalPosition(aWM
, aFrame
->GetSize()).B(aWM
);
5752 if (fType
== LayoutFrameType::ColumnSet
) {
5753 // Note(dshin): This is basically the same as
5754 // `nsColumnSetFrame::GetNaturalBaselineBOffset`, but with line start and
5755 // end, all stored in `LinePosition`. Field value apart from baseline is
5756 // used in one other place
5757 // (`nsBlockFrame`) - if that goes away, this becomes a duplication that
5758 // should be removed.
5759 LinePosition kidPosition
;
5760 for (const auto* kid
: aFrame
->PrincipalChildList()) {
5761 LinePosition position
;
5762 if (!GetFirstLinePosition(aWM
, kid
, &position
)) {
5765 if (position
.mBaseline
< kidPosition
.mBaseline
) {
5766 kidPosition
= position
;
5769 if (kidPosition
.mBaseline
!= nscoord_MAX
) {
5770 *aResult
= kidPosition
;
5779 for (const auto& line
: block
->Lines()) {
5780 if (line
.IsBlock()) {
5781 const nsIFrame
* kid
= line
.mFirstChild
;
5782 LinePosition kidPosition
;
5783 if (GetFirstLinePosition(aWM
, kid
, &kidPosition
)) {
5784 // XXX Not sure if this is the correct value to use for container
5785 // width here. It will only be used in vertical-rl layout,
5786 // which we don't have full support and testing for yet.
5787 const auto& containerSize
= line
.mContainerSize
;
5788 *aResult
= kidPosition
+
5789 kid
->GetLogicalNormalPosition(aWM
, containerSize
).B(aWM
);
5793 // XXX Is this the right test? We have some bogus empty lines
5794 // floating around, but IsEmpty is perhaps too weak.
5795 if (0 != line
.BSize() || !line
.IsEmpty()) {
5796 nscoord bStart
= line
.BStart();
5797 aResult
->mBStart
= bStart
;
5798 aResult
->mBaseline
= bStart
+ line
.GetLogicalAscent();
5799 aResult
->mBEnd
= bStart
+ line
.BSize();
5808 bool nsLayoutUtils::GetLastLineBaseline(WritingMode aWM
, const nsIFrame
* aFrame
,
5810 if (aFrame
->StyleDisplay()->IsContainLayout()) {
5814 const nsBlockFrame
* block
= do_QueryFrame(aFrame
);
5816 if (nsIScrollableFrame
* sFrame
= do_QueryFrame(aFrame
)) {
5817 // Use the baseline position only if the last line's baseline is within
5818 // the scrolling frame's box in the initial position.
5819 const auto* scrolledFrame
= sFrame
->GetScrolledFrame();
5820 if (!GetLastLineBaseline(aWM
, scrolledFrame
, aResult
)) {
5823 // Go from scrolled frame to scrollable frame position.
5824 *aResult
+= aFrame
->GetLogicalUsedBorder(aWM
).BStart(aWM
);
5825 const auto maxBaseline
= aFrame
->GetLogicalSize(aWM
).BSize(aWM
);
5826 // Clamp the last baseline to border (See bug 1791069).
5827 *aResult
= std::clamp(*aResult
, 0, maxBaseline
);
5831 // No need to duplicate the baseline logic (Unlike `GetFirstLinePosition`,
5832 // we don't need to return any other value apart from baseline), just defer
5833 // to `GetNaturalBaselineBOffset`. Technically, we could do this at
5834 // `ColumnSetWrapperFrame` level, but this keeps it symmetric to
5835 // `GetFirstLinePosition`.
5836 if (aFrame
->IsColumnSetFrame()) {
5837 const auto baseline
= aFrame
->GetNaturalBaselineBOffset(
5838 aWM
, BaselineSharingGroup::Last
, BaselineExportContext::Other
);
5842 *aResult
= aFrame
->BSize(aWM
) - *baseline
;
5849 for (nsBlockFrame::ConstReverseLineIterator line
= block
->LinesRBegin(),
5850 line_end
= block
->LinesREnd();
5851 line
!= line_end
; ++line
) {
5852 if (line
->IsBlock()) {
5853 nsIFrame
* kid
= line
->mFirstChild
;
5854 nscoord kidBaseline
;
5855 const nsSize
& containerSize
= line
->mContainerSize
;
5856 if (GetLastLineBaseline(aWM
, kid
, &kidBaseline
)) {
5857 // Ignore relative positioning for baseline calculations
5858 *aResult
= kidBaseline
+
5859 kid
->GetLogicalNormalPosition(aWM
, containerSize
).B(aWM
);
5862 if (kid
->IsScrollFrame()) {
5863 // Defer to nsIFrame::GetLogicalBaseline (which synthesizes a baseline
5864 // from the margin-box).
5865 kidBaseline
= kid
->GetLogicalBaseline(aWM
);
5866 *aResult
= kidBaseline
+
5867 kid
->GetLogicalNormalPosition(aWM
, containerSize
).B(aWM
);
5871 // XXX Is this the right test? We have some bogus empty lines
5872 // floating around, but IsEmpty is perhaps too weak.
5873 if (line
->BSize() != 0 || !line
->IsEmpty()) {
5874 *aResult
= line
->BStart() + line
->GetLogicalAscent();
5882 static nscoord
CalculateBlockContentBEnd(WritingMode aWM
,
5883 nsBlockFrame
* aFrame
) {
5884 MOZ_ASSERT(aFrame
, "null ptr");
5886 nscoord contentBEnd
= 0;
5888 for (const auto& line
: aFrame
->Lines()) {
5889 if (line
.IsBlock()) {
5890 nsIFrame
* child
= line
.mFirstChild
;
5891 const auto& containerSize
= line
.mContainerSize
;
5893 child
->GetLogicalNormalPosition(aWM
, containerSize
).B(aWM
);
5895 std::max(contentBEnd
,
5896 nsLayoutUtils::CalculateContentBEnd(aWM
, child
) + offset
);
5898 contentBEnd
= std::max(contentBEnd
, line
.BEnd());
5905 nscoord
nsLayoutUtils::CalculateContentBEnd(WritingMode aWM
, nsIFrame
* aFrame
) {
5906 MOZ_ASSERT(aFrame
, "null ptr");
5908 nscoord contentBEnd
= aFrame
->BSize(aWM
);
5910 // We want scrollable overflow rather than visual because this
5911 // calculation is intended to affect layout.
5912 LogicalSize
overflowSize(aWM
, aFrame
->ScrollableOverflowRect().Size());
5913 if (overflowSize
.BSize(aWM
) > contentBEnd
) {
5914 FrameChildListIDs skip
= {FrameChildListID::Overflow
,
5915 FrameChildListID::ExcessOverflowContainers
,
5916 FrameChildListID::OverflowOutOfFlow
};
5917 nsBlockFrame
* blockFrame
= do_QueryFrame(aFrame
);
5920 std::max(contentBEnd
, CalculateBlockContentBEnd(aWM
, blockFrame
));
5921 skip
+= FrameChildListID::Principal
;
5923 for (const auto& [list
, listID
] : aFrame
->ChildLists()) {
5924 if (!skip
.contains(listID
)) {
5925 for (nsIFrame
* child
: list
) {
5927 child
->GetLogicalNormalPosition(aWM
, aFrame
->GetSize()).B(aWM
);
5929 std::max(contentBEnd
, CalculateContentBEnd(aWM
, child
) + offset
);
5938 nsIFrame
* nsLayoutUtils::GetClosestLayer(nsIFrame
* aFrame
) {
5940 for (layer
= aFrame
; layer
; layer
= layer
->GetParent()) {
5941 if (layer
->IsAbsPosContainingBlock() ||
5942 (layer
->GetParent() && layer
->GetParent()->IsScrollFrame()))
5945 if (layer
) return layer
;
5946 return aFrame
->PresShell()->GetRootFrame();
5949 SamplingFilter
nsLayoutUtils::GetSamplingFilterForFrame(nsIFrame
* aForFrame
) {
5950 switch (aForFrame
->UsedImageRendering()) {
5951 case StyleImageRendering::Smooth
:
5952 case StyleImageRendering::Optimizequality
:
5953 return SamplingFilter::LINEAR
;
5954 case StyleImageRendering::CrispEdges
:
5955 case StyleImageRendering::Optimizespeed
:
5956 case StyleImageRendering::Pixelated
:
5957 return SamplingFilter::POINT
;
5958 case StyleImageRendering::Auto
:
5959 return SamplingFilter::GOOD
;
5961 MOZ_ASSERT_UNREACHABLE("Unknown image-rendering value");
5962 return SamplingFilter::GOOD
;
5966 * Given an image being drawn into an appunit coordinate system, and
5967 * a point in that coordinate system, map the point back into image
5969 * @param aSize the size of the image, in pixels
5970 * @param aDest the rectangle that the image is being mapped into
5971 * @param aPt a point in the same coordinate system as the rectangle
5973 static gfxPoint
MapToFloatImagePixels(const gfxSize
& aSize
,
5974 const gfxRect
& aDest
,
5975 const gfxPoint
& aPt
) {
5976 return gfxPoint(((aPt
.x
- aDest
.X()) * aSize
.width
) / aDest
.Width(),
5977 ((aPt
.y
- aDest
.Y()) * aSize
.height
) / aDest
.Height());
5981 * Given an image being drawn into an pixel-based coordinate system, and
5982 * a point in image space, map the point into the pixel-based coordinate
5984 * @param aSize the size of the image, in pixels
5985 * @param aDest the rectangle that the image is being mapped into
5986 * @param aPt a point in image space
5988 static gfxPoint
MapToFloatUserPixels(const gfxSize
& aSize
, const gfxRect
& aDest
,
5989 const gfxPoint
& aPt
) {
5990 return gfxPoint(aPt
.x
* aDest
.Width() / aSize
.width
+ aDest
.X(),
5991 aPt
.y
* aDest
.Height() / aSize
.height
+ aDest
.Y());
5995 gfxRect
nsLayoutUtils::RectToGfxRect(const nsRect
& aRect
,
5996 int32_t aAppUnitsPerDevPixel
) {
5997 return gfxRect(gfxFloat(aRect
.x
) / aAppUnitsPerDevPixel
,
5998 gfxFloat(aRect
.y
) / aAppUnitsPerDevPixel
,
5999 gfxFloat(aRect
.width
) / aAppUnitsPerDevPixel
,
6000 gfxFloat(aRect
.height
) / aAppUnitsPerDevPixel
);
6003 struct SnappedImageDrawingParameters
{
6004 // A transform from image space to device space.
6005 gfxMatrix imageSpaceToDeviceSpace
;
6006 // The size at which the image should be drawn (which may not be its
6007 // intrinsic size due to, for example, HQ scaling).
6009 // The region in tiled image space which will be drawn, with an associated
6010 // region to which sampling should be restricted.
6012 // The default viewport size for SVG images, which we use unless a different
6013 // one has been explicitly specified. This is the same as |size| except that
6014 // it does not take into account any transformation on the gfxContext we're
6015 // drawing to - for example, CSS transforms are not taken into account.
6016 CSSIntSize svgViewportSize
;
6017 // Whether there's anything to draw at all.
6020 SnappedImageDrawingParameters()
6021 : region(ImageRegion::Empty()), shouldDraw(false) {}
6023 SnappedImageDrawingParameters(const gfxMatrix
& aImageSpaceToDeviceSpace
,
6024 const nsIntSize
& aSize
,
6025 const ImageRegion
& aRegion
,
6026 const CSSIntSize
& aSVGViewportSize
)
6027 : imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace
),
6030 svgViewportSize(aSVGViewportSize
),
6035 * Given two axis-aligned rectangles, returns the transformation that maps the
6036 * first onto the second.
6038 * @param aFrom The rect to be transformed.
6039 * @param aTo The rect that aFrom should be mapped onto by the transformation.
6041 static gfxMatrix
TransformBetweenRects(const gfxRect
& aFrom
,
6042 const gfxRect
& aTo
) {
6043 MatrixScalesDouble
scale(aTo
.width
/ aFrom
.width
, aTo
.height
/ aFrom
.height
);
6044 gfxPoint
translation(aTo
.x
- aFrom
.x
* scale
.xScale
,
6045 aTo
.y
- aFrom
.y
* scale
.yScale
);
6046 return gfxMatrix(scale
.xScale
, 0, 0, scale
.yScale
, translation
.x
,
6050 static nsRect
TileNearRect(const nsRect
& aAnyTile
, const nsRect
& aTargetRect
) {
6051 nsPoint distance
= aTargetRect
.TopLeft() - aAnyTile
.TopLeft();
6052 return aAnyTile
+ nsPoint(distance
.x
/ aAnyTile
.width
* aAnyTile
.width
,
6053 distance
.y
/ aAnyTile
.height
* aAnyTile
.height
);
6056 static gfxFloat
StableRound(gfxFloat aValue
) {
6057 // Values slightly less than 0.5 should round up like 0.5 would; we're
6058 // assuming they were meant to be 0.5.
6059 return floor(aValue
+ 0.5001);
6062 static gfxPoint
StableRound(const gfxPoint
& aPoint
) {
6063 return gfxPoint(StableRound(aPoint
.x
), StableRound(aPoint
.y
));
6067 * Given a set of input parameters, compute certain output parameters
6068 * for drawing an image with the image snapping algorithm.
6069 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
6071 * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
6073 static SnappedImageDrawingParameters
ComputeSnappedImageDrawingParameters(
6074 gfxContext
* aCtx
, int32_t aAppUnitsPerDevPixel
, const nsRect aDest
,
6075 const nsRect aFill
, const nsPoint aAnchor
, const nsRect aDirty
,
6076 imgIContainer
* aImage
, const SamplingFilter aSamplingFilter
,
6077 uint32_t aImageFlags
, ExtendMode aExtendMode
) {
6078 if (aDest
.IsEmpty() || aFill
.IsEmpty())
6079 return SnappedImageDrawingParameters();
6081 // Avoid unnecessarily large offsets.
6082 bool doTile
= !aDest
.Contains(aFill
);
6083 nsRect appUnitDest
=
6084 doTile
? TileNearRect(aDest
, aFill
.Intersect(aDirty
)) : aDest
;
6085 nsPoint anchor
= aAnchor
+ (appUnitDest
.TopLeft() - aDest
.TopLeft());
6087 gfxRect devPixelDest
=
6088 nsLayoutUtils::RectToGfxRect(appUnitDest
, aAppUnitsPerDevPixel
);
6089 gfxRect devPixelFill
=
6090 nsLayoutUtils::RectToGfxRect(aFill
, aAppUnitsPerDevPixel
);
6091 gfxRect devPixelDirty
=
6092 nsLayoutUtils::RectToGfxRect(aDirty
, aAppUnitsPerDevPixel
);
6094 gfxMatrix currentMatrix
= aCtx
->CurrentMatrixDouble();
6095 gfxRect fill
= devPixelFill
;
6096 gfxRect dest
= devPixelDest
;
6098 // Snap even if we have a scale in the context. But don't snap if
6099 // we have something that's not translation+scale, or if the scale flips in
6100 // the X or Y direction, because snapped image drawing can't handle that yet.
6101 if (!currentMatrix
.HasNonAxisAlignedTransform() && currentMatrix
._11
> 0.0 &&
6102 currentMatrix
._22
> 0.0 &&
6103 aCtx
->UserToDevicePixelSnapped(fill
,
6104 gfxContext::SnapOption::IgnoreScale
) &&
6105 aCtx
->UserToDevicePixelSnapped(dest
,
6106 gfxContext::SnapOption::IgnoreScale
)) {
6107 // We snapped. On this code path, |fill| and |dest| take into account
6108 // currentMatrix's transform.
6111 // We didn't snap. On this code path, |fill| and |dest| do not take into
6112 // account currentMatrix's transform.
6114 fill
= devPixelFill
;
6115 dest
= devPixelDest
;
6118 // If we snapped above, |dest| already takes into account |currentMatrix|'s
6119 // scale and has integer coordinates. If not, we need these properties to
6120 // compute the optimal drawn image size, so compute |snappedDestSize| here.
6121 gfxSize snappedDestSize
= dest
.Size();
6122 auto scaleFactors
= currentMatrix
.ScaleFactors();
6124 snappedDestSize
.Scale(scaleFactors
.xScale
, scaleFactors
.yScale
);
6125 snappedDestSize
.width
= NS_round(snappedDestSize
.width
);
6126 snappedDestSize
.height
= NS_round(snappedDestSize
.height
);
6129 // We need to be sure that this is at least one pixel in width and height,
6130 // or we'll end up drawing nothing even if we have a nonempty fill.
6131 snappedDestSize
.width
= std::max(snappedDestSize
.width
, 1.0);
6132 snappedDestSize
.height
= std::max(snappedDestSize
.height
, 1.0);
6134 // Bail if we're not going to end up drawing anything.
6135 if (fill
.IsEmpty()) {
6136 return SnappedImageDrawingParameters();
6139 nsIntSize intImageSize
= aImage
->OptimalImageSizeForDest(
6140 snappedDestSize
, imgIContainer::FRAME_CURRENT
, aSamplingFilter
,
6143 nsIntSize svgViewportSize
;
6144 if (scaleFactors
.xScale
== 1.0 && scaleFactors
.yScale
== 1.0) {
6145 // intImageSize is scaled by currentMatrix. But since there are no scale
6146 // factors in currentMatrix, it is safe to assign intImageSize to
6147 // svgViewportSize directly.
6148 svgViewportSize
= intImageSize
;
6150 // We should not take into account any transformation of currentMatrix
6151 // when computing svg viewport size. Since currentMatrix contains scale
6152 // factors, we need to recompute SVG viewport by unscaled devPixelDest.
6153 svgViewportSize
= aImage
->OptimalImageSizeForDest(
6154 devPixelDest
.Size(), imgIContainer::FRAME_CURRENT
, aSamplingFilter
,
6158 gfxSize
imageSize(intImageSize
.width
, intImageSize
.height
);
6160 // Compute the set of pixels that would be sampled by an ideal rendering
6161 gfxPoint subimageTopLeft
=
6162 MapToFloatImagePixels(imageSize
, devPixelDest
, devPixelFill
.TopLeft());
6163 gfxPoint subimageBottomRight
= MapToFloatImagePixels(
6164 imageSize
, devPixelDest
, devPixelFill
.BottomRight());
6166 subimage
.MoveTo(NSToIntFloor(subimageTopLeft
.x
),
6167 NSToIntFloor(subimageTopLeft
.y
));
6168 subimage
.SizeTo(NSToIntCeil(subimageBottomRight
.x
) - subimage
.x
,
6169 NSToIntCeil(subimageBottomRight
.y
) - subimage
.y
);
6171 if (subimage
.IsEmpty()) {
6172 // Bail if the subimage is empty (we're not going to be drawing anything).
6173 return SnappedImageDrawingParameters();
6176 gfxMatrix transform
;
6177 gfxMatrix invTransform
;
6179 bool anchorAtUpperLeft
=
6180 anchor
.x
== appUnitDest
.x
&& anchor
.y
== appUnitDest
.y
;
6181 bool exactlyOneImageCopy
= aFill
.IsEqualEdges(appUnitDest
);
6182 if (anchorAtUpperLeft
&& exactlyOneImageCopy
) {
6183 // The simple case: we can ignore the anchor point and compute the
6184 // transformation from the sampled region (the subimage) to the fill rect.
6185 // This approach is preferable when it works since it tends to produce
6186 // less numerical error.
6187 transform
= TransformBetweenRects(subimage
, fill
);
6188 invTransform
= TransformBetweenRects(fill
, subimage
);
6190 // The more complicated case: we compute the transformation from the
6191 // image rect positioned at the image space anchor point to the dest rect
6192 // positioned at the device space anchor point.
6194 // Compute the anchor point in both device space and image space. This
6195 // code assumes that pixel-based devices have one pixel per device unit!
6196 gfxPoint
anchorPoint(gfxFloat(anchor
.x
) / aAppUnitsPerDevPixel
,
6197 gfxFloat(anchor
.y
) / aAppUnitsPerDevPixel
);
6198 gfxPoint imageSpaceAnchorPoint
=
6199 MapToFloatImagePixels(imageSize
, devPixelDest
, anchorPoint
);
6202 imageSpaceAnchorPoint
= StableRound(imageSpaceAnchorPoint
);
6203 anchorPoint
= imageSpaceAnchorPoint
;
6204 anchorPoint
= MapToFloatUserPixels(imageSize
, devPixelDest
, anchorPoint
);
6205 anchorPoint
= currentMatrix
.TransformPoint(anchorPoint
);
6206 anchorPoint
= StableRound(anchorPoint
);
6209 // Compute an unsnapped version of the dest rect's size. We continue to
6210 // follow the pattern that we take |currentMatrix| into account only if
6211 // |didSnap| is true.
6212 gfxSize unsnappedDestSize
=
6213 didSnap
? devPixelDest
.Size() * currentMatrix
.ScaleFactors()
6214 : devPixelDest
.Size();
6216 gfxRect
anchoredDestRect(anchorPoint
, unsnappedDestSize
);
6217 gfxRect
anchoredImageRect(imageSpaceAnchorPoint
, imageSize
);
6219 // Calculate anchoredDestRect with snapped fill rect when the devPixelFill
6220 // rect corresponds to just a single tile in that direction
6221 if (fill
.Width() != devPixelFill
.Width() &&
6222 devPixelDest
.x
== devPixelFill
.x
&&
6223 devPixelDest
.XMost() == devPixelFill
.XMost()) {
6224 anchoredDestRect
.width
= fill
.width
;
6226 if (fill
.Height() != devPixelFill
.Height() &&
6227 devPixelDest
.y
== devPixelFill
.y
&&
6228 devPixelDest
.YMost() == devPixelFill
.YMost()) {
6229 anchoredDestRect
.height
= fill
.height
;
6232 transform
= TransformBetweenRects(anchoredImageRect
, anchoredDestRect
);
6233 invTransform
= TransformBetweenRects(anchoredDestRect
, anchoredImageRect
);
6236 // If the transform is not a straight translation by integers, then
6237 // filtering will occur, and restricting the fill rect to the dirty rect
6238 // would change the values computed for edge pixels, which we can't allow.
6239 // Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not
6240 // produce pixel-aligned coordinates, which would also break the values
6241 // computed for edge pixels.
6242 if (didSnap
&& !invTransform
.HasNonIntegerTranslation()) {
6243 // This form of Transform is safe to call since non-axis-aligned
6244 // transforms wouldn't be snapped.
6245 devPixelDirty
= currentMatrix
.TransformRect(devPixelDirty
);
6246 devPixelDirty
.RoundOut();
6247 fill
= fill
.Intersect(devPixelDirty
);
6249 if (fill
.IsEmpty()) return SnappedImageDrawingParameters();
6251 gfxRect
imageSpaceFill(didSnap
? invTransform
.TransformRect(fill
)
6252 : invTransform
.TransformBounds(fill
));
6254 // If we didn't snap, we need to post-multiply the matrix on the context to
6255 // get the final matrix we'll draw with, because we didn't take it into
6256 // account when computing the matrices above.
6258 transform
= transform
* currentMatrix
;
6261 ExtendMode extendMode
= (aImageFlags
& imgIContainer::FLAG_CLAMP
)
6264 // We were passed in the default extend mode but need to tile.
6265 if (extendMode
== ExtendMode::CLAMP
&& doTile
) {
6266 MOZ_ASSERT(!(aImageFlags
& imgIContainer::FLAG_CLAMP
));
6267 extendMode
= ExtendMode::REPEAT
;
6270 ImageRegion region
= ImageRegion::CreateWithSamplingRestriction(
6271 imageSpaceFill
, subimage
, extendMode
);
6273 return SnappedImageDrawingParameters(
6274 transform
, intImageSize
, region
,
6275 CSSIntSize(svgViewportSize
.width
, svgViewportSize
.height
));
6278 static ImgDrawResult
DrawImageInternal(
6279 gfxContext
& aContext
, nsPresContext
* aPresContext
, imgIContainer
* aImage
,
6280 const SamplingFilter aSamplingFilter
, const nsRect
& aDest
,
6281 const nsRect
& aFill
, const nsPoint
& aAnchor
, const nsRect
& aDirty
,
6282 const SVGImageContext
& aSVGContext
, uint32_t aImageFlags
,
6283 ExtendMode aExtendMode
= ExtendMode::CLAMP
, float aOpacity
= 1.0) {
6284 ImgDrawResult result
= ImgDrawResult::SUCCESS
;
6286 aImageFlags
|= imgIContainer::FLAG_ASYNC_NOTIFY
;
6288 if (aPresContext
->Type() == nsPresContext::eContext_Print
) {
6289 // We want vector images to be passed on as vector commands, not a raster
6291 aImageFlags
|= imgIContainer::FLAG_BYPASS_SURFACE_CACHE
;
6293 if (aDest
.Contains(aFill
)) {
6294 aImageFlags
|= imgIContainer::FLAG_CLAMP
;
6296 int32_t appUnitsPerDevPixel
= aPresContext
->AppUnitsPerDevPixel();
6298 SnappedImageDrawingParameters params
= ComputeSnappedImageDrawingParameters(
6299 &aContext
, appUnitsPerDevPixel
, aDest
, aFill
, aAnchor
, aDirty
, aImage
,
6300 aSamplingFilter
, aImageFlags
, aExtendMode
);
6302 if (!params
.shouldDraw
) {
6307 gfxContextMatrixAutoSaveRestore
contextMatrixRestorer(&aContext
);
6309 aContext
.SetMatrixDouble(params
.imageSpaceToDeviceSpace
);
6311 SVGImageContext newContext
= aSVGContext
;
6312 if (!aSVGContext
.GetViewportSize()) {
6313 newContext
.SetViewportSize(Some(params
.svgViewportSize
));
6316 result
= aImage
->Draw(&aContext
, params
.size
, params
.region
,
6317 imgIContainer::FRAME_CURRENT
, aSamplingFilter
,
6318 newContext
, aImageFlags
, aOpacity
);
6325 ImgDrawResult
nsLayoutUtils::DrawSingleUnscaledImage(
6326 gfxContext
& aContext
, nsPresContext
* aPresContext
, imgIContainer
* aImage
,
6327 const SamplingFilter aSamplingFilter
, const nsPoint
& aDest
,
6328 const nsRect
* aDirty
, const SVGImageContext
& aSVGContext
,
6329 uint32_t aImageFlags
, const nsRect
* aSourceArea
) {
6330 CSSIntSize imageSize
;
6331 aImage
->GetWidth(&imageSize
.width
);
6332 aImage
->GetHeight(&imageSize
.height
);
6333 aImage
->GetResolution().ApplyTo(imageSize
.width
, imageSize
.height
);
6335 if (imageSize
.width
< 1 || imageSize
.height
< 1) {
6336 NS_WARNING("Image width or height is non-positive");
6337 return ImgDrawResult::TEMPORARY_ERROR
;
6340 nsSize
size(CSSPixel::ToAppUnits(imageSize
));
6343 source
= *aSourceArea
;
6345 source
.SizeTo(size
);
6348 nsRect
dest(aDest
- source
.TopLeft(), size
);
6349 nsRect
fill(aDest
, source
.Size());
6350 // Ensure that only a single image tile is drawn. If aSourceArea extends
6351 // outside the image bounds, we want to honor the aSourceArea-to-aDest
6352 // translation but we don't want to actually tile the image.
6353 fill
.IntersectRect(fill
, dest
);
6354 return DrawImageInternal(aContext
, aPresContext
, aImage
, aSamplingFilter
,
6355 dest
, fill
, aDest
, aDirty
? *aDirty
: dest
,
6356 aSVGContext
, aImageFlags
);
6360 ImgDrawResult
nsLayoutUtils::DrawSingleImage(
6361 gfxContext
& aContext
, nsPresContext
* aPresContext
, imgIContainer
* aImage
,
6362 SamplingFilter aSamplingFilter
, const nsRect
& aDest
, const nsRect
& aDirty
,
6363 const SVGImageContext
& aSVGContext
, uint32_t aImageFlags
,
6364 const nsPoint
* aAnchorPoint
) {
6365 // NOTE(emilio): We can hardcode resolution to 1 here, since we're interested
6366 // in the actual image pixels, for snapping purposes, not on the adjusted
6368 CSSIntSize
pixelImageSize(ComputeSizeForDrawingWithFallback(
6369 aImage
, ImageResolution(), aDest
.Size()));
6370 if (pixelImageSize
.width
< 1 || pixelImageSize
.height
< 1) {
6371 NS_ASSERTION(pixelImageSize
.width
>= 0 && pixelImageSize
.height
>= 0,
6372 "Image width or height is negative");
6373 return ImgDrawResult::SUCCESS
; // no point in drawing a zero size image
6376 const nsSize
imageSize(CSSPixel::ToAppUnits(pixelImageSize
));
6377 const nsRect
source(nsPoint(), imageSize
);
6378 const nsRect dest
= GetWholeImageDestination(imageSize
, source
, aDest
);
6380 // Ensure that only a single image tile is drawn. If aSourceArea extends
6381 // outside the image bounds, we want to honor the aSourceArea-to-aDest
6382 // transform but we don't want to actually tile the image.
6384 fill
.IntersectRect(aDest
, dest
);
6385 return DrawImageInternal(aContext
, aPresContext
, aImage
, aSamplingFilter
,
6387 aAnchorPoint
? *aAnchorPoint
: fill
.TopLeft(),
6388 aDirty
, aSVGContext
, aImageFlags
);
6392 void nsLayoutUtils::ComputeSizeForDrawing(
6393 imgIContainer
* aImage
, const ImageResolution
& aResolution
,
6394 /* outparam */ CSSIntSize
& aImageSize
,
6395 /* outparam */ AspectRatio
& aIntrinsicRatio
,
6396 /* outparam */ bool& aGotWidth
,
6397 /* outparam */ bool& aGotHeight
) {
6398 aGotWidth
= NS_SUCCEEDED(aImage
->GetWidth(&aImageSize
.width
));
6399 aGotHeight
= NS_SUCCEEDED(aImage
->GetHeight(&aImageSize
.height
));
6400 Maybe
<AspectRatio
> intrinsicRatio
= aImage
->GetIntrinsicRatio();
6401 aIntrinsicRatio
= intrinsicRatio
.valueOr(AspectRatio());
6404 aResolution
.ApplyXTo(aImageSize
.width
);
6407 aResolution
.ApplyYTo(aImageSize
.height
);
6410 if (!(aGotWidth
&& aGotHeight
) && intrinsicRatio
.isNothing()) {
6411 // We hit an error (say, because the image failed to load or couldn't be
6412 // decoded) and should return zero size.
6413 aGotWidth
= aGotHeight
= true;
6414 aImageSize
= CSSIntSize(0, 0);
6419 CSSIntSize
nsLayoutUtils::ComputeSizeForDrawingWithFallback(
6420 imgIContainer
* aImage
, const ImageResolution
& aResolution
,
6421 const nsSize
& aFallbackSize
) {
6422 CSSIntSize imageSize
;
6423 AspectRatio imageRatio
;
6424 bool gotHeight
, gotWidth
;
6425 ComputeSizeForDrawing(aImage
, aResolution
, imageSize
, imageRatio
, gotWidth
,
6428 // If we didn't get both width and height, try to compute them using the
6429 // intrinsic ratio of the image.
6430 if (gotWidth
!= gotHeight
) {
6433 imageSize
.width
= imageRatio
.ApplyTo(imageSize
.height
);
6438 imageSize
.height
= imageRatio
.Inverted().ApplyTo(imageSize
.width
);
6444 // If we still don't have a width or height, just use the fallback size the
6448 nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize
.width
);
6452 nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize
.height
);
6458 /* static */ LayerIntRect
SnapRectForImage(
6459 const gfx::Matrix
& aTransform
, const gfx::MatrixScales
& aScaleFactors
,
6460 const LayoutDeviceRect
& aRect
) {
6461 // Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.
6462 // Any changes to the algorithm here will need to be reflected there.
6463 bool snapped
= false;
6464 LayerIntRect snapRect
;
6465 if (!aTransform
.HasNonAxisAlignedTransform() && aTransform
._11
> 0.0 &&
6466 aTransform
._22
> 0.0) {
6467 gfxRect
rect(gfxPoint(aRect
.X(), aRect
.Y()),
6468 gfxSize(aRect
.Width(), aRect
.Height()));
6471 ThebesPoint(aTransform
.TransformPoint(ToPoint(rect
.TopLeft())));
6473 ThebesPoint(aTransform
.TransformPoint(ToPoint(rect
.TopRight())));
6475 ThebesPoint(aTransform
.TransformPoint(ToPoint(rect
.BottomRight())));
6477 if (p2
== gfxPoint(p1
.x
, p3
.y
) || p2
== gfxPoint(p3
.x
, p1
.y
)) {
6481 IntPoint
p1i(int32_t(p1
.x
), int32_t(p1
.y
));
6482 IntPoint
p3i(int32_t(p3
.x
), int32_t(p3
.y
));
6484 snapRect
.MoveTo(std::min(p1i
.x
, p3i
.x
), std::min(p1i
.y
, p3i
.y
));
6485 snapRect
.SizeTo(std::max(p1i
.x
, p3i
.x
) - snapRect
.X(),
6486 std::max(p1i
.y
, p3i
.y
) - snapRect
.Y());
6492 // If we couldn't snap directly with the transform, we need to go best
6493 // effort in layer pixels.
6494 snapRect
= RoundedToInt(
6495 aRect
* LayoutDeviceToLayerScale2D::FromUnknownScale(aScaleFactors
));
6498 // An empty size is unacceptable so we ensure our suggested size is at least
6499 // 1 pixel wide/tall.
6500 if (snapRect
.Width() < 1) {
6501 snapRect
.SetWidth(1);
6503 if (snapRect
.Height() < 1) {
6504 snapRect
.SetHeight(1);
6510 IntSize
nsLayoutUtils::ComputeImageContainerDrawingParameters(
6511 imgIContainer
* aImage
, nsIFrame
* aForFrame
,
6512 const LayoutDeviceRect
& aDestRect
, const LayoutDeviceRect
& aFillRect
,
6513 const StackingContextHelper
& aSc
, uint32_t aFlags
,
6514 SVGImageContext
& aSVGContext
, Maybe
<ImageIntRegion
>& aRegion
) {
6516 MOZ_ASSERT(aForFrame
);
6518 MatrixScales scaleFactors
= aSc
.GetInheritedScale();
6519 SamplingFilter samplingFilter
=
6520 nsLayoutUtils::GetSamplingFilterForFrame(aForFrame
);
6522 // Compute our SVG context parameters, if any. Don't replace the viewport
6523 // size if it was already set, prefer what the caller gave.
6524 SVGImageContext::MaybeStoreContextPaint(aSVGContext
, aForFrame
, aImage
);
6525 if ((scaleFactors
.xScale
!= 1.0 || scaleFactors
.yScale
!= 1.0) &&
6526 aImage
->GetType() == imgIContainer::TYPE_VECTOR
&&
6527 (!aSVGContext
.GetViewportSize())) {
6528 gfxSize
gfxDestSize(aDestRect
.Width(), aDestRect
.Height());
6529 IntSize viewportSize
= aImage
->OptimalImageSizeForDest(
6530 gfxDestSize
, imgIContainer::FRAME_CURRENT
, samplingFilter
, aFlags
);
6532 CSSIntSize
cssViewportSize(viewportSize
.width
, viewportSize
.height
);
6533 aSVGContext
.SetViewportSize(Some(cssViewportSize
));
6536 const gfx::Matrix
& itm
= aSc
.GetInheritedTransform();
6537 LayerIntRect destRect
= SnapRectForImage(itm
, scaleFactors
, aDestRect
);
6539 // Since we always decode entire raster images, we only care about the
6540 // ImageIntRegion for vector images when we are recording blobs, for which we
6541 // may only draw part of in some cases.
6542 if ((aImage
->GetType() != imgIContainer::TYPE_VECTOR
) ||
6543 !(aFlags
& imgIContainer::FLAG_RECORD_BLOB
)) {
6544 // If the transform scale of our stacking context helper is being animated
6545 // on the compositor then the transform will have the current value of the
6546 // scale, but the scale factors will have max value of the scale animation.
6547 // So we want to ask for a decoded image that can fulfill that larger size.
6548 int32_t scaleWidth
= int32_t(ceil(aDestRect
.Width() * scaleFactors
.xScale
));
6549 if (scaleWidth
> destRect
.width
+ 2) {
6550 destRect
.width
= scaleWidth
;
6552 int32_t scaleHeight
=
6553 int32_t(ceil(aDestRect
.Height() * scaleFactors
.yScale
));
6554 if (scaleHeight
> destRect
.height
+ 2) {
6555 destRect
.height
= scaleHeight
;
6558 return aImage
->OptimalImageSizeForDest(
6559 gfxSize(destRect
.Width(), destRect
.Height()),
6560 imgIContainer::FRAME_CURRENT
, samplingFilter
, aFlags
);
6563 // We only use the region rect with blob recordings. This is because when we
6564 // rasterize an SVG image in process, we always create a complete
6565 // rasterization of the whole image which can be given to any caller, while
6566 // we support partial rasterization with the blob recordings.
6567 if (aFlags
& imgIContainer::FLAG_RECORD_BLOB
) {
6568 // If the dest rect contains the fill rect, then we are only displaying part
6569 // of the vector image. We need to calculate the restriction region to avoid
6570 // drawing more than we need, and sampling outside the desired bounds.
6571 LayerIntRect clipRect
= SnapRectForImage(itm
, scaleFactors
, aFillRect
);
6572 if (destRect
.Contains(clipRect
)) {
6573 LayerIntRect restrictRect
= destRect
.Intersect(clipRect
);
6574 restrictRect
.MoveBy(-destRect
.TopLeft());
6576 if (restrictRect
.Width() < 1) {
6577 restrictRect
.SetWidth(1);
6579 if (restrictRect
.Height() < 1) {
6580 restrictRect
.SetHeight(1);
6583 if (restrictRect
.X() != 0 || restrictRect
.Y() != 0 ||
6584 restrictRect
.Size() != destRect
.Size()) {
6585 IntRect sampleRect
= restrictRect
.ToUnknownRect();
6586 aRegion
= Some(ImageIntRegion::CreateWithSamplingRestriction(
6587 sampleRect
, sampleRect
, ExtendMode::CLAMP
));
6592 // VectorImage::OptimalImageSizeForDest will just round up, but we already
6593 // have an integer size.
6594 return destRect
.Size().ToUnknownSize();
6598 nsPoint
nsLayoutUtils::GetBackgroundFirstTilePos(const nsPoint
& aDest
,
6599 const nsPoint
& aFill
,
6600 const nsSize
& aRepeatSize
) {
6601 return nsPoint(NSToIntFloor(float(aFill
.x
- aDest
.x
) / aRepeatSize
.width
) *
6603 NSToIntFloor(float(aFill
.y
- aDest
.y
) / aRepeatSize
.height
) *
6604 aRepeatSize
.height
) +
6609 ImgDrawResult
nsLayoutUtils::DrawBackgroundImage(
6610 gfxContext
& aContext
, nsIFrame
* aForFrame
, nsPresContext
* aPresContext
,
6611 imgIContainer
* aImage
, SamplingFilter aSamplingFilter
, const nsRect
& aDest
,
6612 const nsRect
& aFill
, const nsSize
& aRepeatSize
, const nsPoint
& aAnchor
,
6613 const nsRect
& aDirty
, uint32_t aImageFlags
, ExtendMode aExtendMode
,
6615 AUTO_PROFILER_LABEL("nsLayoutUtils::DrawBackgroundImage",
6616 GRAPHICS_Rasterization
);
6618 CSSIntSize destCSSSize
{nsPresContext::AppUnitsToIntCSSPixels(aDest
.width
),
6619 nsPresContext::AppUnitsToIntCSSPixels(aDest
.height
)};
6621 SVGImageContext
svgContext(Some(destCSSSize
));
6622 SVGImageContext::MaybeStoreContextPaint(svgContext
, aForFrame
, aImage
);
6624 /* Fast path when there is no need for image spacing */
6625 if (aRepeatSize
.width
== aDest
.width
&& aRepeatSize
.height
== aDest
.height
) {
6626 return DrawImageInternal(aContext
, aPresContext
, aImage
, aSamplingFilter
,
6627 aDest
, aFill
, aAnchor
, aDirty
, svgContext
,
6628 aImageFlags
, aExtendMode
, aOpacity
);
6631 const nsPoint firstTilePos
=
6632 GetBackgroundFirstTilePos(aDest
.TopLeft(), aFill
.TopLeft(), aRepeatSize
);
6633 const nscoord xMost
= aFill
.XMost();
6634 const nscoord repeatWidth
= aRepeatSize
.width
;
6635 const nscoord yMost
= aFill
.YMost();
6636 const nscoord repeatHeight
= aRepeatSize
.height
;
6637 nsRect
dest(0, 0, aDest
.width
, aDest
.height
);
6638 nsPoint anchor
= aAnchor
;
6639 for (nscoord x
= firstTilePos
.x
; x
< xMost
; x
+= repeatWidth
) {
6640 for (nscoord y
= firstTilePos
.y
; y
< yMost
; y
+= repeatHeight
) {
6643 ImgDrawResult result
= DrawImageInternal(
6644 aContext
, aPresContext
, aImage
, aSamplingFilter
, dest
, dest
, anchor
,
6645 aDirty
, svgContext
, aImageFlags
, ExtendMode::CLAMP
, aOpacity
);
6646 anchor
.y
+= repeatHeight
;
6647 if (result
!= ImgDrawResult::SUCCESS
) {
6651 anchor
.x
+= repeatWidth
;
6652 anchor
.y
= aAnchor
.y
;
6655 return ImgDrawResult::SUCCESS
;
6659 ImgDrawResult
nsLayoutUtils::DrawImage(
6660 gfxContext
& aContext
, ComputedStyle
* aComputedStyle
,
6661 nsPresContext
* aPresContext
, imgIContainer
* aImage
,
6662 const SamplingFilter aSamplingFilter
, const nsRect
& aDest
,
6663 const nsRect
& aFill
, const nsPoint
& aAnchor
, const nsRect
& aDirty
,
6664 uint32_t aImageFlags
, float aOpacity
) {
6665 SVGImageContext svgContext
;
6666 SVGImageContext::MaybeStoreContextPaint(svgContext
, *aPresContext
,
6667 *aComputedStyle
, aImage
);
6669 return DrawImageInternal(aContext
, aPresContext
, aImage
, aSamplingFilter
,
6670 aDest
, aFill
, aAnchor
, aDirty
, svgContext
,
6671 aImageFlags
, ExtendMode::CLAMP
, aOpacity
);
6675 nsRect
nsLayoutUtils::GetWholeImageDestination(const nsSize
& aWholeImageSize
,
6676 const nsRect
& aImageSourceArea
,
6677 const nsRect
& aDestArea
) {
6678 double scaleX
= double(aDestArea
.width
) / aImageSourceArea
.width
;
6679 double scaleY
= double(aDestArea
.height
) / aImageSourceArea
.height
;
6680 nscoord destOffsetX
= NSToCoordRound(aImageSourceArea
.x
* scaleX
);
6681 nscoord destOffsetY
= NSToCoordRound(aImageSourceArea
.y
* scaleY
);
6682 nscoord wholeSizeX
= NSToCoordRound(aWholeImageSize
.width
* scaleX
);
6683 nscoord wholeSizeY
= NSToCoordRound(aWholeImageSize
.height
* scaleY
);
6684 return nsRect(aDestArea
.TopLeft() - nsPoint(destOffsetX
, destOffsetY
),
6685 nsSize(wholeSizeX
, wholeSizeY
));
6689 already_AddRefed
<imgIContainer
> nsLayoutUtils::OrientImage(
6690 imgIContainer
* aContainer
, const StyleImageOrientation
& aOrientation
) {
6691 MOZ_ASSERT(aContainer
, "Should have an image container");
6692 nsCOMPtr
<imgIContainer
> img(aContainer
);
6694 switch (aOrientation
) {
6695 case StyleImageOrientation::FromImage
:
6697 case StyleImageOrientation::None
:
6698 img
= ImageOps::Unorient(img
);
6702 return img
.forget();
6706 bool nsLayoutUtils::ImageRequestUsesCORS(imgIRequest
* aRequest
) {
6707 int32_t corsMode
= mozilla::CORS_NONE
;
6708 return NS_SUCCEEDED(aRequest
->GetCORSMode(&corsMode
)) &&
6709 corsMode
!= mozilla::CORS_NONE
;
6712 static bool NonZeroCorner(const LengthPercentage
& aLength
) {
6713 // Since negative results are clamped to 0, check > 0.
6714 return aLength
.Resolve(nscoord_MAX
) > 0 || aLength
.Resolve(0) > 0;
6718 bool nsLayoutUtils::HasNonZeroCorner(const BorderRadius
& aCorners
) {
6719 for (const auto corner
: mozilla::AllPhysicalHalfCorners()) {
6720 if (NonZeroCorner(aCorners
.Get(corner
))) return true;
6725 // aCorner is a "full corner" value, i.e. eCornerTopLeft etc.
6726 static bool IsCornerAdjacentToSide(uint8_t aCorner
, Side aSide
) {
6727 static_assert((int)eSideTop
== eCornerTopLeft
, "Check for Full Corner");
6728 static_assert((int)eSideRight
== eCornerTopRight
, "Check for Full Corner");
6729 static_assert((int)eSideBottom
== eCornerBottomRight
,
6730 "Check for Full Corner");
6731 static_assert((int)eSideLeft
== eCornerBottomLeft
, "Check for Full Corner");
6732 static_assert((int)eSideTop
== ((eCornerTopRight
- 1) & 3),
6733 "Check for Full Corner");
6734 static_assert((int)eSideRight
== ((eCornerBottomRight
- 1) & 3),
6735 "Check for Full Corner");
6736 static_assert((int)eSideBottom
== ((eCornerBottomLeft
- 1) & 3),
6737 "Check for Full Corner");
6738 static_assert((int)eSideLeft
== ((eCornerTopLeft
- 1) & 3),
6739 "Check for Full Corner");
6741 return aSide
== aCorner
|| aSide
== ((aCorner
- 1) & 3);
6745 bool nsLayoutUtils::HasNonZeroCornerOnSide(const BorderRadius
& aCorners
,
6747 static_assert(eCornerTopLeftX
/ 2 == eCornerTopLeft
,
6748 "Check for Non Zero on side");
6749 static_assert(eCornerTopLeftY
/ 2 == eCornerTopLeft
,
6750 "Check for Non Zero on side");
6751 static_assert(eCornerTopRightX
/ 2 == eCornerTopRight
,
6752 "Check for Non Zero on side");
6753 static_assert(eCornerTopRightY
/ 2 == eCornerTopRight
,
6754 "Check for Non Zero on side");
6755 static_assert(eCornerBottomRightX
/ 2 == eCornerBottomRight
,
6756 "Check for Non Zero on side");
6757 static_assert(eCornerBottomRightY
/ 2 == eCornerBottomRight
,
6758 "Check for Non Zero on side");
6759 static_assert(eCornerBottomLeftX
/ 2 == eCornerBottomLeft
,
6760 "Check for Non Zero on side");
6761 static_assert(eCornerBottomLeftY
/ 2 == eCornerBottomLeft
,
6762 "Check for Non Zero on side");
6764 for (const auto corner
: mozilla::AllPhysicalHalfCorners()) {
6765 // corner is a "half corner" value, so dividing by two gives us a
6766 // "full corner" value.
6767 if (NonZeroCorner(aCorners
.Get(corner
)) &&
6768 IsCornerAdjacentToSide(corner
/ 2, aSide
))
6775 widget::TransparencyMode
nsLayoutUtils::GetFrameTransparency(
6776 nsIFrame
* aBackgroundFrame
, nsIFrame
* aCSSRootFrame
) {
6777 if (!aCSSRootFrame
->StyleEffects()->IsOpaque()) {
6778 return TransparencyMode::Transparent
;
6781 if (HasNonZeroCorner(aCSSRootFrame
->StyleBorder()->mBorderRadius
)) {
6782 return TransparencyMode::Transparent
;
6785 nsITheme::Transparency transparency
;
6786 if (aCSSRootFrame
->IsThemed(&transparency
)) {
6787 return transparency
== nsITheme::eTransparent
6788 ? TransparencyMode::Transparent
6789 : TransparencyMode::Opaque
;
6792 // We need an uninitialized window to be treated as opaque because doing
6793 // otherwise breaks window display effects on some platforms, specifically
6794 // Vista. (bug 450322)
6795 if (aBackgroundFrame
->IsViewportFrame() &&
6796 !aBackgroundFrame
->PrincipalChildList().FirstChild()) {
6797 return TransparencyMode::Opaque
;
6800 const ComputedStyle
* bgSC
= nsCSSRendering::FindBackground(aBackgroundFrame
);
6802 return TransparencyMode::Transparent
;
6804 const nsStyleBackground
* bg
= bgSC
->StyleBackground();
6805 if (NS_GET_A(bg
->BackgroundColor(bgSC
)) < 255 ||
6806 // bottom layer's clip is used for the color
6807 bg
->BottomLayer().mClip
!= StyleGeometryBox::BorderBox
) {
6808 return TransparencyMode::Transparent
;
6810 return TransparencyMode::Opaque
;
6814 bool nsLayoutUtils::IsPopup(const nsIFrame
* aFrame
) {
6815 // Optimization: the frame can't possibly be a popup if it has no view.
6816 if (!aFrame
->HasView()) {
6817 NS_ASSERTION(!aFrame
->IsMenuPopupFrame(), "popup frame must have a view");
6820 return aFrame
->IsMenuPopupFrame();
6824 nsIFrame
* nsLayoutUtils::GetDisplayRootFrame(nsIFrame
* aFrame
) {
6825 return const_cast<nsIFrame
*>(
6826 nsLayoutUtils::GetDisplayRootFrame(const_cast<const nsIFrame
*>(aFrame
)));
6830 const nsIFrame
* nsLayoutUtils::GetDisplayRootFrame(const nsIFrame
* aFrame
) {
6831 // We could use GetRootPresContext() here if the
6832 // NS_FRAME_IN_POPUP frame bit is set.
6833 const nsIFrame
* f
= aFrame
;
6835 if (!f
->HasAnyStateBits(NS_FRAME_IN_POPUP
)) {
6836 f
= f
->PresShell()->GetRootFrame();
6840 } else if (IsPopup(f
)) {
6843 nsIFrame
* parent
= GetCrossDocParentFrameInProcess(f
);
6844 if (!parent
) return f
;
6850 nsIFrame
* nsLayoutUtils::GetReferenceFrame(nsIFrame
* aFrame
) {
6851 nsIFrame
* f
= aFrame
;
6853 if (f
->IsTransformed() || IsPopup(f
)) {
6856 nsIFrame
* parent
= GetCrossDocParentFrameInProcess(f
);
6864 /* static */ gfx::ShapedTextFlags
nsLayoutUtils::GetTextRunFlagsForStyle(
6865 const ComputedStyle
* aComputedStyle
, nsPresContext
* aPresContext
,
6866 const nsStyleFont
* aStyleFont
, const nsStyleText
* aStyleText
,
6867 nscoord aLetterSpacing
) {
6868 gfx::ShapedTextFlags result
= gfx::ShapedTextFlags();
6869 if (aLetterSpacing
!= 0 ||
6870 aStyleText
->mTextJustify
== StyleTextJustify::InterCharacter
) {
6871 result
|= gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES
;
6873 if (aStyleText
->mMozControlCharacterVisibility
==
6874 StyleMozControlCharacterVisibility::Hidden
) {
6875 result
|= gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS
;
6877 switch (aComputedStyle
->StyleText()->mTextRendering
) {
6878 case StyleTextRendering::Optimizespeed
:
6879 result
|= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED
;
6881 case StyleTextRendering::Auto
:
6883 aStyleFont
->mFont
.size
.ToCSSPixels() <
6884 aPresContext
->DevPixelsToFloatCSSPixels(
6885 StaticPrefs::browser_display_auto_quality_min_font_size())) {
6886 result
|= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED
;
6892 return result
| GetTextRunOrientFlagsForStyle(aComputedStyle
);
6895 /* static */ gfx::ShapedTextFlags
nsLayoutUtils::GetTextRunOrientFlagsForStyle(
6896 const ComputedStyle
* aComputedStyle
) {
6897 auto writingMode
= aComputedStyle
->StyleVisibility()->mWritingMode
;
6898 switch (writingMode
) {
6899 case StyleWritingModeProperty::HorizontalTb
:
6900 return gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL
;
6902 case StyleWritingModeProperty::VerticalLr
:
6903 case StyleWritingModeProperty::VerticalRl
:
6904 switch (aComputedStyle
->StyleVisibility()->mTextOrientation
) {
6905 case StyleTextOrientation::Mixed
:
6906 return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
;
6907 case StyleTextOrientation::Upright
:
6908 return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
;
6909 case StyleTextOrientation::Sideways
:
6910 return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
6912 MOZ_ASSERT_UNREACHABLE("unknown text-orientation");
6913 return gfx::ShapedTextFlags();
6916 case StyleWritingModeProperty::SidewaysLr
:
6917 return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
;
6919 case StyleWritingModeProperty::SidewaysRl
:
6920 return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
6923 MOZ_ASSERT_UNREACHABLE("unknown writing-mode");
6924 return gfx::ShapedTextFlags();
6929 void nsLayoutUtils::GetRectDifferenceStrips(const nsRect
& aR1
,
6930 const nsRect
& aR2
, nsRect
* aHStrip
,
6932 NS_ASSERTION(aR1
.TopLeft() == aR2
.TopLeft(),
6933 "expected rects at the same position");
6934 nsRect
unionRect(aR1
.x
, aR1
.y
, std::max(aR1
.width
, aR2
.width
),
6935 std::max(aR1
.height
, aR2
.height
));
6936 nscoord VStripStart
= std::min(aR1
.width
, aR2
.width
);
6937 nscoord HStripStart
= std::min(aR1
.height
, aR2
.height
);
6938 *aVStrip
= unionRect
;
6939 aVStrip
->x
+= VStripStart
;
6940 aVStrip
->width
-= VStripStart
;
6941 *aHStrip
= unionRect
;
6942 aHStrip
->y
+= HStripStart
;
6943 aHStrip
->height
-= HStripStart
;
6946 nsDeviceContext
* nsLayoutUtils::GetDeviceContextForScreenInfo(
6947 nsPIDOMWindowOuter
* aWindow
) {
6952 nsCOMPtr
<nsIDocShell
> docShell
= aWindow
->GetDocShell();
6954 // Now make sure our size is up to date. That will mean that the device
6955 // context does the right thing on multi-monitor systems when we return it
6956 // to the caller. It will also make sure that our prescontext has been
6957 // created, if we're supposed to have one.
6958 nsCOMPtr
<nsPIDOMWindowOuter
> win
= docShell
->GetWindow();
6960 // No reason to go on
6964 win
->EnsureSizeAndPositionUpToDate();
6966 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
6968 nsDeviceContext
* context
= presContext
->DeviceContext();
6974 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
6975 docShell
->GetInProcessParent(getter_AddRefs(parentItem
));
6976 docShell
= do_QueryInterface(parentItem
);
6983 bool nsLayoutUtils::IsReallyFixedPos(const nsIFrame
* aFrame
) {
6984 MOZ_ASSERT(aFrame
->StyleDisplay()->mPosition
== StylePositionProperty::Fixed
,
6985 "IsReallyFixedPos called on non-'position:fixed' frame");
6986 return MayBeReallyFixedPos(aFrame
);
6990 bool nsLayoutUtils::MayBeReallyFixedPos(const nsIFrame
* aFrame
) {
6991 MOZ_ASSERT(aFrame
->GetParent(),
6992 "MayBeReallyFixedPos called on frame not in tree");
6993 LayoutFrameType parentType
= aFrame
->GetParent()->Type();
6994 return parentType
== LayoutFrameType::Viewport
||
6995 parentType
== LayoutFrameType::PageContent
;
6999 bool nsLayoutUtils::IsInPositionFixedSubtree(const nsIFrame
* aFrame
) {
7000 for (const nsIFrame
* f
= aFrame
; f
; f
= f
->GetParent()) {
7001 if (f
->StyleDisplay()->mPosition
== StylePositionProperty::Fixed
&&
7002 nsLayoutUtils::IsReallyFixedPos(f
)) {
7009 static RefPtr
<SourceSurface
> ScaleSourceSurface(SourceSurface
& aSurface
,
7010 const IntSize
& aTargetSize
) {
7011 const IntSize surfaceSize
= aSurface
.GetSize();
7013 MOZ_ASSERT(surfaceSize
!= aTargetSize
);
7014 MOZ_ASSERT(!surfaceSize
.IsEmpty());
7015 MOZ_ASSERT(!aTargetSize
.IsEmpty());
7017 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTarget(
7018 gfxVars::ContentBackend(), aTargetSize
, aSurface
.GetFormat());
7020 if (!dt
|| !dt
->IsValid()) {
7024 dt
->DrawSurface(&aSurface
, Rect(Point(), Size(aTargetSize
)),
7025 Rect(Point(), Size(surfaceSize
)));
7026 return dt
->GetBackingSurface();
7029 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromOffscreenCanvas(
7030 OffscreenCanvas
* aOffscreenCanvas
, uint32_t aSurfaceFlags
,
7031 RefPtr
<DrawTarget
>& aTarget
) {
7032 SurfaceFromElementResult result
;
7034 IntSize size
= aOffscreenCanvas
->GetWidthHeight();
7035 if (size
.IsEmpty()) {
7039 result
.mSourceSurface
=
7040 aOffscreenCanvas
->GetSurfaceSnapshot(&result
.mAlphaType
);
7041 if (!result
.mSourceSurface
) {
7042 // If the element doesn't have a context then we won't get a snapshot. The
7043 // canvas spec wants us to not error and just draw nothing, so return an
7045 result
.mSize
= size
;
7046 result
.mAlphaType
= gfxAlphaType::Opaque
;
7047 RefPtr
<DrawTarget
> ref
=
7048 aTarget
? aTarget
: gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
7049 if (ref
->CanCreateSimilarDrawTarget(size
, SurfaceFormat::B8G8R8A8
)) {
7050 RefPtr
<DrawTarget
> dt
=
7051 ref
->CreateSimilarDrawTarget(size
, SurfaceFormat::B8G8R8A8
);
7053 result
.mSourceSurface
= dt
->Snapshot();
7057 result
.mSize
= result
.mSourceSurface
->GetSize();
7059 // If we want an exact sized surface, then we need to scale if we don't
7060 // match the intrinsic size.
7061 const bool exactSize
= aSurfaceFlags
& SFE_EXACT_SIZE_SURFACE
;
7062 if (exactSize
&& size
!= result
.mSize
) {
7063 result
.mSize
= size
;
7064 result
.mSourceSurface
= ScaleSourceSurface(*result
.mSourceSurface
, size
);
7067 if (aTarget
&& result
.mSourceSurface
) {
7068 RefPtr
<SourceSurface
> opt
=
7069 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
7071 result
.mSourceSurface
= opt
;
7076 result
.mHasSize
= true;
7077 result
.mIntrinsicSize
= size
;
7078 result
.mIsWriteOnly
= aOffscreenCanvas
->IsWriteOnly();
7080 nsIGlobalObject
* global
= aOffscreenCanvas
->GetParentObject();
7082 result
.mPrincipal
= global
->PrincipalOrNull();
7088 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromVideoFrame(
7089 VideoFrame
* aVideoFrame
, uint32_t aSurfaceFlags
,
7090 RefPtr
<DrawTarget
>& aTarget
) {
7091 SurfaceFromElementResult result
;
7093 RefPtr
<layers::Image
> layersImage
= aVideoFrame
->GetImage();
7098 IntSize codedSize
= aVideoFrame
->NativeCodedSize();
7099 IntRect visibleRect
= aVideoFrame
->NativeVisibleRect();
7100 IntSize displaySize
= aVideoFrame
->NativeDisplaySize();
7102 MOZ_ASSERT(layersImage
->GetSize() == codedSize
);
7103 IntRect
codedRect(IntPoint(0, 0), codedSize
);
7105 if (visibleRect
.IsEqualEdges(codedRect
) && displaySize
== codedSize
) {
7106 // The display and coded rects are identical, which means we can just use
7108 result
.mLayersImage
= std::move(layersImage
);
7109 result
.mSize
= codedSize
;
7110 result
.mIntrinsicSize
= codedSize
;
7111 } else if (aSurfaceFlags
& SFE_ALLOW_UNCROPPED_UNSCALED
) {
7112 // The caller supports cropping/scaling.
7113 result
.mLayersImage
= std::move(layersImage
);
7114 result
.mCropRect
= Some(visibleRect
);
7115 result
.mSize
= codedSize
;
7116 result
.mIntrinsicSize
= displaySize
;
7118 // The caller does not support cropping/scaling. We need to on its behalf.
7119 RefPtr
<SourceSurface
> surface
= layersImage
->GetAsSourceSurface();
7124 RefPtr
<DrawTarget
> ref
= aTarget
7126 : gfxPlatform::GetPlatform()
7127 ->ThreadLocalScreenReferenceDrawTarget();
7128 if (!ref
->CanCreateSimilarDrawTarget(displaySize
,
7129 SurfaceFormat::B8G8R8A8
)) {
7133 RefPtr
<DrawTarget
> dt
=
7134 ref
->CreateSimilarDrawTarget(displaySize
, SurfaceFormat::B8G8R8A8
);
7139 gfx::Rect
dstRect(0, 0, displaySize
.Width(), displaySize
.Height());
7140 gfx::Rect
srcRect(visibleRect
.X(), visibleRect
.Y(), visibleRect
.Width(),
7141 visibleRect
.Height());
7142 dt
->DrawSurface(surface
, dstRect
, srcRect
);
7143 result
.mSourceSurface
= dt
->Snapshot();
7144 if (NS_WARN_IF(!result
.mSourceSurface
)) {
7148 result
.mSize
= displaySize
;
7149 result
.mIntrinsicSize
= displaySize
;
7152 result
.mAlphaType
= gfxAlphaType::Premult
;
7153 Nullable
<VideoPixelFormat
> format
= aVideoFrame
->GetFormat();
7154 if (!format
.IsNull()) {
7155 switch (format
.Value()) {
7156 case VideoPixelFormat::I420
:
7157 case VideoPixelFormat::I422
:
7158 case VideoPixelFormat::I444
:
7159 case VideoPixelFormat::NV12
:
7160 case VideoPixelFormat::RGBX
:
7161 case VideoPixelFormat::BGRX
:
7162 result
.mAlphaType
= gfxAlphaType::Opaque
;
7169 result
.mHasSize
= true;
7171 // We shouldn't have a VideoFrame if either of these is true.
7172 result
.mHadCrossOriginRedirects
= false;
7173 result
.mIsWriteOnly
= false;
7175 nsIGlobalObject
* global
= aVideoFrame
->GetParentObject();
7177 result
.mPrincipal
= global
->PrincipalOrNull();
7181 // They gave us a DrawTarget to optimize for, so even though we may have a
7182 // layers::Image, we should unconditionally try to grab a SourceSurface and
7183 // try to optimize it.
7184 if (result
.mLayersImage
) {
7185 MOZ_ASSERT(!result
.mSourceSurface
);
7186 result
.mSourceSurface
= result
.mLayersImage
->GetAsSourceSurface();
7189 if (result
.mSourceSurface
) {
7190 RefPtr
<SourceSurface
> opt
=
7191 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
7193 result
.mSourceSurface
= std::move(opt
);
7201 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromImageBitmap(
7202 mozilla::dom::ImageBitmap
* aImageBitmap
, uint32_t aSurfaceFlags
) {
7203 return aImageBitmap
->SurfaceFrom(aSurfaceFlags
);
7206 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(
7207 nsIImageLoadingContent
* aElement
, const Maybe
<int32_t>& aResizeWidth
,
7208 const Maybe
<int32_t>& aResizeHeight
, uint32_t aSurfaceFlags
,
7209 RefPtr
<DrawTarget
>& aTarget
) {
7210 SurfaceFromElementResult result
;
7213 nsCOMPtr
<imgIRequest
> imgRequest
;
7214 rv
= aElement
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
7215 getter_AddRefs(imgRequest
));
7216 if (NS_FAILED(rv
)) {
7221 // There's no image request. This is either because a request for
7222 // a non-empty URI failed, or the URI is the empty string.
7223 nsCOMPtr
<nsIURI
> currentURI
;
7224 aElement
->GetCurrentURI(getter_AddRefs(currentURI
));
7226 // Treat the empty URI as available instead of broken state.
7227 result
.mHasSize
= true;
7233 imgRequest
->GetImageStatus(&status
);
7234 result
.mHasSize
= status
& imgIRequest::STATUS_SIZE_AVAILABLE
;
7235 if ((status
& imgIRequest::STATUS_LOAD_COMPLETE
) == 0) {
7236 // Spec says to use GetComplete, but that only works on
7237 // HTMLImageElement, and we support all sorts of other stuff
7238 // here. Do this for now pending spec clarification.
7239 result
.mIsStillLoading
= (status
& imgIRequest::STATUS_ERROR
) == 0;
7243 nsCOMPtr
<nsIPrincipal
> principal
;
7244 rv
= imgRequest
->GetImagePrincipal(getter_AddRefs(principal
));
7245 if (NS_FAILED(rv
)) {
7249 nsCOMPtr
<imgIContainer
> imgContainer
;
7250 rv
= imgRequest
->GetImage(getter_AddRefs(imgContainer
));
7251 if (NS_FAILED(rv
)) {
7255 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aElement
);
7257 // Ensure that the image is oriented the same way as it's displayed
7258 // if the image request is of the same origin.
7260 content
->GetPrimaryFrame() &&
7261 !(aSurfaceFlags
& SFE_ORIENTATION_FROM_IMAGE
)
7262 ? content
->GetPrimaryFrame()->StyleVisibility()->UsedImageOrientation(
7264 : nsStyleVisibility::UsedImageOrientation(
7265 imgRequest
, StyleImageOrientation::FromImage
);
7266 imgContainer
= OrientImage(imgContainer
, orientation
);
7268 const bool noRasterize
= aSurfaceFlags
& SFE_NO_RASTERIZING_VECTORS
;
7270 uint32_t whichFrame
= aSurfaceFlags
& SFE_WANT_FIRST_FRAME_IF_IMAGE
7271 ? (uint32_t)imgIContainer::FRAME_FIRST
7272 : (uint32_t)imgIContainer::FRAME_CURRENT
;
7273 const bool exactSize
= aSurfaceFlags
& SFE_EXACT_SIZE_SURFACE
;
7275 uint32_t frameFlags
=
7276 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
7277 if (aSurfaceFlags
& SFE_NO_COLORSPACE_CONVERSION
)
7278 frameFlags
|= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION
;
7279 if (aSurfaceFlags
& SFE_ALLOW_NON_PREMULT
) {
7280 frameFlags
|= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
;
7283 int32_t imgWidth
, imgHeight
;
7284 HTMLImageElement
* element
= HTMLImageElement::FromNodeOrNull(content
);
7285 if (aSurfaceFlags
& SFE_USE_ELEMENT_SIZE_IF_VECTOR
&& element
&&
7286 imgContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
7287 // We're holding a strong ref to "element" via "content".
7288 imgWidth
= MOZ_KnownLive(element
)->Width();
7289 imgHeight
= MOZ_KnownLive(element
)->Height();
7291 auto res
= imgContainer
->GetResolution();
7292 rv
= imgContainer
->GetWidth(&imgWidth
);
7293 if (NS_SUCCEEDED(rv
)) {
7294 res
.ApplyXTo(imgWidth
);
7295 } else if (aResizeWidth
.isSome()) {
7296 imgWidth
= *aResizeWidth
;
7298 // As stated in css-sizing-3 Intrinsic Sizes, the fallback size of
7299 // 300 x 150 for the width and height as needed.
7301 // See https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
7302 imgWidth
= kFallbackIntrinsicWidthInPixels
;
7304 rv
= imgContainer
->GetHeight(&imgHeight
);
7305 if (NS_SUCCEEDED(rv
)) {
7306 res
.ApplyYTo(imgHeight
);
7307 } else if (aResizeHeight
.isSome()) {
7308 imgHeight
= *aResizeHeight
;
7310 // As stated in css-sizing-3 Intrinsic Sizes, the fallback size of
7311 // 300 x 150 for the width and height as needed.
7313 // See https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
7314 imgHeight
= kFallbackIntrinsicHeightInPixels
;
7317 result
.mSize
= result
.mIntrinsicSize
= IntSize(imgWidth
, imgHeight
);
7319 if (!noRasterize
|| imgContainer
->GetType() == imgIContainer::TYPE_RASTER
) {
7320 result
.mSourceSurface
=
7321 imgContainer
->GetFrameAtSize(result
.mSize
, whichFrame
, frameFlags
);
7322 if (!result
.mSourceSurface
) {
7325 IntSize surfSize
= result
.mSourceSurface
->GetSize();
7326 if (exactSize
&& surfSize
!= result
.mSize
) {
7327 result
.mSourceSurface
=
7328 ScaleSourceSurface(*result
.mSourceSurface
, result
.mSize
);
7329 if (!result
.mSourceSurface
) {
7333 result
.mSize
= surfSize
;
7335 // The surface we return is likely to be cached. We don't want to have to
7336 // convert to a surface that's compatible with aTarget each time it's used
7337 // (that would result in terrible performance), so we convert once here
7338 // upfront if aTarget is specified.
7340 RefPtr
<SourceSurface
> optSurface
=
7341 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
7343 result
.mSourceSurface
= optSurface
;
7347 const auto& format
= result
.mSourceSurface
->GetFormat();
7348 if (IsOpaque(format
)) {
7349 result
.mAlphaType
= gfxAlphaType::Opaque
;
7350 } else if (frameFlags
& imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
) {
7351 result
.mAlphaType
= gfxAlphaType::NonPremult
;
7353 result
.mAlphaType
= gfxAlphaType::Premult
;
7356 result
.mDrawInfo
.mImgContainer
= imgContainer
;
7357 result
.mDrawInfo
.mWhichFrame
= whichFrame
;
7358 result
.mDrawInfo
.mDrawingFlags
= frameFlags
;
7361 result
.mCORSUsed
= nsLayoutUtils::ImageRequestUsesCORS(imgRequest
);
7363 bool hadCrossOriginRedirects
= true;
7364 imgRequest
->GetHadCrossOriginRedirects(&hadCrossOriginRedirects
);
7366 result
.mPrincipal
= std::move(principal
);
7367 result
.mHadCrossOriginRedirects
= hadCrossOriginRedirects
;
7368 result
.mImageRequest
= std::move(imgRequest
);
7369 result
.mIsWriteOnly
= CanvasUtils::CheckWriteOnlySecurity(
7370 result
.mCORSUsed
, result
.mPrincipal
, result
.mHadCrossOriginRedirects
);
7375 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(
7376 HTMLImageElement
* aElement
, uint32_t aSurfaceFlags
,
7377 RefPtr
<DrawTarget
>& aTarget
) {
7378 return SurfaceFromElement(static_cast<nsIImageLoadingContent
*>(aElement
),
7379 Nothing(), Nothing(), aSurfaceFlags
, aTarget
);
7382 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(
7383 HTMLCanvasElement
* aElement
, uint32_t aSurfaceFlags
,
7384 RefPtr
<DrawTarget
>& aTarget
) {
7385 SurfaceFromElementResult result
;
7387 IntSize size
= aElement
->GetSize();
7388 if (size
.IsEmpty()) {
7392 auto pAlphaType
= &result
.mAlphaType
;
7393 if (!(aSurfaceFlags
& SFE_ALLOW_NON_PREMULT
)) {
7395 nullptr; // Coersce GetSurfaceSnapshot to give us Opaque/Premult only.
7397 result
.mSourceSurface
= aElement
->GetSurfaceSnapshot(pAlphaType
, aTarget
);
7398 if (!result
.mSourceSurface
) {
7399 // If the element doesn't have a context then we won't get a snapshot. The
7400 // canvas spec wants us to not error and just draw nothing, so return an
7402 result
.mSize
= size
;
7403 result
.mAlphaType
= gfxAlphaType::Opaque
;
7404 RefPtr
<DrawTarget
> ref
=
7406 : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
7407 if (ref
->CanCreateSimilarDrawTarget(size
, SurfaceFormat::B8G8R8A8
)) {
7408 RefPtr
<DrawTarget
> dt
=
7409 ref
->CreateSimilarDrawTarget(size
, SurfaceFormat::B8G8R8A8
);
7411 result
.mSourceSurface
= dt
->Snapshot();
7415 result
.mSize
= result
.mSourceSurface
->GetSize();
7417 // If we want an exact sized surface, then we need to scale if we don't
7418 // match the intrinsic size.
7419 const bool exactSize
= aSurfaceFlags
& SFE_EXACT_SIZE_SURFACE
;
7420 if (exactSize
&& size
!= result
.mSize
) {
7421 result
.mSize
= size
;
7422 result
.mSourceSurface
= ScaleSourceSurface(*result
.mSourceSurface
, size
);
7425 if (aTarget
&& result
.mSourceSurface
) {
7426 RefPtr
<SourceSurface
> opt
=
7427 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
7429 result
.mSourceSurface
= opt
;
7434 // Ensure that any future changes to the canvas trigger proper invalidation,
7435 // in case this is being used by -moz-element()
7436 aElement
->MarkContextClean();
7438 result
.mHasSize
= true;
7439 result
.mIntrinsicSize
= size
;
7440 result
.mPrincipal
= aElement
->NodePrincipal();
7441 result
.mHadCrossOriginRedirects
= false;
7442 result
.mIsWriteOnly
= aElement
->IsWriteOnly();
7447 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(
7448 HTMLVideoElement
* aElement
, uint32_t aSurfaceFlags
,
7449 RefPtr
<DrawTarget
>& aTarget
, bool aOptimizeSourceSurface
) {
7450 SurfaceFromElementResult result
;
7451 result
.mAlphaType
= gfxAlphaType::Opaque
; // Assume opaque.
7453 if (aElement
->ContainsRestrictedContent()) {
7457 uint16_t readyState
= aElement
->ReadyState();
7458 if (readyState
== HAVE_NOTHING
|| readyState
== HAVE_METADATA
) {
7459 result
.mIsStillLoading
= true;
7463 // If it doesn't have a principal, just bail
7464 nsCOMPtr
<nsIPrincipal
> principal
= aElement
->GetCurrentVideoPrincipal();
7469 result
.mLayersImage
= aElement
->GetCurrentImage();
7470 if (!result
.mLayersImage
) {
7474 result
.mCORSUsed
= aElement
->GetCORSMode() != CORS_NONE
;
7475 result
.mHasSize
= true;
7476 result
.mSize
= result
.mLayersImage
->GetSize();
7477 result
.mIntrinsicSize
=
7478 gfx::IntSize(aElement
->VideoWidth(), aElement
->VideoHeight());
7479 result
.mPrincipal
= std::move(principal
);
7480 result
.mHadCrossOriginRedirects
= aElement
->HadCrossOriginRedirects();
7481 result
.mIsWriteOnly
= CanvasUtils::CheckWriteOnlySecurity(
7482 result
.mCORSUsed
, result
.mPrincipal
, result
.mHadCrossOriginRedirects
);
7484 if (aTarget
&& aOptimizeSourceSurface
) {
7485 // They gave us a DrawTarget to optimize for, so even though we have a
7486 // layers::Image, we should unconditionally try to grab a SourceSurface and
7487 // try to optimize it.
7488 if ((result
.mSourceSurface
= result
.mLayersImage
->GetAsSourceSurface())) {
7489 RefPtr
<SourceSurface
> opt
=
7490 aTarget
->OptimizeSourceSurface(result
.mSourceSurface
);
7492 result
.mSourceSurface
= opt
;
7500 SurfaceFromElementResult
nsLayoutUtils::SurfaceFromElement(
7501 dom::Element
* aElement
, const Maybe
<int32_t>& aResizeWidth
,
7502 const Maybe
<int32_t>& aResizeHeight
, uint32_t aSurfaceFlags
,
7503 RefPtr
<DrawTarget
>& aTarget
) {
7504 // If it's a <canvas>, we may be able to just grab its internal surface
7505 if (HTMLCanvasElement
* canvas
= HTMLCanvasElement::FromNodeOrNull(aElement
)) {
7506 return SurfaceFromElement(canvas
, aSurfaceFlags
, aTarget
);
7509 // Maybe it's <video>?
7510 if (HTMLVideoElement
* video
= HTMLVideoElement::FromNodeOrNull(aElement
)) {
7511 return SurfaceFromElement(video
, aSurfaceFlags
, aTarget
);
7514 // Finally, check if it's a normal image
7515 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aElement
);
7518 return SurfaceFromElementResult();
7521 return SurfaceFromElement(imageLoader
, aResizeWidth
, aResizeHeight
,
7522 aSurfaceFlags
, aTarget
);
7526 Element
* nsLayoutUtils::GetEditableRootContentByContentEditable(
7527 Document
* aDocument
) {
7528 // If the document is in designMode we should return nullptr.
7529 if (!aDocument
|| aDocument
->IsInDesignMode()) {
7533 // contenteditable only works with HTML document.
7534 // XXXbz should this test IsHTMLOrXHTML(), or just IsHTML()?
7535 if (!aDocument
->IsHTMLOrXHTML()) {
7539 Element
* rootElement
= aDocument
->GetRootElement();
7540 if (rootElement
&& rootElement
->IsEditable()) {
7544 // If there is no editable root element, check its <body> element.
7545 // Note that the body element could be <frameset> element.
7546 Element
* bodyElement
= aDocument
->GetBody();
7547 if (bodyElement
&& bodyElement
->IsEditable()) {
7555 void nsLayoutUtils::AssertNoDuplicateContinuations(
7556 nsIFrame
* aContainer
, const nsFrameList
& aFrameList
) {
7557 for (nsIFrame
* f
: aFrameList
) {
7558 // Check only later continuations of f; we deal with checking the
7559 // earlier continuations when we hit those earlier continuations in
7561 for (nsIFrame
* c
= f
; (c
= c
->GetNextInFlow());) {
7562 NS_ASSERTION(c
->GetParent() != aContainer
|| !aFrameList
.ContainsFrame(c
),
7563 "Two continuations of the same frame in the same "
7569 // Is one of aFrame's ancestors a letter frame?
7570 static bool IsInLetterFrame(nsIFrame
* aFrame
) {
7571 for (nsIFrame
* f
= aFrame
->GetParent(); f
; f
= f
->GetParent()) {
7572 if (f
->IsLetterFrame()) {
7580 void nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame
* aSubtreeRoot
) {
7581 NS_ASSERTION(aSubtreeRoot
->GetPrevInFlow(),
7582 "frame tree not empty, but caller reported complete status");
7584 // Also assert that text frames map no text.
7585 auto [start
, end
] = aSubtreeRoot
->GetOffsets();
7586 // In some cases involving :first-letter, we'll partially unlink a
7587 // continuation in the middle of a continuation chain from its
7588 // previous and next continuations before destroying it, presumably so
7589 // that we don't also destroy the later continuations. Once we've
7590 // done this, GetOffsets returns incorrect values.
7591 // For examples, see list of tests in
7592 // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
7593 NS_ASSERTION(start
== end
|| IsInLetterFrame(aSubtreeRoot
),
7594 "frame tree not empty, but caller reported complete status");
7596 for (const auto& childList
: aSubtreeRoot
->ChildLists()) {
7597 for (nsIFrame
* child
: childList
.mList
) {
7598 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(child
);
7604 static void GetFontFacesForFramesInner(
7605 nsIFrame
* aFrame
, nsLayoutUtils::UsedFontFaceList
& aResult
,
7606 nsLayoutUtils::UsedFontFaceTable
& aFontFaces
, uint32_t aMaxRanges
,
7607 bool aSkipCollapsedWhitespace
) {
7608 MOZ_ASSERT(aFrame
, "NULL frame pointer");
7610 if (aFrame
->IsTextFrame()) {
7611 if (!aFrame
->GetPrevContinuation()) {
7612 nsLayoutUtils::GetFontFacesForText(aFrame
, 0, INT32_MAX
, true, aResult
,
7613 aFontFaces
, aMaxRanges
,
7614 aSkipCollapsedWhitespace
);
7619 FrameChildListID childLists
[] = {FrameChildListID::Principal
,
7620 FrameChildListID::Popup
};
7621 for (size_t i
= 0; i
< ArrayLength(childLists
); ++i
) {
7622 for (nsIFrame
* child
: aFrame
->GetChildList(childLists
[i
])) {
7623 child
= nsPlaceholderFrame::GetRealFrameFor(child
);
7624 GetFontFacesForFramesInner(child
, aResult
, aFontFaces
, aMaxRanges
,
7625 aSkipCollapsedWhitespace
);
7631 nsresult
nsLayoutUtils::GetFontFacesForFrames(nsIFrame
* aFrame
,
7632 UsedFontFaceList
& aResult
,
7633 UsedFontFaceTable
& aFontFaces
,
7634 uint32_t aMaxRanges
,
7635 bool aSkipCollapsedWhitespace
) {
7636 MOZ_ASSERT(aFrame
, "NULL frame pointer");
7639 GetFontFacesForFramesInner(aFrame
, aResult
, aFontFaces
, aMaxRanges
,
7640 aSkipCollapsedWhitespace
);
7641 aFrame
= GetNextContinuationOrIBSplitSibling(aFrame
);
7647 static void AddFontsFromTextRun(gfxTextRun
* aTextRun
, nsTextFrame
* aFrame
,
7648 gfxSkipCharsIterator
& aSkipIter
,
7649 const gfxTextRun::Range
& aRange
,
7650 nsLayoutUtils::UsedFontFaceList
& aResult
,
7651 nsLayoutUtils::UsedFontFaceTable
& aFontFaces
,
7652 uint32_t aMaxRanges
) {
7653 nsIContent
* content
= aFrame
->GetContent();
7654 int32_t contentLimit
=
7655 aFrame
->GetContentOffset() + aFrame
->GetInFlowContentLength();
7656 for (gfxTextRun::GlyphRunIterator
glyphRuns(aTextRun
, aRange
);
7657 !glyphRuns
.AtEnd(); glyphRuns
.NextRun()) {
7658 gfxFontEntry
* fe
= glyphRuns
.GlyphRun()->mFont
->GetFontEntry();
7659 // if we have already listed this face, just make sure the match type is
7661 InspectorFontFace
* fontFace
= aFontFaces
.Get(fe
);
7663 fontFace
->AddMatchType(glyphRuns
.GlyphRun()->mMatchType
);
7665 // A new font entry we haven't seen before
7666 fontFace
= new InspectorFontFace(fe
, aTextRun
->GetFontGroup(),
7667 glyphRuns
.GlyphRun()->mMatchType
);
7668 aFontFaces
.InsertOrUpdate(fe
, fontFace
);
7669 aResult
.AppendElement(fontFace
);
7672 // Add this glyph run to the fontFace's list of ranges, unless we have
7673 // already collected as many as wanted.
7674 if (fontFace
->RangeCount() < aMaxRanges
) {
7676 aSkipIter
.ConvertSkippedToOriginal(glyphRuns
.StringStart());
7677 int32_t end
= aSkipIter
.ConvertSkippedToOriginal(glyphRuns
.StringEnd());
7679 // Mapping back from textrun offsets ("skipped" offsets that reflect the
7680 // text after whitespace collapsing, etc) to DOM content offsets in the
7681 // original text is ambiguous, because many original characters can
7682 // map to a single skipped offset. aSkipIter.ConvertSkippedToOriginal()
7683 // will return an "original" offset that corresponds to the *end* of
7684 // a collapsed run of characters in this case; but that might extend
7685 // beyond the current content node if the textrun mapped multiple nodes.
7686 // So we clamp the end offset to keep it valid for the content node
7687 // that corresponds to the current textframe.
7688 end
= std::min(end
, contentLimit
);
7691 RefPtr
<nsRange
> range
=
7692 nsRange::Create(content
, start
, content
, end
, IgnoreErrors());
7693 NS_WARNING_ASSERTION(range
,
7694 "nsRange::Create() failed to create valid range");
7696 fontFace
->AddRange(range
);
7704 void nsLayoutUtils::GetFontFacesForText(nsIFrame
* aFrame
, int32_t aStartOffset
,
7706 bool aFollowContinuations
,
7707 UsedFontFaceList
& aResult
,
7708 UsedFontFaceTable
& aFontFaces
,
7709 uint32_t aMaxRanges
,
7710 bool aSkipCollapsedWhitespace
) {
7711 MOZ_ASSERT(aFrame
, "NULL frame pointer");
7713 if (!aFrame
->IsTextFrame()) {
7717 if (!aFrame
->StyleVisibility()->IsVisible()) {
7721 nsTextFrame
* curr
= static_cast<nsTextFrame
*>(aFrame
);
7723 int32_t fstart
= std::max(curr
->GetContentOffset(), aStartOffset
);
7724 int32_t fend
= std::min(curr
->GetContentEnd(), aEndOffset
);
7725 if (fstart
>= fend
) {
7726 curr
= static_cast<nsTextFrame
*>(curr
->GetNextContinuation());
7730 // curr is overlapping with the offset we want
7731 gfxSkipCharsIterator iter
= curr
->EnsureTextRun(nsTextFrame::eInflated
);
7732 gfxTextRun
* textRun
= curr
->GetTextRun(nsTextFrame::eInflated
);
7734 NS_WARNING("failed to get textRun, low memory?");
7738 // include continuations in the range that share the same textrun
7739 nsTextFrame
* next
= nullptr;
7740 if (aFollowContinuations
&& fend
< aEndOffset
) {
7741 next
= static_cast<nsTextFrame
*>(curr
->GetNextContinuation());
7742 while (next
&& next
->GetTextRun(nsTextFrame::eInflated
) == textRun
) {
7743 fend
= std::min(next
->GetContentEnd(), aEndOffset
);
7744 next
= fend
< aEndOffset
7745 ? static_cast<nsTextFrame
*>(next
->GetNextContinuation())
7750 if (!aSkipCollapsedWhitespace
|| (curr
->HasAnyNoncollapsedCharacters() &&
7751 curr
->HasNonSuppressedText())) {
7752 gfxTextRun::Range
range(iter
.ConvertOriginalToSkipped(fstart
),
7753 iter
.ConvertOriginalToSkipped(fend
));
7754 AddFontsFromTextRun(textRun
, curr
, iter
, range
, aResult
, aFontFaces
,
7759 } while (aFollowContinuations
&& curr
);
7763 size_t nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame
* aFrame
,
7764 MallocSizeOf aMallocSizeOf
,
7766 MOZ_ASSERT(aFrame
, "NULL frame pointer");
7770 if (aFrame
->IsTextFrame()) {
7771 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(aFrame
);
7772 for (uint32_t i
= 0; i
< 2; ++i
) {
7773 gfxTextRun
* run
= textFrame
->GetTextRun(
7774 (i
!= 0) ? nsTextFrame::eInflated
: nsTextFrame::eNotInflated
);
7777 run
->ResetSizeOfAccountingFlags();
7779 total
+= run
->MaybeSizeOfIncludingThis(aMallocSizeOf
);
7786 for (const auto& childList
: aFrame
->ChildLists()) {
7787 for (nsIFrame
* f
: childList
.mList
) {
7788 total
+= SizeOfTextRunsForFrames(f
, aMallocSizeOf
, clear
);
7795 void nsLayoutUtils::RecomputeSmoothScrollDefault() {
7796 if (nsContentUtils::ShouldResistFingerprinting(
7797 "We use the global RFP pref to maintain consistent scroll behavior "
7799 RFPTarget::CSSPrefersReducedMotion
)) {
7800 // When resist fingerprinting is enabled, we should not default disable
7801 // smooth scrolls when the user prefers-reduced-motion to avoid leaking
7802 // the value of the OS pref to sites.
7803 Preferences::SetBool(StaticPrefs::GetPrefName_general_smoothScroll(), true,
7804 PrefValueKind::Default
);
7806 // We want prefers-reduced-motion to determine the default
7807 // value of the general.smoothScroll pref. If the user
7808 // changed the pref we want to respect the change.
7809 Preferences::SetBool(
7810 StaticPrefs::GetPrefName_general_smoothScroll(),
7811 !LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion
, 0),
7812 PrefValueKind::Default
);
7817 bool nsLayoutUtils::IsSmoothScrollingEnabled() {
7818 return StaticPrefs::general_smoothScroll();
7822 void nsLayoutUtils::Initialize() {
7823 nsComputedDOMStyle::RegisterPrefChangeCallbacks();
7827 void nsLayoutUtils::Shutdown() {
7829 sContentMap
= nullptr;
7832 nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
7836 void nsLayoutUtils::RegisterImageRequest(nsPresContext
* aPresContext
,
7837 imgIRequest
* aRequest
,
7838 bool* aRequestRegistered
) {
7839 if (!aPresContext
) {
7843 if (aRequestRegistered
&& *aRequestRegistered
) {
7844 // Our request is already registered with the refresh driver, so
7845 // no need to register it again.
7850 aPresContext
->RefreshDriver()->AddImageRequest(aRequest
);
7851 if (aRequestRegistered
) {
7852 *aRequestRegistered
= true;
7858 void nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext
* aPresContext
,
7859 imgIRequest
* aRequest
,
7860 bool* aRequestRegistered
) {
7861 if (!aPresContext
) {
7865 if (aRequestRegistered
&& *aRequestRegistered
) {
7866 // Our request is already registered with the refresh driver, so
7867 // no need to register it again.
7872 nsCOMPtr
<imgIContainer
> image
;
7873 if (NS_SUCCEEDED(aRequest
->GetImage(getter_AddRefs(image
)))) {
7874 // Check to verify that the image is animated. If so, then add it to the
7875 // list of images tracked by the refresh driver.
7876 bool isAnimated
= false;
7877 nsresult rv
= image
->GetAnimated(&isAnimated
);
7878 if (NS_SUCCEEDED(rv
) && isAnimated
) {
7879 aPresContext
->RefreshDriver()->AddImageRequest(aRequest
);
7880 if (aRequestRegistered
) {
7881 *aRequestRegistered
= true;
7889 void nsLayoutUtils::DeregisterImageRequest(nsPresContext
* aPresContext
,
7890 imgIRequest
* aRequest
,
7891 bool* aRequestRegistered
) {
7892 if (!aPresContext
) {
7896 // Deregister our imgIRequest with the refresh driver to
7897 // complete tear-down, but only if it has been registered
7898 if (aRequestRegistered
&& !*aRequestRegistered
) {
7903 nsCOMPtr
<imgIContainer
> image
;
7904 if (NS_SUCCEEDED(aRequest
->GetImage(getter_AddRefs(image
)))) {
7905 aPresContext
->RefreshDriver()->RemoveImageRequest(aRequest
);
7907 if (aRequestRegistered
) {
7908 *aRequestRegistered
= false;
7915 void nsLayoutUtils::PostRestyleEvent(Element
* aElement
,
7916 RestyleHint aRestyleHint
,
7917 nsChangeHint aMinChangeHint
) {
7918 if (Document
* doc
= aElement
->GetComposedDoc()) {
7919 if (nsPresContext
* presContext
= doc
->GetPresContext()) {
7920 presContext
->RestyleManager()->PostRestyleEvent(aElement
, aRestyleHint
,
7926 nsSetAttrRunnable::nsSetAttrRunnable(Element
* aElement
, nsAtom
* aAttrName
,
7927 const nsAString
& aValue
)
7928 : mozilla::Runnable("nsSetAttrRunnable"),
7930 mAttrName(aAttrName
),
7932 NS_ASSERTION(aElement
&& aAttrName
, "Missing stuff, prepare to crash");
7935 nsSetAttrRunnable::nsSetAttrRunnable(Element
* aElement
, nsAtom
* aAttrName
,
7937 : mozilla::Runnable("nsSetAttrRunnable"),
7939 mAttrName(aAttrName
) {
7940 NS_ASSERTION(aElement
&& aAttrName
, "Missing stuff, prepare to crash");
7941 mValue
.AppendInt(aValue
);
7945 nsSetAttrRunnable::Run() {
7946 return mElement
->SetAttr(kNameSpaceID_None
, mAttrName
, mValue
, true);
7949 nsUnsetAttrRunnable::nsUnsetAttrRunnable(Element
* aElement
, nsAtom
* aAttrName
)
7950 : mozilla::Runnable("nsUnsetAttrRunnable"),
7952 mAttrName(aAttrName
) {
7953 NS_ASSERTION(aElement
&& aAttrName
, "Missing stuff, prepare to crash");
7957 nsUnsetAttrRunnable::Run() {
7958 return mElement
->UnsetAttr(kNameSpaceID_None
, mAttrName
, true);
7962 * Compute the minimum font size inside of a container with the given
7963 * width, such that **when the user zooms the container to fill the full
7964 * width of the device**, the fonts satisfy our minima.
7966 static nscoord
MinimumFontSizeFor(nsPresContext
* aPresContext
,
7967 WritingMode aWritingMode
,
7968 nscoord aContainerISize
) {
7969 PresShell
* presShell
= aPresContext
->PresShell();
7971 uint32_t emPerLine
= presShell
->FontSizeInflationEmPerLine();
7972 uint32_t minTwips
= presShell
->FontSizeInflationMinTwips();
7973 if (emPerLine
== 0 && minTwips
== 0) {
7977 nscoord byLine
= 0, byInch
= 0;
7978 if (emPerLine
!= 0) {
7979 byLine
= aContainerISize
/ emPerLine
;
7981 if (minTwips
!= 0) {
7982 // REVIEW: Is this giving us app units and sizes *not* counting
7983 // viewport scaling?
7984 gfxSize screenSize
= aPresContext
->ScreenSizeInchesForFontInflation();
7985 float deviceISizeInches
=
7986 aWritingMode
.IsVertical() ? screenSize
.height
: screenSize
.width
;
7988 NSToCoordRound(aContainerISize
/ (deviceISizeInches
* 1440 / minTwips
));
7990 return std::max(byLine
, byInch
);
7994 float nsLayoutUtils::FontSizeInflationInner(const nsIFrame
* aFrame
,
7995 nscoord aMinFontSize
) {
7996 // Note that line heights should be inflated by the same ratio as the
7997 // font size of the same text; thus we operate only on the font size
7998 // even when we're scaling a line height.
7999 nscoord styleFontSize
= aFrame
->StyleFont()->mFont
.size
.ToAppUnits();
8000 if (styleFontSize
<= 0) {
8001 // Never scale zero font size.
8005 if (aMinFontSize
<= 0) {
8006 // No need to scale.
8010 // If between this current frame and its font inflation container there is a
8011 // non-inline element with fixed width or height, then we should not inflate
8012 // fonts for this frame.
8013 for (const nsIFrame
* f
= aFrame
; f
&& !f
->IsContainerForFontSizeInflation();
8014 f
= f
->GetParent()) {
8015 nsIContent
* content
= f
->GetContent();
8016 LayoutFrameType fType
= f
->Type();
8017 nsIFrame
* parent
= f
->GetParent();
8018 // Also, if there is more than one frame corresponding to a single
8019 // content node, we want the outermost one.
8020 if (!(parent
&& parent
->GetContent() == content
) &&
8021 // ignore width/height on inlines since they don't apply
8022 fType
!= LayoutFrameType::Inline
&&
8023 // ignore width on radios and checkboxes since we enlarge them and
8024 // they have width/height in ua.css
8025 fType
!= LayoutFrameType::CheckboxRadio
) {
8026 // ruby annotations should have the same inflation as its
8027 // grandparent, which is the ruby frame contains the annotation.
8028 if (fType
== LayoutFrameType::RubyText
) {
8029 MOZ_ASSERT(parent
&& parent
->IsRubyTextContainerFrame());
8030 nsIFrame
* grandparent
= parent
->GetParent();
8031 MOZ_ASSERT(grandparent
&& grandparent
->IsRubyFrame());
8032 return FontSizeInflationFor(grandparent
);
8034 WritingMode wm
= f
->GetWritingMode();
8035 const auto& stylePosISize
= f
->StylePosition()->ISize(wm
);
8036 const auto& stylePosBSize
= f
->StylePosition()->BSize(wm
);
8037 if (!stylePosISize
.IsAuto() ||
8038 !stylePosBSize
.BehavesLikeInitialValueOnBlockAxis()) {
8044 int32_t interceptParam
= StaticPrefs::font_size_inflation_mappingIntercept();
8045 float maxRatio
= (float)StaticPrefs::font_size_inflation_maxRatio() / 100.0f
;
8047 float ratio
= float(styleFontSize
) / float(aMinFontSize
);
8048 float inflationRatio
;
8050 // Given a minimum inflated font size m, a specified font size s, we want to
8051 // find the inflated font size i and then return the ratio of i to s (i/s).
8052 if (interceptParam
>= 0) {
8053 // Since the mapping intercept parameter P is greater than zero, we use it
8054 // to determine the point where our mapping function intersects the i=s
8055 // line. This means that we have an equation of the form:
8057 // i = m + s*(P/2)/(1 + P/2), if s <= (1 + P/2)*m
8058 // i = s, if s >= (1 + P/2)*m
8060 float intercept
= 1 + float(interceptParam
) / 2.0f
;
8061 if (ratio
>= intercept
) {
8062 // If we're already at 1+P/2 or more times the minimum, don't scale.
8066 // The point (intercept, intercept) is where the part of the i vs. s graph
8067 // that's not slope 1 meets the i=s line. (This part of the
8068 // graph is a line from (0, m), to that point). We calculate the
8069 // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the
8070 // intercept parameter above. We then need to return i/s.
8071 inflationRatio
= (1.0f
+ (ratio
* (intercept
- 1) / intercept
)) / ratio
;
8073 // This is the case where P is negative. We essentially want to implement
8074 // the case for P=infinity here, so we make i = s + m, which means that
8075 // i/s = s/s + m/s = 1 + 1/ratio
8076 inflationRatio
= 1 + 1.0f
/ ratio
;
8079 if (maxRatio
> 1.0 && inflationRatio
> maxRatio
) {
8082 return inflationRatio
;
8086 static bool ShouldInflateFontsForContainer(const nsIFrame
* aFrame
) {
8087 // We only want to inflate fonts for text that is in a place
8088 // with room to expand. The question is what the best heuristic for
8090 // For now, we're going to use NS_FRAME_IN_CONSTRAINED_BSIZE, which
8091 // indicates whether the frame is inside something with a constrained
8092 // block-size (propagating down the tree), but the propagation stops when
8093 // we hit overflow-y [or -x, for vertical mode]: scroll or auto.
8094 const nsStyleText
* styleText
= aFrame
->StyleText();
8096 return styleText
->mTextSizeAdjust
!= StyleTextSizeAdjust::None
&&
8097 !aFrame
->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE
) &&
8098 // We also want to disable font inflation for containers that have
8099 // preformatted text.
8100 // MathML cells need special treatment. See bug 1002526 comment 56.
8101 (styleText
->WhiteSpaceCanWrap(aFrame
) || aFrame
->IsMathMLFrame());
8104 nscoord
nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame
* aFrame
) {
8105 nsPresContext
* presContext
= aFrame
->PresContext();
8106 if (!FontSizeInflationEnabled(presContext
) ||
8107 presContext
->mInflationDisabledForShrinkWrap
) {
8111 for (const nsIFrame
* f
= aFrame
; f
; f
= f
->GetParent()) {
8112 if (f
->IsContainerForFontSizeInflation()) {
8113 if (!ShouldInflateFontsForContainer(f
)) {
8117 nsFontInflationData
* data
=
8118 nsFontInflationData::FindFontInflationDataFor(aFrame
);
8119 // FIXME: The need to null-check here is sort of a bug, and might
8120 // lead to incorrect results.
8121 if (!data
|| !data
->InflationEnabled()) {
8125 return MinimumFontSizeFor(aFrame
->PresContext(), aFrame
->GetWritingMode(),
8126 data
->UsableISize());
8130 MOZ_ASSERT(false, "root should always be container");
8135 float nsLayoutUtils::FontSizeInflationFor(const nsIFrame
* aFrame
) {
8136 if (aFrame
->IsInSVGTextSubtree()) {
8137 const nsIFrame
* container
= aFrame
;
8138 while (!container
->IsSVGTextFrame()) {
8139 container
= container
->GetParent();
8141 NS_ASSERTION(container
, "expected to find an ancestor SVGTextFrame");
8142 return static_cast<const SVGTextFrame
*>(container
)
8143 ->GetFontSizeScaleFactor();
8146 if (!FontSizeInflationEnabled(aFrame
->PresContext())) {
8150 return FontSizeInflationInner(aFrame
, InflationMinFontSizeFor(aFrame
));
8154 bool nsLayoutUtils::FontSizeInflationEnabled(nsPresContext
* aPresContext
) {
8155 PresShell
* presShell
= aPresContext
->GetPresShell();
8159 return presShell
->FontSizeInflationEnabled();
8163 nsRect
nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame
* aFrame
,
8164 const nsSize
& aFrameSize
) {
8165 auto boxShadows
= aFrame
->StyleEffects()->mBoxShadow
.AsSpan();
8166 if (boxShadows
.IsEmpty()) {
8170 nsRect
inputRect(nsPoint(0, 0), aFrameSize
);
8172 // According to the CSS spec, box-shadow should be based on the border box.
8173 // However, that looks broken when the background extends outside the border
8174 // box, as can be the case with native theming. To fix that we expand the
8175 // area that we shadow to include the bounds of any native theme drawing.
8176 const nsStyleDisplay
* styleDisplay
= aFrame
->StyleDisplay();
8177 nsITheme::Transparency transparency
;
8178 if (aFrame
->IsThemed(styleDisplay
, &transparency
)) {
8179 // For opaque (rectangular) theme widgets we can take the generic
8180 // border-box path with border-radius disabled.
8181 if (transparency
!= nsITheme::eOpaque
) {
8182 nsPresContext
* presContext
= aFrame
->PresContext();
8183 presContext
->Theme()->GetWidgetOverflow(
8184 presContext
->DeviceContext(), aFrame
,
8185 styleDisplay
->EffectiveAppearance(), &inputRect
);
8190 int32_t A2D
= aFrame
->PresContext()->AppUnitsPerDevPixel();
8191 for (auto& shadow
: boxShadows
) {
8192 nsRect tmpRect
= inputRect
;
8194 // inset shadows are never painted outside the frame
8199 tmpRect
.MoveBy(nsPoint(shadow
.base
.horizontal
.ToAppUnits(),
8200 shadow
.base
.vertical
.ToAppUnits()));
8201 tmpRect
.Inflate(shadow
.spread
.ToAppUnits());
8202 tmpRect
.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
8203 shadow
.base
.blur
.ToAppUnits(), A2D
));
8204 shadows
.UnionRect(shadows
, tmpRect
);
8210 bool nsLayoutUtils::GetDocumentViewerSize(
8211 const nsPresContext
* aPresContext
, LayoutDeviceIntSize
& aOutSize
,
8212 SubtractDynamicToolbar aSubtractDynamicToolbar
) {
8213 nsCOMPtr
<nsIDocShell
> docShell
= aPresContext
->GetDocShell();
8218 nsCOMPtr
<nsIDocumentViewer
> viewer
;
8219 docShell
->GetDocViewer(getter_AddRefs(viewer
));
8225 viewer
->GetBounds(bounds
);
8227 if (aPresContext
->IsRootContentDocumentCrossProcess() &&
8228 aSubtractDynamicToolbar
== SubtractDynamicToolbar::Yes
&&
8229 aPresContext
->HasDynamicToolbar() && !bounds
.IsEmpty()) {
8230 MOZ_ASSERT(aPresContext
->IsRootContentDocumentCrossProcess());
8231 bounds
.height
-= aPresContext
->GetDynamicToolbarMaxHeight();
8232 // Collapse the size in the case the dynamic toolbar max height is greater
8233 // than the content bound height so that hopefully embedders of GeckoView
8234 // may notice they set wrong dynamic toolbar max height.
8235 if (bounds
.height
< 0) {
8240 aOutSize
= LayoutDeviceIntRect::FromUnknownRect(bounds
).Size();
8244 bool nsLayoutUtils::UpdateCompositionBoundsForRCDRSF(
8245 ParentLayerRect
& aCompBounds
, const nsPresContext
* aPresContext
,
8246 IncludeDynamicToolbar aIncludeDynamicToolbar
) {
8247 SubtractDynamicToolbar shouldSubtractDynamicToolbar
=
8248 aIncludeDynamicToolbar
== IncludeDynamicToolbar::Force
8249 ? SubtractDynamicToolbar::No
8250 : aPresContext
->IsRootContentDocumentCrossProcess() &&
8251 aPresContext
->HasDynamicToolbar()
8252 ? SubtractDynamicToolbar::Yes
8253 : SubtractDynamicToolbar::No
;
8255 if (shouldSubtractDynamicToolbar
== SubtractDynamicToolbar::Yes
) {
8256 if (RefPtr
<MobileViewportManager
> MVM
=
8257 aPresContext
->PresShell()->GetMobileViewportManager()) {
8258 // Convert the intrinsic composition size to app units here since
8259 // the returned size of below CalculateScrollableRectForFrame call has
8260 // been already converted/rounded to app units.
8261 nsSize intrinsicCompositionSize
=
8262 CSSSize::ToAppUnits(MVM
->GetIntrinsicCompositionSize());
8264 if (nsIScrollableFrame
* rootScrollableFrame
=
8265 aPresContext
->PresShell()->GetRootScrollFrameAsScrollable()) {
8266 // Expand the composition size to include the area initially covered by
8267 // the dynamic toolbar only if the content is taller than the intrinsic
8268 // composition size (i.e. the dynamic toolbar should be able to move
8269 // only if the content is vertically scrollable).
8270 if (intrinsicCompositionSize
.height
<
8271 CalculateScrollableRectForFrame(rootScrollableFrame
, nullptr)
8273 shouldSubtractDynamicToolbar
= SubtractDynamicToolbar::No
;
8279 LayoutDeviceIntSize contentSize
;
8280 if (!GetDocumentViewerSize(aPresContext
, contentSize
,
8281 shouldSubtractDynamicToolbar
)) {
8284 aCompBounds
.SizeTo(ViewAs
<ParentLayerPixel
>(
8285 LayoutDeviceSize(contentSize
),
8286 PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF
));
8291 nsMargin
nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(
8292 const nsIFrame
* aScrollFrame
) {
8293 if (!aScrollFrame
|| !aScrollFrame
->GetScrollTargetFrame()) {
8296 nsPresContext
* presContext
= aScrollFrame
->PresContext();
8297 PresShell
* presShell
= presContext
->GetPresShell();
8301 bool isRootScrollFrame
= aScrollFrame
== presShell
->GetRootScrollFrame();
8302 bool isRootContentDocRootScrollFrame
=
8303 isRootScrollFrame
&& presContext
->IsRootContentDocumentCrossProcess();
8304 if (!isRootContentDocRootScrollFrame
) {
8307 if (presContext
->UseOverlayScrollbars()) {
8310 nsIScrollableFrame
* scrollableFrame
= aScrollFrame
->GetScrollTargetFrame();
8311 if (!scrollableFrame
) {
8314 return scrollableFrame
->GetActualScrollbarSizes(
8315 nsIScrollableFrame::ScrollbarSizesOptions::
8316 INCLUDE_VISUAL_VIEWPORT_SCROLLBARS
);
8320 nsSize
nsLayoutUtils::CalculateCompositionSizeForFrame(
8321 nsIFrame
* aFrame
, bool aSubtractScrollbars
,
8322 const nsSize
* aOverrideScrollPortSize
,
8323 IncludeDynamicToolbar aIncludeDynamicToolbar
) {
8324 // If we have a scrollable frame, restrict the composition bounds to its
8325 // scroll port. The scroll port excludes the frame borders and the scroll
8326 // bars, which we don't want to be part of the composition bounds.
8327 nsIScrollableFrame
* scrollableFrame
= aFrame
->GetScrollTargetFrame();
8328 nsRect rect
= scrollableFrame
? scrollableFrame
->GetScrollPortRect()
8329 : aFrame
->GetRect();
8331 aOverrideScrollPortSize
? *aOverrideScrollPortSize
: rect
.Size();
8333 nsPresContext
* presContext
= aFrame
->PresContext();
8334 PresShell
* presShell
= presContext
->PresShell();
8336 bool isRootContentDocRootScrollFrame
=
8337 presContext
->IsRootContentDocumentCrossProcess() &&
8338 aFrame
== presShell
->GetRootScrollFrame();
8339 if (isRootContentDocRootScrollFrame
) {
8340 ParentLayerRect compBounds
;
8341 if (UpdateCompositionBoundsForRCDRSF(compBounds
, presContext
,
8342 aIncludeDynamicToolbar
)) {
8343 int32_t auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
8344 size
= nsSize(compBounds
.width
* auPerDevPixel
,
8345 compBounds
.height
* auPerDevPixel
);
8349 if (aSubtractScrollbars
) {
8350 nsMargin margins
= ScrollbarAreaToExcludeFromCompositionBoundsFor(aFrame
);
8351 size
.width
-= margins
.LeftRight();
8352 size
.height
-= margins
.TopBottom();
8359 CSSSize
nsLayoutUtils::CalculateBoundingCompositionSize(
8360 const nsIFrame
* aFrame
, bool aIsRootContentDocRootScrollFrame
,
8361 const FrameMetrics
& aMetrics
) {
8362 if (aIsRootContentDocRootScrollFrame
) {
8363 return ViewAs
<LayerPixel
>(
8364 aMetrics
.GetCompositionBounds().Size(),
8365 PixelCastJustification::ParentLayerToLayerForRootComposition
) *
8366 LayerToScreenScale(1.0f
) / aMetrics
.DisplayportPixelsPerCSSPixel();
8368 nsPresContext
* presContext
= aFrame
->PresContext();
8369 ScreenSize rootCompositionSize
;
8370 nsPresContext
* rootPresContext
=
8371 presContext
->GetInProcessRootContentDocumentPresContext();
8372 if (!rootPresContext
) {
8373 rootPresContext
= presContext
->GetRootPresContext();
8375 PresShell
* rootPresShell
= nullptr;
8376 if (rootPresContext
) {
8377 rootPresShell
= rootPresContext
->PresShell();
8378 if (nsIFrame
* rootFrame
= rootPresShell
->GetRootFrame()) {
8379 ParentLayerRect compBounds
;
8380 if (UpdateCompositionBoundsForRCDRSF(compBounds
, rootPresContext
)) {
8381 rootCompositionSize
= ViewAs
<ScreenPixel
>(
8383 PixelCastJustification::ScreenIsParentLayerForRoot
);
8385 // LayoutDeviceToScreenScale2D =
8386 // LayoutDeviceToParentLayerScale *
8387 // ParentLayerToScreenScale2D
8388 LayoutDeviceToScreenScale2D cumulativeResolution
=
8389 LayoutDeviceToParentLayerScale(
8390 rootPresShell
->GetCumulativeResolution()) *
8391 GetTransformToAncestorScaleCrossProcessForFrameMetrics(rootFrame
);
8393 int32_t rootAUPerDevPixel
= rootPresContext
->AppUnitsPerDevPixel();
8394 rootCompositionSize
= (LayoutDeviceRect::FromAppUnits(
8395 rootFrame
->GetRect(), rootAUPerDevPixel
) *
8396 cumulativeResolution
)
8401 nsIWidget
* widget
= aFrame
->GetNearestWidget();
8402 LayoutDeviceIntRect widgetBounds
= widget
->GetBounds();
8403 rootCompositionSize
= ScreenSize(ViewAs
<ScreenPixel
>(
8404 widgetBounds
.Size(),
8405 PixelCastJustification::LayoutDeviceIsScreenForBounds
));
8408 // Adjust composition size for the size of scroll bars.
8409 nsIFrame
* rootRootScrollFrame
=
8410 rootPresShell
? rootPresShell
->GetRootScrollFrame() : nullptr;
8411 nsMargin scrollbarMargins
=
8412 ScrollbarAreaToExcludeFromCompositionBoundsFor(rootRootScrollFrame
);
8413 LayoutDeviceMargin margins
= LayoutDeviceMargin::FromAppUnits(
8414 scrollbarMargins
, rootPresContext
->AppUnitsPerDevPixel());
8415 // Scrollbars are not subject to resolution scaling, so LD pixels = layer
8417 rootCompositionSize
.width
-= margins
.LeftRight();
8418 rootCompositionSize
.height
-= margins
.TopBottom();
8421 rootCompositionSize
/ aMetrics
.DisplayportPixelsPerCSSPixel();
8423 // If this is a nested content process, the in-process root content document's
8424 // composition size may still be arbitrarily large, so bound it further by
8425 // how much of the in-process RCD is visible in the top-level (cross-process
8427 if (rootPresShell
) {
8428 if (BrowserChild
* bc
= BrowserChild::GetFrom(rootPresShell
)) {
8429 if (const auto& visibleRect
=
8430 bc
->GetTopLevelViewportVisibleRectInSelfCoords()) {
8431 CSSSize cssVisibleRect
=
8432 visibleRect
->Size() / rootPresContext
->CSSToDevPixelScale();
8433 result
= Min(result
, cssVisibleRect
);
8442 nsRect
nsLayoutUtils::CalculateScrollableRectForFrame(
8443 const nsIScrollableFrame
* aScrollableFrame
, const nsIFrame
* aRootFrame
) {
8444 nsRect contentBounds
;
8445 if (aScrollableFrame
) {
8446 contentBounds
= aScrollableFrame
->GetScrollRange();
8448 nsPoint scrollPosition
= aScrollableFrame
->GetScrollPosition();
8449 if (aScrollableFrame
->GetScrollStyles().mVertical
==
8450 StyleOverflow::Hidden
) {
8451 contentBounds
.y
= scrollPosition
.y
;
8452 contentBounds
.height
= 0;
8454 if (aScrollableFrame
->GetScrollStyles().mHorizontal
==
8455 StyleOverflow::Hidden
) {
8456 contentBounds
.x
= scrollPosition
.x
;
8457 contentBounds
.width
= 0;
8460 contentBounds
.width
+= aScrollableFrame
->GetScrollPortRect().width
;
8461 contentBounds
.height
+= aScrollableFrame
->GetScrollPortRect().height
;
8463 contentBounds
= aRootFrame
->GetRect();
8464 // Clamp to (0, 0) if there is no corresponding scrollable frame for the
8465 // given |aRootFrame|.
8466 contentBounds
.MoveTo(0, 0);
8468 return contentBounds
;
8472 nsRect
nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame
* aFrame
) {
8473 nsRect scrollableRect
= CalculateScrollableRectForFrame(
8474 aFrame
->GetScrollTargetFrame(), aFrame
->PresShell()->GetRootFrame());
8475 nsSize compSize
= CalculateCompositionSizeForFrame(aFrame
);
8477 if (aFrame
== aFrame
->PresShell()->GetRootScrollFrame()) {
8478 // the composition size for the root scroll frame does not include the
8479 // local resolution, so we adjust.
8480 float res
= aFrame
->PresShell()->GetResolution();
8481 compSize
.width
= NSToCoordRound(compSize
.width
/ res
);
8482 compSize
.height
= NSToCoordRound(compSize
.height
/ res
);
8485 if (scrollableRect
.width
< compSize
.width
) {
8487 std::max(0, scrollableRect
.x
- (compSize
.width
- scrollableRect
.width
));
8488 scrollableRect
.width
= compSize
.width
;
8491 if (scrollableRect
.height
< compSize
.height
) {
8492 scrollableRect
.y
= std::max(
8493 0, scrollableRect
.y
- (compSize
.height
- scrollableRect
.height
));
8494 scrollableRect
.height
= compSize
.height
;
8496 return scrollableRect
;
8500 void nsLayoutUtils::DoLogTestDataForPaint(WebRenderLayerManager
* aManager
,
8502 const std::string
& aKey
,
8503 const std::string
& aValue
) {
8504 MOZ_ASSERT(nsLayoutUtils::IsAPZTestLoggingEnabled(), "don't call me");
8505 aManager
->LogTestDataForCurrentPaint(aScrollId
, aKey
, aValue
);
8508 void nsLayoutUtils::LogAdditionalTestData(nsDisplayListBuilder
* aBuilder
,
8509 const std::string
& aKey
,
8510 const std::string
& aValue
) {
8511 WebRenderLayerManager
* manager
= aBuilder
->GetWidgetLayerManager(nullptr);
8515 manager
->LogAdditionalTestData(aKey
, aValue
);
8519 bool nsLayoutUtils::IsAPZTestLoggingEnabled() {
8520 return StaticPrefs::apz_test_logging_enabled();
8523 ////////////////////////////////////////
8524 // SurfaceFromElementResult
8526 SurfaceFromElementResult::SurfaceFromElementResult()
8527 // Use safe default values here
8528 : mHadCrossOriginRedirects(false),
8530 mIsStillLoading(false),
8533 mAlphaType(gfxAlphaType::Opaque
) {}
8535 const RefPtr
<mozilla::gfx::SourceSurface
>&
8536 SurfaceFromElementResult::GetSourceSurface() {
8537 if (!mSourceSurface
&& mLayersImage
) {
8538 mSourceSurface
= mLayersImage
->GetAsSourceSurface();
8541 return mSourceSurface
;
8544 ////////////////////////////////////////
8546 bool nsLayoutUtils::IsNonWrapperBlock(nsIFrame
* aFrame
) {
8548 return aFrame
->IsBlockFrameOrSubclass() && !aFrame
->IsBlockWrapper();
8551 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame
* aFrame
) {
8552 // FIXME: Now that inflation calculations are based on the flow
8553 // root's NCA's (nearest common ancestor of its inflatable
8554 // descendants) width, we could probably disable inflation in
8555 // fewer cases than we currently do.
8556 // MathML cells need special treatment. See bug 1002526 comment 56.
8557 if (aFrame
->IsContainerForFontSizeInflation() && !aFrame
->IsMathMLFrame()) {
8558 mPresContext
= aFrame
->PresContext();
8559 mOldValue
= mPresContext
->mInflationDisabledForShrinkWrap
;
8560 mPresContext
->mInflationDisabledForShrinkWrap
= true;
8562 // indicate we have nothing to restore
8563 mPresContext
= nullptr;
8568 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation() {
8570 mPresContext
->mInflationDisabledForShrinkWrap
= mOldValue
;
8576 Rect
NSRectToRect(const nsRect
& aRect
, double aAppUnitsPerPixel
) {
8577 // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8578 // division using a larger type and avoiding rounding error.
8579 return Rect(Float(aRect
.x
/ aAppUnitsPerPixel
),
8580 Float(aRect
.y
/ aAppUnitsPerPixel
),
8581 Float(aRect
.width
/ aAppUnitsPerPixel
),
8582 Float(aRect
.height
/ aAppUnitsPerPixel
));
8585 Rect
NSRectToSnappedRect(const nsRect
& aRect
, double aAppUnitsPerPixel
,
8586 const gfx::DrawTarget
& aSnapDT
) {
8587 // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8588 // division using a larger type and avoiding rounding error.
8589 Rect
rect(Float(aRect
.x
/ aAppUnitsPerPixel
),
8590 Float(aRect
.y
/ aAppUnitsPerPixel
),
8591 Float(aRect
.width
/ aAppUnitsPerPixel
),
8592 Float(aRect
.height
/ aAppUnitsPerPixel
));
8593 MaybeSnapToDevicePixels(rect
, aSnapDT
, true);
8596 // Similar to a snapped rect, except an axis is left unsnapped if the snapping
8597 // process results in a length of 0.
8598 Rect
NSRectToNonEmptySnappedRect(const nsRect
& aRect
, double aAppUnitsPerPixel
,
8599 const gfx::DrawTarget
& aSnapDT
) {
8600 // Note that by making aAppUnitsPerPixel a double we're doing floating-point
8601 // division using a larger type and avoiding rounding error.
8602 Rect
rect(Float(aRect
.x
/ aAppUnitsPerPixel
),
8603 Float(aRect
.y
/ aAppUnitsPerPixel
),
8604 Float(aRect
.width
/ aAppUnitsPerPixel
),
8605 Float(aRect
.height
/ aAppUnitsPerPixel
));
8606 MaybeSnapToDevicePixels(rect
, aSnapDT
, true, false);
8610 void StrokeLineWithSnapping(const nsPoint
& aP1
, const nsPoint
& aP2
,
8611 int32_t aAppUnitsPerDevPixel
,
8612 DrawTarget
& aDrawTarget
, const Pattern
& aPattern
,
8613 const StrokeOptions
& aStrokeOptions
,
8614 const DrawOptions
& aDrawOptions
) {
8615 Point p1
= NSPointToPoint(aP1
, aAppUnitsPerDevPixel
);
8616 Point p2
= NSPointToPoint(aP2
, aAppUnitsPerDevPixel
);
8617 SnapLineToDevicePixelsForStroking(p1
, p2
, aDrawTarget
,
8618 aStrokeOptions
.mLineWidth
);
8619 aDrawTarget
.StrokeLine(p1
, p2
, aPattern
, aStrokeOptions
, aDrawOptions
);
8622 } // namespace mozilla
8625 void nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame
* aFrame
,
8626 ReflowOutput
& aMetrics
,
8627 const LogicalMargin
& aFramePadding
,
8628 WritingMode aLineWM
,
8629 WritingMode aFrameWM
) {
8630 RefPtr
<nsFontMetrics
> fm
=
8631 nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame
);
8634 // Compute final height of the frame.
8636 // Do things the standard css2 way -- though it's hard to find it
8637 // in the css2 spec! It's actually found in the css1 spec section
8638 // 4.4 (you will have to read between the lines to really see
8641 // The height of our box is the sum of our font size plus the top
8642 // and bottom border and padding. The height of children do not
8643 // affect our height.
8644 aMetrics
.SetBlockStartAscent(
8645 aLineWM
.IsAlphabeticalBaseline()
8646 ? aLineWM
.IsLineInverted() ? fm
->MaxDescent() : fm
->MaxAscent()
8647 : fm
->MaxHeight() / 2);
8648 aMetrics
.BSize(aLineWM
) = fm
->MaxHeight();
8650 NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
8651 aMetrics
.SetBlockStartAscent(aMetrics
.BSize(aLineWM
) = 0);
8653 aMetrics
.SetBlockStartAscent(aMetrics
.BlockStartAscent() +
8654 aFramePadding
.BStart(aFrameWM
));
8655 aMetrics
.BSize(aLineWM
) += aFramePadding
.BStartEnd(aFrameWM
);
8659 // _BOUNDARY because Dispatch() with `targets` must not handle the event.
8660 MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool
8661 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
8662 PresShell
* aPresShell
) {
8663 if (RefPtr
<Document
> doc
= aPresShell
->GetDocument()) {
8664 WidgetEvent
event(true, eVoidEvent
);
8665 nsTArray
<EventTarget
*> targets
;
8666 nsresult rv
= EventDispatcher::Dispatch(doc
, nullptr, &event
, nullptr,
8667 nullptr, nullptr, &targets
);
8668 NS_ENSURE_SUCCESS(rv
, false);
8669 for (size_t i
= 0; i
< targets
.Length(); i
++) {
8670 if (targets
[i
]->IsApzAware()) {
8679 bool nsLayoutUtils::CanScrollOriginClobberApz(ScrollOrigin aScrollOrigin
) {
8680 switch (aScrollOrigin
) {
8681 case ScrollOrigin::None
:
8682 case ScrollOrigin::NotSpecified
:
8683 case ScrollOrigin::Apz
:
8684 case ScrollOrigin::Restore
:
8692 ScrollMetadata
nsLayoutUtils::ComputeScrollMetadata(
8693 const nsIFrame
* aForFrame
, const nsIFrame
* aScrollFrame
,
8694 nsIContent
* aContent
, const nsIFrame
* aItemFrame
,
8695 const nsPoint
& aOffsetToReferenceFrame
,
8696 WebRenderLayerManager
* aLayerManager
, ViewID aScrollParentId
,
8697 const nsSize
& aScrollPortSize
, bool aIsRootContent
) {
8698 const nsPresContext
* presContext
= aForFrame
->PresContext();
8699 int32_t auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
8701 PresShell
* presShell
= presContext
->GetPresShell();
8702 ScrollMetadata metadata
;
8703 FrameMetrics
& metrics
= metadata
.GetMetrics();
8704 metrics
.SetLayoutViewport(
8705 CSSRect(CSSPoint(), CSSSize::FromAppUnits(aScrollPortSize
)));
8707 nsIDocShell
* docShell
= presContext
->GetDocShell();
8708 const BrowsingContext
* bc
=
8709 docShell
? docShell
->GetBrowsingContext() : nullptr;
8710 bool isTouchEventsEnabled
=
8712 bc
->TouchEventsOverride() == mozilla::dom::TouchEventsOverride::Enabled
;
8714 if (bc
&& bc
->InRDMPane() && isTouchEventsEnabled
) {
8715 metadata
.SetIsRDMTouchSimulationActive(true);
8718 ViewID scrollId
= ScrollableLayerGuid::NULL_SCROLL_ID
;
8720 if (void* paintRequestTime
=
8721 aContent
->GetProperty(nsGkAtoms::paintRequestTime
)) {
8722 metrics
.SetPaintRequestTime(*static_cast<TimeStamp
*>(paintRequestTime
));
8723 aContent
->RemoveProperty(nsGkAtoms::paintRequestTime
);
8725 scrollId
= nsLayoutUtils::FindOrCreateIDFor(aContent
);
8727 if (DisplayPortUtils::GetDisplayPort(aContent
, &dp
)) {
8728 metrics
.SetDisplayPort(CSSRect::FromAppUnits(dp
));
8729 DisplayPortUtils::MarkDisplayPortAsPainted(aContent
);
8732 metrics
.SetHasNonZeroDisplayPortMargins(false);
8733 if (DisplayPortMarginsPropertyData
* currentData
=
8734 static_cast<DisplayPortMarginsPropertyData
*>(
8735 aContent
->GetProperty(nsGkAtoms::DisplayPortMargins
))) {
8736 if (currentData
->mMargins
.mMargins
!= ScreenMargin()) {
8737 metrics
.SetHasNonZeroDisplayPortMargins(true);
8741 // Note: GetProperty() will return nullptr both in the case where
8742 // the property hasn't been set, and in the case where the property
8743 // has been set to false (in which case the property value is
8744 // `reinterpret_cast<void*>(false)` which is nullptr.
8745 if (aContent
->GetProperty(nsGkAtoms::forceMousewheelAutodir
)) {
8746 metadata
.SetForceMousewheelAutodir(true);
8749 if (aContent
->GetProperty(nsGkAtoms::forceMousewheelAutodirHonourRoot
)) {
8750 metadata
.SetForceMousewheelAutodirHonourRoot(true);
8753 if (IsAPZTestLoggingEnabled()) {
8754 LogTestDataForPaint(aLayerManager
, scrollId
, "displayport",
8755 metrics
.GetDisplayPort());
8758 metrics
.SetMinimalDisplayPort(
8759 aContent
->GetProperty(nsGkAtoms::MinimalDisplayPort
));
8762 nsIScrollableFrame
* scrollableFrame
= nullptr;
8763 if (aScrollFrame
) scrollableFrame
= aScrollFrame
->GetScrollTargetFrame();
8765 metrics
.SetScrollableRect(
8766 CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(
8767 scrollableFrame
, aForFrame
)));
8769 if (scrollableFrame
) {
8770 CSSPoint layoutScrollOffset
=
8771 CSSPoint::FromAppUnits(scrollableFrame
->GetScrollPosition());
8772 CSSPoint visualScrollOffset
=
8774 ? CSSPoint::FromAppUnits(presShell
->GetVisualViewportOffset())
8775 : layoutScrollOffset
;
8776 metrics
.SetVisualScrollOffset(visualScrollOffset
);
8777 // APZ sometimes reads this even if we haven't set a visual scroll
8778 // update type (specifically, in the isFirstPaint case), so always
8780 metrics
.SetVisualDestination(visualScrollOffset
);
8782 if (aIsRootContent
) {
8783 if (aLayerManager
->GetIsFirstPaint() &&
8784 presShell
->IsVisualViewportOffsetSet()) {
8785 // Restore the visual viewport offset to the copy stored on the
8787 presShell
->ScrollToVisual(presShell
->GetVisualViewportOffset(),
8788 FrameMetrics::eRestore
, ScrollMode::Instant
);
8792 if (scrollableFrame
->IsRootScrollFrameOfDocument()) {
8793 if (const Maybe
<PresShell::VisualScrollUpdate
>& visualUpdate
=
8794 presShell
->GetPendingVisualScrollUpdate()) {
8795 metrics
.SetVisualDestination(
8796 CSSPoint::FromAppUnits(visualUpdate
->mVisualScrollOffset
));
8797 metrics
.SetVisualScrollUpdateType(visualUpdate
->mUpdateType
);
8798 presShell
->AcknowledgePendingVisualScrollUpdate();
8802 if (aIsRootContent
) {
8803 // Expand the layout viewport to the size including the area covered by
8804 // the dynamic toolbar in the case where the dynamic toolbar is being
8805 // used, otherwise when the dynamic toolbar transitions on the compositor,
8806 // the layout viewport will be smaller than the visual viewport on the
8807 // compositor, thus the layout viewport offset will be forced to be moved
8808 // in FrameMetrics::KeepLayoutViewportEnclosingVisualViewport.
8809 if (presContext
->HasDynamicToolbar()) {
8810 CSSRect viewport
= metrics
.GetLayoutViewport();
8811 viewport
.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
8812 presContext
, viewport
.Size()));
8813 metrics
.SetLayoutViewport(viewport
);
8815 // We need to set 'fixed margins' to adjust 'fixed margins' value on the
8816 // composiutor in the case where the dynamic toolbar is completely
8817 // hidden because the margin value on the compositor is offset from the
8818 // position where the dynamic toolbar is completely VISIBLE but now the
8819 // toolbar is completely HIDDEN we need to adjust the difference on the
8821 if (presContext
->GetDynamicToolbarState() ==
8822 DynamicToolbarState::Collapsed
) {
8823 metrics
.SetFixedLayerMargins(ScreenMargin(
8825 ScreenCoord(presContext
->GetDynamicToolbarHeight() -
8826 presContext
->GetDynamicToolbarMaxHeight()),
8832 metrics
.SetScrollGeneration(scrollableFrame
->CurrentScrollGeneration());
8834 CSSRect viewport
= metrics
.GetLayoutViewport();
8835 viewport
.MoveTo(layoutScrollOffset
);
8836 metrics
.SetLayoutViewport(viewport
);
8838 nsSize lineScrollAmount
= scrollableFrame
->GetLineScrollAmount();
8839 LayoutDeviceIntSize lineScrollAmountInDevPixels
=
8840 LayoutDeviceIntSize::FromAppUnitsRounded(
8841 lineScrollAmount
, presContext
->AppUnitsPerDevPixel());
8842 metadata
.SetLineScrollAmount(lineScrollAmountInDevPixels
);
8844 nsSize pageScrollAmount
= scrollableFrame
->GetPageScrollAmount();
8845 LayoutDeviceIntSize pageScrollAmountInDevPixels
=
8846 LayoutDeviceIntSize::FromAppUnitsRounded(
8847 pageScrollAmount
, presContext
->AppUnitsPerDevPixel());
8848 metadata
.SetPageScrollAmount(pageScrollAmountInDevPixels
);
8850 if (aScrollFrame
->GetParent()) {
8851 metadata
.SetDisregardedDirection(
8852 WheelHandlingUtils::GetDisregardedWheelScrollDirection(
8853 aScrollFrame
->GetParent()));
8856 metadata
.SetSnapInfo(scrollableFrame
->GetScrollSnapInfo());
8857 metadata
.SetOverscrollBehavior(
8858 scrollableFrame
->GetOverscrollBehaviorInfo());
8859 metadata
.SetScrollUpdates(scrollableFrame
->GetScrollUpdates());
8862 // If we have the scrollparent being the same as the scroll id, the
8863 // compositor-side code could get into an infinite loop while building the
8864 // overscroll handoff chain.
8865 MOZ_ASSERT(aScrollParentId
== ScrollableLayerGuid::NULL_SCROLL_ID
||
8866 scrollId
!= aScrollParentId
);
8867 metrics
.SetScrollId(scrollId
);
8868 metrics
.SetIsRootContent(aIsRootContent
);
8869 metadata
.SetScrollParentId(aScrollParentId
);
8871 const nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame();
8872 bool isRootScrollFrame
= aScrollFrame
== rootScrollFrame
;
8873 Document
* document
= presShell
->GetDocument();
8875 if (scrollId
!= ScrollableLayerGuid::NULL_SCROLL_ID
&&
8876 !presContext
->GetParentPresContext()) {
8877 if ((aScrollFrame
&& isRootScrollFrame
)) {
8878 metadata
.SetIsLayersIdRoot(true);
8880 MOZ_ASSERT(document
, "A non-root-scroll frame must be in a document");
8881 if (aContent
== document
->GetDocumentElement()) {
8882 metadata
.SetIsLayersIdRoot(true);
8887 // Get whether the root content is RTL(E.g. it's true either if
8888 // "writing-mode: vertical-rl", or if
8889 // "writing-mode: horizontal-tb; direction: rtl;" in CSS).
8890 // For the concept of this and the reason why we need to get this kind of
8891 // information, see the definition of |mIsAutoDirRootContentRTL| in struct
8892 // |ScrollMetadata|.
8893 const Element
* bodyElement
= document
? document
->GetBodyElement() : nullptr;
8894 const nsIFrame
* primaryFrame
=
8895 bodyElement
? bodyElement
->GetPrimaryFrame() : rootScrollFrame
;
8896 if (!primaryFrame
) {
8897 primaryFrame
= rootScrollFrame
;
8900 WritingMode writingModeOfRootScrollFrame
= primaryFrame
->GetWritingMode();
8901 WritingMode::BlockDir blockDirOfRootScrollFrame
=
8902 writingModeOfRootScrollFrame
.GetBlockDir();
8903 WritingMode::InlineDir inlineDirOfRootScrollFrame
=
8904 writingModeOfRootScrollFrame
.GetInlineDir();
8905 if (blockDirOfRootScrollFrame
== WritingMode::BlockDir::eBlockRL
||
8906 (blockDirOfRootScrollFrame
== WritingMode::BlockDir::eBlockTB
&&
8907 inlineDirOfRootScrollFrame
== WritingMode::InlineDir::eInlineRTL
)) {
8908 metadata
.SetIsAutoDirRootContentRTL(true);
8912 // Only the root scrollable frame for a given presShell should pick up
8913 // the presShell's resolution. All the other frames are 1.0.
8914 if (isRootScrollFrame
) {
8915 metrics
.SetPresShellResolution(presShell
->GetResolution());
8917 metrics
.SetPresShellResolution(1.0f
);
8920 if (presShell
->IsResolutionUpdated()) {
8921 metadata
.SetResolutionUpdated(true);
8924 // The cumulative resolution is the resolution at which the scroll frame's
8925 // content is actually rendered. It includes the pres shell resolutions of
8926 // all the pres shells from here up to the root, as well as any css-driven
8927 // resolution. We don't need to compute it as it's already stored in the
8928 // container parameters... except if we're in WebRender in which case we
8929 // don't have a aContainerParameters. In that case we're also not rasterizing
8930 // in Gecko anyway, so the only resolution we care about here is the presShell
8931 // resolution which we need to propagate to WebRender.
8932 metrics
.SetCumulativeResolution(
8933 LayoutDeviceToLayerScale(presShell
->GetCumulativeResolution()));
8935 metrics
.SetTransformToAncestorScale(
8936 GetTransformToAncestorScaleCrossProcessForFrameMetrics(
8937 aScrollFrame
? aScrollFrame
: aForFrame
));
8938 metrics
.SetDevPixelsPerCSSPixel(presContext
->CSSToDevPixelScale());
8940 // Initially, AsyncPanZoomController should render the content to the screen
8941 // at the painted resolution.
8942 const LayerToParentLayerScale
layerToParentLayerScale(1.0f
);
8943 metrics
.SetZoom(metrics
.GetCumulativeResolution() *
8944 metrics
.GetDevPixelsPerCSSPixel() * layerToParentLayerScale
);
8946 // Calculate the composition bounds as the size of the scroll frame and
8947 // its origin relative to the reference frame.
8948 // If aScrollFrame is null, we are in a document without a root scroll frame,
8949 // so it's a xul document. In this case, use the size of the viewport frame.
8950 const nsIFrame
* frameForCompositionBoundsCalculation
=
8951 aScrollFrame
? aScrollFrame
: aForFrame
;
8952 nsRect
compositionBounds(
8953 frameForCompositionBoundsCalculation
->GetOffsetToCrossDoc(aItemFrame
) +
8954 aOffsetToReferenceFrame
,
8955 frameForCompositionBoundsCalculation
->GetSize());
8956 if (scrollableFrame
) {
8957 // If we have a scrollable frame, restrict the composition bounds to its
8958 // scroll port. The scroll port excludes the frame borders and the scroll
8959 // bars, which we don't want to be part of the composition bounds.
8960 nsRect scrollPort
= scrollableFrame
->GetScrollPortRect();
8961 compositionBounds
= nsRect(
8962 compositionBounds
.TopLeft() + scrollPort
.TopLeft(), scrollPort
.Size());
8964 ParentLayerRect frameBounds
=
8965 LayoutDeviceRect::FromAppUnits(compositionBounds
, auPerDevPixel
) *
8966 metrics
.GetCumulativeResolution() * layerToParentLayerScale
;
8968 // For the root scroll frame of the root content document (RCD-RSF), the above
8969 // calculation will yield the size of the viewport frame as the composition
8970 // bounds, which doesn't actually correspond to what is visible when
8971 // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible
8972 // area of the prescontext that the viewport frame is reflowed into. In that
8973 // case if our document has a widget then the widget's bounds will correspond
8974 // to what is visible. If we don't have a widget the root view's bounds
8975 // correspond to what would be visible because they don't get modified by
8977 bool isRootContentDocRootScrollFrame
=
8978 isRootScrollFrame
&& presContext
->IsRootContentDocumentCrossProcess();
8979 if (isRootContentDocRootScrollFrame
) {
8980 UpdateCompositionBoundsForRCDRSF(frameBounds
, presContext
);
8981 if (RefPtr
<MobileViewportManager
> MVM
=
8982 presContext
->PresShell()->GetMobileViewportManager()) {
8983 metrics
.SetCompositionSizeWithoutDynamicToolbar(
8984 MVM
->GetCompositionSizeWithoutDynamicToolbar());
8988 metrics
.SetCompositionBoundsWidthIgnoringScrollbars(frameBounds
.width
);
8990 nsMargin sizes
= ScrollbarAreaToExcludeFromCompositionBoundsFor(aScrollFrame
);
8991 // Scrollbars are not subject to resolution scaling, so LD pixels = layer
8993 ParentLayerMargin boundMargins
=
8994 LayoutDeviceMargin::FromAppUnits(sizes
, auPerDevPixel
) *
8995 LayoutDeviceToParentLayerScale(1.0f
);
8996 frameBounds
.Deflate(boundMargins
);
8998 metrics
.SetCompositionBounds(frameBounds
);
9000 metrics
.SetBoundingCompositionSize(
9001 nsLayoutUtils::CalculateBoundingCompositionSize(
9002 aScrollFrame
? aScrollFrame
: aForFrame
,
9003 isRootContentDocRootScrollFrame
, metrics
));
9005 if (StaticPrefs::apz_printtree() || StaticPrefs::apz_test_logging_enabled()) {
9006 if (const nsIContent
* content
=
9007 frameForCompositionBoundsCalculation
->GetContent()) {
9008 nsAutoString contentDescription
;
9009 if (content
->IsElement()) {
9010 content
->AsElement()->Describe(contentDescription
);
9012 contentDescription
.AssignLiteral("(not an element)");
9014 metadata
.SetContentDescription(
9015 NS_LossyConvertUTF16toASCII(contentDescription
));
9016 if (IsAPZTestLoggingEnabled()) {
9017 LogTestDataForPaint(aLayerManager
, scrollId
, "contentDescription",
9018 metadata
.GetContentDescription().get());
9023 metrics
.SetPresShellId(presShell
->GetPresShellId());
9025 // If the scroll frame's content is marked 'scrollgrab', record this
9026 // in the FrameMetrics so APZ knows to provide the scroll grabbing
9029 nsContentUtils::HasScrollgrab(aScrollFrame
->GetContent())) {
9030 metadata
.SetHasScrollgrab(true);
9033 if (ShouldDisableApzForElement(aContent
)) {
9034 metadata
.SetForceDisableApz(true);
9037 metadata
.SetIsPaginatedPresentation(presContext
->Type() !=
9038 nsPresContext::eContext_Galley
);
9044 Maybe
<ScrollMetadata
> nsLayoutUtils::GetRootMetadata(
9045 nsDisplayListBuilder
* aBuilder
, WebRenderLayerManager
* aLayerManager
,
9046 const std::function
<bool(ViewID
& aScrollId
)>& aCallback
) {
9047 nsIFrame
* frame
= aBuilder
->RootReferenceFrame();
9048 nsPresContext
* presContext
= frame
->PresContext();
9049 PresShell
* presShell
= presContext
->PresShell();
9050 Document
* document
= presShell
->GetDocument();
9052 // There is one case where we want the root container layer to have metrics.
9053 // If the parent process is using XUL windows, there is no root scrollframe,
9054 // and without explicitly creating metrics there will be no guaranteed
9056 bool addMetrics
= XRE_IsParentProcess() && !presShell
->GetRootScrollFrame();
9058 // Add metrics if there are none in the layer tree with the id (create an id
9059 // if there isn't one already) of the root scroll frame/root content.
9060 bool ensureMetricsForRootId
= nsLayoutUtils::AsyncPanZoomEnabled(frame
) &&
9061 aBuilder
->IsPaintingToWindow() &&
9062 !presContext
->GetParentPresContext();
9064 nsIContent
* content
= nullptr;
9065 nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame();
9066 if (rootScrollFrame
) {
9067 content
= rootScrollFrame
->GetContent();
9069 // If there is no root scroll frame, pick the document element instead.
9070 // The only case we don't want to do this is in non-APZ fennec, where
9071 // we want the root xul document to get a null scroll id so that the root
9072 // content document gets the first non-null scroll id.
9073 content
= document
->GetDocumentElement();
9076 if (ensureMetricsForRootId
&& content
) {
9077 ViewID scrollId
= nsLayoutUtils::FindOrCreateIDFor(content
);
9078 if (aCallback(scrollId
)) {
9079 ensureMetricsForRootId
= false;
9083 if (addMetrics
|| ensureMetricsForRootId
) {
9084 bool isRootContent
= presContext
->IsRootContentDocumentCrossProcess();
9086 nsSize scrollPortSize
= frame
->GetSize();
9087 if (isRootContent
&& rootScrollFrame
) {
9088 nsIScrollableFrame
* scrollableFrame
=
9089 rootScrollFrame
->GetScrollTargetFrame();
9090 scrollPortSize
= scrollableFrame
->GetScrollPortRect().Size();
9092 return Some(nsLayoutUtils::ComputeScrollMetadata(
9093 frame
, rootScrollFrame
, content
, frame
,
9094 aBuilder
->ToReferenceFrame(frame
), aLayerManager
,
9095 ScrollableLayerGuid::NULL_SCROLL_ID
, scrollPortSize
, isRootContent
));
9102 void nsLayoutUtils::TransformToAncestorAndCombineRegions(
9103 const nsRegion
& aRegion
, nsIFrame
* aFrame
, const nsIFrame
* aAncestorFrame
,
9104 nsRegion
* aPreciseTargetDest
, nsRegion
* aImpreciseTargetDest
,
9105 Maybe
<Matrix4x4Flagged
>* aMatrixCache
, const DisplayItemClip
* aClip
) {
9106 if (aRegion
.IsEmpty()) {
9110 RegionBuilder
<nsRegion
> transformedRegion
;
9111 for (nsRegion::RectIterator it
= aRegion
.RectIter(); !it
.Done(); it
.Next()) {
9112 nsRect transformed
= TransformFrameRectToAncestor(
9113 aFrame
, it
.Get(), aAncestorFrame
, &isPrecise
, aMatrixCache
);
9115 transformed
= aClip
->ApplyNonRoundedIntersection(transformed
);
9116 if (aClip
->GetRoundedRectCount() > 0) {
9120 transformedRegion
.OrWith(transformed
);
9122 nsRegion
* dest
= isPrecise
? aPreciseTargetDest
: aImpreciseTargetDest
;
9123 dest
->OrWith(transformedRegion
.ToRegion());
9124 // If the region becomes too complex this has a large performance impact.
9125 // We limit its complexity here.
9126 if (dest
->GetNumRects() > 12) {
9127 dest
->SimplifyOutward(6);
9129 aPreciseTargetDest
->OrWith(*aImpreciseTargetDest
);
9130 *aImpreciseTargetDest
= std::move(*aPreciseTargetDest
);
9131 aImpreciseTargetDest
->SimplifyOutward(6);
9132 *aPreciseTargetDest
= nsRegion();
9138 bool nsLayoutUtils::ShouldUseNoFramesSheet(Document
* aDocument
) {
9139 bool allowSubframes
= true;
9140 nsIDocShell
* docShell
= aDocument
->GetDocShell();
9142 docShell
->GetAllowSubframes(&allowSubframes
);
9144 return !allowSubframes
;
9148 void nsLayoutUtils::GetFrameTextContent(nsIFrame
* aFrame
, nsAString
& aResult
) {
9150 AppendFrameTextContent(aFrame
, aResult
);
9154 void nsLayoutUtils::AppendFrameTextContent(nsIFrame
* aFrame
,
9155 nsAString
& aResult
) {
9156 if (aFrame
->IsTextFrame()) {
9157 auto* const textFrame
= static_cast<nsTextFrame
*>(aFrame
);
9158 const auto offset
= AssertedCast
<uint32_t>(textFrame
->GetContentOffset());
9159 const auto length
= AssertedCast
<uint32_t>(textFrame
->GetContentLength());
9160 textFrame
->TextFragment()->AppendTo(aResult
, offset
, length
);
9162 for (nsIFrame
* child
: aFrame
->PrincipalChildList()) {
9163 AppendFrameTextContent(child
, aResult
);
9169 nsRect
nsLayoutUtils::GetSelectionBoundingRect(const Selection
* aSel
) {
9171 // Bounding client rect may be empty after calling GetBoundingClientRect
9172 // when range is collapsed. So we get caret's rect when range is
9174 if (aSel
->IsCollapsed()) {
9175 nsIFrame
* frame
= nsCaret::GetGeometry(aSel
, &res
);
9177 nsIFrame
* relativeTo
= GetContainingBlockForClientRect(frame
);
9178 res
= TransformFrameRectToAncestor(frame
, res
, relativeTo
);
9181 RectAccumulator accumulator
;
9182 const uint32_t rangeCount
= aSel
->RangeCount();
9183 for (const uint32_t idx
: IntegerRange(rangeCount
)) {
9184 MOZ_ASSERT(aSel
->RangeCount() == rangeCount
);
9185 nsRange
* range
= aSel
->GetRangeAt(idx
);
9186 nsRange::CollectClientRectsAndText(
9187 &accumulator
, nullptr, range
, range
->GetStartContainer(),
9188 range
->StartOffset(), range
->GetEndContainer(), range
->EndOffset(),
9191 res
= accumulator
.mResultRect
.IsEmpty() ? accumulator
.mFirstRect
9192 : accumulator
.mResultRect
;
9199 nsBlockFrame
* nsLayoutUtils::GetFloatContainingBlock(nsIFrame
* aFrame
) {
9200 nsIFrame
* ancestor
= aFrame
->GetParent();
9201 while (ancestor
&& !ancestor
->IsFloatContainingBlock()) {
9202 ancestor
= ancestor
->GetParent();
9204 MOZ_ASSERT(!ancestor
|| ancestor
->IsBlockFrameOrSubclass(),
9205 "Float containing block can only be block frame");
9206 return static_cast<nsBlockFrame
*>(ancestor
);
9209 // The implementations of this calculation are adapted from
9210 // Element::GetBoundingClientRect().
9212 CSSRect
nsLayoutUtils::GetBoundingContentRect(
9213 const nsIContent
* aContent
, const nsIScrollableFrame
* aRootScrollFrame
,
9214 Maybe
<CSSRect
>* aOutNearestScrollClip
) {
9215 if (nsIFrame
* frame
= aContent
->GetPrimaryFrame()) {
9216 return GetBoundingFrameRect(frame
, aRootScrollFrame
, aOutNearestScrollClip
);
9222 CSSRect
nsLayoutUtils::GetBoundingFrameRect(
9223 nsIFrame
* aFrame
, const nsIScrollableFrame
* aRootScrollFrame
,
9224 Maybe
<CSSRect
>* aOutNearestScrollClip
) {
9226 nsIFrame
* relativeTo
= aRootScrollFrame
->GetScrolledFrame();
9227 result
= CSSRect::FromAppUnits(nsLayoutUtils::GetAllInFlowRectsUnion(
9228 aFrame
, relativeTo
, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS
));
9230 // If the element is contained in a scrollable frame that is not
9231 // the root scroll frame, make sure to clip the result so that it is
9232 // not larger than the containing scrollable frame's bounds.
9233 nsIScrollableFrame
* scrollFrame
= nsLayoutUtils::GetNearestScrollableFrame(
9234 aFrame
, SCROLLABLE_INCLUDE_HIDDEN
| SCROLLABLE_FIXEDPOS_FINDS_ROOT
);
9235 if (scrollFrame
&& scrollFrame
!= aRootScrollFrame
) {
9236 nsIFrame
* subFrame
= do_QueryFrame(scrollFrame
);
9237 MOZ_ASSERT(subFrame
);
9238 // Get the bounds of the scroll frame in the same coordinate space
9240 nsRect subFrameRect
= subFrame
->GetRectRelativeToSelf();
9241 TransformResult res
=
9242 nsLayoutUtils::TransformRect(subFrame
, relativeTo
, subFrameRect
);
9243 MOZ_ASSERT(res
== TRANSFORM_SUCCEEDED
|| res
== NONINVERTIBLE_TRANSFORM
);
9244 if (res
== TRANSFORM_SUCCEEDED
) {
9245 CSSRect subFrameRectCSS
= CSSRect::FromAppUnits(subFrameRect
);
9246 if (aOutNearestScrollClip
) {
9247 *aOutNearestScrollClip
= Some(subFrameRectCSS
);
9250 result
= subFrameRectCSS
.Intersect(result
);
9257 bool nsLayoutUtils::IsTransformed(nsIFrame
* aForFrame
, nsIFrame
* aTopFrame
) {
9258 for (nsIFrame
* f
= aForFrame
; f
!= aTopFrame
; f
= f
->GetParent()) {
9259 if (f
->IsTransformed()) {
9267 CSSPoint
nsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame
* aFrame
) {
9272 nsIFrame
* frame
= aFrame
;
9273 nsCOMPtr
<nsIContent
> lastContent
;
9274 bool seenRcdRsf
= false;
9276 // Helper lambda to apply the callback transform for a single frame.
9277 auto applyCallbackTransformForFrame
= [&](nsIFrame
* frame
) {
9279 nsCOMPtr
<nsIContent
> content
= frame
->GetContent();
9280 if (content
&& (content
!= lastContent
)) {
9281 void* property
= content
->GetProperty(nsGkAtoms::apzCallbackTransform
);
9283 delta
+= *static_cast<CSSPoint
*>(property
);
9286 lastContent
= content
;
9291 // Apply the callback transform for the current frame.
9292 applyCallbackTransformForFrame(frame
);
9294 // Keep track of whether we've encountered the RCD-RSF's content element.
9295 nsPresContext
* pc
= frame
->PresContext();
9296 if (pc
->IsRootContentDocumentCrossProcess()) {
9297 if (PresShell
* shell
= pc
->GetPresShell()) {
9298 if (nsIFrame
* rsf
= shell
->GetRootScrollFrame()) {
9299 if (frame
->GetContent() == rsf
->GetContent()) {
9306 // If we reach the RCD's viewport frame, but have not encountered
9307 // the RCD-RSF, we were inside fixed content in the RCD.
9308 // We still want to apply the RCD-RSF's callback transform because
9309 // it contains the offset between the visual and layout viewports
9310 // which applies to fixed content as well.
9311 ViewportFrame
* viewportFrame
= do_QueryFrame(frame
);
9312 if (viewportFrame
) {
9313 if (pc
->IsRootContentDocumentCrossProcess() && !seenRcdRsf
) {
9314 applyCallbackTransformForFrame(pc
->PresShell()->GetRootScrollFrame());
9318 // Proceed to the parent frame.
9319 frame
= GetCrossDocParentFrameInProcess(frame
);
9324 static nsSize
ComputeMaxSizeForPartialPrerender(nsIFrame
* aFrame
,
9326 Matrix4x4Flagged transform
= nsLayoutUtils::GetTransformToAncestor(
9328 RelativeTo
{nsLayoutUtils::GetDisplayRootFrame(aFrame
)});
9331 if (!transform
.Is2D(&transform2D
)) {
9335 gfx::Rect
result(0, 0, aMaxSize
.width
, aMaxSize
.height
);
9336 auto scale
= transform2D
.ScaleFactors();
9337 if (scale
.xScale
!= 0 && scale
.yScale
!= 0) {
9338 result
.width
/= scale
.xScale
;
9339 result
.height
/= scale
.yScale
;
9342 // Don't apply translate.
9343 transform2D
._31
= 0.0f
;
9344 transform2D
._32
= 0.0f
;
9346 // Don't apply scale.
9347 if (scale
.xScale
!= 0 && scale
.yScale
!= 0) {
9348 transform2D
._11
/= scale
.xScale
;
9349 transform2D
._12
/= scale
.xScale
;
9350 transform2D
._21
/= scale
.yScale
;
9351 transform2D
._22
/= scale
.yScale
;
9354 // Theoretically we should use transform2D.Inverse() here but in this case
9355 // |transform2D| is a pure rotation matrix, no scaling, no translate at all,
9356 // so that the result bound's width and height would be pretty much same
9357 // as the one rotated by the inverse matrix.
9358 result
= transform2D
.TransformBounds(result
);
9360 result
.width
< (float)nscoord_MAX
? result
.width
: nscoord_MAX
,
9361 result
.height
< (float)nscoord_MAX
? result
.height
: nscoord_MAX
);
9365 nsRect
nsLayoutUtils::ComputePartialPrerenderArea(
9366 nsIFrame
* aFrame
, const nsRect
& aDirtyRect
, const nsRect
& aOverflow
,
9367 const nsSize
& aPrerenderSize
) {
9368 nsSize maxSizeForPartialPrerender
=
9369 ComputeMaxSizeForPartialPrerender(aFrame
, aPrerenderSize
);
9370 // Simple calculation for now: center the pre-render area on the dirty rect,
9371 // and clamp to the overflow area. Later we can do more advanced things like
9372 // redistributing from one axis to another, or from one side to another.
9374 std::max(maxSizeForPartialPrerender
.width
- aDirtyRect
.width
, 0);
9376 std::max(maxSizeForPartialPrerender
.height
- aDirtyRect
.height
, 0);
9377 nsRect result
= aDirtyRect
;
9378 result
.Inflate(xExcess
/ 2, yExcess
/ 2);
9379 return result
.MoveInsideAndClamp(aOverflow
);
9382 static bool LineHasNonEmptyContentWorker(nsIFrame
* aFrame
) {
9383 // Look for non-empty frames, but ignore inline and br frames.
9384 // For inline frames, descend into the children, if any.
9385 if (aFrame
->IsInlineFrame()) {
9386 for (nsIFrame
* child
: aFrame
->PrincipalChildList()) {
9387 if (LineHasNonEmptyContentWorker(child
)) {
9392 if (!aFrame
->IsBrFrame() && !aFrame
->IsEmpty()) {
9399 static bool LineHasNonEmptyContent(nsLineBox
* aLine
) {
9400 int32_t count
= aLine
->GetChildCount();
9401 for (nsIFrame
* frame
= aLine
->mFirstChild
; count
> 0;
9402 --count
, frame
= frame
->GetNextSibling()) {
9403 if (LineHasNonEmptyContentWorker(frame
)) {
9411 bool nsLayoutUtils::IsInvisibleBreak(nsINode
* aNode
,
9412 nsIFrame
** aNextLineFrame
) {
9413 if (aNextLineFrame
) {
9414 *aNextLineFrame
= nullptr;
9417 if (!aNode
->IsElement() || !aNode
->IsEditable()) {
9420 nsIFrame
* frame
= aNode
->AsElement()->GetPrimaryFrame();
9421 if (!frame
|| !frame
->IsBrFrame()) {
9425 nsContainerFrame
* f
= frame
->GetParent();
9426 while (f
&& f
->IsLineParticipant()) {
9429 nsBlockFrame
* blockAncestor
= do_QueryFrame(f
);
9430 if (!blockAncestor
) {
9431 // The container frame doesn't support line breaking.
9436 nsBlockInFlowLineIterator
iter(blockAncestor
, frame
, &valid
);
9441 bool lineNonEmpty
= LineHasNonEmptyContent(iter
.GetLine());
9442 if (!lineNonEmpty
) {
9446 while (iter
.Next()) {
9447 auto currentLine
= iter
.GetLine();
9448 // Completely skip empty lines.
9449 if (!currentLine
->IsEmpty()) {
9450 // If we come across an inline line, the BR has caused a visible line
9452 if (currentLine
->IsInline()) {
9453 if (aNextLineFrame
) {
9454 *aNextLineFrame
= currentLine
->mFirstChild
;
9462 return lineNonEmpty
;
9466 nsRect
nsLayoutUtils::ComputeSVGOriginBox(SVGViewportElement
* aElement
) {
9471 if (aElement
->HasViewBox()) {
9472 // Return the "origin box", which is defined as a rect positioned at the
9473 // origin, but with the width and height given by the viewBox attribute
9475 // https://drafts.csswg.org/css-box-3/#valdef-box-view-box
9477 // For more discussion see
9478 // https://github.com/web-platform-tests/interop/issues/509
9479 const SVGViewBox
& value
= aElement
->GetAnimatedViewBox()->GetAnimValue();
9480 return nsRect(0, 0, nsPresContext::CSSPixelsToAppUnits(value
.width
),
9481 nsPresContext::CSSPixelsToAppUnits(value
.height
));
9484 // No viewBox is specified, uses the nearest SVG viewport as reference
9486 auto viewportSize
= aElement
->GetViewportSize();
9487 return nsRect(0, 0, nsPresContext::CSSPixelsToAppUnits(viewportSize
.width
),
9488 nsPresContext::CSSPixelsToAppUnits(viewportSize
.height
));
9492 nsRect
nsLayoutUtils::ComputeSVGReferenceRect(
9493 nsIFrame
* aFrame
, StyleGeometryBox aGeometryBox
,
9494 MayHaveNonScalingStrokeCyclicDependency aMayHaveCyclicDependency
) {
9495 MOZ_ASSERT(aFrame
->GetContent()->IsSVGElement());
9498 switch (aGeometryBox
) {
9499 case StyleGeometryBox::StrokeBox
: {
9501 // The size of stroke-box is not correct if this graphic element has
9502 // specific stroke-linejoin or stroke-linecap.
9503 const uint32_t flags
= SVGUtils::eBBoxIncludeFillGeometry
|
9504 SVGUtils::eBBoxIncludeStroke
|
9505 (bool(aMayHaveCyclicDependency
)
9506 ? SVGUtils::eAvoidCycleIfNonScalingStroke
9508 gfxRect bbox
= SVGUtils::GetBBox(aFrame
, flags
);
9509 r
= nsLayoutUtils::RoundGfxRectToAppRect(bbox
, AppUnitsPerCSSPixel());
9512 case StyleGeometryBox::ViewBox
: {
9513 SVGViewportElement
* viewportElement
=
9514 SVGElement::FromNode(aFrame
->GetContent())->GetCtx();
9515 if (!viewportElement
) {
9516 // We should not render without a viewport so return an empty rect.
9519 r
= nsLayoutUtils::ComputeSVGOriginBox(viewportElement
);
9522 case StyleGeometryBox::FillBox
: {
9524 SVGUtils::GetBBox(aFrame
, SVGUtils::eBBoxIncludeFillGeometry
);
9525 r
= nsLayoutUtils::RoundGfxRectToAppRect(bbox
, AppUnitsPerCSSPixel());
9529 MOZ_ASSERT_UNREACHABLE("unsupported SVG box");
9538 nsRect
nsLayoutUtils::ComputeHTMLReferenceRect(const nsIFrame
* aFrame
,
9539 StyleGeometryBox aGeometryBox
) {
9542 switch (aGeometryBox
) {
9543 case StyleGeometryBox::ContentBox
:
9544 r
= aFrame
->GetContentRectRelativeToSelf();
9546 case StyleGeometryBox::PaddingBox
:
9547 r
= aFrame
->GetPaddingRectRelativeToSelf();
9549 case StyleGeometryBox::MarginBox
:
9550 r
= aFrame
->GetMarginRectRelativeToSelf();
9552 case StyleGeometryBox::BorderBox
:
9553 r
= aFrame
->GetRectRelativeToSelf();
9556 MOZ_ASSERT_UNREACHABLE("unsupported CSS box");
9563 static StyleGeometryBox
ShapeBoxToGeometryBox(const StyleShapeBox
& aBox
) {
9565 case StyleShapeBox::BorderBox
:
9566 return StyleGeometryBox::BorderBox
;
9567 case StyleShapeBox::ContentBox
:
9568 return StyleGeometryBox::ContentBox
;
9569 case StyleShapeBox::MarginBox
:
9570 return StyleGeometryBox::MarginBox
;
9571 case StyleShapeBox::PaddingBox
:
9572 return StyleGeometryBox::PaddingBox
;
9574 MOZ_ASSERT_UNREACHABLE("Unknown shape box type");
9575 return StyleGeometryBox::MarginBox
;
9578 static StyleGeometryBox
ClipPathBoxToGeometryBox(
9579 const StyleShapeGeometryBox
& aBox
) {
9580 using Tag
= StyleShapeGeometryBox::Tag
;
9583 return ShapeBoxToGeometryBox(aBox
.AsShapeBox());
9584 case Tag::ElementDependent
:
9585 return StyleGeometryBox::NoBox
;
9587 return StyleGeometryBox::FillBox
;
9588 case Tag::StrokeBox
:
9589 return StyleGeometryBox::StrokeBox
;
9591 return StyleGeometryBox::ViewBox
;
9593 MOZ_ASSERT_UNREACHABLE("Unknown shape box type");
9594 return StyleGeometryBox::NoBox
;
9597 // The mapping is from
9598 // https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
9600 nsRect
nsLayoutUtils::ComputeClipPathGeometryBox(
9601 nsIFrame
* aFrame
, const StyleShapeGeometryBox
& aBox
) {
9602 StyleGeometryBox box
= ClipPathBoxToGeometryBox(aBox
);
9604 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
9605 // For SVG elements without associated CSS layout box, the used value for
9606 // content-box and padding-box is fill-box and for border-box and margin-box
9609 case StyleGeometryBox::ContentBox
:
9610 case StyleGeometryBox::PaddingBox
:
9611 case StyleGeometryBox::FillBox
:
9612 return ComputeSVGReferenceRect(aFrame
, StyleGeometryBox::FillBox
);
9613 case StyleGeometryBox::NoBox
:
9614 case StyleGeometryBox::BorderBox
:
9615 case StyleGeometryBox::MarginBox
:
9616 case StyleGeometryBox::StrokeBox
:
9617 return ComputeSVGReferenceRect(aFrame
, StyleGeometryBox::StrokeBox
);
9618 case StyleGeometryBox::ViewBox
:
9619 return ComputeSVGReferenceRect(aFrame
, StyleGeometryBox::ViewBox
);
9621 MOZ_ASSERT_UNREACHABLE("Unknown clip-path geometry box");
9622 // Use default, border-box (as stroke-box in SVG layout).
9623 return ComputeSVGReferenceRect(aFrame
, StyleGeometryBox::StrokeBox
);
9627 // For elements with associated CSS layout box, the used value for fill-box is
9628 // content-box and for stroke-box and view-box is border-box.
9630 case StyleGeometryBox::FillBox
:
9631 case StyleGeometryBox::ContentBox
:
9632 return ComputeHTMLReferenceRect(aFrame
, StyleGeometryBox::ContentBox
);
9633 case StyleGeometryBox::NoBox
:
9634 case StyleGeometryBox::StrokeBox
:
9635 case StyleGeometryBox::ViewBox
:
9636 case StyleGeometryBox::BorderBox
:
9637 return ComputeHTMLReferenceRect(aFrame
, StyleGeometryBox::BorderBox
);
9638 case StyleGeometryBox::PaddingBox
:
9639 return ComputeHTMLReferenceRect(aFrame
, StyleGeometryBox::PaddingBox
);
9640 case StyleGeometryBox::MarginBox
:
9641 return ComputeHTMLReferenceRect(aFrame
, StyleGeometryBox::MarginBox
);
9643 MOZ_ASSERT_UNREACHABLE("Unknown clip-path geometry box");
9644 // Use default, border-box.
9645 return ComputeHTMLReferenceRect(aFrame
, StyleGeometryBox::BorderBox
);
9650 nsPoint
nsLayoutUtils::ComputeOffsetToUserSpace(nsDisplayListBuilder
* aBuilder
,
9652 nsPoint offsetToBoundingBox
=
9653 aBuilder
->ToReferenceFrame(aFrame
) -
9654 SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame
);
9655 if (!aFrame
->IsSVGFrame()) {
9656 // Snap the offset if the reference frame is not a SVG frame, since other
9657 // frames will be snapped to pixel when rendering.
9658 offsetToBoundingBox
=
9659 nsPoint(aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
9660 offsetToBoundingBox
.x
),
9661 aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
9662 offsetToBoundingBox
.y
));
9665 // During SVG painting, the offset computed here is applied to the gfxContext
9666 // "ctx" used to paint the mask. After applying only "offsetToBoundingBox",
9667 // "ctx" would have its origin at the top left corner of frame's bounding box
9668 // (over all continuations).
9669 // However, SVG painting needs the origin to be located at the origin of the
9670 // SVG frame's "user space", i.e. the space in which, for example, the
9671 // frame's BBox lives.
9672 // SVG geometry frames and foreignObject frames apply their own offsets, so
9673 // their position is relative to their user space. So for these frame types,
9674 // if we want "ctx" to be in user space, we first need to subtract the
9675 // frame's position so that SVG painting can later add it again and the
9676 // frame is painted in the right place.
9677 gfxPoint toUserSpaceGfx
=
9678 SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame
);
9679 nsPoint toUserSpace
=
9680 nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.x
)),
9681 nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.y
)));
9683 return (offsetToBoundingBox
- toUserSpace
);
9687 already_AddRefed
<nsFontMetrics
> nsLayoutUtils::GetMetricsFor(
9688 nsPresContext
* aPresContext
, bool aIsVertical
,
9689 const nsStyleFont
* aStyleFont
, Length aFontSize
, bool aUseUserFontSet
) {
9690 nsFont font
= aStyleFont
->mFont
;
9691 font
.size
= aFontSize
;
9692 gfxFont::Orientation orientation
=
9693 aIsVertical
? nsFontMetrics::eVertical
: nsFontMetrics::eHorizontal
;
9694 nsFontMetrics::Params params
;
9695 params
.language
= aStyleFont
->mLanguage
;
9696 params
.explicitLanguage
= aStyleFont
->mExplicitLanguage
;
9697 params
.orientation
= orientation
;
9698 params
.userFontSet
=
9699 aUseUserFontSet
? aPresContext
->GetUserFontSet() : nullptr;
9700 params
.textPerf
= aPresContext
->GetTextPerfMetrics();
9701 params
.featureValueLookup
= aPresContext
->GetFontFeatureValuesLookup();
9702 return aPresContext
->GetMetricsFor(font
, params
);
9706 void nsLayoutUtils::ComputeSystemFont(nsFont
* aSystemFont
,
9707 LookAndFeel::FontID aFontID
,
9708 const nsFont
& aDefaultVariableFont
,
9709 const Document
* aDocument
) {
9710 gfxFontStyle fontStyle
;
9711 nsAutoString systemFontName
;
9712 if (!LookAndFeel::GetFont(aFontID
, systemFontName
, fontStyle
)) {
9715 systemFontName
.Trim("\"'");
9716 NS_ConvertUTF16toUTF8
nameu8(systemFontName
);
9717 Servo_FontFamily_ForSystemFont(&nameu8
, &aSystemFont
->family
);
9718 aSystemFont
->style
= fontStyle
.style
;
9719 aSystemFont
->family
.is_system_font
= fontStyle
.systemFont
;
9720 aSystemFont
->weight
= fontStyle
.weight
;
9721 aSystemFont
->stretch
= fontStyle
.stretch
;
9722 aSystemFont
->size
= Length::FromPixels(fontStyle
.size
);
9724 // aSystemFont->langGroup = fontStyle.langGroup;
9726 switch (StyleFontSizeAdjust::Tag(fontStyle
.sizeAdjustBasis
)) {
9727 case StyleFontSizeAdjust::Tag::None
:
9728 aSystemFont
->sizeAdjust
= StyleFontSizeAdjust::None();
9730 case StyleFontSizeAdjust::Tag::ExHeight
:
9731 aSystemFont
->sizeAdjust
=
9732 StyleFontSizeAdjust::ExHeight(fontStyle
.sizeAdjust
);
9734 case StyleFontSizeAdjust::Tag::CapHeight
:
9735 aSystemFont
->sizeAdjust
=
9736 StyleFontSizeAdjust::CapHeight(fontStyle
.sizeAdjust
);
9738 case StyleFontSizeAdjust::Tag::ChWidth
:
9739 aSystemFont
->sizeAdjust
=
9740 StyleFontSizeAdjust::ChWidth(fontStyle
.sizeAdjust
);
9742 case StyleFontSizeAdjust::Tag::IcWidth
:
9743 aSystemFont
->sizeAdjust
=
9744 StyleFontSizeAdjust::IcWidth(fontStyle
.sizeAdjust
);
9746 case StyleFontSizeAdjust::Tag::IcHeight
:
9747 aSystemFont
->sizeAdjust
=
9748 StyleFontSizeAdjust::IcHeight(fontStyle
.sizeAdjust
);
9752 if (aFontID
== LookAndFeel::FontID::MozField
||
9753 aFontID
== LookAndFeel::FontID::MozButton
||
9754 aFontID
== LookAndFeel::FontID::MozList
) {
9755 // For textfields, buttons and selects, we use whatever font is defined by
9756 // the system. Which it appears (and the assumption is) it is always a
9757 // proportional font. Then we always use 2 points smaller than what the
9758 // browser has defined as the default proportional font.
9760 // This matches historical Windows behavior and other browsers.
9762 aDefaultVariableFont
.size
.ToCSSPixels() - CSSPixel::FromPoints(2.0f
);
9763 aSystemFont
->size
= Length::FromPixels(std::max(float(newSize
), 0.0f
));
9768 bool nsLayoutUtils::ShouldHandleMetaViewport(const Document
* aDocument
) {
9769 BrowsingContext
* bc
= aDocument
->GetBrowsingContext();
9770 return StaticPrefs::dom_meta_viewport_enabled() || (bc
&& bc
->InRDMPane());
9774 ComputedStyle
* nsLayoutUtils::StyleForScrollbar(
9775 const nsIFrame
* aScrollbarPart
) {
9776 // Get the closest content node which is not an anonymous scrollbar
9777 // part. It should be the originating element of the scrollbar part.
9778 nsIContent
* content
= aScrollbarPart
->GetContent();
9779 // Note that the content may be a normal element with scrollbar part
9780 // value specified for its -moz-appearance, so don't rely on it being
9781 // a native anonymous. Also note that we have to check the node name
9782 // because anonymous element like generated content may originate a
9784 MOZ_ASSERT(content
, "No content for the scrollbar part?");
9785 while (content
&& content
->IsInNativeAnonymousSubtree() &&
9786 content
->IsAnyOfXULElements(
9787 nsGkAtoms::scrollbar
, nsGkAtoms::scrollbarbutton
,
9788 nsGkAtoms::scrollcorner
, nsGkAtoms::slider
, nsGkAtoms::thumb
)) {
9789 content
= content
->GetParent();
9791 MOZ_ASSERT(content
, "Native anonymous element with no originating node?");
9792 // Use the style from the primary frame of the content.
9793 // Note: it is important to use the primary frame rather than an
9794 // ancestor frame of the scrollbar part for the correct handling of
9795 // viewport scrollbar. The content of the scroll frame of the viewport
9796 // is the root element, but its style inherits from the viewport.
9797 // Since we need to use the style of root element for the viewport
9798 // scrollbar, we have to get the style from the primary frame.
9799 if (nsIFrame
* primaryFrame
= content
->GetPrimaryFrame()) {
9800 return primaryFrame
->Style();
9802 // If the element doesn't have primary frame, get the computed style
9803 // from the element directly. This can happen on viewport, because
9804 // the scrollbar of viewport may be shown when the root element has
9805 // > display: none; overflow: scroll;
9807 content
== aScrollbarPart
->PresContext()->Document()->GetRootElement(),
9808 "Root element is the only case for this fallback "
9809 "path to be triggered");
9810 RefPtr
<ComputedStyle
> style
=
9811 ServoStyleSet::ResolveServoStyle(*content
->AsElement());
9812 // Dropping the strong reference is fine because the style should be
9813 // held strongly by the element.
9817 enum class FramePosition
: uint8_t {
9823 // NOTE: Returns a pair of Nothing() and `FramePosition::Unknown` if |aFrame|
9824 // is not in out-of-process or if we haven't received enough information from
9826 static std::pair
<Maybe
<ScreenRect
>, FramePosition
> GetFrameVisibleRectOnScreen(
9827 const nsIFrame
* aFrame
) {
9828 // We actually want the in-process top prescontext here.
9829 nsPresContext
* topContextInProcess
=
9830 aFrame
->PresContext()->GetInProcessRootContentDocumentPresContext();
9831 if (!topContextInProcess
) {
9832 // We are in chrome process.
9833 return std::make_pair(Nothing(), FramePosition::Unknown
);
9836 if (topContextInProcess
->Document()->IsTopLevelContentDocument()) {
9837 // We are in the top of content document.
9838 return std::make_pair(Nothing(), FramePosition::Unknown
);
9841 nsIDocShell
* docShell
= topContextInProcess
->GetDocShell();
9842 BrowserChild
* browserChild
= BrowserChild::GetFrom(docShell
);
9843 if (!browserChild
) {
9844 // We are not in out-of-process iframe.
9845 return std::make_pair(Nothing(), FramePosition::Unknown
);
9848 if (!browserChild
->GetEffectsInfo().IsVisible()) {
9849 // There is no visible rect on this iframe at all.
9850 return std::make_pair(Some(ScreenRect()), FramePosition::Unknown
);
9853 Maybe
<ScreenRect
> visibleRect
=
9854 browserChild
->GetTopLevelViewportVisibleRectInBrowserCoords();
9856 // We are unsure if we haven't received the transformed rectangle of the
9857 // iframe's visible area.
9858 return std::make_pair(Nothing(), FramePosition::Unknown
);
9861 nsIFrame
* rootFrame
= topContextInProcess
->PresShell()->GetRootFrame();
9862 nsRect transformedToIFrame
= nsLayoutUtils::TransformFrameRectToAncestor(
9863 aFrame
, aFrame
->InkOverflowRectRelativeToSelf(), rootFrame
);
9865 LayoutDeviceRect rectInLayoutDevicePixel
= LayoutDeviceRect::FromAppUnits(
9866 transformedToIFrame
, topContextInProcess
->AppUnitsPerDevPixel());
9868 ScreenRect transformedToRoot
= ViewAs
<ScreenPixel
>(
9869 browserChild
->GetChildToParentConversionMatrix().TransformBounds(
9870 rectInLayoutDevicePixel
),
9871 PixelCastJustification::ContentProcessIsLayerInUiProcess
);
9873 FramePosition position
= FramePosition::Unknown
;
9874 // we need to check whether the transformed rect is outside the iframe
9875 // visible rect or not because in some cases the rect size is (0x0), thus
9876 // the intersection between the transformed rect and the iframe visible rect
9877 // would also be (0x0), then we can't tell whether the given nsIFrame is
9878 // inside the iframe visible rect or not by calling BaseRect::IsEmpty for the
9880 if (transformedToRoot
.x
> visibleRect
->XMost() ||
9881 transformedToRoot
.y
> visibleRect
->YMost() ||
9882 visibleRect
->x
> transformedToRoot
.XMost() ||
9883 visibleRect
->y
> transformedToRoot
.YMost()) {
9884 position
= FramePosition::OutOfView
;
9886 position
= FramePosition::InView
;
9889 return std::make_pair(Some(visibleRect
->Intersect(transformedToRoot
)),
9894 bool nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(
9895 const nsIFrame
* aFrame
) {
9896 auto [visibleRect
, framePosition
] = GetFrameVisibleRectOnScreen(aFrame
);
9897 if (visibleRect
.isNothing()) {
9901 return visibleRect
->IsEmpty() && framePosition
!= FramePosition::InView
;
9905 bool nsLayoutUtils::FrameIsMostlyScrolledOutOfViewInCrossProcess(
9906 const nsIFrame
* aFrame
, nscoord aMargin
) {
9907 auto [visibleRect
, framePosition
] = GetFrameVisibleRectOnScreen(aFrame
);
9908 (void)framePosition
;
9909 if (visibleRect
.isNothing()) {
9913 nsPresContext
* topContextInProcess
=
9914 aFrame
->PresContext()->GetInProcessRootContentDocumentPresContext();
9915 MOZ_ASSERT(topContextInProcess
);
9917 nsIDocShell
* docShell
= topContextInProcess
->GetDocShell();
9918 BrowserChild
* browserChild
= BrowserChild::GetFrom(docShell
);
9919 MOZ_ASSERT(browserChild
);
9922 browserChild
->GetChildToParentConversionMatrix().As2D().ScaleFactors();
9923 const CSSCoord cssMargin
= CSSPixel::FromAppUnits(aMargin
);
9925 CSSSize(cssMargin
, cssMargin
) * ViewAs
<CSSToScreenScale2D
>(scale
);
9927 return visibleRect
->width
< margin
.width
||
9928 visibleRect
->height
< margin
.height
;
9932 nsSize
nsLayoutUtils::ExpandHeightForViewportUnits(nsPresContext
* aPresContext
,
9933 const nsSize
& aSize
) {
9934 nsSize sizeForViewportUnits
= aPresContext
->GetSizeForViewportUnits();
9936 // |aSize| might be the size expanded to the minimum-scale size whereas the
9937 // size for viewport units is not scaled so that we need to expand the |aSize|
9938 // height by multiplying by the ratio of the viewport units height to the
9939 // visible area height.
9940 float vhExpansionRatio
= (float)sizeForViewportUnits
.height
/
9941 aPresContext
->GetVisibleArea().height
;
9943 MOZ_ASSERT(aSize
.height
<= NSCoordSaturatingNonnegativeMultiply(
9944 aSize
.height
, vhExpansionRatio
));
9945 return nsSize(aSize
.width
, NSCoordSaturatingNonnegativeMultiply(
9946 aSize
.height
, vhExpansionRatio
));
9949 template <typename SizeType
>
9950 /* static */ SizeType
ExpandHeightForDynamicToolbarImpl(
9951 const nsPresContext
* aPresContext
, const SizeType
& aSize
) {
9952 MOZ_ASSERT(aPresContext
);
9954 LayoutDeviceIntSize displaySize
;
9955 if (RefPtr
<MobileViewportManager
> MVM
=
9956 aPresContext
->PresShell()->GetMobileViewportManager()) {
9957 displaySize
= MVM
->DisplaySize();
9958 } else if (!nsLayoutUtils::GetDocumentViewerSize(aPresContext
, displaySize
)) {
9962 float toolbarHeightRatio
=
9963 mozilla::ScreenCoord(aPresContext
->GetDynamicToolbarMaxHeight()) /
9964 mozilla::ViewAs
<mozilla::ScreenPixel
>(
9966 mozilla::PixelCastJustification::LayoutDeviceIsScreenForBounds
)
9969 SizeType expandedSize
= aSize
;
9970 static_assert(std::is_same_v
<nsSize
, SizeType
> ||
9971 std::is_same_v
<CSSSize
, SizeType
>);
9972 if constexpr (std::is_same_v
<nsSize
, SizeType
>) {
9973 expandedSize
.height
=
9974 NSCoordSaturatingAdd(aSize
.height
, aSize
.height
* toolbarHeightRatio
);
9975 } else if (std::is_same_v
<CSSSize
, SizeType
>) {
9976 expandedSize
.height
= aSize
.height
+ aSize
.height
* toolbarHeightRatio
;
9978 return expandedSize
;
9981 CSSSize
nsLayoutUtils::ExpandHeightForDynamicToolbar(
9982 const nsPresContext
* aPresContext
, const CSSSize
& aSize
) {
9983 return ExpandHeightForDynamicToolbarImpl(aPresContext
, aSize
);
9985 nsSize
nsLayoutUtils::ExpandHeightForDynamicToolbar(
9986 const nsPresContext
* aPresContext
, const nsSize
& aSize
) {
9987 return ExpandHeightForDynamicToolbarImpl(aPresContext
, aSize
);