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/. */
6 
7 #include "ColumnSetWrapperFrame.h"
8 
9 #include "mozilla/ColumnUtils.h"
10 #include "mozilla/PresShell.h"
11 #include "nsContentUtils.h"
12 #include "nsIFrame.h"
13 #include "nsIFrameInlines.h"
14 
15 using namespace mozilla;
16 
NS_NewColumnSetWrapperFrame(PresShell * aPresShell,ComputedStyle * aStyle,nsFrameState aStateFlags)17 nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell,
18                                           ComputedStyle* aStyle,
19                                           nsFrameState aStateFlags) {
20   ColumnSetWrapperFrame* frame = new (aPresShell)
21       ColumnSetWrapperFrame(aStyle, aPresShell->GetPresContext());
22 
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;
28 }
29 
30 NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame)
31 
NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)32 NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame)
33   NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame)
34 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
35 
36 ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle,
37                                              nsPresContext* aPresContext)
38     : nsBlockFrame(aStyle, aPresContext, kClassID) {}
39 
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)40 void ColumnSetWrapperFrame::Init(nsIContent* aContent,
41                                  nsContainerFrame* aParent,
42                                  nsIFrame* aPrevInFlow) {
43   nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
44 
45   // ColumnSetWrapperFrame doesn't need to call ResolveBidi().
46   RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
47 }
48 
GetContentInsertionFrame()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();
57   }
58 
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;
63 }
64 
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)65 void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes(
66     nsTArray<OwnedAnonBox>& aResult) {
67   MOZ_ASSERT(!GetPrevContinuation(),
68              "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?");
69 
70   // It's sufficient to append the first ColumnSet child, which is the first
71   // continuation of all the other ColumnSets.
72   //
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()) {
85           return firstChild;
86         }
87       }
88     }
89     return nullptr;
90   };
91 
92   nsIFrame* columnSet = FindFirstChildInChildLists();
93   MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(),
94              "The first child should always be ColumnSet!");
95   aResult.AppendElement(OwnedAnonBox(columnSet));
96 }
97 
98 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const99 nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const {
100   return MakeFrameName(NS_LITERAL_STRING("ColumnSetWrapper"), aResult);
101 }
102 #endif
103 
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.
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)107 void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID,
108                                          nsFrameList& aFrameList) {
109 #ifdef DEBUG
110   MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!");
111   mFinishedBuildingColumns = true;
112 #endif
113 
114   nsBlockFrame::AppendFrames(aListID, aFrameList);
115 
116 #ifdef DEBUG
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?");
125     }
126   }
127 #endif
128 }
129 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)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);
135 }
136 
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)137 void ColumnSetWrapperFrame::RemoveFrame(ChildListID aListID,
138                                         nsIFrame* aOldFrame) {
139   MOZ_ASSERT_UNREACHABLE("Unsupported operation!");
140   nsBlockFrame::RemoveFrame(aListID, aOldFrame);
141 }
142 
MarkIntrinsicISizesDirty()143 void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() {
144   nsBlockFrame::MarkIntrinsicISizesDirty();
145 
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);
150   }
151 }
152 
GetMinISize(gfxContext * aRenderingContext)153 nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
154   nscoord iSize = 0;
155   DISPLAY_MIN_INLINE_SIZE(this, iSize);
156 
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
161     // scenario.
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.
168       iSize = 0;
169     } else {
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);
178     }
179   } else {
180     for (nsIFrame* f : PrincipalChildList()) {
181       iSize = std::max(iSize, f->GetMinISize(aRenderingContext));
182     }
183   }
184 
185   return iSize;
186 }
187 
GetPrefISize(gfxContext * aRenderingContext)188 nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
189   nscoord iSize = 0;
190   DISPLAY_PREF_INLINE_SIZE(this, iSize);
191 
192   if (StyleDisplay()->IsContainSize()) {
193     const nsStyleColumn* colStyle = StyleColumn();
194     nscoord colISize;
195     if (colStyle->mColumnWidth.IsLength()) {
196       colISize =
197           ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength());
198     } else {
199       MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto,
200                  "column-count and column-width can't both be auto!");
201       colISize = 0;
202     }
203 
204     // If column-count is auto, assume one column.
205     const uint32_t numColumns =
206         colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto
207             ? 1
208             : colStyle->mColumnCount;
209     const nscoord colGap =
210         ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE);
211     iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize);
212   } else {
213     for (nsIFrame* f : PrincipalChildList()) {
214       iSize = std::max(iSize, f->GetPrefISize(aRenderingContext));
215     }
216   }
217 
218   return iSize;
219 }
220 
221 #ifdef DEBUG
222 
223 /* static */
AssertColumnSpanWrapperSubtreeIsSane(const nsIFrame * aFrame)224 void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane(
225     const nsIFrame* aFrame) {
226   MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?");
227 
228   if (!nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aFrame))
229            ->Style()
230            ->IsAnonBox()) {
231     // aFrame's style frame has "column-span: all". Traverse no further.
232     return;
233   }
234 
235   MOZ_ASSERT(
236       aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper,
237       "aFrame should be ::-moz-column-span-wrapper");
238 
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!");
242 
243   for (const nsIFrame* child : aFrame->PrincipalChildList()) {
244     AssertColumnSpanWrapperSubtreeIsSane(child);
245   }
246 }
247 
248 #endif
249