Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / generic / ColumnSetWrapperFrame.cpp
blob0f142ef4f31b460a7b203ca0d13c178a715849d6
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());
23 // CSS Multi-column level 1 section 2: A multi-column container
24 // establishes a new block formatting context, as per CSS 2.1 section
25 // 9.4.1.
26 frame->AddStateBits(aStateFlags | NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
27 return frame;
30 NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame)
32 NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)
33 NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame)
34 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
36 ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle,
37 nsPresContext* aPresContext)
38 : nsBlockFrame(aStyle, aPresContext, kClassID) {}
40 void ColumnSetWrapperFrame::Init(nsIContent* aContent,
41 nsContainerFrame* aParent,
42 nsIFrame* aPrevInFlow) {
43 nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
45 // ColumnSetWrapperFrame doesn't need to call ResolveBidi().
46 RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
49 nsContainerFrame* ColumnSetWrapperFrame::GetContentInsertionFrame() {
50 nsIFrame* columnSet = PrincipalChildList().OnlyChild();
51 if (columnSet) {
52 // We have only one child, which means we don't have any column-span
53 // descendants. Thus we can safely return our only ColumnSet child's
54 // insertion frame as ours.
55 MOZ_ASSERT(columnSet->IsColumnSetFrame());
56 return columnSet->GetContentInsertionFrame();
59 // We have column-span descendants. Return ourselves as the insertion
60 // frame to let nsCSSFrameConstructor::WipeContainingBlock() figure out
61 // what to do.
62 return this;
65 void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(
66 nsTArray<OwnedAnonBox>& aResult) {
67 MOZ_ASSERT(!GetPrevContinuation(),
68 "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?");
70 // It's sufficient to append the first ColumnSet child, which is the first
71 // continuation of all the other ColumnSets.
73 // We don't need to append -moz-column-span-wrapper children because
74 // they're non-inheriting anon boxes, and they cannot have any directly
75 // owned anon boxes nor generate any native anonymous content themselves.
76 // Thus, no need to restyle them. AssertColumnSpanWrapperSubtreeIsSane()
77 // asserts all the conditions above which allow us to skip appending
78 // -moz-column-span-wrappers.
79 auto FindFirstChildInChildLists = [this]() -> nsIFrame* {
80 const ChildListID listIDs[] = {FrameChildListID::Principal,
81 FrameChildListID::Overflow};
82 for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) {
83 for (ChildListID id : listIDs) {
84 const nsFrameList& list = frag->GetChildList(id);
85 if (nsIFrame* firstChild = list.FirstChild()) {
86 return firstChild;
90 return nullptr;
93 nsIFrame* columnSet = FindFirstChildInChildLists();
94 MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(),
95 "The first child should always be ColumnSet!");
96 aResult.AppendElement(OwnedAnonBox(columnSet));
99 #ifdef DEBUG_FRAME_DUMP
100 nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const {
101 return MakeFrameName(u"ColumnSetWrapper"_ns, aResult);
103 #endif
105 // Disallow any append, insert, or remove operations after building the
106 // column hierarchy since any change to the column hierarchy in the column
107 // sub-tree need to be re-created.
108 void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID,
109 nsFrameList&& aFrameList) {
110 #ifdef DEBUG
111 MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!");
112 mFinishedBuildingColumns = true;
113 #endif
115 nsBlockFrame::AppendFrames(aListID, std::move(aFrameList));
117 #ifdef DEBUG
118 nsIFrame* firstColumnSet = PrincipalChildList().FirstChild();
119 for (nsIFrame* child : PrincipalChildList()) {
120 if (child->IsColumnSpan()) {
121 AssertColumnSpanWrapperSubtreeIsSane(child);
122 } else if (child != firstColumnSet) {
123 // All the other ColumnSets are the continuation of the first ColumnSet.
124 MOZ_ASSERT(child->IsColumnSetFrame() && child->GetPrevContinuation(),
125 "ColumnSet's prev-continuation is not set properly?");
128 #endif
131 void ColumnSetWrapperFrame::InsertFrames(
132 ChildListID aListID, nsIFrame* aPrevFrame,
133 const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
134 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
135 nsBlockFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
136 std::move(aFrameList));
139 void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext,
140 ChildListID aListID,
141 nsIFrame* aOldFrame) {
142 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
143 nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame);
146 void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
147 nsBlockFrame::MarkIntrinsicISizesDirty();
149 // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
150 // continuations. Clear the bit because we don't want to call ResolveBidi().
151 for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) {
152 f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
156 nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
157 nscoord iSize = 0;
158 DISPLAY_MIN_INLINE_SIZE(this, iSize);
160 if (Maybe<nscoord> containISize =
161 ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
162 // If we're size-contained in inline axis and contain-intrinsic-inline-size
163 // is not 'none', then use that size.
164 if (*containISize != NS_UNCONSTRAINEDSIZE) {
165 return *containISize;
168 // In the 'none' case, we determine our minimum intrinsic size purely from
169 // our column styling, as if we had no descendants. This should match what
170 // happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
171 // scenario.
172 const nsStyleColumn* colStyle = StyleColumn();
173 if (colStyle->mColumnWidth.IsLength()) {
174 // As available inline size reduces to zero, our number of columns reduces
175 // to one, so no column gaps contribute to our minimum intrinsic size.
176 // Also, column-width doesn't set a lower bound on our minimum intrinsic
177 // size, either. Just use 0 because we're size-contained.
178 iSize = 0;
179 } else {
180 MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
181 "column-count and column-width can't both be auto!");
182 // As available inline size reduces to zero, we still have mColumnCount
183 // columns, so compute our minimum intrinsic size based on N zero-width
184 // columns, with specified gap size between them.
185 const nscoord colGap =
186 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
187 iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, 0);
189 } else {
190 for (nsIFrame* f : PrincipalChildList()) {
191 iSize = std::max(iSize, f->GetMinISize(aRenderingContext));
195 return iSize;
198 nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
199 nscoord iSize = 0;
200 DISPLAY_PREF_INLINE_SIZE(this, iSize);
202 if (Maybe<nscoord> containISize =
203 ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) {
204 if (*containISize != NS_UNCONSTRAINEDSIZE) {
205 return *containISize;
208 const nsStyleColumn* colStyle = StyleColumn();
209 nscoord colISize;
210 if (colStyle->mColumnWidth.IsLength()) {
211 colISize =
212 ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
213 } else {
214 MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
215 "column-count and column-width can't both be auto!");
216 colISize = 0;
219 // If column-count is auto, assume one column.
220 const uint32_t numColumns =
221 colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
223 : colStyle->mColumnCount;
224 const nscoord colGap =
225 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
226 iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
227 } else {
228 for (nsIFrame* f : PrincipalChildList()) {
229 iSize = std::max(iSize, f->GetPrefISize(aRenderingContext));
233 return iSize;
236 template <typename Iterator>
237 Maybe<nscoord> ColumnSetWrapperFrame::GetBaselineBOffset(
238 Iterator aStart, Iterator aEnd, WritingMode aWM,
239 BaselineSharingGroup aBaselineGroup,
240 BaselineExportContext aExportContext) const {
241 // Either forward iterator + first baseline, or reverse iterator + last
242 // baseline
243 MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() &&
244 aBaselineGroup == BaselineSharingGroup::First) ||
245 (*aStart == PrincipalChildList().LastChild() &&
246 aBaselineGroup == BaselineSharingGroup::Last),
247 "Iterator direction must match baseline sharing group.");
248 if (StyleDisplay()->IsContainLayout()) {
249 return Nothing{};
252 // Start from start/end of principal child list, and use the first valid
253 // baseline.
254 for (auto itr = aStart; itr != aEnd; ++itr) {
255 const nsIFrame* kid = *itr;
256 auto kidBaseline =
257 kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext);
258 if (!kidBaseline) {
259 continue;
261 // Baseline is offset from the kid's rectangle, so find the offset to the
262 // kid's rectangle.
263 LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
264 kid->GetLogicalSize(aWM)};
265 if (aBaselineGroup == BaselineSharingGroup::First) {
266 *kidBaseline += kidRect.BStart(aWM);
267 } else {
268 *kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
270 return kidBaseline;
272 return Nothing{};
275 Maybe<nscoord> ColumnSetWrapperFrame::GetNaturalBaselineBOffset(
276 WritingMode aWM, BaselineSharingGroup aBaselineGroup,
277 BaselineExportContext aExportContext) const {
278 if (aBaselineGroup == BaselineSharingGroup::First) {
279 return GetBaselineBOffset(PrincipalChildList().cbegin(),
280 PrincipalChildList().cend(), aWM, aBaselineGroup,
281 aExportContext);
283 return GetBaselineBOffset(PrincipalChildList().crbegin(),
284 PrincipalChildList().crend(), aWM, aBaselineGroup,
285 aExportContext);
288 #ifdef DEBUG
290 /* static */
291 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
292 const nsIFrame* aFrame) {
293 MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
295 if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
296 ->Style()
297 ->IsAnonBox()) {
298 // aFrame's style frame has "column-span: all". Traverse no further.
299 return;
302 MOZ_ASSERT(
303 aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper,
304 "aFrame should be ::-moz-column-span-wrapper");
306 MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES),
307 "::-moz-column-span-wrapper anonymous blocks cannot own "
308 "other types of anonymous blocks!");
310 for (const nsIFrame* child : aFrame->PrincipalChildList()) {
311 AssertColumnSpanWrapperSubtreeIsSane(child);
315 #endif