1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMeterFrame.h"
8 #include "nsIContent.h"
9 #include "nsPresContext.h"
10 #include "nsGkAtoms.h"
11 #include "nsINameSpaceManager.h"
12 #include "nsIDocument.h"
13 #include "nsIPresShell.h"
14 #include "nsNodeInfoManager.h"
15 #include "nsINodeInfo.h"
16 #include "nsContentCreatorFunctions.h"
17 #include "nsContentUtils.h"
18 #include "nsFormControlFrame.h"
19 #include "nsFontMetrics.h"
20 #include "mozilla/dom/Element.h"
21 #include "mozilla/dom/HTMLMeterElement.h"
22 #include "nsContentList.h"
23 #include "nsStyleSet.h"
24 #include "nsThemeConstants.h"
27 using mozilla::dom::Element
;
28 using mozilla::dom::HTMLMeterElement
;
31 NS_NewMeterFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
33 return new (aPresShell
) nsMeterFrame(aContext
);
36 NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame
)
38 nsMeterFrame::nsMeterFrame(nsStyleContext
* aContext
)
39 : nsContainerFrame(aContext
)
44 nsMeterFrame::~nsMeterFrame()
49 nsMeterFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
51 NS_ASSERTION(!GetPrevContinuation(),
52 "nsMeterFrame should not have continuations; if it does we "
53 "need to call RegUnregAccessKey only for the first.");
54 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame
*>(this), false);
55 nsContentUtils::DestroyAnonymousContent(&mBarDiv
);
56 nsContainerFrame::DestroyFrom(aDestructRoot
);
60 nsMeterFrame::CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
)
62 // Get the NodeInfoManager and tag necessary to create the meter bar div.
63 nsCOMPtr
<nsIDocument
> doc
= mContent
->GetDocument();
66 mBarDiv
= doc
->CreateHTMLElement(nsGkAtoms::div
);
68 // Associate ::-moz-meter-bar pseudo-element to the anonymous child.
69 nsCSSPseudoElements::Type pseudoType
= nsCSSPseudoElements::ePseudo_mozMeterBar
;
70 nsRefPtr
<nsStyleContext
> newStyleContext
= PresContext()->StyleSet()->
71 ResolvePseudoElementStyle(mContent
->AsElement(), pseudoType
,
72 StyleContext(), mBarDiv
->AsElement());
74 if (!aElements
.AppendElement(ContentInfo(mBarDiv
, newStyleContext
))) {
75 return NS_ERROR_OUT_OF_MEMORY
;
82 nsMeterFrame::AppendAnonymousContentTo(nsBaseContentList
& aElements
,
85 aElements
.MaybeAppendElement(mBarDiv
);
88 NS_QUERYFRAME_HEAD(nsMeterFrame
)
89 NS_QUERYFRAME_ENTRY(nsMeterFrame
)
90 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
91 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
94 NS_IMETHODIMP
nsMeterFrame::Reflow(nsPresContext
* aPresContext
,
95 nsHTMLReflowMetrics
& aDesiredSize
,
96 const nsHTMLReflowState
& aReflowState
,
97 nsReflowStatus
& aStatus
)
99 DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
100 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
102 NS_ASSERTION(mBarDiv
, "Meter bar div must exist!");
103 NS_ASSERTION(!GetPrevContinuation(),
104 "nsMeterFrame should not have continuations; if it does we "
105 "need to call RegUnregAccessKey only for the first.");
107 if (mState
& NS_FRAME_FIRST_REFLOW
) {
108 nsFormControlFrame::RegUnRegAccessKey(this, true);
111 nsIFrame
* barFrame
= mBarDiv
->GetPrimaryFrame();
112 NS_ASSERTION(barFrame
, "The meter frame should have a child with a frame!");
114 ReflowBarFrame(barFrame
, aPresContext
, aReflowState
, aStatus
);
116 aDesiredSize
.Width() = aReflowState
.ComputedWidth() +
117 aReflowState
.ComputedPhysicalBorderPadding().LeftRight();
118 aDesiredSize
.Height() = aReflowState
.ComputedHeight() +
119 aReflowState
.ComputedPhysicalBorderPadding().TopBottom();
121 aDesiredSize
.SetOverflowAreasToDesiredBounds();
122 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, barFrame
);
123 FinishAndStoreOverflow(&aDesiredSize
);
125 aStatus
= NS_FRAME_COMPLETE
;
127 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
133 nsMeterFrame::ReflowBarFrame(nsIFrame
* aBarFrame
,
134 nsPresContext
* aPresContext
,
135 const nsHTMLReflowState
& aReflowState
,
136 nsReflowStatus
& aStatus
)
138 bool vertical
= StyleDisplay()->mOrient
== NS_STYLE_ORIENT_VERTICAL
;
139 nsHTMLReflowState
reflowState(aPresContext
, aReflowState
, aBarFrame
,
140 nsSize(aReflowState
.ComputedWidth(),
141 NS_UNCONSTRAINEDSIZE
));
142 nscoord size
= vertical
? aReflowState
.ComputedHeight()
143 : aReflowState
.ComputedWidth();
144 nscoord xoffset
= aReflowState
.ComputedPhysicalBorderPadding().left
;
145 nscoord yoffset
= aReflowState
.ComputedPhysicalBorderPadding().top
;
147 // NOTE: Introduce a new function getPosition in the content part ?
148 HTMLMeterElement
* meterElement
= static_cast<HTMLMeterElement
*>(mContent
);
150 double max
= meterElement
->Max();
151 double min
= meterElement
->Min();
152 double value
= meterElement
->Value();
154 double position
= max
- min
;
155 position
= position
!= 0 ? (value
- min
) / position
: 1;
157 size
= NSToCoordRound(size
* position
);
159 if (!vertical
&& StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
160 xoffset
+= aReflowState
.ComputedWidth() - size
;
163 // The bar position is *always* constrained.
165 // We want the bar to begin at the bottom.
166 yoffset
+= aReflowState
.ComputedHeight() - size
;
168 size
-= reflowState
.ComputedPhysicalMargin().TopBottom() +
169 reflowState
.ComputedPhysicalBorderPadding().TopBottom();
170 size
= std::max(size
, 0);
171 reflowState
.SetComputedHeight(size
);
173 size
-= reflowState
.ComputedPhysicalMargin().LeftRight() +
174 reflowState
.ComputedPhysicalBorderPadding().LeftRight();
175 size
= std::max(size
, 0);
176 reflowState
.SetComputedWidth(size
);
179 xoffset
+= reflowState
.ComputedPhysicalMargin().left
;
180 yoffset
+= reflowState
.ComputedPhysicalMargin().top
;
182 nsHTMLReflowMetrics
barDesiredSize(reflowState
.GetWritingMode());
183 ReflowChild(aBarFrame
, aPresContext
, barDesiredSize
, reflowState
, xoffset
,
184 yoffset
, 0, aStatus
);
185 FinishReflowChild(aBarFrame
, aPresContext
, &reflowState
, barDesiredSize
,
186 xoffset
, yoffset
, 0);
190 nsMeterFrame::AttributeChanged(int32_t aNameSpaceID
,
194 NS_ASSERTION(mBarDiv
, "Meter bar div must exist!");
196 if (aNameSpaceID
== kNameSpaceID_None
&&
197 (aAttribute
== nsGkAtoms::value
||
198 aAttribute
== nsGkAtoms::max
||
199 aAttribute
== nsGkAtoms::min
)) {
200 nsIFrame
* barFrame
= mBarDiv
->GetPrimaryFrame();
201 NS_ASSERTION(barFrame
, "The meter frame should have a child with a frame!");
202 PresContext()->PresShell()->FrameNeedsReflow(barFrame
,
203 nsIPresShell::eResize
,
208 return nsContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
213 nsMeterFrame::ComputeAutoSize(nsRenderingContext
*aRenderingContext
,
214 nsSize aCBSize
, nscoord aAvailableWidth
,
215 nsSize aMargin
, nsSize aBorder
,
216 nsSize aPadding
, bool aShrinkWrap
)
218 nsRefPtr
<nsFontMetrics
> fontMet
;
219 NS_ENSURE_SUCCESS(nsLayoutUtils::GetFontMetricsForFrame(this,
220 getter_AddRefs(fontMet
)),
224 autoSize
.height
= autoSize
.width
= fontMet
->Font().size
; // 1em
226 if (StyleDisplay()->mOrient
== NS_STYLE_ORIENT_VERTICAL
) {
227 autoSize
.height
*= 5; // 5em
229 autoSize
.width
*= 5; // 5em
236 nsMeterFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
238 nsRefPtr
<nsFontMetrics
> fontMet
;
240 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet
)), 0);
242 nscoord minWidth
= fontMet
->Font().size
; // 1em
244 if (StyleDisplay()->mOrient
== NS_STYLE_ORIENT_AUTO
||
245 StyleDisplay()->mOrient
== NS_STYLE_ORIENT_HORIZONTAL
) {
246 // The orientation is horizontal
247 minWidth
*= 5; // 5em
254 nsMeterFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
256 return GetMinWidth(aRenderingContext
);
260 nsMeterFrame::ShouldUseNativeStyle() const
262 // Use the native style if these conditions are satisfied:
263 // - both frames use the native appearance;
264 // - neither frame has author specified rules setting the border or the
266 return StyleDisplay()->mAppearance
== NS_THEME_METERBAR
&&
267 mBarDiv
->GetPrimaryFrame()->StyleDisplay()->mAppearance
== NS_THEME_METERBAR_CHUNK
&&
268 !PresContext()->HasAuthorSpecifiedRules(const_cast<nsMeterFrame
*>(this),
269 NS_AUTHOR_SPECIFIED_BORDER
| NS_AUTHOR_SPECIFIED_BACKGROUND
) &&
270 !PresContext()->HasAuthorSpecifiedRules(mBarDiv
->GetPrimaryFrame(),
271 NS_AUTHOR_SPECIFIED_BORDER
| NS_AUTHOR_SPECIFIED_BACKGROUND
);
275 nsMeterFrame::GetPseudoElement(nsCSSPseudoElements::Type aType
)
277 if (aType
== nsCSSPseudoElements::ePseudo_mozMeterBar
) {
281 return nsContainerFrame::GetPseudoElement(aType
);