1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsProgressFrame.h"
9 #include "nsIContent.h"
10 #include "nsPresContext.h"
11 #include "nsGkAtoms.h"
12 #include "nsNameSpaceManager.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsIPresShell.h"
15 #include "nsNodeInfoManager.h"
16 #include "nsContentCreatorFunctions.h"
17 #include "nsCheckboxRadioFrame.h"
18 #include "nsFontMetrics.h"
19 #include "mozilla/dom/Element.h"
20 #include "mozilla/dom/HTMLProgressElement.h"
21 #include "nsCSSPseudoElements.h"
22 #include "nsStyleConsts.h"
25 using namespace mozilla
;
26 using namespace mozilla::dom
;
28 nsIFrame
* NS_NewProgressFrame(nsIPresShell
* aPresShell
, ComputedStyle
* aStyle
) {
29 return new (aPresShell
) nsProgressFrame(aStyle
);
32 NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame
)
34 nsProgressFrame::nsProgressFrame(ComputedStyle
* aStyle
)
35 : nsContainerFrame(aStyle
, kClassID
), mBarDiv(nullptr) {}
37 nsProgressFrame::~nsProgressFrame() {}
39 void nsProgressFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
40 PostDestroyData
& aPostDestroyData
) {
41 NS_ASSERTION(!GetPrevContinuation(),
42 "nsProgressFrame should not have continuations; if it does we "
43 "need to call RegUnregAccessKey only for the first.");
44 nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame
*>(this), false);
45 aPostDestroyData
.AddAnonymousContent(mBarDiv
.forget());
46 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
49 nsresult
nsProgressFrame::CreateAnonymousContent(
50 nsTArray
<ContentInfo
>& aElements
) {
51 // Create the progress bar div.
52 nsCOMPtr
<Document
> doc
= mContent
->GetComposedDoc();
53 mBarDiv
= doc
->CreateHTMLElement(nsGkAtoms::div
);
55 // Associate ::-moz-progress-bar pseudo-element to the anonymous child.
56 mBarDiv
->SetPseudoElementType(CSSPseudoElementType::mozProgressBar
);
58 if (!aElements
.AppendElement(mBarDiv
)) {
59 return NS_ERROR_OUT_OF_MEMORY
;
65 void nsProgressFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
68 aElements
.AppendElement(mBarDiv
);
72 NS_QUERYFRAME_HEAD(nsProgressFrame
)
73 NS_QUERYFRAME_ENTRY(nsProgressFrame
)
74 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
75 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
77 void nsProgressFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
78 const nsDisplayListSet
& aLists
) {
79 BuildDisplayListForInline(aBuilder
, aLists
);
82 void nsProgressFrame::Reflow(nsPresContext
* aPresContext
,
83 ReflowOutput
& aDesiredSize
,
84 const ReflowInput
& aReflowInput
,
85 nsReflowStatus
& aStatus
) {
87 DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
88 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
89 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
91 NS_ASSERTION(mBarDiv
, "Progress bar div must exist!");
93 PrincipalChildList().GetLength() == 1 &&
94 PrincipalChildList().FirstChild() == mBarDiv
->GetPrimaryFrame(),
95 "unexpected child frames");
96 NS_ASSERTION(!GetPrevContinuation(),
97 "nsProgressFrame should not have continuations; if it does we "
98 "need to call RegUnregAccessKey only for the first.");
100 if (mState
& NS_FRAME_FIRST_REFLOW
) {
101 nsCheckboxRadioFrame::RegUnRegAccessKey(this, true);
104 aDesiredSize
.SetSize(aReflowInput
.GetWritingMode(),
105 aReflowInput
.ComputedSizeWithBorderPadding());
106 aDesiredSize
.SetOverflowAreasToDesiredBounds();
108 for (auto childFrame
: PrincipalChildList()) {
109 ReflowChildFrame(childFrame
, aPresContext
, aReflowInput
, aStatus
);
110 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, childFrame
);
113 FinishAndStoreOverflow(&aDesiredSize
);
115 aStatus
.Reset(); // This type of frame can't be split.
117 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aDesiredSize
);
120 void nsProgressFrame::ReflowChildFrame(nsIFrame
* aChild
,
121 nsPresContext
* aPresContext
,
122 const ReflowInput
& aReflowInput
,
123 nsReflowStatus
& aStatus
) {
124 bool vertical
= ResolvedOrientationIsVertical();
125 WritingMode wm
= aChild
->GetWritingMode();
126 LogicalSize availSize
= aReflowInput
.ComputedSize(wm
);
127 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
128 ReflowInput
reflowInput(aPresContext
, aReflowInput
, aChild
, availSize
);
130 vertical
? aReflowInput
.ComputedHeight() : aReflowInput
.ComputedWidth();
131 nscoord xoffset
= aReflowInput
.ComputedPhysicalBorderPadding().left
;
132 nscoord yoffset
= aReflowInput
.ComputedPhysicalBorderPadding().top
;
134 double position
= static_cast<HTMLProgressElement
*>(GetContent())->Position();
136 // Force the bar's size to match the current progress.
137 // When indeterminate, the progress' size will be 100%.
138 if (position
>= 0.0) {
142 if (!vertical
&& (wm
.IsVertical() ? wm
.IsVerticalRL() : !wm
.IsBidiLTR())) {
143 xoffset
+= aReflowInput
.ComputedWidth() - size
;
146 // The bar size is fixed in these cases:
147 // - the progress position is determined: the bar size is fixed according
149 // - the progress position is indeterminate and the bar appearance should be
150 // shown as native: the bar size is forced to 100%.
151 // Otherwise (when the progress is indeterminate and the bar appearance isn't
152 // native), the bar size isn't fixed and can be set by the author.
153 if (position
!= -1 || ShouldUseNativeStyle()) {
155 // We want the bar to begin at the bottom.
156 yoffset
+= aReflowInput
.ComputedHeight() - size
;
158 size
-= reflowInput
.ComputedPhysicalMargin().TopBottom() +
159 reflowInput
.ComputedPhysicalBorderPadding().TopBottom();
160 size
= std::max(size
, 0);
161 reflowInput
.SetComputedHeight(size
);
163 size
-= reflowInput
.ComputedPhysicalMargin().LeftRight() +
164 reflowInput
.ComputedPhysicalBorderPadding().LeftRight();
165 size
= std::max(size
, 0);
166 reflowInput
.SetComputedWidth(size
);
168 } else if (vertical
) {
169 // For vertical progress bars, we need to position the bar specificly when
170 // the width isn't constrained (position == -1 and !ShouldUseNativeStyle())
171 // because aReflowInput.ComputedHeight() - size == 0.
172 yoffset
+= aReflowInput
.ComputedHeight() - reflowInput
.ComputedHeight();
175 xoffset
+= reflowInput
.ComputedPhysicalMargin().left
;
176 yoffset
+= reflowInput
.ComputedPhysicalMargin().top
;
178 ReflowOutput
barDesiredSize(aReflowInput
);
179 ReflowChild(aChild
, aPresContext
, barDesiredSize
, reflowInput
, xoffset
,
180 yoffset
, 0, aStatus
);
181 FinishReflowChild(aChild
, aPresContext
, barDesiredSize
, &reflowInput
, xoffset
,
185 nsresult
nsProgressFrame::AttributeChanged(int32_t aNameSpaceID
,
188 NS_ASSERTION(mBarDiv
, "Progress bar div must exist!");
190 if (aNameSpaceID
== kNameSpaceID_None
&&
191 (aAttribute
== nsGkAtoms::value
|| aAttribute
== nsGkAtoms::max
)) {
192 auto shell
= PresShell();
193 for (auto childFrame
: PrincipalChildList()) {
194 shell
->FrameNeedsReflow(childFrame
, nsIPresShell::eResize
,
200 return nsContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
203 LogicalSize
nsProgressFrame::ComputeAutoSize(
204 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
205 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
206 const LogicalSize
& aBorder
, const LogicalSize
& aPadding
,
207 ComputeSizeFlags aFlags
) {
208 const WritingMode wm
= GetWritingMode();
209 LogicalSize
autoSize(wm
);
210 autoSize
.BSize(wm
) = autoSize
.ISize(wm
) =
211 NSToCoordRound(StyleFont()->mFont
.size
*
212 nsLayoutUtils::FontSizeInflationFor(this)); // 1em
214 if (ResolvedOrientationIsVertical() == wm
.IsVertical()) {
215 autoSize
.ISize(wm
) *= 10; // 10em
217 autoSize
.BSize(wm
) *= 10; // 10em
220 return autoSize
.ConvertTo(aWM
, wm
);
223 nscoord
nsProgressFrame::GetMinISize(gfxContext
* aRenderingContext
) {
224 RefPtr
<nsFontMetrics
> fontMet
=
225 nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f
);
227 nscoord minISize
= fontMet
->Font().size
; // 1em
229 if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) {
230 // The orientation is inline
231 minISize
*= 10; // 10em
237 nscoord
nsProgressFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
238 return GetMinISize(aRenderingContext
);
241 bool nsProgressFrame::ShouldUseNativeStyle() const {
242 nsIFrame
* barFrame
= PrincipalChildList().FirstChild();
244 // Use the native style if these conditions are satisfied:
245 // - both frames use the native appearance;
246 // - neither frame has author specified rules setting the border or the
248 return StyleDisplay()->mAppearance
== StyleAppearance::ProgressBar
&&
249 !PresContext()->HasAuthorSpecifiedRules(
251 NS_AUTHOR_SPECIFIED_BORDER
| NS_AUTHOR_SPECIFIED_BACKGROUND
) &&
253 barFrame
->StyleDisplay()->mAppearance
==
254 StyleAppearance::Progresschunk
&&
255 !PresContext()->HasAuthorSpecifiedRules(
257 NS_AUTHOR_SPECIFIED_BORDER
| NS_AUTHOR_SPECIFIED_BACKGROUND
);