Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / tables / nsTableColGroupFrame.cpp
blob44cf928ff4ebf252e21f45de9a24971535cfa2b1
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"
16 #include "nsCOMPtr.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()) ||
43 !aStartColFrame) {
44 colGroupFrame->SetStartColumnIndex(colIndex);
46 nsIFrame* colFrame = aStartColFrame;
47 if (!colFrame || (colIndex != aFirstColIndex)) {
48 colFrame = colGroupFrame->PrincipalChildList().FirstChild();
50 while (colFrame) {
51 if (colFrame->IsTableColFrame()) {
52 ((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
53 colIndex++;
55 colFrame = colFrame->GetNextSibling();
58 colGroupFrame =
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
75 // of *colIter.
76 auto colIter = aCols.begin();
77 for (auto colIterEnd = aCols.end(); *colIter && colIter != colIterEnd;
78 ++colIter) {
79 auto* colFrame = static_cast<nsTableColFrame*>(*colIter);
80 colFrame->SetColIndex(colIndex);
81 mColCount++;
82 tableFrame->InsertCol(*colFrame, colIndex);
83 colIndex++;
86 for (; *colIter; ++colIter) {
87 auto* colFrame = static_cast<nsTableColFrame*>(*colIter);
88 colFrame->SetColIndex(colIndex);
89 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);
100 return NS_OK;
103 nsTableColGroupFrame* nsTableColGroupFrame::GetLastRealColGroup(
104 nsTableFrame* aTableFrame) {
105 const nsFrameList& colGroups = aTableFrame->GetColGroups();
107 auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild());
108 if (!lastColGroup) {
109 return nullptr;
112 if (!lastColGroup->IsSynthetic()) {
113 return lastColGroup;
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");
125 #ifdef DEBUG
126 for (nsIFrame* f : aChildList) {
127 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
129 #endif
130 if (aChildList.IsEmpty()) {
131 GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
132 eColAnonymousColGroup, false);
133 return;
136 mFrames.AppendFrames(this, std::move(aChildList));
139 /* virtual */
140 void nsTableColGroupFrame::DidSetComputedStyle(
141 ComputedStyle* aOldComputedStyle) {
142 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
144 if (!aOldComputedStyle) // avoid this on init
145 return;
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);
172 col = nextCol;
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);
211 col = nextCol;
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);
256 mColCount--;
257 if (aResetSubsequentColIndices) {
258 if (nextChild) { // reset inside this and all following colgroups
259 ResetColIndices(this, colIndex, nextChild);
260 } else {
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,
272 ChildListID aListID,
273 nsIFrame* aOldFrame) {
274 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
276 if (!aOldFrame) {
277 return;
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);
291 col = nextCol;
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);
305 } else {
306 mFrames.DestroyFrame(aContext, aOldFrame);
310 nsIFrame::LogicalSides nsTableColGroupFrame::GetLogicalSkipSides() const {
311 LogicalSides skip(mWritingMode);
312 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
313 StyleBoxDecorationBreak::Clone)) {
314 return skip;
317 if (GetPrevInFlow()) {
318 skip |= eLogicalSideBitsBStart;
320 if (GetNextInFlow()) {
321 skip |= eLogicalSideBitsBEnd;
323 return skip;
326 void nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
327 ReflowOutput& aDesiredSize,
328 const ReflowInput& aReflowInput,
329 nsReflowStatus& aStatus) {
330 MarkInReflow();
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;
338 if (collapseGroup) {
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
345 // size
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
367 // on these boxes.
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;
378 if (!childFrame) {
379 childFrame = mFrames.FirstChild();
380 } else {
381 childFrame = childFrame->GetNextSibling();
383 while (childFrame) {
384 if (mozilla::StyleDisplay::TableColumn ==
385 childFrame->StyleDisplay()->mDisplay) {
386 result = (nsTableColFrame*)childFrame;
387 break;
389 childFrame = childFrame->GetNextSibling();
391 return result;
394 int32_t nsTableColGroupFrame::GetSpan() { return StyleTable()->mXSpan; }
396 void nsTableColGroupFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
397 BCPixelSize aPixelValue) {
398 switch (aForSide) {
399 case eLogicalSideBStart:
400 mBStartContBorderWidth = aPixelValue;
401 return;
402 case eLogicalSideBEnd:
403 mBEndContBorderWidth = aPixelValue;
404 return;
405 default:
406 NS_ERROR("invalid side arg");
410 void nsTableColGroupFrame::GetContinuousBCBorderWidth(WritingMode aWM,
411 LogicalMargin& aBorder) {
412 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
413 nsTableColFrame* col =
414 GetTableFrame()->GetColFrame(mStartColIndex + mColCount - 1);
415 col->GetContinuousBCBorderWidth(aWM, aBorder);
416 aBorder.BStart(aWM) = BC_BORDER_END_HALF_COORD(d2a, mBStartContBorderWidth);
417 aBorder.BEnd(aWM) = BC_BORDER_START_HALF_COORD(d2a, mBEndContBorderWidth);
420 /* ----- global methods ----- */
422 nsTableColGroupFrame* NS_NewTableColGroupFrame(PresShell* aPresShell,
423 ComputedStyle* aStyle) {
424 return new (aPresShell)
425 nsTableColGroupFrame(aStyle, aPresShell->GetPresContext());
428 NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)
430 void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
431 bool aRebuildDisplayItems) {
432 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
433 if (GetTableFrame()->IsBorderCollapse()) {
434 const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
435 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
436 aDisplayItemKey, rebuild);
440 void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
441 uint32_t aDisplayItemKey,
442 bool aRebuildDisplayItems) {
443 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
444 aRebuildDisplayItems);
445 // If we have filters applied that would affects our bounds, then
446 // we get an inactive layer created and this is computed
447 // within FrameLayerBuilder
448 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
449 aRebuildDisplayItems);
452 #ifdef DEBUG_FRAME_DUMP
453 nsresult nsTableColGroupFrame::GetFrameName(nsAString& aResult) const {
454 return MakeFrameName(u"TableColGroup"_ns, aResult);
457 void nsTableColGroupFrame::Dump(int32_t aIndent) {
458 char* indent = new char[aIndent + 1];
459 if (!indent) return;
460 for (int32_t i = 0; i < aIndent + 1; i++) {
461 indent[i] = ' ';
463 indent[aIndent] = 0;
465 printf(
466 "%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d "
467 "isSynthetic=%s",
468 indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan(),
469 IsSynthetic() ? "true" : "false");
471 // verify the colindices
472 DebugOnly<int32_t> j = GetStartColumnIndex();
473 nsTableColFrame* col = GetFirstColumn();
474 while (col) {
475 NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
476 col = col->GetNextCol();
477 j++;
479 NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
480 "number of cols out of sync");
481 printf("\n%s**END COLGROUP DUMP** ", indent);
482 delete[] indent;
484 #endif