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