Bug 1516621 [wpt PR 14683] - Update wpt metadata, a=testonly
[gecko.git] / layout / forms / nsProgressFrame.cpp
blobc2058a0b1f12be045007b7422b0885bb29dd7d91
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"
23 #include <algorithm>
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;
62 return NS_OK;
65 void nsProgressFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
66 uint32_t aFilter) {
67 if (mBarDiv) {
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) {
86 MarkInReflow();
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!");
92 NS_ASSERTION(
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);
129 nscoord size =
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) {
139 size *= position;
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
148 // to it's value.
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()) {
154 if (vertical) {
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);
162 } else {
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,
182 yoffset, 0);
185 nsresult nsProgressFrame::AttributeChanged(int32_t aNameSpaceID,
186 nsAtom* aAttribute,
187 int32_t aModType) {
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,
195 NS_FRAME_IS_DIRTY);
197 InvalidateFrame();
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
216 } else {
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
234 return minISize;
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
247 // background.
248 return StyleDisplay()->mAppearance == StyleAppearance::ProgressBar &&
249 !PresContext()->HasAuthorSpecifiedRules(
250 this,
251 NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) &&
252 barFrame &&
253 barFrame->StyleDisplay()->mAppearance ==
254 StyleAppearance::Progresschunk &&
255 !PresContext()->HasAuthorSpecifiedRules(
256 barFrame,
257 NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);