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"
6
7 #include "mozilla/ComputedStyle.h"
8 #include "mozilla/PresShell.h"
9 #include "nsTableColFrame.h"
10 #include "nsTableFrame.h"
11 #include "nsStyleConsts.h"
12 #include "nsPresContext.h"
13 #include "nsHTMLParts.h"
14 #include "nsGkAtoms.h"
15 #include "nsCOMPtr.h"
16 #include "nsCSSRendering.h"
17
18 using namespace mozilla;
19
20 #define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30)
21
IsSynthetic() const22 bool nsTableColGroupFrame::IsSynthetic() const {
23 return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT);
24 }
25
SetIsSynthetic()26 void nsTableColGroupFrame::SetIsSynthetic() {
27 AddStateBits(COLGROUP_SYNTHETIC_BIT);
28 }
29
ResetColIndices(nsIFrame * aFirstColGroup,int32_t aFirstColIndex,nsIFrame * aStartColFrame)30 void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup,
31 int32_t aFirstColIndex,
32 nsIFrame* aStartColFrame) {
33 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
34 int32_t colIndex = aFirstColIndex;
35 while (colGroupFrame) {
36 if (colGroupFrame->IsTableColGroupFrame()) {
37 // reset the starting col index for the first cg only if we should reset
38 // the whole colgroup (aStartColFrame defaults to nullptr) or if
39 // aFirstColIndex is smaller than the existing starting col index
40 if ((colIndex != aFirstColIndex) ||
41 (colIndex < colGroupFrame->GetStartColumnIndex()) ||
42 !aStartColFrame) {
43 colGroupFrame->SetStartColumnIndex(colIndex);
44 }
45 nsIFrame* colFrame = aStartColFrame;
46 if (!colFrame || (colIndex != aFirstColIndex)) {
47 colFrame = colGroupFrame->PrincipalChildList().FirstChild();
48 }
49 while (colFrame) {
50 if (colFrame->IsTableColFrame()) {
51 ((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
52 colIndex++;
53 }
54 colFrame = colFrame->GetNextSibling();
55 }
56 }
57 colGroupFrame =
58 static_cast<nsTableColGroupFrame*>(colGroupFrame->GetNextSibling());
59 }
60 }
61
AddColsToTable(int32_t aFirstColIndex,bool aResetSubsequentColIndices,const nsFrameList::Slice & aCols)62 nsresult nsTableColGroupFrame::AddColsToTable(int32_t aFirstColIndex,
63 bool aResetSubsequentColIndices,
64 const nsFrameList::Slice& aCols) {
65 nsTableFrame* tableFrame = GetTableFrame();
66
67 tableFrame->InvalidateFrameSubtree();
68
69 // set the col indices of the col frames and and add col info to the table
70 int32_t colIndex = aFirstColIndex;
71 nsFrameList::Enumerator e(aCols);
72 for (; !e.AtEnd(); e.Next()) {
73 ((nsTableColFrame*)e.get())->SetColIndex(colIndex);
74 mColCount++;
75 tableFrame->InsertCol((nsTableColFrame&)*e.get(), colIndex);
76 colIndex++;
77 }
78
79 for (nsFrameList::Enumerator eTail = e.GetUnlimitedEnumerator();
80 !eTail.AtEnd(); eTail.Next()) {
81 ((nsTableColFrame*)eTail.get())->SetColIndex(colIndex);
82 colIndex++;
83 }
84
85 // We have already set the colindex for all the colframes in this
86 // colgroup that come after the first inserted colframe, but there could
87 // be other colgroups following this one and their colframes need
88 // correct colindices too.
89 if (aResetSubsequentColIndices && GetNextSibling()) {
90 ResetColIndices(GetNextSibling(), colIndex);
91 }
92
93 return NS_OK;
94 }
95
GetLastRealColGroup(nsTableFrame * aTableFrame)96 nsTableColGroupFrame* nsTableColGroupFrame::GetLastRealColGroup(
97 nsTableFrame* aTableFrame) {
98 nsFrameList colGroups = aTableFrame->GetColGroups();
99
100 auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild());
101 if (!lastColGroup) {
102 return nullptr;
103 }
104
105 if (!lastColGroup->IsSynthetic()) {
106 return lastColGroup;
107 }
108
109 return static_cast<nsTableColGroupFrame*>(lastColGroup->GetPrevSibling());
110 }
111
112 // don't set mColCount here, it is done in AddColsToTable
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)113 void nsTableColGroupFrame::SetInitialChildList(ChildListID aListID,
114 nsFrameList& aChildList) {
115 MOZ_ASSERT(mFrames.IsEmpty(),
116 "unexpected second call to SetInitialChildList");
117 MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
118 #ifdef DEBUG
119 for (nsIFrame* f : aChildList) {
120 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
121 }
122 #endif
123 if (aChildList.IsEmpty()) {
124 GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
125 eColAnonymousColGroup, false);
126 return;
127 }
128
129 mFrames.AppendFrames(this, aChildList);
130 }
131
132 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)133 void nsTableColGroupFrame::DidSetComputedStyle(
134 ComputedStyle* aOldComputedStyle) {
135 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
136
137 if (!aOldComputedStyle) // avoid this on init
138 return;
139
140 nsTableFrame* tableFrame = GetTableFrame();
141 if (tableFrame->IsBorderCollapse() &&
142 tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
143 int32_t colCount = GetColCount();
144 if (!colCount) return; // this is a degenerated colgroup
145 TableArea damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
146 tableFrame->GetRowCount());
147 tableFrame->AddBCDamageArea(damageArea);
148 }
149 }
150
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)151 void nsTableColGroupFrame::AppendFrames(ChildListID aListID,
152 nsFrameList& aFrameList) {
153 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
154
155 nsTableColFrame* col = GetFirstColumn();
156 nsTableColFrame* nextCol;
157 while (col && col->GetColType() == eColAnonymousColGroup) {
158 // this colgroup spans one or more columns but now that there is a
159 // real column below, spanned anonymous columns should be removed,
160 // since the HTML spec says to ignore the span of a colgroup if it
161 // has content columns in it.
162 nextCol = col->GetNextCol();
163 RemoveFrame(kPrincipalList, col);
164 col = nextCol;
165 }
166
167 // Our next colframe should be an eColContent. We've removed all the
168 // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
169 // eColContent ones, and eColAnonymousCell colframes only appear in a
170 // synthetic colgroup, which never gets AppendFrames() called on it.
171 MOZ_ASSERT(!col || col->GetColType() == eColContent,
172 "What's going on with our columns?");
173
174 const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
175 InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames);
176 }
177
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)178 void nsTableColGroupFrame::InsertFrames(
179 ChildListID aListID, nsIFrame* aPrevFrame,
180 const nsLineList::iterator* aPrevFrameLine, nsFrameList& aFrameList) {
181 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
182 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
183 "inserting after sibling frame with different parent");
184
185 nsTableColFrame* col = GetFirstColumn();
186 nsTableColFrame* nextCol;
187 while (col && col->GetColType() == eColAnonymousColGroup) {
188 // this colgroup spans one or more columns but now that there is a
189 // real column below, spanned anonymous columns should be removed,
190 // since the HTML spec says to ignore the span of a colgroup if it
191 // has content columns in it.
192 nextCol = col->GetNextCol();
193 if (col == aPrevFrame) {
194 // This can happen when we're being appended to
195 NS_ASSERTION(!nextCol || nextCol->GetColType() != eColAnonymousColGroup,
196 "Inserting in the middle of our anonymous cols?");
197 // We'll want to insert at the beginning
198 aPrevFrame = nullptr;
199 }
200 RemoveFrame(kPrincipalList, col);
201 col = nextCol;
202 }
203
204 // Our next colframe should be an eColContent. We've removed all the
205 // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
206 // eColContent ones, and eColAnonymousCell colframes only appear in a
207 // synthetic colgroup, which never gets InsertFrames() called on it.
208 MOZ_ASSERT(!col || col->GetColType() == eColContent,
209 "What's going on with our columns?");
210
211 NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->LastContinuation(),
212 "Prev frame should be last in continuation chain");
213 NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
214 GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
215 "Shouldn't be inserting before a spanned colframe");
216
217 const nsFrameList::Slice& newFrames =
218 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
219 nsIFrame* prevFrame = nsTableFrame::GetFrameAtOrBefore(
220 this, aPrevFrame, LayoutFrameType::TableCol);
221
222 int32_t colIndex = (prevFrame)
223 ? ((nsTableColFrame*)prevFrame)->GetColIndex() + 1
224 : GetStartColumnIndex();
225 InsertColsReflow(colIndex, newFrames);
226 }
227
InsertColsReflow(int32_t aColIndex,const nsFrameList::Slice & aCols)228 void nsTableColGroupFrame::InsertColsReflow(int32_t aColIndex,
229 const nsFrameList::Slice& aCols) {
230 AddColsToTable(aColIndex, true, aCols);
231
232 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
233 NS_FRAME_HAS_DIRTY_CHILDREN);
234 }
235
RemoveChild(nsTableColFrame & aChild,bool aResetSubsequentColIndices)236 void nsTableColGroupFrame::RemoveChild(nsTableColFrame& aChild,
237 bool aResetSubsequentColIndices) {
238 int32_t colIndex = 0;
239 nsIFrame* nextChild = nullptr;
240 if (aResetSubsequentColIndices) {
241 colIndex = aChild.GetColIndex();
242 nextChild = aChild.GetNextSibling();
243 }
244 mFrames.DestroyFrame(&aChild);
245 mColCount--;
246 if (aResetSubsequentColIndices) {
247 if (nextChild) { // reset inside this and all following colgroups
248 ResetColIndices(this, colIndex, nextChild);
249 } else {
250 nsIFrame* nextGroup = GetNextSibling();
251 if (nextGroup) // reset next and all following colgroups
252 ResetColIndices(nextGroup, colIndex);
253 }
254 }
255
256 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
257 NS_FRAME_HAS_DIRTY_CHILDREN);
258 }
259
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)260 void nsTableColGroupFrame::RemoveFrame(ChildListID aListID,
261 nsIFrame* aOldFrame) {
262 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
263
264 if (!aOldFrame) {
265 return;
266 }
267 bool contentRemoval = false;
268
269 if (aOldFrame->IsTableColFrame()) {
270 nsTableColFrame* colFrame = (nsTableColFrame*)aOldFrame;
271 if (colFrame->GetColType() == eColContent) {
272 contentRemoval = true;
273 // Remove any anonymous column frames this <col> produced via a colspan
274 nsTableColFrame* col = colFrame->GetNextCol();
275 nsTableColFrame* nextCol;
276 while (col && col->GetColType() == eColAnonymousCol) {
277 #ifdef DEBUG
278 #endif
279 nextCol = col->GetNextCol();
280 RemoveFrame(kPrincipalList, col);
281 col = nextCol;
282 }
283 }
284
285 int32_t colIndex = colFrame->GetColIndex();
286 // The RemoveChild call handles calling FrameNeedsReflow on us.
287 RemoveChild(*colFrame, true);
288
289 nsTableFrame* tableFrame = GetTableFrame();
290 tableFrame->RemoveCol(this, colIndex, true, true);
291 if (mFrames.IsEmpty() && contentRemoval && !IsSynthetic()) {
292 tableFrame->AppendAnonymousColFrames(this, GetSpan(),
293 eColAnonymousColGroup, true);
294 }
295 } else {
296 mFrames.DestroyFrame(aOldFrame);
297 }
298 }
299
GetLogicalSkipSides() const300 nsIFrame::LogicalSides nsTableColGroupFrame::GetLogicalSkipSides() const {
301 LogicalSides skip(mWritingMode);
302 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
303 StyleBoxDecorationBreak::Clone)) {
304 return skip;
305 }
306
307 if (GetPrevInFlow()) {
308 skip |= eLogicalSideBitsBStart;
309 }
310 if (GetNextInFlow()) {
311 skip |= eLogicalSideBitsBEnd;
312 }
313 return skip;
314 }
315
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)316 void nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
317 ReflowOutput& aDesiredSize,
318 const ReflowInput& aReflowInput,
319 nsReflowStatus& aStatus) {
320 MarkInReflow();
321 DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
322 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
323 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
324 NS_ASSERTION(nullptr != mContent, "bad state -- null content for frame");
325
326 const nsStyleVisibility* groupVis = StyleVisibility();
327 bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
328 if (collapseGroup) {
329 GetTableFrame()->SetNeedToCollapse(true);
330 }
331 // for every content child that (is a column thingy and does not already have
332 // a frame) create a frame and adjust it's style
333
334 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
335 kidFrame = kidFrame->GetNextSibling()) {
336 // Give the child frame a chance to reflow, even though we know it'll have 0
337 // size
338 ReflowOutput kidSize(aReflowInput);
339 ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
340 LogicalSize(kidFrame->GetWritingMode()));
341
342 nsReflowStatus status;
343 ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, 0, 0,
344 ReflowChildFlags::Default, status);
345 FinishReflowChild(kidFrame, aPresContext, kidSize, &kidReflowInput, 0, 0,
346 ReflowChildFlags::Default);
347 }
348
349 aDesiredSize.ClearSize();
350 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
351 }
352
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)353 void nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
354 const nsDisplayListSet& aLists) {
355 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
356 // "All css properties of table-column and table-column-group boxes are
357 // ignored, except when explicitly specified by this specification."
358 // CSS outlines and box-shadows fall into this category, so we skip them
359 // on these boxes.
360 MOZ_ASSERT_UNREACHABLE("Colgroups don't paint themselves");
361 }
362
GetFirstColumn()363 nsTableColFrame* nsTableColGroupFrame::GetFirstColumn() {
364 return GetNextColumn(nullptr);
365 }
366
GetNextColumn(nsIFrame * aChildFrame)367 nsTableColFrame* nsTableColGroupFrame::GetNextColumn(nsIFrame* aChildFrame) {
368 nsTableColFrame* result = nullptr;
369 nsIFrame* childFrame = aChildFrame;
370 if (!childFrame) {
371 childFrame = mFrames.FirstChild();
372 } else {
373 childFrame = childFrame->GetNextSibling();
374 }
375 while (childFrame) {
376 if (mozilla::StyleDisplay::TableColumn ==
377 childFrame->StyleDisplay()->mDisplay) {
378 result = (nsTableColFrame*)childFrame;
379 break;
380 }
381 childFrame = childFrame->GetNextSibling();
382 }
383 return result;
384 }
385
GetSpan()386 int32_t nsTableColGroupFrame::GetSpan() { return StyleTable()->mXSpan; }
387
SetContinuousBCBorderWidth(LogicalSide aForSide,BCPixelSize aPixelValue)388 void nsTableColGroupFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
389 BCPixelSize aPixelValue) {
390 switch (aForSide) {
391 case eLogicalSideBStart:
392 mBStartContBorderWidth = aPixelValue;
393 return;
394 case eLogicalSideBEnd:
395 mBEndContBorderWidth = aPixelValue;
396 return;
397 default:
398 NS_ERROR("invalid side arg");
399 }
400 }
401
GetContinuousBCBorderWidth(WritingMode aWM,LogicalMargin & aBorder)402 void nsTableColGroupFrame::GetContinuousBCBorderWidth(WritingMode aWM,
403 LogicalMargin& aBorder) {
404 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
405 nsTableColFrame* col =
406 GetTableFrame()->GetColFrame(mStartColIndex + mColCount - 1);
407 col->GetContinuousBCBorderWidth(aWM, aBorder);
408 aBorder.BStart(aWM) = BC_BORDER_END_HALF_COORD(d2a, mBStartContBorderWidth);
409 aBorder.BEnd(aWM) = BC_BORDER_START_HALF_COORD(d2a, mBEndContBorderWidth);
410 }
411
412 /* ----- global methods ----- */
413
NS_NewTableColGroupFrame(PresShell * aPresShell,ComputedStyle * aStyle)414 nsTableColGroupFrame* NS_NewTableColGroupFrame(PresShell* aPresShell,
415 ComputedStyle* aStyle) {
416 return new (aPresShell)
417 nsTableColGroupFrame(aStyle, aPresShell->GetPresContext());
418 }
419
NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)420 NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)
421
422 void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
423 bool aRebuildDisplayItems) {
424 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
425 if (GetTableFrame()->IsBorderCollapse()) {
426 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
427 aDisplayItemKey, false);
428 }
429 }
430
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)431 void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
432 uint32_t aDisplayItemKey,
433 bool aRebuildDisplayItems) {
434 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
435 aRebuildDisplayItems);
436 // If we have filters applied that would affects our bounds, then
437 // we get an inactive layer created and this is computed
438 // within FrameLayerBuilder
439 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
440 false);
441 }
442
443 #ifdef DEBUG_FRAME_DUMP
GetFrameName(nsAString & aResult) const444 nsresult nsTableColGroupFrame::GetFrameName(nsAString& aResult) const {
445 return MakeFrameName(u"TableColGroup"_ns, aResult);
446 }
447
Dump(int32_t aIndent)448 void nsTableColGroupFrame::Dump(int32_t aIndent) {
449 char* indent = new char[aIndent + 1];
450 if (!indent) return;
451 for (int32_t i = 0; i < aIndent + 1; i++) {
452 indent[i] = ' ';
453 }
454 indent[aIndent] = 0;
455
456 printf(
457 "%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d "
458 "isSynthetic=%s",
459 indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan(),
460 IsSynthetic() ? "true" : "false");
461
462 // verify the colindices
463 int32_t j = GetStartColumnIndex();
464 nsTableColFrame* col = GetFirstColumn();
465 while (col) {
466 NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
467 col = col->GetNextCol();
468 j++;
469 }
470 NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
471 "number of cols out of sync");
472 printf("\n%s**END COLGROUP DUMP** ", indent);
473 delete[] indent;
474 }
475 #endif
476