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 "nsMeterFrame.h"
9 #include "mozilla/PresShell.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/Element.h"
12 #include "mozilla/dom/HTMLMeterElement.h"
13 #include "nsIContent.h"
14 #include "nsLayoutUtils.h"
15 #include "nsPresContext.h"
16 #include "nsGkAtoms.h"
17 #include "nsNodeInfoManager.h"
18 #include "nsContentCreatorFunctions.h"
19 #include "nsFontMetrics.h"
22 using namespace mozilla
;
23 using mozilla::dom::Document
;
24 using mozilla::dom::Element
;
25 using mozilla::dom::HTMLMeterElement
;
27 nsIFrame
* NS_NewMeterFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
28 return new (aPresShell
) nsMeterFrame(aStyle
, aPresShell
->GetPresContext());
31 NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame
)
33 nsMeterFrame::nsMeterFrame(ComputedStyle
* aStyle
, nsPresContext
* aPresContext
)
34 : nsContainerFrame(aStyle
, aPresContext
, kClassID
), mBarDiv(nullptr) {}
36 nsMeterFrame::~nsMeterFrame() = default;
38 void nsMeterFrame::Destroy(DestroyContext
& aContext
) {
39 NS_ASSERTION(!GetPrevContinuation(),
40 "nsMeterFrame should not have continuations; if it does we "
41 "need to call RegUnregAccessKey only for the first.");
42 aContext
.AddAnonymousContent(mBarDiv
.forget());
43 nsContainerFrame::Destroy(aContext
);
46 nsresult
nsMeterFrame::CreateAnonymousContent(
47 nsTArray
<ContentInfo
>& aElements
) {
48 // Get the NodeInfoManager and tag necessary to create the meter bar div.
49 nsCOMPtr
<Document
> doc
= mContent
->GetComposedDoc();
52 mBarDiv
= doc
->CreateHTMLElement(nsGkAtoms::div
);
54 // Associate the right pseudo-element to the anonymous child.
55 if (StaticPrefs::layout_css_modern_range_pseudos_enabled()) {
56 // TODO(emilio): Create also a slider-track pseudo-element.
57 mBarDiv
->SetPseudoElementType(PseudoStyleType::sliderFill
);
59 mBarDiv
->SetPseudoElementType(PseudoStyleType::mozMeterBar
);
62 aElements
.AppendElement(mBarDiv
);
67 void nsMeterFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
70 aElements
.AppendElement(mBarDiv
);
74 NS_QUERYFRAME_HEAD(nsMeterFrame
)
75 NS_QUERYFRAME_ENTRY(nsMeterFrame
)
76 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
77 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
79 void nsMeterFrame::Reflow(nsPresContext
* aPresContext
,
80 ReflowOutput
& aDesiredSize
,
81 const ReflowInput
& aReflowInput
,
82 nsReflowStatus
& aStatus
) {
84 DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
85 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
86 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
88 NS_ASSERTION(mBarDiv
, "Meter bar div must exist!");
89 NS_ASSERTION(!GetPrevContinuation(),
90 "nsMeterFrame should not have continuations; if it does we "
91 "need to call RegUnregAccessKey only for the first.");
93 nsIFrame
* barFrame
= mBarDiv
->GetPrimaryFrame();
94 NS_ASSERTION(barFrame
, "The meter frame should have a child with a frame!");
96 ReflowBarFrame(barFrame
, aPresContext
, aReflowInput
, aStatus
);
98 const auto wm
= aReflowInput
.GetWritingMode();
99 aDesiredSize
.SetSize(wm
, aReflowInput
.ComputedSizeWithBorderPadding(wm
));
101 aDesiredSize
.SetOverflowAreasToDesiredBounds();
102 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, barFrame
);
103 FinishAndStoreOverflow(&aDesiredSize
);
105 aStatus
.Reset(); // This type of frame can't be split.
108 void nsMeterFrame::ReflowBarFrame(nsIFrame
* aBarFrame
,
109 nsPresContext
* aPresContext
,
110 const ReflowInput
& aReflowInput
,
111 nsReflowStatus
& aStatus
) {
112 bool vertical
= ResolvedOrientationIsVertical();
113 WritingMode wm
= aBarFrame
->GetWritingMode();
114 LogicalSize availSize
= aReflowInput
.ComputedSize(wm
);
115 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
116 ReflowInput
reflowInput(aPresContext
, aReflowInput
, aBarFrame
, availSize
);
118 vertical
? aReflowInput
.ComputedHeight() : aReflowInput
.ComputedWidth();
119 nscoord xoffset
= aReflowInput
.ComputedPhysicalBorderPadding().left
;
120 nscoord yoffset
= aReflowInput
.ComputedPhysicalBorderPadding().top
;
122 auto* meterElement
= static_cast<HTMLMeterElement
*>(GetContent());
123 size
= NSToCoordRound(size
* meterElement
->Position());
125 if (!vertical
&& wm
.IsPhysicalRTL()) {
126 xoffset
+= aReflowInput
.ComputedWidth() - size
;
129 // The bar position is *always* constrained.
131 // We want the bar to begin at the bottom.
132 yoffset
+= aReflowInput
.ComputedHeight() - size
;
134 size
-= reflowInput
.ComputedPhysicalMargin().TopBottom() +
135 reflowInput
.ComputedPhysicalBorderPadding().TopBottom();
136 size
= std::max(size
, 0);
137 reflowInput
.SetComputedHeight(size
);
139 size
-= reflowInput
.ComputedPhysicalMargin().LeftRight() +
140 reflowInput
.ComputedPhysicalBorderPadding().LeftRight();
141 size
= std::max(size
, 0);
142 reflowInput
.SetComputedWidth(size
);
145 xoffset
+= reflowInput
.ComputedPhysicalMargin().left
;
146 yoffset
+= reflowInput
.ComputedPhysicalMargin().top
;
148 ReflowOutput
barDesiredSize(reflowInput
);
149 ReflowChild(aBarFrame
, aPresContext
, barDesiredSize
, reflowInput
, xoffset
,
150 yoffset
, ReflowChildFlags::Default
, aStatus
);
151 FinishReflowChild(aBarFrame
, aPresContext
, barDesiredSize
, &reflowInput
,
152 xoffset
, yoffset
, ReflowChildFlags::Default
);
155 nsresult
nsMeterFrame::AttributeChanged(int32_t aNameSpaceID
,
156 nsAtom
* aAttribute
, int32_t aModType
) {
157 NS_ASSERTION(mBarDiv
, "Meter bar div must exist!");
159 if (aNameSpaceID
== kNameSpaceID_None
&&
160 (aAttribute
== nsGkAtoms::value
|| aAttribute
== nsGkAtoms::max
||
161 aAttribute
== nsGkAtoms::min
)) {
162 nsIFrame
* barFrame
= mBarDiv
->GetPrimaryFrame();
163 NS_ASSERTION(barFrame
, "The meter frame should have a child with a frame!");
164 PresShell()->FrameNeedsReflow(barFrame
, IntrinsicDirty::None
,
169 return nsContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
172 LogicalSize
nsMeterFrame::ComputeAutoSize(
173 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
174 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
175 const LogicalSize
& aBorderPadding
, const StyleSizeOverrides
& aSizeOverrides
,
176 ComputeSizeFlags aFlags
) {
177 RefPtr
<nsFontMetrics
> fontMet
=
178 nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f
);
180 const WritingMode wm
= GetWritingMode();
181 LogicalSize
autoSize(wm
);
182 autoSize
.BSize(wm
) = autoSize
.ISize(wm
) =
183 fontMet
->Font().size
.ToAppUnits(); // 1em
185 if (ResolvedOrientationIsVertical() == wm
.IsVertical()) {
186 autoSize
.ISize(wm
) *= 5; // 5em
188 autoSize
.BSize(wm
) *= 5; // 5em
191 return autoSize
.ConvertTo(aWM
, wm
);
194 nscoord
nsMeterFrame::GetMinISize(gfxContext
* aRenderingContext
) {
195 RefPtr
<nsFontMetrics
> fontMet
=
196 nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f
);
198 nscoord minISize
= fontMet
->Font().size
.ToAppUnits(); // 1em
200 if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) {
201 // The orientation is inline
202 minISize
*= 5; // 5em
208 nscoord
nsMeterFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
209 return GetMinISize(aRenderingContext
);
212 bool nsMeterFrame::ShouldUseNativeStyle() const {
213 nsIFrame
* barFrame
= mBarDiv
->GetPrimaryFrame();
215 // Use the native style if these conditions are satisfied:
216 // - both frames use the native appearance;
217 // - neither frame has author specified rules setting the border or the
219 return StyleDisplay()->EffectiveAppearance() == StyleAppearance::Meter
&&
220 !Style()->HasAuthorSpecifiedBorderOrBackground() && barFrame
&&
221 barFrame
->StyleDisplay()->EffectiveAppearance() ==
222 StyleAppearance::Meterchunk
&&
223 !barFrame
->Style()->HasAuthorSpecifiedBorderOrBackground();