|
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/. */ |
|
5 |
|
6 #include "nsMeterFrame.h" |
|
7 |
|
8 #include "nsIContent.h" |
|
9 #include "nsPresContext.h" |
|
10 #include "nsGkAtoms.h" |
|
11 #include "nsNameSpaceManager.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" |
|
25 #include <algorithm> |
|
26 |
|
27 using mozilla::dom::Element; |
|
28 using mozilla::dom::HTMLMeterElement; |
|
29 |
|
30 nsIFrame* |
|
31 NS_NewMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
32 { |
|
33 return new (aPresShell) nsMeterFrame(aContext); |
|
34 } |
|
35 |
|
36 NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame) |
|
37 |
|
38 nsMeterFrame::nsMeterFrame(nsStyleContext* aContext) |
|
39 : nsContainerFrame(aContext) |
|
40 , mBarDiv(nullptr) |
|
41 { |
|
42 } |
|
43 |
|
44 nsMeterFrame::~nsMeterFrame() |
|
45 { |
|
46 } |
|
47 |
|
48 void |
|
49 nsMeterFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
50 { |
|
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); |
|
57 } |
|
58 |
|
59 nsresult |
|
60 nsMeterFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) |
|
61 { |
|
62 // Get the NodeInfoManager and tag necessary to create the meter bar div. |
|
63 nsCOMPtr<nsIDocument> doc = mContent->GetDocument(); |
|
64 |
|
65 // Create the div. |
|
66 mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); |
|
67 |
|
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()); |
|
73 |
|
74 if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { |
|
75 return NS_ERROR_OUT_OF_MEMORY; |
|
76 } |
|
77 |
|
78 return NS_OK; |
|
79 } |
|
80 |
|
81 void |
|
82 nsMeterFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, |
|
83 uint32_t aFilter) |
|
84 { |
|
85 aElements.MaybeAppendElement(mBarDiv); |
|
86 } |
|
87 |
|
88 NS_QUERYFRAME_HEAD(nsMeterFrame) |
|
89 NS_QUERYFRAME_ENTRY(nsMeterFrame) |
|
90 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) |
|
91 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
|
92 |
|
93 |
|
94 nsresult nsMeterFrame::Reflow(nsPresContext* aPresContext, |
|
95 nsHTMLReflowMetrics& aDesiredSize, |
|
96 const nsHTMLReflowState& aReflowState, |
|
97 nsReflowStatus& aStatus) |
|
98 { |
|
99 DO_GLOBAL_REFLOW_COUNT("nsMeterFrame"); |
|
100 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
101 |
|
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."); |
|
106 |
|
107 if (mState & NS_FRAME_FIRST_REFLOW) { |
|
108 nsFormControlFrame::RegUnRegAccessKey(this, true); |
|
109 } |
|
110 |
|
111 nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); |
|
112 NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!"); |
|
113 |
|
114 ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus); |
|
115 |
|
116 aDesiredSize.Width() = aReflowState.ComputedWidth() + |
|
117 aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
118 aDesiredSize.Height() = aReflowState.ComputedHeight() + |
|
119 aReflowState.ComputedPhysicalBorderPadding().TopBottom(); |
|
120 |
|
121 aDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
122 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame); |
|
123 FinishAndStoreOverflow(&aDesiredSize); |
|
124 |
|
125 aStatus = NS_FRAME_COMPLETE; |
|
126 |
|
127 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
128 |
|
129 return NS_OK; |
|
130 } |
|
131 |
|
132 void |
|
133 nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, |
|
134 nsPresContext* aPresContext, |
|
135 const nsHTMLReflowState& aReflowState, |
|
136 nsReflowStatus& aStatus) |
|
137 { |
|
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; |
|
146 |
|
147 // NOTE: Introduce a new function getPosition in the content part ? |
|
148 HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(mContent); |
|
149 |
|
150 double max = meterElement->Max(); |
|
151 double min = meterElement->Min(); |
|
152 double value = meterElement->Value(); |
|
153 |
|
154 double position = max - min; |
|
155 position = position != 0 ? (value - min) / position : 1; |
|
156 |
|
157 size = NSToCoordRound(size * position); |
|
158 |
|
159 if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
|
160 xoffset += aReflowState.ComputedWidth() - size; |
|
161 } |
|
162 |
|
163 // The bar position is *always* constrained. |
|
164 if (vertical) { |
|
165 // We want the bar to begin at the bottom. |
|
166 yoffset += aReflowState.ComputedHeight() - size; |
|
167 |
|
168 size -= reflowState.ComputedPhysicalMargin().TopBottom() + |
|
169 reflowState.ComputedPhysicalBorderPadding().TopBottom(); |
|
170 size = std::max(size, 0); |
|
171 reflowState.SetComputedHeight(size); |
|
172 } else { |
|
173 size -= reflowState.ComputedPhysicalMargin().LeftRight() + |
|
174 reflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
175 size = std::max(size, 0); |
|
176 reflowState.SetComputedWidth(size); |
|
177 } |
|
178 |
|
179 xoffset += reflowState.ComputedPhysicalMargin().left; |
|
180 yoffset += reflowState.ComputedPhysicalMargin().top; |
|
181 |
|
182 nsHTMLReflowMetrics barDesiredSize(reflowState); |
|
183 ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, |
|
184 yoffset, 0, aStatus); |
|
185 FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, |
|
186 xoffset, yoffset, 0); |
|
187 } |
|
188 |
|
189 nsresult |
|
190 nsMeterFrame::AttributeChanged(int32_t aNameSpaceID, |
|
191 nsIAtom* aAttribute, |
|
192 int32_t aModType) |
|
193 { |
|
194 NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); |
|
195 |
|
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, |
|
204 NS_FRAME_IS_DIRTY); |
|
205 InvalidateFrame(); |
|
206 } |
|
207 |
|
208 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, |
|
209 aModType); |
|
210 } |
|
211 |
|
212 nsSize |
|
213 nsMeterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
|
214 nsSize aCBSize, nscoord aAvailableWidth, |
|
215 nsSize aMargin, nsSize aBorder, |
|
216 nsSize aPadding, bool aShrinkWrap) |
|
217 { |
|
218 nsRefPtr<nsFontMetrics> fontMet; |
|
219 NS_ENSURE_SUCCESS(nsLayoutUtils::GetFontMetricsForFrame(this, |
|
220 getter_AddRefs(fontMet)), |
|
221 nsSize(0, 0)); |
|
222 |
|
223 nsSize autoSize; |
|
224 autoSize.height = autoSize.width = fontMet->Font().size; // 1em |
|
225 |
|
226 if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) { |
|
227 autoSize.height *= 5; // 5em |
|
228 } else { |
|
229 autoSize.width *= 5; // 5em |
|
230 } |
|
231 |
|
232 return autoSize; |
|
233 } |
|
234 |
|
235 nscoord |
|
236 nsMeterFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
237 { |
|
238 nsRefPtr<nsFontMetrics> fontMet; |
|
239 NS_ENSURE_SUCCESS( |
|
240 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0); |
|
241 |
|
242 nscoord minWidth = fontMet->Font().size; // 1em |
|
243 |
|
244 if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_AUTO || |
|
245 StyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) { |
|
246 // The orientation is horizontal |
|
247 minWidth *= 5; // 5em |
|
248 } |
|
249 |
|
250 return minWidth; |
|
251 } |
|
252 |
|
253 nscoord |
|
254 nsMeterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
255 { |
|
256 return GetMinWidth(aRenderingContext); |
|
257 } |
|
258 |
|
259 bool |
|
260 nsMeterFrame::ShouldUseNativeStyle() const |
|
261 { |
|
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 |
|
265 // background. |
|
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); |
|
272 } |
|
273 |
|
274 Element* |
|
275 nsMeterFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) |
|
276 { |
|
277 if (aType == nsCSSPseudoElements::ePseudo_mozMeterBar) { |
|
278 return mBarDiv; |
|
279 } |
|
280 |
|
281 return nsContainerFrame::GetPseudoElement(aType); |
|
282 } |