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 "mozilla/PresShell.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/Element.h"
12 #include "mozilla/dom/HTMLProgressElement.h"
13 #include "nsIContent.h"
14 #include "nsLayoutUtils.h"
15 #include "nsPresContext.h"
16 #include "nsGkAtoms.h"
17 #include "nsNodeInfoManager.h"
18 #include "nsContentCreatorFunctions.h"
19 #include "nsFontMetrics.h"
22 using namespace mozilla
;
23 using namespace mozilla::dom
;
25 nsIFrame
* NS_NewProgressFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
26 return new (aPresShell
) nsProgressFrame(aStyle
, aPresShell
->GetPresContext());
29 NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame
)
31 nsProgressFrame::nsProgressFrame(ComputedStyle
* aStyle
,
32 nsPresContext
* aPresContext
)
33 : nsContainerFrame(aStyle
, aPresContext
, kClassID
), mBarDiv(nullptr) {}
35 nsProgressFrame::~nsProgressFrame() = default;
37 void nsProgressFrame::Destroy(DestroyContext
& aContext
) {
38 NS_ASSERTION(!GetPrevContinuation(),
39 "nsProgressFrame should not have continuations; if it does we "
40 "need to call RegUnregAccessKey only for the first.");
41 aContext
.AddAnonymousContent(mBarDiv
.forget());
42 nsContainerFrame::Destroy(aContext
);
45 nsresult
nsProgressFrame::CreateAnonymousContent(
46 nsTArray
<ContentInfo
>& aElements
) {
47 // Create the progress bar div.
48 nsCOMPtr
<Document
> doc
= mContent
->GetComposedDoc();
49 mBarDiv
= doc
->CreateHTMLElement(nsGkAtoms::div
);
51 // Associate ::-moz-progress-bar pseudo-element to the anonymous child.
52 if (StaticPrefs::layout_css_modern_range_pseudos_enabled()) {
53 // TODO(emilio): Create also a slider-track pseudo-element.
54 mBarDiv
->SetPseudoElementType(PseudoStyleType::sliderFill
);
56 mBarDiv
->SetPseudoElementType(PseudoStyleType::mozProgressBar
);
59 // XXX(Bug 1631371) Check if this should use a fallible operation as it
60 // pretended earlier, or change the return type to void.
61 aElements
.AppendElement(mBarDiv
);
66 void nsProgressFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
69 aElements
.AppendElement(mBarDiv
);
73 NS_QUERYFRAME_HEAD(nsProgressFrame
)
74 NS_QUERYFRAME_ENTRY(nsProgressFrame
)
75 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
76 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
78 void nsProgressFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
79 const nsDisplayListSet
& aLists
) {
80 BuildDisplayListForInline(aBuilder
, aLists
);
83 void nsProgressFrame::Reflow(nsPresContext
* aPresContext
,
84 ReflowOutput
& aDesiredSize
,
85 const ReflowInput
& aReflowInput
,
86 nsReflowStatus
& aStatus
) {
88 DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
89 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
90 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
92 NS_ASSERTION(mBarDiv
, "Progress bar div must exist!");
94 PrincipalChildList().GetLength() == 1 &&
95 PrincipalChildList().FirstChild() == mBarDiv
->GetPrimaryFrame(),
96 "unexpected child frames");
97 NS_ASSERTION(!GetPrevContinuation(),
98 "nsProgressFrame should not have continuations; if it does we "
99 "need to call RegUnregAccessKey only for the first.");
101 const auto wm
= aReflowInput
.GetWritingMode();
102 aDesiredSize
.SetSize(wm
, aReflowInput
.ComputedSizeWithBorderPadding(wm
));
103 aDesiredSize
.SetOverflowAreasToDesiredBounds();
105 for (auto childFrame
: PrincipalChildList()) {
106 ReflowChildFrame(childFrame
, aPresContext
, aReflowInput
, aStatus
);
107 ConsiderChildOverflow(aDesiredSize
.mOverflowAreas
, childFrame
);
110 FinishAndStoreOverflow(&aDesiredSize
);
112 aStatus
.Reset(); // This type of frame can't be split.
115 void nsProgressFrame::ReflowChildFrame(nsIFrame
* aChild
,
116 nsPresContext
* aPresContext
,
117 const ReflowInput
& aReflowInput
,
118 nsReflowStatus
& aStatus
) {
119 bool vertical
= ResolvedOrientationIsVertical();
120 WritingMode wm
= aChild
->GetWritingMode();
121 LogicalSize availSize
= aReflowInput
.ComputedSize(wm
);
122 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
123 ReflowInput
reflowInput(aPresContext
, aReflowInput
, aChild
, availSize
);
125 vertical
? aReflowInput
.ComputedHeight() : aReflowInput
.ComputedWidth();
126 nscoord xoffset
= aReflowInput
.ComputedPhysicalBorderPadding().left
;
127 nscoord yoffset
= aReflowInput
.ComputedPhysicalBorderPadding().top
;
129 double position
= static_cast<HTMLProgressElement
*>(GetContent())->Position();
131 // Force the bar's size to match the current progress.
132 // When indeterminate, the progress' size will be 100%.
133 if (position
>= 0.0) {
137 if (!vertical
&& wm
.IsPhysicalRTL()) {
138 xoffset
+= aReflowInput
.ComputedWidth() - size
;
141 // The bar size is fixed in these cases:
142 // - the progress position is determined: the bar size is fixed according
144 // - the progress position is indeterminate and the bar appearance should be
145 // shown as native: the bar size is forced to 100%.
146 // Otherwise (when the progress is indeterminate and the bar appearance isn't
147 // native), the bar size isn't fixed and can be set by the author.
148 if (position
!= -1 || ShouldUseNativeStyle()) {
150 // We want the bar to begin at the bottom.
151 yoffset
+= aReflowInput
.ComputedHeight() - size
;
153 size
-= reflowInput
.ComputedPhysicalMargin().TopBottom() +
154 reflowInput
.ComputedPhysicalBorderPadding().TopBottom();
155 size
= std::max(size
, 0);
156 reflowInput
.SetComputedHeight(size
);
158 size
-= reflowInput
.ComputedPhysicalMargin().LeftRight() +
159 reflowInput
.ComputedPhysicalBorderPadding().LeftRight();
160 size
= std::max(size
, 0);
161 reflowInput
.SetComputedWidth(size
);
163 } else if (vertical
) {
164 // For vertical progress bars, we need to position the bar specificly when
165 // the width isn't constrained (position == -1 and !ShouldUseNativeStyle())
166 // because aReflowInput.ComputedHeight() - size == 0.
167 yoffset
+= aReflowInput
.ComputedHeight() - reflowInput
.ComputedHeight();
170 xoffset
+= reflowInput
.ComputedPhysicalMargin().left
;
171 yoffset
+= reflowInput
.ComputedPhysicalMargin().top
;
173 ReflowOutput
barDesiredSize(aReflowInput
);
174 ReflowChild(aChild
, aPresContext
, barDesiredSize
, reflowInput
, xoffset
,
175 yoffset
, ReflowChildFlags::Default
, aStatus
);
176 FinishReflowChild(aChild
, aPresContext
, barDesiredSize
, &reflowInput
, xoffset
,
177 yoffset
, ReflowChildFlags::Default
);
180 nsresult
nsProgressFrame::AttributeChanged(int32_t aNameSpaceID
,
183 NS_ASSERTION(mBarDiv
, "Progress bar div must exist!");
185 if (aNameSpaceID
== kNameSpaceID_None
&&
186 (aAttribute
== nsGkAtoms::value
|| aAttribute
== nsGkAtoms::max
)) {
187 auto presShell
= PresShell();
188 for (auto childFrame
: PrincipalChildList()) {
189 presShell
->FrameNeedsReflow(childFrame
, IntrinsicDirty::None
,
195 return nsContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
198 LogicalSize
nsProgressFrame::ComputeAutoSize(
199 gfxContext
* aRenderingContext
, WritingMode aWM
, const LogicalSize
& aCBSize
,
200 nscoord aAvailableISize
, const LogicalSize
& aMargin
,
201 const LogicalSize
& aBorderPadding
, const StyleSizeOverrides
& aSizeOverrides
,
202 ComputeSizeFlags aFlags
) {
203 const WritingMode wm
= GetWritingMode();
204 LogicalSize
autoSize(wm
);
205 autoSize
.BSize(wm
) = autoSize
.ISize(wm
) =
207 ->mFont
.size
.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this))
208 .ToAppUnits(); // 1em
210 if (ResolvedOrientationIsVertical() == wm
.IsVertical()) {
211 autoSize
.ISize(wm
) *= 10; // 10em
213 autoSize
.BSize(wm
) *= 10; // 10em
216 return autoSize
.ConvertTo(aWM
, wm
);
219 nscoord
nsProgressFrame::GetMinISize(gfxContext
* aRenderingContext
) {
220 RefPtr
<nsFontMetrics
> fontMet
=
221 nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f
);
223 nscoord minISize
= fontMet
->Font().size
.ToAppUnits(); // 1em
225 if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) {
226 // The orientation is inline
227 minISize
*= 10; // 10em
233 nscoord
nsProgressFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
234 return GetMinISize(aRenderingContext
);
237 bool nsProgressFrame::ShouldUseNativeStyle() const {
238 nsIFrame
* barFrame
= PrincipalChildList().FirstChild();
240 // Use the native style if these conditions are satisfied:
241 // - both frames use the native appearance;
242 // - neither frame has author specified rules setting the border or the
244 return StyleDisplay()->EffectiveAppearance() ==
245 StyleAppearance::ProgressBar
&&
246 !Style()->HasAuthorSpecifiedBorderOrBackground() && barFrame
&&
247 barFrame
->StyleDisplay()->EffectiveAppearance() ==
248 StyleAppearance::Progresschunk
&&
249 !barFrame
->Style()->HasAuthorSpecifiedBorderOrBackground();