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"
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
26 frame
->AddStateBits(aStateFlags
| NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS
);
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();
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
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
[] = {kPrincipalList
, kOverflowList
};
81 for (nsIFrame
* frag
= this; frag
; frag
= frag
->GetNextInFlow()) {
82 for (ChildListID id
: listIDs
) {
83 const nsFrameList
& list
= frag
->GetChildList(id
);
84 if (nsIFrame
* firstChild
= list
.FirstChild()) {
92 nsIFrame
* columnSet
= FindFirstChildInChildLists();
93 MOZ_ASSERT(columnSet
&& columnSet
->IsColumnSetFrame(),
94 "The first child should always be ColumnSet!");
95 aResult
.AppendElement(OwnedAnonBox(columnSet
));
98 #ifdef DEBUG_FRAME_DUMP
99 nsresult
ColumnSetWrapperFrame::GetFrameName(nsAString
& aResult
) const {
100 return MakeFrameName(u
"ColumnSetWrapper"_ns
, aResult
);
104 // Disallow any append, insert, or remove operations after building the
105 // column hierarchy since any change to the column hierarchy in the column
106 // sub-tree need to be re-created.
107 void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID
,
108 nsFrameList
& aFrameList
) {
110 MOZ_ASSERT(!mFinishedBuildingColumns
, "Should only call once!");
111 mFinishedBuildingColumns
= true;
114 nsBlockFrame::AppendFrames(aListID
, aFrameList
);
117 nsIFrame
* firstColumnSet
= PrincipalChildList().FirstChild();
118 for (nsIFrame
* child
: PrincipalChildList()) {
119 if (child
->IsColumnSpan()) {
120 AssertColumnSpanWrapperSubtreeIsSane(child
);
121 } else if (child
!= firstColumnSet
) {
122 // All the other ColumnSets are the continuation of the first ColumnSet.
123 MOZ_ASSERT(child
->IsColumnSetFrame() && child
->GetPrevContinuation(),
124 "ColumnSet's prev-continuation is not set properly?");
130 void ColumnSetWrapperFrame::InsertFrames(
131 ChildListID aListID
, nsIFrame
* aPrevFrame
,
132 const nsLineList::iterator
* aPrevFrameLine
, nsFrameList
& aFrameList
) {
133 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
134 nsBlockFrame::InsertFrames(aListID
, aPrevFrame
, aPrevFrameLine
, aFrameList
);
137 void ColumnSetWrapperFrame::RemoveFrame(ChildListID aListID
,
138 nsIFrame
* aOldFrame
) {
139 MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
140 nsBlockFrame::RemoveFrame(aListID
, aOldFrame
);
143 void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
144 nsBlockFrame::MarkIntrinsicISizesDirty();
146 // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our
147 // continuations. Clear the bit because we don't want to call ResolveBidi().
148 for (nsIFrame
* f
= FirstContinuation(); f
; f
= f
->GetNextContinuation()) {
149 f
->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
153 nscoord
ColumnSetWrapperFrame::GetMinISize(gfxContext
* aRenderingContext
) {
155 DISPLAY_MIN_INLINE_SIZE(this, iSize
);
157 if (StyleDisplay()->IsContainSize()) {
158 // If we're size-contained, we determine our minimum intrinsic size purely
159 // from our column styling, as if we had no descendants. This should match
160 // what happens in nsColumnSetFrame::GetMinISize in an actual no-descendants
162 const nsStyleColumn
* colStyle
= StyleColumn();
163 if (colStyle
->mColumnWidth
.IsLength()) {
164 // As available inline size reduces to zero, our number of columns reduces
165 // to one, so no column gaps contribute to our minimum intrinsic size.
166 // Also, column-width doesn't set a lower bound on our minimum intrinsic
167 // size, either. Just use 0 because we're size-contained.
170 MOZ_ASSERT(colStyle
->mColumnCount
!= nsStyleColumn::kColumnCountAuto
,
171 "column-count and column-width can't both be auto!");
172 // As available inline size reduces to zero, we still have mColumnCount
173 // columns, so compute our minimum intrinsic size based on N zero-width
174 // columns, with specified gap size between them.
175 const nscoord colGap
=
176 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE
);
177 iSize
= ColumnUtils::IntrinsicISize(colStyle
->mColumnCount
, colGap
, 0);
180 for (nsIFrame
* f
: PrincipalChildList()) {
181 iSize
= std::max(iSize
, f
->GetMinISize(aRenderingContext
));
188 nscoord
ColumnSetWrapperFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
190 DISPLAY_PREF_INLINE_SIZE(this, iSize
);
192 if (StyleDisplay()->IsContainSize()) {
193 const nsStyleColumn
* colStyle
= StyleColumn();
195 if (colStyle
->mColumnWidth
.IsLength()) {
197 ColumnUtils::ClampUsedColumnWidth(colStyle
->mColumnWidth
.AsLength());
199 MOZ_ASSERT(colStyle
->mColumnCount
!= nsStyleColumn::kColumnCountAuto
,
200 "column-count and column-width can't both be auto!");
204 // If column-count is auto, assume one column.
205 const uint32_t numColumns
=
206 colStyle
->mColumnCount
== nsStyleColumn::kColumnCountAuto
208 : colStyle
->mColumnCount
;
209 const nscoord colGap
=
210 ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE
);
211 iSize
= ColumnUtils::IntrinsicISize(numColumns
, colGap
, colISize
);
213 for (nsIFrame
* f
: PrincipalChildList()) {
214 iSize
= std::max(iSize
, f
->GetPrefISize(aRenderingContext
));
224 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
225 const nsIFrame
* aFrame
) {
226 MOZ_ASSERT(aFrame
->IsColumnSpan(), "aFrame is not column-span?");
228 if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame
*>(aFrame
))
231 // aFrame's style frame has "column-span: all". Traverse no further.
236 aFrame
->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper
,
237 "aFrame should be ::-moz-column-span-wrapper");
239 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES
),
240 "::-moz-column-span-wrapper anonymous blocks cannot own "
241 "other types of anonymous blocks!");
243 for (const nsIFrame
* child
: aFrame
->PrincipalChildList()) {
244 AssertColumnSpanWrapperSubtreeIsSane(child
);