Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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 "nsProgressFrame.h"
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/HTMLProgressElement.h"
22 #include "nsContentList.h"
23 #include "nsStyleSet.h"
24 #include "nsThemeConstants.h"
25 #include <algorithm>
27 using namespace mozilla::dom;
29 nsIFrame*
30 NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
31 {
32 return new (aPresShell) nsProgressFrame(aContext);
33 }
35 NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame)
37 nsProgressFrame::nsProgressFrame(nsStyleContext* aContext)
38 : nsContainerFrame(aContext)
39 , mBarDiv(nullptr)
40 {
41 }
43 nsProgressFrame::~nsProgressFrame()
44 {
45 }
47 void
48 nsProgressFrame::DestroyFrom(nsIFrame* aDestructRoot)
49 {
50 NS_ASSERTION(!GetPrevContinuation(),
51 "nsProgressFrame should not have continuations; if it does we "
52 "need to call RegUnregAccessKey only for the first.");
53 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
54 nsContentUtils::DestroyAnonymousContent(&mBarDiv);
55 nsContainerFrame::DestroyFrom(aDestructRoot);
56 }
58 nsresult
59 nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
60 {
61 // Create the progress bar div.
62 nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
63 mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div);
65 // Associate ::-moz-progress-bar pseudo-element to the anonymous child.
66 nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozProgressBar;
67 nsRefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()->
68 ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
69 StyleContext(), mBarDiv->AsElement());
71 if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) {
72 return NS_ERROR_OUT_OF_MEMORY;
73 }
75 return NS_OK;
76 }
78 void
79 nsProgressFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
80 uint32_t aFilter)
81 {
82 aElements.MaybeAppendElement(mBarDiv);
83 }
85 NS_QUERYFRAME_HEAD(nsProgressFrame)
86 NS_QUERYFRAME_ENTRY(nsProgressFrame)
87 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
88 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
91 void
92 nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
93 const nsRect& aDirtyRect,
94 const nsDisplayListSet& aLists)
95 {
96 BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
97 }
99 nsresult nsProgressFrame::Reflow(nsPresContext* aPresContext,
100 nsHTMLReflowMetrics& aDesiredSize,
101 const nsHTMLReflowState& aReflowState,
102 nsReflowStatus& aStatus)
103 {
104 DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
105 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
107 NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
108 NS_ASSERTION(!GetPrevContinuation(),
109 "nsProgressFrame should not have continuations; if it does we "
110 "need to call RegUnregAccessKey only for the first.");
112 if (mState & NS_FRAME_FIRST_REFLOW) {
113 nsFormControlFrame::RegUnRegAccessKey(this, true);
114 }
116 nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
117 NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!");
119 ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus);
121 aDesiredSize.Width() = aReflowState.ComputedWidth() +
122 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
123 aDesiredSize.Height() = aReflowState.ComputedHeight() +
124 aReflowState.ComputedPhysicalBorderPadding().TopBottom();
126 aDesiredSize.SetOverflowAreasToDesiredBounds();
127 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame);
128 FinishAndStoreOverflow(&aDesiredSize);
130 aStatus = NS_FRAME_COMPLETE;
132 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
134 return NS_OK;
135 }
137 void
138 nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame,
139 nsPresContext* aPresContext,
140 const nsHTMLReflowState& aReflowState,
141 nsReflowStatus& aStatus)
142 {
143 bool vertical = StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL;
144 nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame,
145 nsSize(aReflowState.ComputedWidth(),
146 NS_UNCONSTRAINEDSIZE));
147 nscoord size = vertical ? aReflowState.ComputedHeight()
148 : aReflowState.ComputedWidth();
149 nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left;
150 nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top;
152 double position = static_cast<HTMLProgressElement*>(mContent)->Position();
154 // Force the bar's size to match the current progress.
155 // When indeterminate, the progress' size will be 100%.
156 if (position >= 0.0) {
157 size *= position;
158 }
160 if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
161 xoffset += aReflowState.ComputedWidth() - size;
162 }
164 // The bar size is fixed in these cases:
165 // - the progress position is determined: the bar size is fixed according
166 // to it's value.
167 // - the progress position is indeterminate and the bar appearance should be
168 // shown as native: the bar size is forced to 100%.
169 // Otherwise (when the progress is indeterminate and the bar appearance isn't
170 // native), the bar size isn't fixed and can be set by the author.
171 if (position != -1 || ShouldUseNativeStyle()) {
172 if (vertical) {
173 // We want the bar to begin at the bottom.
174 yoffset += aReflowState.ComputedHeight() - size;
176 size -= reflowState.ComputedPhysicalMargin().TopBottom() +
177 reflowState.ComputedPhysicalBorderPadding().TopBottom();
178 size = std::max(size, 0);
179 reflowState.SetComputedHeight(size);
180 } else {
181 size -= reflowState.ComputedPhysicalMargin().LeftRight() +
182 reflowState.ComputedPhysicalBorderPadding().LeftRight();
183 size = std::max(size, 0);
184 reflowState.SetComputedWidth(size);
185 }
186 } else if (vertical) {
187 // For vertical progress bars, we need to position the bar specificly when
188 // the width isn't constrained (position == -1 and !ShouldUseNativeStyle())
189 // because aReflowState.ComputedHeight() - size == 0.
190 yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight();
191 }
193 xoffset += reflowState.ComputedPhysicalMargin().left;
194 yoffset += reflowState.ComputedPhysicalMargin().top;
196 nsHTMLReflowMetrics barDesiredSize(aReflowState);
197 ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset,
198 yoffset, 0, aStatus);
199 FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState,
200 xoffset, yoffset, 0);
201 }
203 nsresult
204 nsProgressFrame::AttributeChanged(int32_t aNameSpaceID,
205 nsIAtom* aAttribute,
206 int32_t aModType)
207 {
208 NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
210 if (aNameSpaceID == kNameSpaceID_None &&
211 (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max)) {
212 nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
213 NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!");
214 PresContext()->PresShell()->FrameNeedsReflow(barFrame, nsIPresShell::eResize,
215 NS_FRAME_IS_DIRTY);
216 InvalidateFrame();
217 }
219 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
220 }
222 nsSize
223 nsProgressFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
224 nsSize aCBSize, nscoord aAvailableWidth,
225 nsSize aMargin, nsSize aBorder,
226 nsSize aPadding, bool aShrinkWrap)
227 {
228 nsSize autoSize;
229 autoSize.height = autoSize.width =
230 NSToCoordRound(StyleFont()->mFont.size *
231 nsLayoutUtils::FontSizeInflationFor(this)); // 1em
233 if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) {
234 autoSize.height *= 10; // 10em
235 } else {
236 autoSize.width *= 10; // 10em
237 }
239 return autoSize;
240 }
242 nscoord
243 nsProgressFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
244 {
245 nsRefPtr<nsFontMetrics> fontMet;
246 NS_ENSURE_SUCCESS(
247 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0);
249 nscoord minWidth = fontMet->Font().size; // 1em
251 if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_AUTO ||
252 StyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) {
253 // The orientation is horizontal
254 minWidth *= 10; // 10em
255 }
257 return minWidth;
258 }
260 nscoord
261 nsProgressFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
262 {
263 return GetMinWidth(aRenderingContext);
264 }
266 bool
267 nsProgressFrame::ShouldUseNativeStyle() const
268 {
269 // Use the native style if these conditions are satisfied:
270 // - both frames use the native appearance;
271 // - neither frame has author specified rules setting the border or the
272 // background.
273 return StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR &&
274 mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK &&
275 !PresContext()->HasAuthorSpecifiedRules(const_cast<nsProgressFrame*>(this),
276 NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) &&
277 !PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(),
278 NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);
279 }
281 Element*
282 nsProgressFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
283 {
284 if (aType == nsCSSPseudoElements::ePseudo_mozProgressBar) {
285 return mBarDiv;
286 }
288 return nsContainerFrame::GetPseudoElement(aType);
289 }