layout/forms/nsMeterFrame.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:390788237b95
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 }

mercurial