Backout a74bd5095902, Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with...
[gecko.git] / layout / forms / nsMeterFrame.cpp
blob4403acddddefbc61296b6c2f7273872ed6e8b449
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"
25 #include <algorithm>
27 using mozilla::dom::Element;
28 using mozilla::dom::HTMLMeterElement;
30 nsIFrame*
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)
40 , mBarDiv(nullptr)
44 nsMeterFrame::~nsMeterFrame()
48 void
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);
59 nsresult
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();
65 // Create the div.
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;
78 return NS_OK;
81 void
82 nsMeterFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
83 uint32_t aFilter)
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);
129 return NS_OK;
132 void
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.
164 if (vertical) {
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);
172 } else {
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);
189 NS_IMETHODIMP
190 nsMeterFrame::AttributeChanged(int32_t aNameSpaceID,
191 nsIAtom* aAttribute,
192 int32_t aModType)
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,
204 NS_FRAME_IS_DIRTY);
205 InvalidateFrame();
208 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
209 aModType);
212 nsSize
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)),
221 nsSize(0, 0));
223 nsSize autoSize;
224 autoSize.height = autoSize.width = fontMet->Font().size; // 1em
226 if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) {
227 autoSize.height *= 5; // 5em
228 } else {
229 autoSize.width *= 5; // 5em
232 return autoSize;
235 nscoord
236 nsMeterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
238 nsRefPtr<nsFontMetrics> fontMet;
239 NS_ENSURE_SUCCESS(
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
250 return minWidth;
253 nscoord
254 nsMeterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
256 return GetMinWidth(aRenderingContext);
259 bool
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
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);
274 Element*
275 nsMeterFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
277 if (aType == nsCSSPseudoElements::ePseudo_mozMeterBar) {
278 return mBarDiv;
281 return nsContainerFrame::GetPseudoElement(aType);