nsTableCellFrame.cpp (44525B)
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 6 #include "nsTableCellFrame.h" 7 8 #include <algorithm> 9 10 #include "celldata.h" 11 #include "gfxContext.h" 12 #include "gfxUtils.h" 13 #include "mozilla/ComputedStyle.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/ScrollContainerFrame.h" 16 #include "mozilla/StaticPrefs_layout.h" 17 #include "mozilla/gfx/2D.h" 18 #include "mozilla/gfx/Helpers.h" 19 #include "nsAttrValueInlines.h" 20 #include "nsCSSRendering.h" 21 #include "nsDisplayList.h" 22 #include "nsGenericHTMLElement.h" 23 #include "nsGkAtoms.h" 24 #include "nsHTMLParts.h" 25 #include "nsIContent.h" 26 #include "nsIFrame.h" 27 #include "nsIFrameInlines.h" 28 #include "nsLayoutUtils.h" 29 #include "nsPresContext.h" 30 #include "nsStyleConsts.h" 31 #include "nsTableColFrame.h" 32 #include "nsTableFrame.h" 33 #include "nsTableRowFrame.h" 34 #include "nsTableRowGroupFrame.h" 35 #include "nsTextFrame.h" 36 37 // TABLECELL SELECTION 38 #include "mozilla/LookAndFeel.h" 39 #include "nsFrameSelection.h" 40 41 #ifdef ACCESSIBILITY 42 # include "nsAccessibilityService.h" 43 #endif 44 45 using namespace mozilla; 46 using namespace mozilla::gfx; 47 using namespace mozilla::image; 48 49 nsTableCellFrame::nsTableCellFrame(ComputedStyle* aStyle, 50 nsTableFrame* aTableFrame, ClassID aID) 51 : nsContainerFrame(aStyle, aTableFrame->PresContext(), aID), 52 mDesiredSize(aTableFrame->GetWritingMode()) { 53 SetContentEmpty(false); 54 } 55 56 nsTableCellFrame::~nsTableCellFrame() = default; 57 58 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame) 59 60 void nsTableCellFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 61 nsIFrame* aPrevInFlow) { 62 // Let the base class do its initialization 63 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 64 65 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) { 66 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); 67 } 68 69 if (aPrevInFlow) { 70 // Set the column index 71 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow; 72 uint32_t colIndex = cellFrame->ColIndex(); 73 SetColIndex(colIndex); 74 } else { 75 // Although the spec doesn't say that writing-mode is not applied to 76 // table-cells, we still override style value here because we want to 77 // make effective writing mode of table structure frames consistent 78 // within a table. The content inside table cells is reflowed by an 79 // anonymous block, hence their writing mode is not affected. 80 mWritingMode = GetTableFrame()->GetWritingMode(); 81 } 82 } 83 84 void nsTableCellFrame::Destroy(DestroyContext& aContext) { 85 nsTableFrame::MaybeUnregisterPositionedTablePart(this); 86 nsContainerFrame::Destroy(aContext); 87 } 88 89 // nsIPercentBSizeObserver methods 90 91 void nsTableCellFrame::NotifyPercentBSize(const ReflowInput& aReflowInput) { 92 // ReflowInput ensures the mCBReflowInput of blocks inside a 93 // cell is the cell frame, not the inner-cell block, and that the 94 // containing block of an inner table is the containing block of its 95 // table wrapper. 96 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of 97 // these tests are probably unnecessary. 98 99 // Maybe the cell reflow input; we sure if we're inside the |if|. 100 const ReflowInput* cellRI = aReflowInput.mCBReflowInput; 101 102 if (cellRI && cellRI->mFrame == this && 103 (cellRI->ComputedBSize() == NS_UNCONSTRAINEDSIZE || 104 cellRI->ComputedBSize() == 0)) { // XXXldb Why 0? 105 // This is a percentage bsize on a frame whose percentage bsizes 106 // are based on the bsize of the cell, since its containing block 107 // is the inner cell frame. 108 109 // We'll only honor the percent bsize if sibling-cells/ancestors 110 // have specified/pct bsize. (Also, siblings only count for this if 111 // both this cell and the sibling cell span exactly 1 row.) 112 113 if (nsTableFrame::AncestorsHaveStyleBSize(*cellRI) || 114 (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 && 115 cellRI->mParentReflowInput->mFrame->HasAnyStateBits( 116 NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) { 117 for (const ReflowInput* rs = aReflowInput.mParentReflowInput; 118 rs != cellRI; rs = rs->mParentReflowInput) { 119 rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 120 } 121 122 nsTableFrame::RequestSpecialBSizeReflow(*cellRI); 123 } 124 } 125 } 126 127 // The cell needs to observe its block and things inside its block but nothing 128 // below that 129 bool nsTableCellFrame::NeedsToObserve(const ReflowInput& aReflowInput) { 130 const ReflowInput* rs = aReflowInput.mParentReflowInput; 131 if (!rs) { 132 return false; 133 } 134 if (rs->mFrame == this) { 135 // We always observe the child block. It will never send any 136 // notifications, but we need this so that the observer gets 137 // propagated to its kids. 138 return true; 139 } 140 rs = rs->mParentReflowInput; 141 if (!rs) { 142 return false; 143 } 144 145 // We always need to let the percent bsize observer be propagated 146 // from a table wrapper frame to an inner table frame. 147 LayoutFrameType fType = aReflowInput.mFrame->Type(); 148 if (fType == LayoutFrameType::Table) { 149 return true; 150 } 151 152 // We need the observer to be propagated to all children of the cell 153 // (i.e., children of the child block) in quirks mode, but only to 154 // tables in standards mode. 155 // XXX This may not be true in the case of orthogonal flows within 156 // the cell (bug 1174711 comment 8); we may need to observe isizes 157 // instead of bsizes for orthogonal children. 158 return rs->mFrame == this && 159 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks || 160 fType == LayoutFrameType::TableWrapper); 161 } 162 163 nsresult nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID, 164 nsAtom* aAttribute, AttrModType) { 165 // We need to recalculate in this case because of the nowrap quirk in 166 // BasicTableLayoutStrategy 167 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap && 168 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { 169 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors, 170 NS_FRAME_IS_DIRTY); 171 } 172 173 const nsAtom* colSpanAttribute = 174 MOZ_UNLIKELY(mContent->AsElement()->IsMathMLElement()) 175 ? nsGkAtoms::columnspan 176 : nsGkAtoms::colspan; 177 if (aAttribute == nsGkAtoms::rowspan || aAttribute == colSpanAttribute) { 178 nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0}, 179 nsChangeHint_UpdateTableCellSpans); 180 } 181 return NS_OK; 182 } 183 184 /* virtual */ 185 void nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { 186 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle); 187 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle); 188 189 if (!aOldComputedStyle) { 190 return; // avoid the following on init 191 } 192 193 #ifdef ACCESSIBILITY 194 if (nsAccessibilityService* accService = GetAccService()) { 195 if (StyleBorder()->GetComputedBorder() != 196 aOldComputedStyle->StyleBorder()->GetComputedBorder()) { 197 // If a table cell's computed border changes, it can change whether or 198 // not its parent table is classified as a layout or data table. We 199 // send a notification here to invalidate the a11y cache on the table 200 // so the next fetch of IsProbablyLayoutTable() is accurate. 201 accService->TableLayoutGuessMaybeChanged(PresShell(), mContent); 202 } 203 } 204 #endif 205 206 nsTableFrame* tableFrame = GetTableFrame(); 207 if (tableFrame->IsBorderCollapse() && 208 tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) { 209 uint32_t colIndex = ColIndex(); 210 uint32_t rowIndex = RowIndex(); 211 // row span needs to be clamped as we do not create rows in the cellmap 212 // which do not have cells originating in them 213 TableArea damageArea(colIndex, rowIndex, GetColSpan(), 214 std::min(static_cast<uint32_t>(GetRowSpan()), 215 tableFrame->GetRowCount() - rowIndex)); 216 tableFrame->AddBCDamageArea(damageArea); 217 } 218 } 219 220 #ifdef DEBUG 221 void nsTableCellFrame::AppendFrames(ChildListID aListID, 222 nsFrameList&& aFrameList) { 223 MOZ_CRASH("unsupported operation"); 224 } 225 226 void nsTableCellFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, 227 const nsLineList::iterator* aPrevFrameLine, 228 nsFrameList&& aFrameList) { 229 MOZ_CRASH("unsupported operation"); 230 } 231 232 void nsTableCellFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) { 233 MOZ_CRASH("unsupported operation"); 234 } 235 #endif 236 237 void nsTableCellFrame::SetColIndex(int32_t aColIndex) { mColIndex = aColIndex; } 238 239 /* virtual */ 240 nsMargin nsTableCellFrame::GetUsedMargin() const { 241 return nsMargin(0, 0, 0, 0); 242 } 243 244 // ASSURE DIFFERENT COLORS for selection 245 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) { 246 if (colorA == colorB) { 247 nscolor res; 248 res = NS_RGB(NS_GET_R(colorA) ^ 0xff, NS_GET_G(colorA) ^ 0xff, 249 NS_GET_B(colorA) ^ 0xff); 250 return res; 251 } 252 return colorA; 253 } 254 255 void nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget, 256 nsPoint aPt) { 257 NS_ASSERTION(IsSelected(), "Should only be called for selected cells"); 258 if (!IsSelectable()) { 259 return; 260 } 261 RefPtr<nsFrameSelection> frameSelection = PresShell()->FrameSelection(); 262 if (!frameSelection->IsInTableSelectionMode()) { 263 return; 264 } 265 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3); 266 if (mRect.width <= threePx || mRect.height <= threePx) { 267 return; 268 } 269 nscolor bordercolor; 270 if (frameSelection->GetDisplaySelection() == 271 nsISelectionController::SELECTION_DISABLED) { 272 bordercolor = NS_RGB(176, 176, 176); // disabled color 273 } else { 274 bordercolor = LookAndFeel::Color(LookAndFeel::ColorID::Highlight, this); 275 } 276 // compare bordercolor to background-color 277 bordercolor = EnsureDifferentColors(bordercolor, 278 StyleBackground()->BackgroundColor(this)); 279 280 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); 281 Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel); 282 283 AutoRestoreTransform autoRestoreTransform(aDrawTarget); 284 aDrawTarget->SetTransform( 285 aDrawTarget->GetTransform().PreTranslate(devPixelOffset)); 286 287 ColorPattern color(ToDeviceColor(bordercolor)); 288 289 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 290 291 StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0), 292 appUnitsPerDevPixel, *aDrawTarget, color); 293 StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height), 294 appUnitsPerDevPixel, *aDrawTarget, color); 295 StrokeLineWithSnapping(nsPoint(onePixel, mRect.height), 296 nsPoint(mRect.width, mRect.height), 297 appUnitsPerDevPixel, *aDrawTarget, color); 298 StrokeLineWithSnapping(nsPoint(mRect.width, onePixel), 299 nsPoint(mRect.width, mRect.height), 300 appUnitsPerDevPixel, *aDrawTarget, color); 301 // middle 302 nsRect r(onePixel, onePixel, mRect.width - onePixel, mRect.height - onePixel); 303 Rect devPixelRect = NSRectToSnappedRect(r, appUnitsPerDevPixel, *aDrawTarget); 304 aDrawTarget->StrokeRect(devPixelRect, color); 305 // shading 306 StrokeLineWithSnapping( 307 nsPoint(2 * onePixel, mRect.height - 2 * onePixel), 308 nsPoint(mRect.width - onePixel, mRect.height - (2 * onePixel)), 309 appUnitsPerDevPixel, *aDrawTarget, color); 310 StrokeLineWithSnapping( 311 nsPoint(mRect.width - (2 * onePixel), 2 * onePixel), 312 nsPoint(mRect.width - (2 * onePixel), mRect.height - onePixel), 313 appUnitsPerDevPixel, *aDrawTarget, color); 314 } 315 316 void nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame, 317 nsDisplayListBuilder* aBuilder, 318 const nsDisplayListSet& aLists) { 319 const nsStyleBorder* borderStyle = StyleBorder(); 320 if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) { 321 return; 322 } 323 324 if (!GetContentEmpty() || 325 StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show) { 326 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this); 327 } 328 } 329 330 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey, 331 bool aRebuildDisplayItems) { 332 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems); 333 if (GetTableFrame()->IsBorderCollapse()) { 334 const bool rebuild = StaticPrefs::layout_display_list_retain_sc(); 335 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(), 336 aDisplayItemKey, rebuild); 337 } 338 } 339 340 void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect, 341 uint32_t aDisplayItemKey, 342 bool aRebuildDisplayItems) { 343 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey, 344 aRebuildDisplayItems); 345 // If we have filters applied that would affects our bounds, then 346 // we get an inactive layer created and this is computed 347 // within FrameLayerBuilder 348 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey, 349 aRebuildDisplayItems); 350 } 351 352 bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const { 353 // If we're not visible, we don't paint. 354 if (!StyleVisibility()->IsVisible()) { 355 return false; 356 } 357 358 // Consider 'empty-cells', but only in separated borders mode. 359 if (!GetContentEmpty()) { 360 return true; 361 } 362 363 nsTableFrame* tableFrame = GetTableFrame(); 364 if (tableFrame->IsBorderCollapse()) { 365 return true; 366 } 367 368 return StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show; 369 } 370 371 bool nsTableCellFrame::ShouldPaintBackground(nsDisplayListBuilder* aBuilder) { 372 return ShouldPaintBordersAndBackgrounds(); 373 } 374 375 LogicalSides nsTableCellFrame::GetLogicalSkipSides() const { 376 LogicalSides skip(mWritingMode); 377 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == 378 StyleBoxDecorationBreak::Clone)) { 379 return skip; 380 } 381 382 if (GetPrevInFlow()) { 383 skip += LogicalSide::BStart; 384 } 385 if (GetNextInFlow()) { 386 skip += LogicalSide::BEnd; 387 } 388 return skip; 389 } 390 391 /* virtual */ 392 nsMargin nsTableCellFrame::GetBorderOverflow() { return nsMargin(0, 0, 0, 0); } 393 394 void nsTableCellFrame::AlignChildWithinCell( 395 nscoord aMaxAscent, ForceAlignTopForTableCell aForceAlignTop) { 396 MOZ_ASSERT(aForceAlignTop != ForceAlignTopForTableCell::Yes || 397 PresContext()->IsPaginated(), 398 "We shouldn't force table-cells to do top alignment if " 399 "we're not in printing!"); 400 401 nsIFrame* const inner = Inner(); 402 const WritingMode tableWM = GetWritingMode(); 403 const WritingMode innerWM = inner->GetWritingMode(); 404 405 // The anonymous block child is to be placed within the cell's padding rect. 406 // Get it in the inner frame's writing mode for alignment calculation. 407 const nsSize containerSize = mRect.Size(); 408 const LogicalRect paddingRect(innerWM, GetPaddingRectRelativeToSelf(), 409 containerSize); 410 411 const LogicalRect kidRect = inner->GetLogicalRect(innerWM, containerSize); 412 413 // Calculate the position for the inner frame, initializing to the origin. 414 LogicalPoint kidPosition = paddingRect.Origin(innerWM); 415 416 // Apply table cell alignment to the block coordinate. 417 const auto alignment = aForceAlignTop == ForceAlignTopForTableCell::Yes 418 ? TableCellAlignment::Top 419 : GetTableCellAlignment(); 420 switch (alignment) { 421 case TableCellAlignment::Baseline: 422 if (auto baseline = GetCellBaseline()) { 423 // Align the baseline of the child frame with the baselines of other 424 // children in the same row which have baseline alignment 425 kidPosition.B(innerWM) = 426 paddingRect.BStart(innerWM) + aMaxAscent - *baseline; 427 break; 428 } 429 // fallback to start alignment 430 [[fallthrough]]; 431 case TableCellAlignment::Top: 432 // Leave kidPosition at the origin: the child frame will be aligned 433 // with the padding rect's block-start. 434 break; 435 436 case TableCellAlignment::Bottom: 437 // Align the block-end of the child frame with the block-end of the 438 // padding rect. 439 kidPosition.B(innerWM) = 440 paddingRect.BEnd(innerWM) - kidRect.BSize(innerWM); 441 break; 442 443 default: 444 case TableCellAlignment::Middle: 445 // Align the middle of the child frame with the middle of the cell's 446 // padding rect. 447 kidPosition.B(innerWM) = 448 paddingRect.BStart(innerWM) + 449 (paddingRect.BSize(innerWM) - kidRect.BSize(innerWM)) / 2; 450 } 451 452 // If the content is larger than the cell bSize, align from the padding-rect's 453 // bStart edge. 454 kidPosition.B(innerWM) = 455 std::max(paddingRect.BStart(innerWM), kidPosition.B(innerWM)); 456 457 if (kidPosition != kidRect.Origin(innerWM)) { 458 // If we're moving the inner frame, invalidate at the old position first. 459 inner->InvalidateFrameSubtree(); 460 } 461 462 inner->SetPosition(innerWM, kidPosition, containerSize); 463 464 ReflowOutput reflowOutput(tableWM); 465 reflowOutput.SetSize(tableWM, GetLogicalSize(tableWM)); 466 467 nsRect overflow(nsPoint(), GetSize()); 468 overflow.Inflate(GetBorderOverflow()); 469 reflowOutput.mOverflowAreas.SetAllTo(overflow); 470 ConsiderChildOverflow(reflowOutput.mOverflowAreas, inner); 471 FinishAndStoreOverflow(&reflowOutput); 472 473 if (kidPosition != kidRect.Origin(innerWM)) { 474 // Invalidate new overflow rect. 475 inner->InvalidateFrameSubtree(); 476 } 477 } 478 479 bool nsTableCellFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) { 480 nsRect bounds(nsPoint(0, 0), GetSize()); 481 bounds.Inflate(GetBorderOverflow()); 482 483 aOverflowAreas.UnionAllWith(bounds); 484 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas); 485 } 486 487 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom', 488 // length, percentage, and calc() values to 'baseline'. 489 TableCellAlignment nsTableCellFrame::GetTableCellAlignment() const { 490 const StyleVerticalAlign& verticalAlign = StyleDisplay()->mVerticalAlign; 491 if (verticalAlign.IsKeyword()) { 492 auto value = verticalAlign.AsKeyword(); 493 switch (value) { 494 case StyleVerticalAlignKeyword::Top: 495 return TableCellAlignment::Top; 496 case StyleVerticalAlignKeyword::Middle: 497 return TableCellAlignment::Middle; 498 case StyleVerticalAlignKeyword::Bottom: 499 return TableCellAlignment::Bottom; 500 default: 501 break; 502 } 503 } 504 return TableCellAlignment::Baseline; 505 } 506 507 static bool CellHasVisibleContent(nsTableFrame* aTableFrame, 508 nsTableCellFrame* aCell) { 509 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells 510 nsIFrame* content = aCell->CellContentFrame(); 511 if (content->GetContentRect().Height() > 0) { 512 return true; 513 } 514 if (aTableFrame->IsBorderCollapse()) { 515 return true; 516 } 517 for (nsIFrame* innerFrame : content->PrincipalChildList()) { 518 LayoutFrameType frameType = innerFrame->Type(); 519 if (LayoutFrameType::Text == frameType) { 520 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame); 521 if (textFrame->HasNoncollapsedCharacters()) { 522 return true; 523 } 524 } else if (LayoutFrameType::Placeholder != frameType) { 525 return true; 526 } else if (nsLayoutUtils::GetFloatFromPlaceholder(innerFrame)) { 527 return true; 528 } 529 } 530 return false; 531 } 532 533 nsIFrame* nsTableCellFrame::Inner() const { 534 MOZ_ASSERT(mFrames.OnlyChild(), 535 "A table cell should have exactly one child!"); 536 return mFrames.FirstChild(); 537 } 538 539 nsIFrame* nsTableCellFrame::CellContentFrame() const { 540 nsIFrame* inner = Inner(); 541 if (ScrollContainerFrame* sf = do_QueryFrame(inner)) { 542 return sf->GetScrolledFrame(); 543 } 544 return inner; 545 } 546 547 Maybe<nscoord> nsTableCellFrame::GetCellBaseline() const { 548 // Empty cells don't participate in baseline alignment - fallback to 549 // start alignment. 550 if (GetContentEmpty()) { 551 return {}; 552 } 553 // Ignore the position of the inner frame relative to the cell frame 554 // since we want the position as though the inner were top-aligned. 555 const auto wm = GetWritingMode(); 556 nscoord result; 557 if (StyleDisplay()->IsContainLayout() || 558 !nsLayoutUtils::GetFirstLineBaseline(wm, Inner(), &result)) { 559 // Synthesize a baseline from our content box, see bug 1591219. 560 return Some(CellContentFrame()->ContentBSize(wm) + 561 GetLogicalUsedBorderAndPadding(wm).BStart(wm)); 562 } 563 // `result` already includes the padding-start from the inner frame. 564 return Some(result + GetLogicalUsedBorder(wm).BStart(wm)); 565 } 566 567 int32_t nsTableCellFrame::GetRowSpan() { 568 int32_t rowSpan = 1; 569 570 // Don't look at the content's rowspan if we're a pseudo cell 571 if (!Style()->IsPseudoOrAnonBox()) { 572 dom::Element* elem = mContent->AsElement(); 573 const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan); 574 // Note that we don't need to check the tag name, because only table cells 575 // (including MathML <mtd>) and table headers parse the "rowspan" attribute 576 // into an integer. 577 if (attr && attr->Type() == nsAttrValue::eInteger) { 578 rowSpan = attr->GetIntegerValue(); 579 } 580 } 581 return rowSpan; 582 } 583 584 int32_t nsTableCellFrame::GetColSpan() { 585 int32_t colSpan = 1; 586 587 // Don't look at the content's colspan if we're a pseudo cell 588 if (!Style()->IsPseudoOrAnonBox()) { 589 dom::Element* elem = mContent->AsElement(); 590 const nsAttrValue* attr = elem->GetParsedAttr( 591 MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan 592 : nsGkAtoms::colspan); 593 // Note that we don't need to check the tag name, because only table cells 594 // (including MathML <mtd>) and table headers parse the "colspan" attribute 595 // into an integer. 596 if (attr && attr->Type() == nsAttrValue::eInteger) { 597 colSpan = attr->GetIntegerValue(); 598 } 599 } 600 return colSpan; 601 } 602 603 ScrollContainerFrame* nsTableCellFrame::GetScrollTargetFrame() const { 604 return do_QueryFrame(Inner()); 605 } 606 607 nscoord nsTableCellFrame::IntrinsicISize(const IntrinsicSizeInput& aInput, 608 IntrinsicISizeType aType) { 609 // Note: a table cell has the same writing mode as its table ancestor, which 610 // may differ from its inner frame that derives its writing mode from the 611 // style of the <td> element. See nsTableCellFrame::Init(). 612 const IntrinsicSizeInput innerInput(aInput, Inner()->GetWritingMode(), 613 GetWritingMode()); 614 return nsLayoutUtils::IntrinsicForContainer( 615 innerInput.mContext, Inner(), aType, 616 innerInput.mPercentageBasisForChildren, nsLayoutUtils::IGNORE_PADDING); 617 } 618 619 /* virtual */ nsIFrame::IntrinsicSizeOffsetData 620 nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) { 621 IntrinsicSizeOffsetData result = 622 nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis); 623 624 result.margin = 0; 625 626 WritingMode wm = GetWritingMode(); 627 result.border = GetBorderWidth(wm).IStartEnd(wm); 628 629 return result; 630 } 631 632 #ifdef DEBUG 633 # define PROBABLY_TOO_LARGE 1000000 634 static void DebugCheckChildSize(nsIFrame* aChild, ReflowOutput& aMet) { 635 WritingMode wm = aMet.GetWritingMode(); 636 if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) { 637 printf("WARNING: cell content %p has large inline size %d \n", 638 static_cast<void*>(aChild), int32_t(aMet.ISize(wm))); 639 } 640 } 641 #endif 642 643 // the computed bsize for the cell, which descendants use for percent bsize 644 // calculations it is the bsize (minus border, padding) of the cell's first in 645 // flow during its final reflow without an unconstrained bsize. 646 static nscoord CalcUnpaginatedBSize(nsTableCellFrame& aCellFrame, 647 nsTableFrame& aTableFrame, 648 nscoord aBlockDirBorderPadding) { 649 const nsTableCellFrame* firstCellInFlow = 650 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow()); 651 nsTableFrame* firstTableInFlow = 652 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); 653 nsTableRowFrame* row = 654 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent()); 655 nsTableRowGroupFrame* firstRGInFlow = 656 static_cast<nsTableRowGroupFrame*>(row->GetParent()); 657 658 uint32_t rowIndex = firstCellInFlow->RowIndex(); 659 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); 660 661 nscoord computedBSize = 662 firstTableInFlow->GetRowSpacing(rowIndex, rowIndex + rowSpan - 1); 663 computedBSize -= aBlockDirBorderPadding; 664 uint32_t rowX; 665 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; 666 row = row->GetNextRow(), rowX++) { 667 if (rowX > rowIndex + rowSpan - 1) { 668 break; 669 } else if (rowX >= rowIndex) { 670 computedBSize += row->GetUnpaginatedBSize(); 671 } 672 } 673 return computedBSize; 674 } 675 676 void nsTableCellFrame::Reflow(nsPresContext* aPresContext, 677 ReflowOutput& aDesiredSize, 678 const ReflowInput& aReflowInput, 679 nsReflowStatus& aStatus) { 680 MarkInReflow(); 681 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); 682 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 683 684 if (aReflowInput.mFlags.mSpecialBSizeReflow) { 685 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); 686 } 687 688 // see if a special bsize reflow needs to occur due to having a pct height 689 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput); 690 691 WritingMode wm = aReflowInput.GetWritingMode(); 692 LogicalSize availSize = aReflowInput.AvailableSize(); 693 694 // @note |this| frame applies borders but not any padding. Our anonymous 695 // inner frame applies the padding (but not borders). 696 LogicalMargin border = GetBorderWidth(wm); 697 698 ReflowOutput kidSize(wm); 699 SetPriorAvailISize(aReflowInput.AvailableISize()); 700 nsIFrame* inner = Inner(); 701 nsTableFrame* tableFrame = GetTableFrame(); 702 703 if (aReflowInput.mFlags.mSpecialBSizeReflow || aPresContext->IsPaginated()) { 704 // Here, we're changing our own reflow input, so we need to account for our 705 // padding, even though we don't apply it anywhere else, to get the correct 706 // percentage resolution on children. 707 const LogicalMargin bp = border + aReflowInput.ComputedLogicalPadding(wm); 708 if (aReflowInput.mFlags.mSpecialBSizeReflow) { 709 const_cast<ReflowInput&>(aReflowInput) 710 .SetComputedBSize(BSize(wm) - bp.BStartEnd(wm)); 711 } else { 712 const nscoord computedUnpaginatedBSize = 713 CalcUnpaginatedBSize(*this, *tableFrame, bp.BStartEnd(wm)); 714 if (computedUnpaginatedBSize > 0) { 715 const_cast<ReflowInput&>(aReflowInput) 716 .SetComputedBSize(computedUnpaginatedBSize); 717 } 718 } 719 } 720 721 // We need to apply the skip sides for current fragmentainer's border after 722 // we finish calculating the special block-size or unpaginated block-size to 723 // prevent the skip sides from affecting the results. 724 // 725 // We assume we are the last fragment by using 726 // PreReflowBlockLevelLogicalSkipSides(), i.e. the block-end border and 727 // padding is not skipped. 728 border.ApplySkipSides(PreReflowBlockLevelLogicalSkipSides()); 729 730 availSize.ISize(wm) -= border.IStartEnd(wm); 731 732 // If we have a constrained available block-size, shrink it by subtracting our 733 // block-direction border and padding for our children. 734 if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { 735 availSize.BSize(wm) -= border.BStart(wm); 736 737 if (aReflowInput.mStyleBorder->mBoxDecorationBreak == 738 StyleBoxDecorationBreak::Clone) { 739 // We have box-decoration-break:clone. Subtract block-end border from the 740 // available block-size as well. 741 availSize.BSize(wm) -= border.BEnd(wm); 742 } 743 } 744 745 // Available block-size can became negative after subtracting block-direction 746 // border and padding. Per spec, to guarantee progress, fragmentainers are 747 // assumed to have a minimum block size of 1px regardless of their used size. 748 // https://drafts.csswg.org/css-break/#breaking-rules 749 availSize.BSize(wm) = 750 std::max(availSize.BSize(wm), nsPresContext::CSSPixelsToAppUnits(1)); 751 752 WritingMode kidWM = inner->GetWritingMode(); 753 ReflowInput kidReflowInput(aPresContext, aReflowInput, inner, 754 availSize.ConvertTo(kidWM, wm), Nothing(), 755 ReflowInput::InitFlag::CallerWillInit); 756 // Override computed padding, in case it's percentage padding 757 { 758 const auto padding = aReflowInput.ComputedLogicalPadding(kidWM); 759 kidReflowInput.Init(aPresContext, Nothing(), Nothing(), Some(padding)); 760 if (inner->IsScrollContainerFrame()) { 761 // Propagate explicit block sizes to our inner frame, if it's a scroll 762 // frame. Note that in table layout, explicit heights act as a minimum 763 // height, see nsTableRowFrame::CalcCellActualBSize. 764 // 765 // Table cells don't respect box-sizing, so we need to remove the 766 // padding, so that the scroll-frame sizes properly (since the 767 // scrollbars also add to the padding area). 768 auto ToScrolledBSize = [&](const nscoord aBSize) { 769 return std::max(0, aBSize - padding.BStartEnd(kidWM)); 770 }; 771 nscoord minBSize = aReflowInput.ComputedMinBSize(); 772 if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { 773 minBSize = std::max(minBSize, aReflowInput.ComputedBSize()); 774 } 775 if (minBSize > 0) { 776 kidReflowInput.SetComputedMinBSize(ToScrolledBSize(minBSize)); 777 } 778 } 779 } 780 781 // Don't be a percent height observer if we're in the middle of 782 // special-bsize reflow, in case we get an accidental NotifyPercentBSize() 783 // call (which we shouldn't honor during special-bsize reflow) 784 if (!aReflowInput.mFlags.mSpecialBSizeReflow) { 785 // mPercentBSizeObserver is for children of cells in quirks mode, 786 // but only those than are tables in standards mode. NeedsToObserve 787 // will determine how far this is propagated to descendants. 788 kidReflowInput.mPercentBSizeObserver = this; 789 } 790 // Don't propagate special bsize reflow input to our kids 791 kidReflowInput.mFlags.mSpecialBSizeReflow = false; 792 793 if (aReflowInput.mFlags.mSpecialBSizeReflow || 794 FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { 795 // We need to force the kid to have mBResize set if we've had a 796 // special reflow in the past, since the non-special reflow needs to 797 // resize back to what it was without the special bsize reflow. 798 kidReflowInput.SetBResize(true); 799 } 800 801 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained(); 802 803 const LogicalPoint kidOrigin = border.StartOffset(wm); 804 const nsRect origRect = inner->GetRect(); 805 const nsRect origInkOverflow = inner->InkOverflowRect(); 806 const bool firstReflow = inner->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); 807 808 ReflowChild(inner, aPresContext, kidSize, kidReflowInput, wm, kidOrigin, 809 containerSize, ReflowChildFlags::Default, aStatus); 810 if (aStatus.IsOverflowIncomplete()) { 811 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually 812 // handle it 813 // XXX should paginate overflow as overflow, but not in this patch (bug 814 // 379349) 815 aStatus.SetIncomplete(); 816 NS_WARNING(nsPrintfCString("Set table cell incomplete %p", this).get()); 817 } 818 819 // XXXbz is this invalidate actually needed, really? 820 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { 821 InvalidateFrameSubtree(); 822 } 823 824 #ifdef DEBUG 825 DebugCheckChildSize(inner, kidSize); 826 #endif 827 828 // Place the child 829 FinishReflowChild(inner, aPresContext, kidSize, &kidReflowInput, wm, 830 kidOrigin, containerSize, ReflowChildFlags::Default); 831 832 { 833 nsIFrame* prevInFlow = GetPrevInFlow(); 834 const bool isEmpty = 835 prevInFlow 836 ? static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty() 837 : !CellHasVisibleContent(tableFrame, this); 838 SetContentEmpty(isEmpty); 839 } 840 841 if (tableFrame->IsBorderCollapse()) { 842 nsTableFrame::InvalidateTableFrame(inner, origRect, origInkOverflow, 843 firstReflow); 844 } 845 // first, compute the bsize which can be set w/o being restricted by 846 // available bsize 847 LogicalSize cellSize(wm); 848 cellSize.BSize(wm) = kidSize.BSize(wm); 849 850 if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) { 851 cellSize.BSize(wm) += border.BStart(wm); 852 853 if (aStatus.IsComplete() || 854 aReflowInput.mStyleBorder->mBoxDecorationBreak == 855 StyleBoxDecorationBreak::Clone) { 856 cellSize.BSize(wm) += border.BEnd(wm); 857 } 858 } 859 860 // next determine the cell's isize. At this point, we've factored in the 861 // cell's style attributes. 862 cellSize.ISize(wm) = kidSize.ISize(wm); 863 864 // factor in border (and disregard padding, which is handled by our child). 865 if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) { 866 cellSize.ISize(wm) += border.IStartEnd(wm); 867 } 868 869 // set the cell's desired size and max element size 870 aDesiredSize.SetSize(wm, cellSize); 871 872 // the overflow area will be computed when BlockDirAlignChild() gets called 873 874 if (aReflowInput.mFlags.mSpecialBSizeReflow && 875 NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) { 876 aDesiredSize.BSize(wm) = BSize(wm); 877 } 878 879 // If our parent is in initial reflow, it'll handle invalidating our 880 // entire overflow rect. 881 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && 882 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { 883 InvalidateFrame(); 884 } 885 886 // remember the desired size for this reflow 887 SetDesiredSize(aDesiredSize); 888 889 // Any absolutely-positioned children will get reflowed in 890 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our 891 // dirtiness to them before our parent clears our dirty bits. 892 PushDirtyBitToAbsoluteFrames(); 893 } 894 895 void nsBCTableCellFrame::Reflow(nsPresContext* aPresContext, 896 ReflowOutput& aDesiredSize, 897 const ReflowInput& aReflowInput, 898 nsReflowStatus& aStatus) { 899 nsTableCellFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); 900 mLastUsedBorder = GetUsedBorder(); 901 } 902 903 /* ----- global methods ----- */ 904 905 NS_QUERYFRAME_HEAD(nsTableCellFrame) 906 NS_QUERYFRAME_ENTRY(nsTableCellFrame) 907 NS_QUERYFRAME_ENTRY(nsITableCellLayout) 908 NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver) 909 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 910 911 #ifdef ACCESSIBILITY 912 a11y::AccType nsTableCellFrame::AccessibleType() { 913 return a11y::eHTMLTableCellType; 914 } 915 #endif 916 917 /* This is primarily for editor access via nsITableLayout */ 918 NS_IMETHODIMP 919 nsTableCellFrame::GetCellIndexes(int32_t& aRowIndex, int32_t& aColIndex) { 920 aRowIndex = RowIndex(); 921 aColIndex = mColIndex; 922 return NS_OK; 923 } 924 925 nsTableCellFrame* NS_NewTableCellFrame(PresShell* aPresShell, 926 ComputedStyle* aStyle, 927 nsTableFrame* aTableFrame) { 928 if (aTableFrame->IsBorderCollapse()) { 929 return new (aPresShell) nsBCTableCellFrame(aStyle, aTableFrame); 930 } 931 return new (aPresShell) nsTableCellFrame(aStyle, aTableFrame); 932 } 933 934 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame) 935 936 LogicalMargin nsTableCellFrame::GetBorderWidth(WritingMode aWM) const { 937 return LogicalMargin(aWM, StyleBorder()->GetComputedBorder()); 938 } 939 940 void nsTableCellFrame::AppendDirectlyOwnedAnonBoxes( 941 nsTArray<OwnedAnonBox>& aResult) { 942 aResult.AppendElement(OwnedAnonBox(Inner())); 943 } 944 945 #ifdef DEBUG_FRAME_DUMP 946 nsresult nsTableCellFrame::GetFrameName(nsAString& aResult) const { 947 return MakeFrameName(u"TableCell"_ns, aResult); 948 } 949 #endif 950 951 // nsBCTableCellFrame 952 953 nsBCTableCellFrame::nsBCTableCellFrame(ComputedStyle* aStyle, 954 nsTableFrame* aTableFrame) 955 : nsTableCellFrame(aStyle, aTableFrame, kClassID) {} 956 957 nsBCTableCellFrame::~nsBCTableCellFrame() = default; 958 959 /* virtual */ 960 nsMargin nsBCTableCellFrame::GetUsedBorder() const { 961 WritingMode wm = GetWritingMode(); 962 return GetBorderWidth(wm).GetPhysicalMargin(wm); 963 } 964 965 #ifdef DEBUG_FRAME_DUMP 966 nsresult nsBCTableCellFrame::GetFrameName(nsAString& aResult) const { 967 return MakeFrameName(u"BCTableCell"_ns, aResult); 968 } 969 #endif 970 971 LogicalMargin nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const { 972 return LogicalMargin( 973 aWM, BC_BORDER_END_HALF(mBStartBorder), BC_BORDER_START_HALF(mIEndBorder), 974 BC_BORDER_START_HALF(mBEndBorder), BC_BORDER_END_HALF(mIStartBorder)); 975 } 976 977 nscoord nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const { 978 switch (aSide) { 979 case LogicalSide::BStart: 980 return BC_BORDER_END_HALF(mBStartBorder); 981 case LogicalSide::IEnd: 982 return BC_BORDER_START_HALF(mIEndBorder); 983 case LogicalSide::BEnd: 984 return BC_BORDER_START_HALF(mBEndBorder); 985 default: 986 return BC_BORDER_END_HALF(mIStartBorder); 987 } 988 } 989 990 void nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, nscoord aValue) { 991 switch (aSide) { 992 case LogicalSide::BStart: 993 mBStartBorder = aValue; 994 break; 995 case LogicalSide::IEnd: 996 mIEndBorder = aValue; 997 break; 998 case LogicalSide::BEnd: 999 mBEndBorder = aValue; 1000 break; 1001 default: 1002 mIStartBorder = aValue; 1003 } 1004 } 1005 1006 /* virtual */ 1007 nsMargin nsBCTableCellFrame::GetBorderOverflow() { 1008 WritingMode wm = GetWritingMode(); 1009 LogicalMargin halfBorder( 1010 wm, BC_BORDER_START_HALF(mBStartBorder), BC_BORDER_END_HALF(mIEndBorder), 1011 BC_BORDER_END_HALF(mBEndBorder), BC_BORDER_START_HALF(mIStartBorder)); 1012 return halfBorder.GetPhysicalMargin(wm); 1013 } 1014 1015 namespace mozilla { 1016 1017 class nsDisplayTableCellSelection final : public nsPaintedDisplayItem { 1018 public: 1019 nsDisplayTableCellSelection(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) 1020 : nsPaintedDisplayItem(aBuilder, aFrame) { 1021 MOZ_COUNT_CTOR(nsDisplayTableCellSelection); 1022 } 1023 1024 MOZ_COUNTED_DTOR_FINAL(nsDisplayTableCellSelection) 1025 1026 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override { 1027 static_cast<nsTableCellFrame*>(mFrame)->DecorateForSelection( 1028 aCtx->GetDrawTarget(), ToReferenceFrame()); 1029 } 1030 NS_DISPLAY_DECL_NAME("TableCellSelection", TYPE_TABLE_CELL_SELECTION) 1031 1032 bool CreateWebRenderCommands( 1033 mozilla::wr::DisplayListBuilder& aBuilder, 1034 mozilla::wr::IpcResourceUpdateQueue& aResources, 1035 const StackingContextHelper& aSc, 1036 mozilla::layers::RenderRootStateManager* aManager, 1037 nsDisplayListBuilder* aDisplayListBuilder) override { 1038 RefPtr<nsFrameSelection> frameSelection = 1039 mFrame->PresShell()->FrameSelection(); 1040 return !frameSelection->IsInTableSelectionMode(); 1041 } 1042 }; 1043 1044 } // namespace mozilla 1045 1046 void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1047 const nsDisplayListSet& aLists) { 1048 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame"); 1049 if (ShouldPaintBordersAndBackgrounds()) { 1050 // display outset box-shadows if we need to. 1051 bool hasBoxShadow = !StyleEffects()->mBoxShadow.IsEmpty(); 1052 if (hasBoxShadow) { 1053 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>( 1054 aBuilder, this); 1055 } 1056 1057 nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this); 1058 nsRect bgRectInsideBorder = bgRect; 1059 1060 // If we're doing collapsed borders, and this element forms a new stacking 1061 // context or has position:relative (which paints as though it did), inset 1062 // the background rect so that we don't overpaint the inset part of our 1063 // borders. 1064 nsTableFrame* tableFrame = GetTableFrame(); 1065 if (tableFrame->IsBorderCollapse() && 1066 (IsStackingContext() || 1067 StyleDisplay()->mPosition == StylePositionProperty::Relative)) { 1068 bgRectInsideBorder.Deflate(GetUsedBorder()); 1069 } 1070 1071 // display background if we need to. 1072 const AppendedBackgroundType result = 1073 nsDisplayBackgroundImage::AppendBackgroundItemsToTop( 1074 aBuilder, this, bgRectInsideBorder, aLists.BorderBackground(), true, 1075 bgRect); 1076 if (result == AppendedBackgroundType::None) { 1077 aBuilder->BuildCompositorHitTestInfoIfNeeded(this, 1078 aLists.BorderBackground()); 1079 } 1080 1081 // display inset box-shadows if we need to. 1082 if (hasBoxShadow) { 1083 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>( 1084 aBuilder, this); 1085 } 1086 1087 // display borders if we need to 1088 ProcessBorders(tableFrame, aBuilder, aLists); 1089 1090 // and display the selection border if we need to 1091 if (IsSelected()) { 1092 aLists.BorderBackground()->AppendNewToTop<nsDisplayTableCellSelection>( 1093 aBuilder, this); 1094 } 1095 1096 // This can be null if display list building initiated in the middle 1097 // of the table, which can happen with background-clip:text and 1098 // -moz-element. 1099 nsDisplayTableBackgroundSet* backgrounds = 1100 aBuilder->GetTableBackgroundSet(); 1101 if (backgrounds) { 1102 // Compute bgRect relative to reference frame, but using the 1103 // normal (without position:relative offsets) positions for the 1104 // cell, row and row group. 1105 bgRect = GetRectRelativeToSelf() + GetNormalPosition(); 1106 1107 nsTableRowFrame* row = GetTableRowFrame(); 1108 bgRect += row->GetNormalPosition(); 1109 1110 nsTableRowGroupFrame* rowGroup = row->GetTableRowGroupFrame(); 1111 bgRect += rowGroup->GetNormalPosition(); 1112 1113 bgRect += backgrounds->TableToReferenceFrame(); 1114 1115 DisplayListClipState::AutoSaveRestore clipState(aBuilder); 1116 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter( 1117 aBuilder); 1118 if (IsStackingContext() || row->IsStackingContext() || 1119 rowGroup->IsStackingContext() || tableFrame->IsStackingContext()) { 1120 // The col/colgroup items we create below will be inserted directly into 1121 // the BorderBackgrounds list of the table frame. That means that 1122 // they'll be moved *outside* of any wrapper items created for any 1123 // frames between this table cell frame and the table wrapper frame, and 1124 // will not participate in those frames's opacity / transform / filter / 1125 // mask effects. If one of those frames is a stacking context, then we 1126 // may have one or more of those wrapper items, and one of them may have 1127 // captured a clip. In order to ensure correct clipping and scrolling of 1128 // the col/colgroup items, restore the clip and ASR that we observed 1129 // when we entered the table frame. If that frame is a stacking context 1130 // but doesn't have any clip capturing wrapper items, then we'll 1131 // double-apply the clip. That's ok. 1132 clipState.SetClipChainForContainingBlockDescendants( 1133 backgrounds->GetTableClipChain()); 1134 asrSetter.SetCurrentActiveScrolledRoot(backgrounds->GetTableASR()); 1135 } 1136 1137 // Create backgrounds items as needed for the column and column 1138 // group that this cell occupies. 1139 nsTableColFrame* col = backgrounds->GetColForIndex(ColIndex()); 1140 nsTableColGroupFrame* colGroup = col->GetTableColGroupFrame(); 1141 1142 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForColGroup; 1143 nsDisplayBackgroundImage::AppendBackgroundItemsToTop( 1144 aBuilder, colGroup, bgRect, backgrounds->ColGroupBackgrounds(), false, 1145 colGroup->GetRect() + backgrounds->TableToReferenceFrame(), this, 1146 &buildingForColGroup); 1147 1148 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForCol; 1149 nsDisplayBackgroundImage::AppendBackgroundItemsToTop( 1150 aBuilder, col, bgRect, backgrounds->ColBackgrounds(), false, 1151 col->GetRect() + colGroup->GetPosition() + 1152 backgrounds->TableToReferenceFrame(), 1153 this, &buildingForCol); 1154 } 1155 } 1156 1157 // the 'empty-cells' property has no effect on 'outline' 1158 DisplayOutline(aBuilder, aLists); 1159 if (HidesContent()) { 1160 return; 1161 } 1162 1163 // The child's background will go in our BorderBackground() list. 1164 // This isn't a problem since it won't have a real background except for 1165 // event handling. We do not call BuildDisplayListForNonBlockChildren 1166 // because that/ would put the child's background in the Content() list 1167 // which isn't right (e.g., would end up on top of our child floats for 1168 // event handling). 1169 BuildDisplayListForChild(aBuilder, Inner(), aLists); 1170 }