1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Mounir Lamouri <mounir.lamouri@mozilla.com> (original author)
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsProgressFrame.h"
39 :
40 : #include "nsIDOMHTMLProgressElement.h"
41 : #include "nsIContent.h"
42 : #include "prtypes.h"
43 : #include "nsPresContext.h"
44 : #include "nsGkAtoms.h"
45 : #include "nsINameSpaceManager.h"
46 : #include "nsIDocument.h"
47 : #include "nsIPresShell.h"
48 : #include "nsNodeInfoManager.h"
49 : #include "nsINodeInfo.h"
50 : #include "nsContentCreatorFunctions.h"
51 : #include "nsContentUtils.h"
52 : #include "nsFormControlFrame.h"
53 : #include "nsFontMetrics.h"
54 : #include "mozilla/dom/Element.h"
55 :
56 :
57 : nsIFrame*
58 0 : NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
59 : {
60 0 : return new (aPresShell) nsProgressFrame(aContext);
61 : }
62 :
63 0 : NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame)
64 :
65 0 : nsProgressFrame::nsProgressFrame(nsStyleContext* aContext)
66 : : nsContainerFrame(aContext)
67 0 : , mBarDiv(nsnull)
68 : {
69 0 : }
70 :
71 0 : nsProgressFrame::~nsProgressFrame()
72 : {
73 0 : }
74 :
75 : void
76 0 : nsProgressFrame::DestroyFrom(nsIFrame* aDestructRoot)
77 : {
78 0 : NS_ASSERTION(!GetPrevContinuation(),
79 : "nsProgressFrame should not have continuations; if it does we "
80 : "need to call RegUnregAccessKey only for the first.");
81 0 : nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
82 0 : nsContentUtils::DestroyAnonymousContent(&mBarDiv);
83 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
84 0 : }
85 :
86 : nsresult
87 0 : nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
88 : {
89 : // Get the NodeInfoManager and tag necessary to create the progress bar div.
90 0 : nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
91 :
92 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
93 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
94 : kNameSpaceID_XHTML,
95 0 : nsIDOMNode::ELEMENT_NODE);
96 0 : NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
97 :
98 : // Create the div.
99 0 : nsresult rv = NS_NewHTMLElement(getter_AddRefs(mBarDiv), nodeInfo.forget(),
100 0 : mozilla::dom::NOT_FROM_PARSER);
101 0 : NS_ENSURE_SUCCESS(rv, rv);
102 :
103 : // Associate ::-moz-progress-bar pseudo-element to the anonymous child.
104 0 : nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozProgressBar;
105 : nsRefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()->
106 : ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
107 0 : GetStyleContext());
108 :
109 0 : if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) {
110 0 : return NS_ERROR_OUT_OF_MEMORY;
111 : }
112 :
113 0 : return NS_OK;
114 : }
115 :
116 : void
117 0 : nsProgressFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
118 : PRUint32 aFilter)
119 : {
120 0 : aElements.MaybeAppendElement(mBarDiv);
121 0 : }
122 :
123 0 : NS_QUERYFRAME_HEAD(nsProgressFrame)
124 0 : NS_QUERYFRAME_ENTRY(nsProgressFrame)
125 0 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
126 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
127 :
128 :
129 : NS_IMETHODIMP
130 0 : nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
131 : const nsRect& aDirtyRect,
132 : const nsDisplayListSet& aLists)
133 : {
134 0 : return BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
135 : }
136 :
137 0 : NS_IMETHODIMP nsProgressFrame::Reflow(nsPresContext* aPresContext,
138 : nsHTMLReflowMetrics& aDesiredSize,
139 : const nsHTMLReflowState& aReflowState,
140 : nsReflowStatus& aStatus)
141 : {
142 0 : DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
143 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
144 :
145 0 : NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
146 0 : NS_ASSERTION(!GetPrevContinuation(),
147 : "nsProgressFrame should not have continuations; if it does we "
148 : "need to call RegUnregAccessKey only for the first.");
149 :
150 0 : if (mState & NS_FRAME_FIRST_REFLOW) {
151 0 : nsFormControlFrame::RegUnRegAccessKey(this, true);
152 : }
153 :
154 0 : nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
155 0 : NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!");
156 :
157 0 : ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus);
158 :
159 0 : aDesiredSize.width = aReflowState.ComputedWidth() +
160 0 : aReflowState.mComputedBorderPadding.LeftRight();
161 0 : aDesiredSize.height = aReflowState.ComputedHeight() +
162 0 : aReflowState.mComputedBorderPadding.TopBottom();
163 : aDesiredSize.height = NS_CSS_MINMAX(aDesiredSize.height,
164 : aReflowState.mComputedMinHeight,
165 0 : aReflowState.mComputedMaxHeight);
166 :
167 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
168 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame);
169 0 : FinishAndStoreOverflow(&aDesiredSize);
170 :
171 0 : aStatus = NS_FRAME_COMPLETE;
172 :
173 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
174 :
175 0 : return NS_OK;
176 : }
177 :
178 : void
179 0 : nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame,
180 : nsPresContext* aPresContext,
181 : const nsHTMLReflowState& aReflowState,
182 : nsReflowStatus& aStatus)
183 : {
184 0 : bool vertical = GetStyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL;
185 : nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame,
186 : nsSize(aReflowState.ComputedWidth(),
187 0 : NS_UNCONSTRAINEDSIZE));
188 : nscoord size = vertical ? aReflowState.ComputedHeight()
189 0 : : aReflowState.ComputedWidth();
190 0 : nscoord xoffset = aReflowState.mComputedBorderPadding.left;
191 0 : nscoord yoffset = aReflowState.mComputedBorderPadding.top;
192 :
193 : double position;
194 : nsCOMPtr<nsIDOMHTMLProgressElement> progressElement =
195 0 : do_QueryInterface(mContent);
196 0 : progressElement->GetPosition(&position);
197 :
198 : // Force the bar's size to match the current progress.
199 : // When indeterminate, the progress' size will be 100%.
200 0 : if (position >= 0.0) {
201 0 : size *= position;
202 : }
203 :
204 0 : if (!vertical && GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
205 0 : xoffset += aReflowState.ComputedWidth() - size;
206 : }
207 :
208 : // The bar size is fixed in these cases:
209 : // - the progress position is determined: the bar size is fixed according
210 : // to it's value.
211 : // - the progress position is indeterminate and the bar appearance should be
212 : // shown as native: the bar size is forced to 100%.
213 : // Otherwise (when the progress is indeterminate and the bar appearance isn't
214 : // native), the bar size isn't fixed and can be set by the author.
215 0 : if (position != -1 || ShouldUseNativeStyle()) {
216 0 : if (vertical) {
217 : // We want the bar to begin at the bottom.
218 0 : yoffset += aReflowState.ComputedHeight() - size;
219 :
220 0 : size -= reflowState.mComputedMargin.TopBottom() +
221 0 : reflowState.mComputedBorderPadding.TopBottom();
222 0 : size = NS_MAX(size, 0);
223 0 : reflowState.SetComputedHeight(size);
224 : } else {
225 0 : size -= reflowState.mComputedMargin.LeftRight() +
226 0 : reflowState.mComputedBorderPadding.LeftRight();
227 0 : size = NS_MAX(size, 0);
228 0 : reflowState.SetComputedWidth(size);
229 : }
230 0 : } else if (vertical) {
231 : // For vertical progress bars, we need to position the bar specificly when
232 : // the width isn't constrained (position == -1 and !ShouldUseNativeStyle())
233 : // because aReflowState.ComputedHeight() - size == 0.
234 0 : yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight();
235 : }
236 :
237 0 : xoffset += reflowState.mComputedMargin.left;
238 0 : yoffset += reflowState.mComputedMargin.top;
239 :
240 0 : nsHTMLReflowMetrics barDesiredSize;
241 : ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset,
242 0 : yoffset, 0, aStatus);
243 : FinishReflowChild(aBarFrame, aPresContext, &reflowState, barDesiredSize,
244 0 : xoffset, yoffset, 0);
245 0 : }
246 :
247 : NS_IMETHODIMP
248 0 : nsProgressFrame::AttributeChanged(PRInt32 aNameSpaceID,
249 : nsIAtom* aAttribute,
250 : PRInt32 aModType)
251 : {
252 0 : NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
253 :
254 0 : if (aNameSpaceID == kNameSpaceID_None &&
255 : (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max)) {
256 0 : nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
257 0 : NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!");
258 0 : PresContext()->PresShell()->FrameNeedsReflow(barFrame, nsIPresShell::eResize,
259 0 : NS_FRAME_IS_DIRTY);
260 0 : Invalidate(GetVisualOverflowRectRelativeToSelf());
261 : }
262 :
263 0 : return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
264 : }
265 :
266 : nsSize
267 0 : nsProgressFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
268 : nsSize aCBSize, nscoord aAvailableWidth,
269 : nsSize aMargin, nsSize aBorder,
270 : nsSize aPadding, bool aShrinkWrap)
271 : {
272 : float inflation =
273 0 : nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow);
274 0 : nsRefPtr<nsFontMetrics> fontMet;
275 0 : NS_ENSURE_SUCCESS(nsLayoutUtils::GetFontMetricsForFrame(this,
276 : getter_AddRefs(fontMet),
277 : inflation),
278 : nsSize(0, 0));
279 :
280 0 : nsSize autoSize;
281 0 : autoSize.height = autoSize.width = fontMet->Font().size; // 1em
282 :
283 0 : if (GetStyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) {
284 0 : autoSize.height *= 10; // 10em
285 : } else {
286 0 : autoSize.width *= 10; // 10em
287 : }
288 :
289 0 : return autoSize;
290 : }
291 :
292 : nscoord
293 0 : nsProgressFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
294 : {
295 0 : nsRefPtr<nsFontMetrics> fontMet;
296 0 : NS_ENSURE_SUCCESS(
297 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0);
298 :
299 0 : nscoord minWidth = fontMet->Font().size; // 1em
300 :
301 0 : if (GetStyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) {
302 0 : minWidth *= 10; // 10em
303 : }
304 :
305 0 : return minWidth;
306 : }
307 :
308 : nscoord
309 0 : nsProgressFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
310 : {
311 0 : return GetMinWidth(aRenderingContext);
312 : }
313 :
314 : bool
315 0 : nsProgressFrame::ShouldUseNativeStyle() const
316 : {
317 : // Use the native style if these conditions are satisfied:
318 : // - both frames use the native appearance;
319 : // - neither frame has author specified rules setting the border or the
320 : // background.
321 0 : return GetStyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR &&
322 0 : mBarDiv->GetPrimaryFrame()->GetStyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK &&
323 0 : !PresContext()->HasAuthorSpecifiedRules(const_cast<nsProgressFrame*>(this),
324 0 : NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) &&
325 0 : !PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(),
326 0 : NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);
327 : }
328 :
|