1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsTableColGroupFrame.h"
7 #include "mozilla/ComputedStyle.h"
8 #include "mozilla/PresShell.h"
9 #include "mozilla/StaticPrefs_layout.h"
10 #include "nsTableColFrame.h"
11 #include "nsTableFrame.h"
12 #include "nsStyleConsts.h"
13 #include "nsPresContext.h"
14 #include "nsHTMLParts.h"
15 #include "nsGkAtoms.h"
17 #include "nsCSSRendering.h"
19 using namespace mozilla
;
21 #define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30)
23 bool nsTableColGroupFrame::IsSynthetic() const {
24 return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT
);
27 void nsTableColGroupFrame::SetIsSynthetic() {
28 AddStateBits(COLGROUP_SYNTHETIC_BIT
);
31 void nsTableColGroupFrame::ResetColIndices(nsIFrame
* aFirstColGroup
,
32 int32_t aFirstColIndex
,
33 nsIFrame
* aStartColFrame
) {
34 nsTableColGroupFrame
* colGroupFrame
= (nsTableColGroupFrame
*)aFirstColGroup
;
35 int32_t colIndex
= aFirstColIndex
;
36 while (colGroupFrame
) {
37 if (colGroupFrame
->IsTableColGroupFrame()) {
38 // reset the starting col index for the first cg only if we should reset
39 // the whole colgroup (aStartColFrame defaults to nullptr) or if
40 // aFirstColIndex is smaller than the existing starting col index
41 if ((colIndex
!= aFirstColIndex
) ||
42 (colIndex
< colGroupFrame
->GetStartColumnIndex()) ||
44 colGroupFrame
->SetStartColumnIndex(colIndex
);
46 nsIFrame
* colFrame
= aStartColFrame
;
47 if (!colFrame
|| (colIndex
!= aFirstColIndex
)) {
48 colFrame
= colGroupFrame
->PrincipalChildList().FirstChild();
51 if (colFrame
->IsTableColFrame()) {
52 ((nsTableColFrame
*)colFrame
)->SetColIndex(colIndex
);
55 colFrame
= colFrame
->GetNextSibling();
59 static_cast<nsTableColGroupFrame
*>(colGroupFrame
->GetNextSibling());
63 nsresult
nsTableColGroupFrame::AddColsToTable(int32_t aFirstColIndex
,
64 bool aResetSubsequentColIndices
,
65 const nsFrameList::Slice
& aCols
) {
66 nsTableFrame
* tableFrame
= GetTableFrame();
68 tableFrame
->InvalidateFrameSubtree();
70 // set the col indices of the col frames and and add col info to the table
71 int32_t colIndex
= aFirstColIndex
;
73 // XXX: We cannot use range-based for loop because InsertCol() can destroy the
74 // nsTableColFrame in the slice we're traversing! Need to check the validity
76 auto colIter
= aCols
.begin();
77 for (auto colIterEnd
= aCols
.end(); *colIter
&& colIter
!= colIterEnd
;
79 auto* colFrame
= static_cast<nsTableColFrame
*>(*colIter
);
80 colFrame
->SetColIndex(colIndex
);
82 tableFrame
->InsertCol(*colFrame
, colIndex
);
86 for (; *colIter
; ++colIter
) {
87 auto* colFrame
= static_cast<nsTableColFrame
*>(*colIter
);
88 colFrame
->SetColIndex(colIndex
);
92 // We have already set the colindex for all the colframes in this
93 // colgroup that come after the first inserted colframe, but there could
94 // be other colgroups following this one and their colframes need
95 // correct colindices too.
96 if (aResetSubsequentColIndices
&& GetNextSibling()) {
97 ResetColIndices(GetNextSibling(), colIndex
);
103 nsTableColGroupFrame
* nsTableColGroupFrame::GetLastRealColGroup(
104 nsTableFrame
* aTableFrame
) {
105 const nsFrameList
& colGroups
= aTableFrame
->GetColGroups();
107 auto lastColGroup
= static_cast<nsTableColGroupFrame
*>(colGroups
.LastChild());
112 if (!lastColGroup
->IsSynthetic()) {
116 return static_cast<nsTableColGroupFrame
*>(lastColGroup
->GetPrevSibling());
119 // don't set mColCount here, it is done in AddColsToTable
120 void nsTableColGroupFrame::SetInitialChildList(ChildListID aListID
,
121 nsFrameList
&& aChildList
) {
122 MOZ_ASSERT(mFrames
.IsEmpty(),
123 "unexpected second call to SetInitialChildList");
124 MOZ_ASSERT(aListID
== FrameChildListID::Principal
, "unexpected child list");
126 for (nsIFrame
* f
: aChildList
) {
127 MOZ_ASSERT(f
->GetParent() == this, "Unexpected parent");
130 if (aChildList
.IsEmpty()) {
131 GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
132 eColAnonymousColGroup
, false);
136 mFrames
.AppendFrames(this, std::move(aChildList
));
140 void nsTableColGroupFrame::DidSetComputedStyle(
141 ComputedStyle
* aOldComputedStyle
) {
142 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
144 if (!aOldComputedStyle
) // avoid this on init
147 nsTableFrame
* tableFrame
= GetTableFrame();
148 if (tableFrame
->IsBorderCollapse() &&
149 tableFrame
->BCRecalcNeeded(aOldComputedStyle
, Style())) {
150 int32_t colCount
= GetColCount();
151 if (!colCount
) return; // this is a degenerated colgroup
152 TableArea
damageArea(GetFirstColumn()->GetColIndex(), 0, colCount
,
153 tableFrame
->GetRowCount());
154 tableFrame
->AddBCDamageArea(damageArea
);
158 void nsTableColGroupFrame::AppendFrames(ChildListID aListID
,
159 nsFrameList
&& aFrameList
) {
160 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
162 nsTableColFrame
* col
= GetFirstColumn();
163 nsTableColFrame
* nextCol
;
164 while (col
&& col
->GetColType() == eColAnonymousColGroup
) {
165 // this colgroup spans one or more columns but now that there is a
166 // real column below, spanned anonymous columns should be removed,
167 // since the HTML spec says to ignore the span of a colgroup if it
168 // has content columns in it.
169 nextCol
= col
->GetNextCol();
170 DestroyContext
context(PresShell());
171 RemoveFrame(context
, FrameChildListID::Principal
, col
);
175 // Our next colframe should be an eColContent. We've removed all the
176 // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
177 // eColContent ones, and eColAnonymousCell colframes only appear in a
178 // synthetic colgroup, which never gets AppendFrames() called on it.
179 MOZ_ASSERT(!col
|| col
->GetColType() == eColContent
,
180 "What's going on with our columns?");
182 const nsFrameList::Slice
& newFrames
=
183 mFrames
.AppendFrames(this, std::move(aFrameList
));
184 InsertColsReflow(GetStartColumnIndex() + mColCount
, newFrames
);
187 void nsTableColGroupFrame::InsertFrames(
188 ChildListID aListID
, nsIFrame
* aPrevFrame
,
189 const nsLineList::iterator
* aPrevFrameLine
, nsFrameList
&& aFrameList
) {
190 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
191 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
192 "inserting after sibling frame with different parent");
194 nsTableColFrame
* col
= GetFirstColumn();
195 nsTableColFrame
* nextCol
;
196 while (col
&& col
->GetColType() == eColAnonymousColGroup
) {
197 // this colgroup spans one or more columns but now that there is a
198 // real column below, spanned anonymous columns should be removed,
199 // since the HTML spec says to ignore the span of a colgroup if it
200 // has content columns in it.
201 nextCol
= col
->GetNextCol();
202 if (col
== aPrevFrame
) {
203 // This can happen when we're being appended to
204 NS_ASSERTION(!nextCol
|| nextCol
->GetColType() != eColAnonymousColGroup
,
205 "Inserting in the middle of our anonymous cols?");
206 // We'll want to insert at the beginning
207 aPrevFrame
= nullptr;
209 DestroyContext
context(PresShell());
210 RemoveFrame(context
, FrameChildListID::Principal
, col
);
214 // Our next colframe should be an eColContent. We've removed all the
215 // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
216 // eColContent ones, and eColAnonymousCell colframes only appear in a
217 // synthetic colgroup, which never gets InsertFrames() called on it.
218 MOZ_ASSERT(!col
|| col
->GetColType() == eColContent
,
219 "What's going on with our columns?");
221 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
== aPrevFrame
->LastContinuation(),
222 "Prev frame should be last in continuation chain");
223 NS_ASSERTION(!aPrevFrame
|| !GetNextColumn(aPrevFrame
) ||
224 GetNextColumn(aPrevFrame
)->GetColType() != eColAnonymousCol
,
225 "Shouldn't be inserting before a spanned colframe");
227 const nsFrameList::Slice
& newFrames
=
228 mFrames
.InsertFrames(this, aPrevFrame
, std::move(aFrameList
));
229 nsIFrame
* prevFrame
= nsTableFrame::GetFrameAtOrBefore(
230 this, aPrevFrame
, LayoutFrameType::TableCol
);
232 int32_t colIndex
= (prevFrame
)
233 ? ((nsTableColFrame
*)prevFrame
)->GetColIndex() + 1
234 : GetStartColumnIndex();
235 InsertColsReflow(colIndex
, newFrames
);
238 void nsTableColGroupFrame::InsertColsReflow(int32_t aColIndex
,
239 const nsFrameList::Slice
& aCols
) {
240 AddColsToTable(aColIndex
, true, aCols
);
242 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
243 NS_FRAME_HAS_DIRTY_CHILDREN
);
246 void nsTableColGroupFrame::RemoveChild(DestroyContext
& aContext
,
247 nsTableColFrame
& aChild
,
248 bool aResetSubsequentColIndices
) {
249 int32_t colIndex
= 0;
250 nsIFrame
* nextChild
= nullptr;
251 if (aResetSubsequentColIndices
) {
252 colIndex
= aChild
.GetColIndex();
253 nextChild
= aChild
.GetNextSibling();
255 mFrames
.DestroyFrame(aContext
, &aChild
);
257 if (aResetSubsequentColIndices
) {
258 if (nextChild
) { // reset inside this and all following colgroups
259 ResetColIndices(this, colIndex
, nextChild
);
261 nsIFrame
* nextGroup
= GetNextSibling();
262 if (nextGroup
) // reset next and all following colgroups
263 ResetColIndices(nextGroup
, colIndex
);
267 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors
,
268 NS_FRAME_HAS_DIRTY_CHILDREN
);
271 void nsTableColGroupFrame::RemoveFrame(DestroyContext
& aContext
,
273 nsIFrame
* aOldFrame
) {
274 NS_ASSERTION(aListID
== FrameChildListID::Principal
, "unexpected child list");
279 bool contentRemoval
= false;
281 if (aOldFrame
->IsTableColFrame()) {
282 nsTableColFrame
* colFrame
= (nsTableColFrame
*)aOldFrame
;
283 if (colFrame
->GetColType() == eColContent
) {
284 contentRemoval
= true;
285 // Remove any anonymous column frames this <col> produced via a colspan
286 nsTableColFrame
* col
= colFrame
->GetNextCol();
287 nsTableColFrame
* nextCol
;
288 while (col
&& col
->GetColType() == eColAnonymousCol
) {
289 nextCol
= col
->GetNextCol();
290 RemoveFrame(aContext
, FrameChildListID::Principal
, col
);
295 int32_t colIndex
= colFrame
->GetColIndex();
296 // The RemoveChild call handles calling FrameNeedsReflow on us.
297 RemoveChild(aContext
, *colFrame
, true);
299 nsTableFrame
* tableFrame
= GetTableFrame();
300 tableFrame
->RemoveCol(this, colIndex
, true, true);
301 if (mFrames
.IsEmpty() && contentRemoval
&& !IsSynthetic()) {
302 tableFrame
->AppendAnonymousColFrames(this, GetSpan(),
303 eColAnonymousColGroup
, true);
306 mFrames
.DestroyFrame(aContext
, aOldFrame
);
310 nsIFrame::LogicalSides
nsTableColGroupFrame::GetLogicalSkipSides() const {
311 LogicalSides
skip(mWritingMode
);
312 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
313 StyleBoxDecorationBreak::Clone
)) {
317 if (GetPrevInFlow()) {
318 skip
|= eLogicalSideBitsBStart
;
320 if (GetNextInFlow()) {
321 skip
|= eLogicalSideBitsBEnd
;
326 void nsTableColGroupFrame::Reflow(nsPresContext
* aPresContext
,
327 ReflowOutput
& aDesiredSize
,
328 const ReflowInput
& aReflowInput
,
329 nsReflowStatus
& aStatus
) {
331 DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
332 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
333 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
334 NS_ASSERTION(nullptr != mContent
, "bad state -- null content for frame");
336 const nsStyleVisibility
* groupVis
= StyleVisibility();
337 bool collapseGroup
= StyleVisibility::Collapse
== groupVis
->mVisible
;
339 GetTableFrame()->SetNeedToCollapse(true);
342 const WritingMode wm
= GetWritingMode();
343 for (nsIFrame
* kidFrame
: mFrames
) {
344 // Give the child frame a chance to reflow, even though we know it'll have 0
346 ReflowOutput
kidSize(aReflowInput
);
347 ReflowInput
kidReflowInput(aPresContext
, aReflowInput
, kidFrame
,
348 LogicalSize(kidFrame
->GetWritingMode()));
349 const LogicalPoint
dummyPos(wm
);
350 const nsSize dummyContainerSize
;
351 nsReflowStatus status
;
352 ReflowChild(kidFrame
, aPresContext
, kidSize
, kidReflowInput
, wm
, dummyPos
,
353 dummyContainerSize
, ReflowChildFlags::Default
, status
);
354 FinishReflowChild(kidFrame
, aPresContext
, kidSize
, &kidReflowInput
, wm
,
355 dummyPos
, dummyContainerSize
, ReflowChildFlags::Default
);
358 aDesiredSize
.ClearSize();
361 void nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
362 const nsDisplayListSet
& aLists
) {
363 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
364 // "All css properties of table-column and table-column-group boxes are
365 // ignored, except when explicitly specified by this specification."
366 // CSS outlines and box-shadows fall into this category, so we skip them
368 MOZ_ASSERT_UNREACHABLE("Colgroups don't paint themselves");
371 nsTableColFrame
* nsTableColGroupFrame::GetFirstColumn() {
372 return GetNextColumn(nullptr);
375 nsTableColFrame
* nsTableColGroupFrame::GetNextColumn(nsIFrame
* aChildFrame
) {
376 nsTableColFrame
* result
= nullptr;
377 nsIFrame
* childFrame
= aChildFrame
;
379 childFrame
= mFrames
.FirstChild();
381 childFrame
= childFrame
->GetNextSibling();
384 if (mozilla::StyleDisplay::TableColumn
==
385 childFrame
->StyleDisplay()->mDisplay
) {
386 result
= (nsTableColFrame
*)childFrame
;
389 childFrame
= childFrame
->GetNextSibling();
394 int32_t nsTableColGroupFrame::GetSpan() { return StyleTable()->mXSpan
; }
396 /* ----- global methods ----- */
398 nsTableColGroupFrame
* NS_NewTableColGroupFrame(PresShell
* aPresShell
,
399 ComputedStyle
* aStyle
) {
400 return new (aPresShell
)
401 nsTableColGroupFrame(aStyle
, aPresShell
->GetPresContext());
404 NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame
)
406 void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey
,
407 bool aRebuildDisplayItems
) {
408 nsIFrame::InvalidateFrame(aDisplayItemKey
, aRebuildDisplayItems
);
409 if (GetTableFrame()->IsBorderCollapse()) {
410 const bool rebuild
= StaticPrefs::layout_display_list_retain_sc();
411 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
412 aDisplayItemKey
, rebuild
);
416 void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect
& aRect
,
417 uint32_t aDisplayItemKey
,
418 bool aRebuildDisplayItems
) {
419 nsIFrame::InvalidateFrameWithRect(aRect
, aDisplayItemKey
,
420 aRebuildDisplayItems
);
421 // If we have filters applied that would affects our bounds, then
422 // we get an inactive layer created and this is computed
423 // within FrameLayerBuilder
424 GetParent()->InvalidateFrameWithRect(aRect
+ GetPosition(), aDisplayItemKey
,
425 aRebuildDisplayItems
);
428 #ifdef DEBUG_FRAME_DUMP
429 nsresult
nsTableColGroupFrame::GetFrameName(nsAString
& aResult
) const {
430 return MakeFrameName(u
"TableColGroup"_ns
, aResult
);
433 void nsTableColGroupFrame::Dump(int32_t aIndent
) {
434 char* indent
= new char[aIndent
+ 1];
436 for (int32_t i
= 0; i
< aIndent
+ 1; i
++) {
442 "%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d "
444 indent
, indent
, GetStartColumnIndex(), GetColCount(), GetSpan(),
445 IsSynthetic() ? "true" : "false");
447 // verify the colindices
448 DebugOnly
<int32_t> j
= GetStartColumnIndex();
449 nsTableColFrame
* col
= GetFirstColumn();
451 NS_ASSERTION(j
== col
->GetColIndex(), "wrong colindex on col frame");
452 col
= col
->GetNextCol();
455 NS_ASSERTION((j
- GetStartColumnIndex()) == GetColCount(),
456 "number of cols out of sync");
457 printf("\n%s**END COLGROUP DUMP** ", indent
);