Bug 1879774 [wpt PR 44524] - WebKit export: Implement field-sizing support for input...
[gecko.git] / layout / generic / ColumnSetWrapperFrame.cpp
blob7717f14df8ec3085353c67f06e0099ed2e72292e
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 https://mozilla.org/MPL/2.0/. */
7 #include "ColumnSetWrapperFrame.h"
9 #include "mozilla/ColumnUtils.h"
10 #include "mozilla/PresShell.h"
11 #include "nsContentUtils.h"
12 #include "nsIFrame.h"
13 #include "nsIFrameInlines.h"
15 using namespace mozilla;
17 nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell,
18 ComputedStyle* aStyle,
19 nsFrameState aStateFlags) {
20 ColumnSetWrapperFrame* frame = new (aPresShell)
21 ColumnSetWrapperFrame(aStyle, aPresShell->GetPresContext());
22 frame->AddStateBits(aStateFlags);
23 return frame;
26 NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame)
28 NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)
29 NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame)
30 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
32 ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle,
33 nsPresContext* aPresContext)
34 : nsBlockFrame(aStyle, aPresContext, kClassID) {}
36 void ColumnSetWrapperFrame::Init(nsIContent* aContent,
37 nsContainerFrame* aParent,
38 nsIFrame* aPrevInFlow) {
39 nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
41 // ColumnSetWrapperFrame doesn't need to call ResolveBidi().
42 RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
45 nsContainerFrame* ColumnSetWrapperFrame::GetContentInsertionFrame() {
46 nsIFrame* columnSet = PrincipalChildList().OnlyChild();
47 if (columnSet) {
48 // We have only one child, which means we don't have any column-span
49 // descendants. Thus we can safely return our only ColumnSet child's
50 // insertion frame as ours.
51 MOZ_ASSERT(columnSet->IsColumnSetFrame());
52 return columnSet->GetContentInsertionFrame();
55 // We have column-span descendants. Return ourselves as the insertion
56 // frame to let nsCSSFrameConstructor::WipeContainingBlock() figure out
57 // what to do.
58 return this;
61 void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(
62 nsTArray<OwnedAnonBox>& aResult) {
63 MOZ_ASSERT(!GetPrevContinuation(),
64 "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?");
66 // It's sufficient to append the first ColumnSet child, which is the first
67 // continuation of all the other ColumnSets.
69 // We don't need to append -moz-column-span-wrapper children because
70 // they're non-inheriting anon boxes, and they cannot have any directly
71 // owned anon boxes nor generate any native anonymous content themselves.
72 // Thus, no need to restyle them. AssertColumnSpanWrapperSubtreeIsSane()
73 // asserts all the conditions above which allow us to skip appending
74 // -moz-column-span-wrappers.
75 auto FindFirstChildInChildLists = [this]() -> nsIFrame* {
76 const ChildListID listIDs[] = {FrameChildListID::Principal,
77 FrameChildListID::Overflow};
78 for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) {
79 for (ChildListID id : listIDs) {
80 const nsFrameList& list = frag->GetChildList(id);
81 if (nsIFrame* firstChild = list.FirstChild()) {
82 return firstChild;
86 return nullptr;
89 nsIFrame* columnSet = FindFirstChildInChildLists();
90 MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(),
91 "The first child should always be ColumnSet!");
92 aResult.AppendElement(OwnedAnonBox(columnSet));
95 #ifdef DEBUG_FRAME_DUMP
96 nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const {
97 return MakeFrameName(u"ColumnSetWrapper"_ns, aResult);
99 #endif
101 // Disallow any append, insert, or remove operations after building the
102 // column hierarchy since any change to the column hierarchy in the column
103 // sub-tree need to be re-created.
104 void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID,
105 nsFrameList&& aFrameList) {
106 #ifdef DEBUG
107 MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!");
108 mFinishedBuildingColumns = true;
109 #endif
111 nsBlockFrame::AppendFrames(aListID, std::move(aFrameList));
113 #ifdef DEBUG
114 nsIFrame* firstColumnSet = PrincipalChildList().FirstChild();
115 for (nsIFrame* child : PrincipalChildList()) {
116 if (child->IsColumnSpan()) {
117 AssertColumnSpanWrapperSubtreeIsSane(child);
118 } else if (child != firstColumnSet) {
119 // All the other ColumnSets are the continuation of the first ColumnSet.
120 MOZ_ASSERT(child->IsColumnSetFrame() && child->GetPrevContinuation(),
121 "ColumnSet's prev-continuation is not set properly?");
124 #endif
127 void ColumnSetWrapperFrame::InsertFrames(
128 ChildListID aListID, nsIFrame* aPrevFrame,
129 const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
130 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
131 nsBlockFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
132 std::move(aFrameList));
135 void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext,
136 ChildListID aListID,
137 nsIFrame* aOldFrame) {
138 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
139 nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame);
142 void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
143 nsBlockFrame::MarkIntrinsicISizesDirty();
145 // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
146 // continuations. Clear the bit because we don't want to call ResolveBidi().
147 for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) {
148 f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
152 nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
153 nscoord iSize = 0;
154 DISPLAY_MIN_INLINE_SIZE(this, iSize);
156 if (Maybe<nscoord> containISize =
157 ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
158 // If we're size-contained in inline axis and contain-intrinsic-inline-size
159 // is not 'none', then use that size.
160 if (*containISize != NS_UNCONSTRAINEDSIZE) {
161 return *containISize;
164 // In the 'none' case, we determine our minimum intrinsic size purely from
165 // our column styling, as if we had no descendants. This should match what
166 // happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
167 // scenario.
168 const nsStyleColumn* colStyle = StyleColumn();
169 if (colStyle->mColumnWidth.IsLength()) {
170 // As available inline size reduces to zero, our number of columns reduces
171 // to one, so no column gaps contribute to our minimum intrinsic size.
172 // Also, column-width doesn't set a lower bound on our minimum intrinsic
173 // size, either. Just use 0 because we're size-contained.
174 iSize = 0;
175 } else {
176 MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
177 "column-count and column-width can't both be auto!");
178 // As available inline size reduces to zero, we still have mColumnCount
179 // columns, so compute our minimum intrinsic size based on N zero-width
180 // columns, with specified gap size between them.
181 const nscoord colGap =
182 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
183 iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, 0);
185 } else {
186 for (nsIFrame* f : PrincipalChildList()) {
187 iSize = std::max(iSize, f->GetMinISize(aRenderingContext));
191 return iSize;
194 nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
195 nscoord iSize = 0;
196 DISPLAY_PREF_INLINE_SIZE(this, iSize);
198 if (Maybe<nscoord> containISize =
199 ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
200 if (*containISize != NS_UNCONSTRAINEDSIZE) {
201 return *containISize;
204 const nsStyleColumn* colStyle = StyleColumn();
205 nscoord colISize;
206 if (colStyle->mColumnWidth.IsLength()) {
207 colISize =
208 ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
209 } else {
210 MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
211 "column-count and column-width can't both be auto!");
212 colISize = 0;
215 // If column-count is auto, assume one column.
216 const uint32_t numColumns =
217 colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
219 : colStyle->mColumnCount;
220 const nscoord colGap =
221 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
222 iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
223 } else {
224 for (nsIFrame* f : PrincipalChildList()) {
225 iSize = std::max(iSize, f->GetPrefISize(aRenderingContext));
229 return iSize;
232 template <typename Iterator>
233 Maybe<nscoord> ColumnSetWrapperFrame::GetBaselineBOffset(
234 Iterator aStart, Iterator aEnd, WritingMode aWM,
235 BaselineSharingGroup aBaselineGroup,
236 BaselineExportContext aExportContext) const {
237 // Either forward iterator + first baseline, or reverse iterator + last
238 // baseline
239 MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() &&
240 aBaselineGroup == BaselineSharingGroup::First) ||
241 (*aStart == PrincipalChildList().LastChild() &&
242 aBaselineGroup == BaselineSharingGroup::Last),
243 "Iterator direction must match baseline sharing group.");
244 if (StyleDisplay()->IsContainLayout()) {
245 return Nothing{};
248 // Start from start/end of principal child list, and use the first valid
249 // baseline.
250 for (auto itr = aStart; itr != aEnd; ++itr) {
251 const nsIFrame* kid = *itr;
252 auto kidBaseline =
253 kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext);
254 if (!kidBaseline) {
255 continue;
257 // Baseline is offset from the kid's rectangle, so find the offset to the
258 // kid's rectangle.
259 LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
260 kid->GetLogicalSize(aWM)};
261 if (aBaselineGroup == BaselineSharingGroup::First) {
262 *kidBaseline += kidRect.BStart(aWM);
263 } else {
264 *kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
266 return kidBaseline;
268 return Nothing{};
271 Maybe<nscoord> ColumnSetWrapperFrame::GetNaturalBaselineBOffset(
272 WritingMode aWM, BaselineSharingGroup aBaselineGroup,
273 BaselineExportContext aExportContext) const {
274 if (aBaselineGroup == BaselineSharingGroup::First) {
275 return GetBaselineBOffset(PrincipalChildList().cbegin(),
276 PrincipalChildList().cend(), aWM, aBaselineGroup,
277 aExportContext);
279 return GetBaselineBOffset(PrincipalChildList().crbegin(),
280 PrincipalChildList().crend(), aWM, aBaselineGroup,
281 aExportContext);
284 #ifdef DEBUG
286 /* static */
287 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
288 const nsIFrame* aFrame) {
289 MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
291 if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
292 ->Style()
293 ->IsAnonBox()) {
294 // aFrame's style frame has "column-span: all". Traverse no further.
295 return;
298 MOZ_ASSERT(
299 aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper,
300 "aFrame should be ::-moz-column-span-wrapper");
302 MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES),
303 "::-moz-column-span-wrapper anonymous blocks cannot own "
304 "other types of anonymous blocks!");
306 for (const nsIFrame* child : aFrame->PrincipalChildList()) {
307 AssertColumnSpanWrapperSubtreeIsSane(child);
311 #endif