Bug 1881621 - Add colors/color_canvas.html tests to dom/canvas/test/reftest. r=bradwerth
[gecko.git] / layout / tables / nsTableCellFrame.cpp
blob6eb1c3146a6c881f41e81bac982d463c554999b6
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/. */
6 #include "nsTableCellFrame.h"
8 #include "gfxContext.h"
9 #include "gfxUtils.h"
10 #include "mozilla/ComputedStyle.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/StaticPrefs_layout.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/gfx/Helpers.h"
15 #include "nsTableFrame.h"
16 #include "nsTableColFrame.h"
17 #include "nsTableRowFrame.h"
18 #include "nsTableRowGroupFrame.h"
19 #include "nsStyleConsts.h"
20 #include "nsPresContext.h"
21 #include "nsCSSRendering.h"
22 #include "nsIContent.h"
23 #include "nsIFrame.h"
24 #include "nsIFrameInlines.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsGenericHTMLElement.h"
27 #include "nsAttrValueInlines.h"
28 #include "nsHTMLParts.h"
29 #include "nsGkAtoms.h"
30 #include "nsDisplayList.h"
31 #include "nsLayoutUtils.h"
32 #include "nsTextFrame.h"
33 #include <algorithm>
35 // TABLECELL SELECTION
36 #include "nsFrameSelection.h"
37 #include "mozilla/LookAndFeel.h"
39 #ifdef ACCESSIBILITY
40 # include "nsAccessibilityService.h"
41 #endif
43 using namespace mozilla;
44 using namespace mozilla::gfx;
45 using namespace mozilla::image;
47 nsTableCellFrame::nsTableCellFrame(ComputedStyle* aStyle,
48 nsTableFrame* aTableFrame, ClassID aID)
49 : nsContainerFrame(aStyle, aTableFrame->PresContext(), aID),
50 mDesiredSize(aTableFrame->GetWritingMode()) {
51 mColIndex = 0;
52 mPriorAvailISize = 0;
54 SetContentEmpty(false);
57 nsTableCellFrame::~nsTableCellFrame() = default;
59 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame)
61 void nsTableCellFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
62 nsIFrame* aPrevInFlow) {
63 // Let the base class do its initialization
64 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
66 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
67 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
70 if (aPrevInFlow) {
71 // Set the column index
72 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
73 uint32_t colIndex = cellFrame->ColIndex();
74 SetColIndex(colIndex);
75 } else {
76 // Although the spec doesn't say that writing-mode is not applied to
77 // table-cells, we still override style value here because we want to
78 // make effective writing mode of table structure frames consistent
79 // within a table. The content inside table cells is reflowed by an
80 // anonymous block, hence their writing mode is not affected.
81 mWritingMode = GetTableFrame()->GetWritingMode();
85 void nsTableCellFrame::Destroy(DestroyContext& aContext) {
86 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
87 nsContainerFrame::Destroy(aContext);
90 // nsIPercentBSizeObserver methods
92 void nsTableCellFrame::NotifyPercentBSize(const ReflowInput& aReflowInput) {
93 // ReflowInput ensures the mCBReflowInput of blocks inside a
94 // cell is the cell frame, not the inner-cell block, and that the
95 // containing block of an inner table is the containing block of its
96 // table wrapper.
97 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
98 // these tests are probably unnecessary.
100 // Maybe the cell reflow input; we sure if we're inside the |if|.
101 const ReflowInput* cellRI = aReflowInput.mCBReflowInput;
103 if (cellRI && cellRI->mFrame == this &&
104 (cellRI->ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
105 cellRI->ComputedBSize() == 0)) { // XXXldb Why 0?
106 // This is a percentage bsize on a frame whose percentage bsizes
107 // are based on the bsize of the cell, since its containing block
108 // is the inner cell frame.
110 // We'll only honor the percent bsize if sibling-cells/ancestors
111 // have specified/pct bsize. (Also, siblings only count for this if
112 // both this cell and the sibling cell span exactly 1 row.)
114 if (nsTableFrame::AncestorsHaveStyleBSize(*cellRI) ||
115 (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 &&
116 cellRI->mParentReflowInput->mFrame->HasAnyStateBits(
117 NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) {
118 for (const ReflowInput* rs = aReflowInput.mParentReflowInput;
119 rs != cellRI; rs = rs->mParentReflowInput) {
120 rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
123 nsTableFrame::RequestSpecialBSizeReflow(*cellRI);
128 // The cell needs to observe its block and things inside its block but nothing
129 // below that
130 bool nsTableCellFrame::NeedsToObserve(const ReflowInput& aReflowInput) {
131 const ReflowInput* rs = aReflowInput.mParentReflowInput;
132 if (!rs) return false;
133 if (rs->mFrame == this) {
134 // We always observe the child block. It will never send any
135 // notifications, but we need this so that the observer gets
136 // propagated to its kids.
137 return true;
139 rs = rs->mParentReflowInput;
140 if (!rs) {
141 return false;
144 // We always need to let the percent bsize observer be propagated
145 // from a table wrapper frame to an inner table frame.
146 LayoutFrameType fType = aReflowInput.mFrame->Type();
147 if (fType == LayoutFrameType::Table) {
148 return true;
151 // We need the observer to be propagated to all children of the cell
152 // (i.e., children of the child block) in quirks mode, but only to
153 // tables in standards mode.
154 // XXX This may not be true in the case of orthogonal flows within
155 // the cell (bug 1174711 comment 8); we may need to observe isizes
156 // instead of bsizes for orthogonal children.
157 return rs->mFrame == this &&
158 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
159 fType == LayoutFrameType::TableWrapper);
162 nsresult nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID,
163 nsAtom* aAttribute,
164 int32_t aModType) {
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);
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);
181 return NS_OK;
184 /* virtual */
185 void nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
186 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
187 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle);
189 if (!aOldComputedStyle) {
190 return; // avoid the following on init
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);
204 #endif
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);
220 #ifdef DEBUG
221 void nsTableCellFrame::AppendFrames(ChildListID aListID,
222 nsFrameList&& aFrameList) {
223 MOZ_CRASH("unsupported operation");
226 void nsTableCellFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
227 const nsLineList::iterator* aPrevFrameLine,
228 nsFrameList&& aFrameList) {
229 MOZ_CRASH("unsupported operation");
232 void nsTableCellFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) {
233 MOZ_CRASH("unsupported operation");
235 #endif
237 void nsTableCellFrame::SetColIndex(int32_t aColIndex) { mColIndex = aColIndex; }
239 /* virtual */
240 nsMargin nsTableCellFrame::GetUsedMargin() const {
241 return nsMargin(0, 0, 0, 0);
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;
252 return colorA;
255 void nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget,
256 nsPoint aPt) {
257 NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
258 int16_t displaySelection;
259 displaySelection = DetermineDisplaySelection();
260 if (displaySelection) {
261 RefPtr<nsFrameSelection> frameSelection = PresShell()->FrameSelection();
263 if (frameSelection->IsInTableSelectionMode()) {
264 nscolor bordercolor;
265 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
266 bordercolor = NS_RGB(176, 176, 176); // disabled color
267 } else {
268 bordercolor = LookAndFeel::Color(LookAndFeel::ColorID::Highlight, this);
270 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3);
271 if ((mRect.width > threePx) && (mRect.height > threePx)) {
272 // compare bordercolor to background-color
273 bordercolor = EnsureDifferentColors(
274 bordercolor, StyleBackground()->BackgroundColor(this));
276 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
277 Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel);
279 AutoRestoreTransform autoRestoreTransform(aDrawTarget);
280 aDrawTarget->SetTransform(
281 aDrawTarget->GetTransform().PreTranslate(devPixelOffset));
283 ColorPattern color(ToDeviceColor(bordercolor));
285 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
287 StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0),
288 appUnitsPerDevPixel, *aDrawTarget, color);
289 StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height),
290 appUnitsPerDevPixel, *aDrawTarget, color);
291 StrokeLineWithSnapping(nsPoint(onePixel, mRect.height),
292 nsPoint(mRect.width, mRect.height),
293 appUnitsPerDevPixel, *aDrawTarget, color);
294 StrokeLineWithSnapping(nsPoint(mRect.width, onePixel),
295 nsPoint(mRect.width, mRect.height),
296 appUnitsPerDevPixel, *aDrawTarget, color);
297 // middle
298 nsRect r(onePixel, onePixel, mRect.width - onePixel,
299 mRect.height - onePixel);
300 Rect devPixelRect =
301 NSRectToSnappedRect(r, appUnitsPerDevPixel, *aDrawTarget);
302 aDrawTarget->StrokeRect(devPixelRect, color);
303 // shading
304 StrokeLineWithSnapping(
305 nsPoint(2 * onePixel, mRect.height - 2 * onePixel),
306 nsPoint(mRect.width - onePixel, mRect.height - (2 * onePixel)),
307 appUnitsPerDevPixel, *aDrawTarget, color);
308 StrokeLineWithSnapping(
309 nsPoint(mRect.width - (2 * onePixel), 2 * onePixel),
310 nsPoint(mRect.width - (2 * onePixel), mRect.height - onePixel),
311 appUnitsPerDevPixel, *aDrawTarget, color);
317 void nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame,
318 nsDisplayListBuilder* aBuilder,
319 const nsDisplayListSet& aLists) {
320 const nsStyleBorder* borderStyle = StyleBorder();
321 if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) {
322 return;
325 if (!GetContentEmpty() ||
326 StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show) {
327 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
331 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey,
332 bool aRebuildDisplayItems) {
333 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
334 if (GetTableFrame()->IsBorderCollapse()) {
335 const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
336 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
337 aDisplayItemKey, rebuild);
341 void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect,
342 uint32_t aDisplayItemKey,
343 bool aRebuildDisplayItems) {
344 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
345 aRebuildDisplayItems);
346 // If we have filters applied that would affects our bounds, then
347 // we get an inactive layer created and this is computed
348 // within FrameLayerBuilder
349 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
350 aRebuildDisplayItems);
353 bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const {
354 // If we're not visible, we don't paint.
355 if (!StyleVisibility()->IsVisible()) {
356 return false;
359 // Consider 'empty-cells', but only in separated borders mode.
360 if (!GetContentEmpty()) {
361 return true;
364 nsTableFrame* tableFrame = GetTableFrame();
365 if (tableFrame->IsBorderCollapse()) {
366 return true;
369 return StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show;
372 bool nsTableCellFrame::ShouldPaintBackground(nsDisplayListBuilder* aBuilder) {
373 return ShouldPaintBordersAndBackgrounds();
376 LogicalSides nsTableCellFrame::GetLogicalSkipSides() const {
377 LogicalSides skip(mWritingMode);
378 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
379 StyleBoxDecorationBreak::Clone)) {
380 return skip;
383 if (GetPrevInFlow()) {
384 skip |= eLogicalSideBitsBStart;
386 if (GetNextInFlow()) {
387 skip |= eLogicalSideBitsBEnd;
389 return skip;
392 /* virtual */
393 nsMargin nsTableCellFrame::GetBorderOverflow() { return nsMargin(0, 0, 0, 0); }
395 // Align the cell's child frame within the cell
397 void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) {
398 /* It's the 'border-collapse' on the table that matters */
399 const LogicalMargin border = GetLogicalUsedBorder(GetWritingMode())
400 .ApplySkipSides(GetLogicalSkipSides())
401 .ConvertTo(aWM, GetWritingMode());
403 nscoord bStartInset = border.BStart(aWM);
404 nscoord bEndInset = border.BEnd(aWM);
406 nscoord bSize = BSize(aWM);
407 nsIFrame* firstKid = mFrames.FirstChild();
408 nsSize containerSize = mRect.Size();
409 NS_ASSERTION(firstKid,
410 "Frame construction error, a table cell always has "
411 "an inner cell frame");
412 LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize);
413 nscoord childBSize = kidRect.BSize(aWM);
415 // Vertically align the child
416 nscoord kidBStart = 0;
417 switch (GetVerticalAlign()) {
418 case StyleVerticalAlignKeyword::Baseline:
419 if (!GetContentEmpty()) {
420 // Align the baselines of the child frame with the baselines of
421 // other children in the same row which have 'vertical-align: baseline'
422 kidBStart = bStartInset + aMaxAscent - GetCellBaseline();
423 break;
425 // Empty cells don't participate in baseline alignment -
426 // fallback to start alignment.
427 [[fallthrough]];
428 case StyleVerticalAlignKeyword::Top:
429 // Align the top of the child frame with the top of the content area,
430 kidBStart = bStartInset;
431 break;
433 case StyleVerticalAlignKeyword::Bottom:
434 // Align the bottom of the child frame with the bottom of the content
435 // area,
436 kidBStart = bSize - childBSize - bEndInset;
437 break;
439 default:
440 case StyleVerticalAlignKeyword::Middle:
441 // Align the middle of the child frame with the middle of the content
442 // area,
443 kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2;
445 // If the content is larger than the cell bsize, align from bStartInset
446 // (cell's content-box bstart edge).
447 kidBStart = std::max(bStartInset, kidBStart);
449 if (kidBStart != kidRect.BStart(aWM)) {
450 // Invalidate at the old position first
451 firstKid->InvalidateFrameSubtree();
454 firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), kidBStart),
455 containerSize);
456 ReflowOutput desiredSize(aWM);
457 desiredSize.SetSize(aWM, GetLogicalSize(aWM));
459 nsRect overflow(nsPoint(), GetSize());
460 overflow.Inflate(GetBorderOverflow());
461 desiredSize.mOverflowAreas.SetAllTo(overflow);
462 ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid);
463 FinishAndStoreOverflow(&desiredSize);
464 if (kidBStart != kidRect.BStart(aWM)) {
465 // Make sure any child views are correctly positioned. We know the inner
466 // table cell won't have a view
467 nsContainerFrame::PositionChildViews(firstKid);
469 // Invalidate new overflow rect
470 firstKid->InvalidateFrameSubtree();
472 if (HasView()) {
473 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
474 desiredSize.InkOverflow(),
475 ReflowChildFlags::Default);
479 bool nsTableCellFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
480 nsRect bounds(nsPoint(0, 0), GetSize());
481 bounds.Inflate(GetBorderOverflow());
483 aOverflowAreas.UnionAllWith(bounds);
484 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
487 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom',
488 // length, percentage, and calc() values to 'baseline'.
489 StyleVerticalAlignKeyword nsTableCellFrame::GetVerticalAlign() const {
490 const StyleVerticalAlign& verticalAlign = StyleDisplay()->mVerticalAlign;
491 if (verticalAlign.IsKeyword()) {
492 auto value = verticalAlign.AsKeyword();
493 if (value == StyleVerticalAlignKeyword::Top ||
494 value == StyleVerticalAlignKeyword::Middle ||
495 value == StyleVerticalAlignKeyword::Bottom) {
496 return value;
499 return StyleVerticalAlignKeyword::Baseline;
502 static bool CellHasVisibleContent(nsTableFrame* aTableFrame,
503 nsTableCellFrame* aCell) {
504 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
505 nsIFrame* content = aCell->CellContentFrame();
506 if (content->GetContentRect().Height() > 0) {
507 return true;
509 if (aTableFrame->IsBorderCollapse()) {
510 return true;
512 for (nsIFrame* innerFrame : content->PrincipalChildList()) {
513 LayoutFrameType frameType = innerFrame->Type();
514 if (LayoutFrameType::Text == frameType) {
515 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
516 if (textFrame->HasNoncollapsedCharacters()) {
517 return true;
519 } else if (LayoutFrameType::Placeholder != frameType) {
520 return true;
521 } else if (nsLayoutUtils::GetFloatFromPlaceholder(innerFrame)) {
522 return true;
525 return false;
528 nsIFrame* nsTableCellFrame::CellContentFrame() const {
529 nsIFrame* inner = mFrames.FirstChild();
530 if (nsIScrollableFrame* sf = do_QueryFrame(inner)) {
531 return sf->GetScrolledFrame();
533 return inner;
536 nscoord nsTableCellFrame::GetCellBaseline() const {
537 // Ignore the position of the inner frame relative to the cell frame
538 // since we want the position as though the inner were top-aligned.
539 nsIFrame* inner = mFrames.FirstChild();
540 const auto wm = GetWritingMode();
541 nscoord result;
542 if (!StyleDisplay()->IsContainLayout() &&
543 nsLayoutUtils::GetFirstLineBaseline(wm, inner, &result)) {
544 // `result` already includes the padding-start from the inner frame.
545 return result + GetLogicalUsedBorder(wm).BStart(wm);
547 return CellContentFrame()->ContentBSize(wm) +
548 GetLogicalUsedBorderAndPadding(wm).BStart(wm);
551 int32_t nsTableCellFrame::GetRowSpan() {
552 int32_t rowSpan = 1;
554 // Don't look at the content's rowspan if we're a pseudo cell
555 if (!Style()->IsPseudoOrAnonBox()) {
556 dom::Element* elem = mContent->AsElement();
557 const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan);
558 // Note that we don't need to check the tag name, because only table cells
559 // (including MathML <mtd>) and table headers parse the "rowspan" attribute
560 // into an integer.
561 if (attr && attr->Type() == nsAttrValue::eInteger) {
562 rowSpan = attr->GetIntegerValue();
565 return rowSpan;
568 int32_t nsTableCellFrame::GetColSpan() {
569 int32_t colSpan = 1;
571 // Don't look at the content's colspan if we're a pseudo cell
572 if (!Style()->IsPseudoOrAnonBox()) {
573 dom::Element* elem = mContent->AsElement();
574 const nsAttrValue* attr = elem->GetParsedAttr(
575 MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_
576 : nsGkAtoms::colspan);
577 // Note that we don't need to check the tag name, because only table cells
578 // (including MathML <mtd>) and table headers parse the "colspan" attribute
579 // into an integer.
580 if (attr && attr->Type() == nsAttrValue::eInteger) {
581 colSpan = attr->GetIntegerValue();
584 return colSpan;
587 nsIScrollableFrame* nsTableCellFrame::GetScrollTargetFrame() const {
588 return do_QueryFrame(mFrames.FirstChild());
591 /* virtual */
592 nscoord nsTableCellFrame::GetMinISize(gfxContext* aRenderingContext) {
593 nscoord result = 0;
594 DISPLAY_MIN_INLINE_SIZE(this, result);
596 nsIFrame* inner = mFrames.FirstChild();
597 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
598 IntrinsicISizeType::MinISize,
599 nsLayoutUtils::IGNORE_PADDING);
600 return result;
603 /* virtual */
604 nscoord nsTableCellFrame::GetPrefISize(gfxContext* aRenderingContext) {
605 nscoord result = 0;
606 DISPLAY_PREF_INLINE_SIZE(this, result);
608 nsIFrame* inner = mFrames.FirstChild();
609 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
610 IntrinsicISizeType::PrefISize,
611 nsLayoutUtils::IGNORE_PADDING);
612 return result;
615 /* virtual */ nsIFrame::IntrinsicSizeOffsetData
616 nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) {
617 IntrinsicSizeOffsetData result =
618 nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
620 result.margin = 0;
622 WritingMode wm = GetWritingMode();
623 result.border = GetBorderWidth(wm).IStartEnd(wm);
625 return result;
628 #ifdef DEBUG
629 # define PROBABLY_TOO_LARGE 1000000
630 static void DebugCheckChildSize(nsIFrame* aChild, ReflowOutput& aMet) {
631 WritingMode wm = aMet.GetWritingMode();
632 if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) {
633 printf("WARNING: cell content %p has large inline size %d \n",
634 static_cast<void*>(aChild), int32_t(aMet.ISize(wm)));
637 #endif
639 // the computed bsize for the cell, which descendants use for percent bsize
640 // calculations it is the bsize (minus border, padding) of the cell's first in
641 // flow during its final reflow without an unconstrained bsize.
642 static nscoord CalcUnpaginatedBSize(nsTableCellFrame& aCellFrame,
643 nsTableFrame& aTableFrame,
644 nscoord aBlockDirBorderPadding) {
645 const nsTableCellFrame* firstCellInFlow =
646 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow());
647 nsTableFrame* firstTableInFlow =
648 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
649 nsTableRowFrame* row =
650 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
651 nsTableRowGroupFrame* firstRGInFlow =
652 static_cast<nsTableRowGroupFrame*>(row->GetParent());
654 uint32_t rowIndex = firstCellInFlow->RowIndex();
655 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
657 nscoord computedBSize =
658 firstTableInFlow->GetRowSpacing(rowIndex, rowIndex + rowSpan - 1);
659 computedBSize -= aBlockDirBorderPadding;
660 uint32_t rowX;
661 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row;
662 row = row->GetNextRow(), rowX++) {
663 if (rowX > rowIndex + rowSpan - 1) {
664 break;
665 } else if (rowX >= rowIndex) {
666 computedBSize += row->GetUnpaginatedBSize();
669 return computedBSize;
672 void nsTableCellFrame::Reflow(nsPresContext* aPresContext,
673 ReflowOutput& aDesiredSize,
674 const ReflowInput& aReflowInput,
675 nsReflowStatus& aStatus) {
676 MarkInReflow();
677 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
678 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
679 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
681 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
682 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
685 // see if a special bsize reflow needs to occur due to having a pct height
686 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
688 WritingMode wm = aReflowInput.GetWritingMode();
689 LogicalSize availSize = aReflowInput.AvailableSize();
691 // @note |this| frame applies borders but not any padding. Our anonymous
692 // inner frame applies the padding (but not borders).
693 LogicalMargin border = GetBorderWidth(wm);
695 ReflowOutput kidSize(wm);
696 SetPriorAvailISize(aReflowInput.AvailableISize());
697 nsIFrame* firstKid = mFrames.FirstChild();
698 NS_ASSERTION(
699 firstKid,
700 "Frame construction error, a table cell always has an inner cell frame");
701 nsTableFrame* tableFrame = GetTableFrame();
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 DISPLAY_REFLOW_CHANGE();
712 } else {
713 const nscoord computedUnpaginatedBSize =
714 CalcUnpaginatedBSize(*this, *tableFrame, bp.BStartEnd(wm));
715 if (computedUnpaginatedBSize > 0) {
716 const_cast<ReflowInput&>(aReflowInput)
717 .SetComputedBSize(computedUnpaginatedBSize);
718 DISPLAY_REFLOW_CHANGE();
723 // We need to apply the skip sides for current fragmentainer's border after
724 // we finish calculating the special block-size or unpaginated block-size to
725 // prevent the skip sides from affecting the results.
727 // We assume we are the last fragment by using
728 // PreReflowBlockLevelLogicalSkipSides(), i.e. the block-end border and
729 // padding is not skipped.
730 border.ApplySkipSides(PreReflowBlockLevelLogicalSkipSides());
732 availSize.ISize(wm) -= border.IStartEnd(wm);
734 // If we have a constrained available block-size, shrink it by subtracting our
735 // block-direction border and padding for our children.
736 if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
737 availSize.BSize(wm) -= border.BStart(wm);
739 if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
740 StyleBoxDecorationBreak::Clone) {
741 // We have box-decoration-break:clone. Subtract block-end border from the
742 // available block-size as well.
743 availSize.BSize(wm) -= border.BEnd(wm);
747 // Available block-size can became negative after subtracting block-direction
748 // border and padding. Per spec, to guarantee progress, fragmentainers are
749 // assumed to have a minimum block size of 1px regardless of their used size.
750 // https://drafts.csswg.org/css-break/#breaking-rules
751 availSize.BSize(wm) =
752 std::max(availSize.BSize(wm), nsPresContext::CSSPixelsToAppUnits(1));
754 WritingMode kidWM = firstKid->GetWritingMode();
755 ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid,
756 availSize.ConvertTo(kidWM, wm), Nothing(),
757 ReflowInput::InitFlag::CallerWillInit);
758 // Override computed padding, in case it's percentage padding
760 const auto padding = aReflowInput.ComputedLogicalPadding(kidWM);
761 kidReflowInput.Init(aPresContext, Nothing(), Nothing(), Some(padding));
762 if (firstKid->IsScrollFrame()) {
763 // Propagate explicit block sizes to our inner frame, if it's a scroll
764 // frame. Note that in table layout, explicit heights act as a minimum
765 // height, see nsTableRowFrame::CalcCellActualBSize.
767 // Table cells don't respect box-sizing, so we need to remove the
768 // padding, so that the scroll-frame sizes properly (since the
769 // scrollbars also add to the padding area).
770 auto ToScrolledBSize = [&](const nscoord aBSize) {
771 return std::max(0, aBSize - padding.BStartEnd(kidWM));
773 nscoord minBSize = aReflowInput.ComputedMinBSize();
774 if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
775 minBSize = std::max(minBSize, aReflowInput.ComputedBSize());
777 if (minBSize > 0) {
778 kidReflowInput.SetComputedMinBSize(ToScrolledBSize(minBSize));
783 // Don't be a percent height observer if we're in the middle of
784 // special-bsize reflow, in case we get an accidental NotifyPercentBSize()
785 // call (which we shouldn't honor during special-bsize reflow)
786 if (!aReflowInput.mFlags.mSpecialBSizeReflow) {
787 // mPercentBSizeObserver is for children of cells in quirks mode,
788 // but only those than are tables in standards mode. NeedsToObserve
789 // will determine how far this is propagated to descendants.
790 kidReflowInput.mPercentBSizeObserver = this;
792 // Don't propagate special bsize reflow input to our kids
793 kidReflowInput.mFlags.mSpecialBSizeReflow = false;
795 if (aReflowInput.mFlags.mSpecialBSizeReflow ||
796 FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
797 // We need to force the kid to have mBResize set if we've had a
798 // special reflow in the past, since the non-special reflow needs to
799 // resize back to what it was without the special bsize reflow.
800 kidReflowInput.SetBResize(true);
803 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
805 const LogicalPoint kidOrigin = border.StartOffset(wm);
806 const nsRect origRect = firstKid->GetRect();
807 const nsRect origInkOverflow = firstKid->InkOverflowRect();
808 const bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
810 ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput, wm, kidOrigin,
811 containerSize, ReflowChildFlags::Default, aStatus);
812 if (aStatus.IsOverflowIncomplete()) {
813 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually
814 // handle it
815 // XXX should paginate overflow as overflow, but not in this patch (bug
816 // 379349)
817 aStatus.SetIncomplete();
818 NS_WARNING(nsPrintfCString("Set table cell incomplete %p", this).get());
821 // XXXbz is this invalidate actually needed, really?
822 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
823 InvalidateFrameSubtree();
826 #ifdef DEBUG
827 DebugCheckChildSize(firstKid, kidSize);
828 #endif
830 // Place the child
831 FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput, wm,
832 kidOrigin, containerSize, ReflowChildFlags::Default);
835 nsIFrame* prevInFlow = GetPrevInFlow();
836 const bool isEmpty =
837 prevInFlow
838 ? static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty()
839 : !CellHasVisibleContent(tableFrame, this);
840 SetContentEmpty(isEmpty);
843 if (tableFrame->IsBorderCollapse()) {
844 nsTableFrame::InvalidateTableFrame(firstKid, origRect, origInkOverflow,
845 firstReflow);
847 // first, compute the bsize which can be set w/o being restricted by
848 // available bsize
849 LogicalSize cellSize(wm);
850 cellSize.BSize(wm) = kidSize.BSize(wm);
852 if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) {
853 cellSize.BSize(wm) += border.BStart(wm);
855 if (aStatus.IsComplete() ||
856 aReflowInput.mStyleBorder->mBoxDecorationBreak ==
857 StyleBoxDecorationBreak::Clone) {
858 cellSize.BSize(wm) += border.BEnd(wm);
862 // next determine the cell's isize. At this point, we've factored in the
863 // cell's style attributes.
864 cellSize.ISize(wm) = kidSize.ISize(wm);
866 // factor in border (and disregard padding, which is handled by our child).
867 if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) {
868 cellSize.ISize(wm) += border.IStartEnd(wm);
871 // set the cell's desired size and max element size
872 aDesiredSize.SetSize(wm, cellSize);
874 // the overflow area will be computed when BlockDirAlignChild() gets called
876 if (aReflowInput.mFlags.mSpecialBSizeReflow &&
877 NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
878 aDesiredSize.BSize(wm) = BSize(wm);
881 // If our parent is in initial reflow, it'll handle invalidating our
882 // entire overflow rect.
883 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
884 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
885 InvalidateFrame();
888 // remember the desired size for this reflow
889 SetDesiredSize(aDesiredSize);
891 // Any absolutely-positioned children will get reflowed in
892 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
893 // dirtiness to them before our parent clears our dirty bits.
894 PushDirtyBitToAbsoluteFrames();
897 /* ----- global methods ----- */
899 NS_QUERYFRAME_HEAD(nsTableCellFrame)
900 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
901 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
902 NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver)
903 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
905 #ifdef ACCESSIBILITY
906 a11y::AccType nsTableCellFrame::AccessibleType() {
907 return a11y::eHTMLTableCellType;
909 #endif
911 /* This is primarily for editor access via nsITableLayout */
912 NS_IMETHODIMP
913 nsTableCellFrame::GetCellIndexes(int32_t& aRowIndex, int32_t& aColIndex) {
914 aRowIndex = RowIndex();
915 aColIndex = mColIndex;
916 return NS_OK;
919 nsTableCellFrame* NS_NewTableCellFrame(PresShell* aPresShell,
920 ComputedStyle* aStyle,
921 nsTableFrame* aTableFrame) {
922 if (aTableFrame->IsBorderCollapse())
923 return new (aPresShell) nsBCTableCellFrame(aStyle, aTableFrame);
924 else
925 return new (aPresShell) nsTableCellFrame(aStyle, aTableFrame);
928 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
930 LogicalMargin nsTableCellFrame::GetBorderWidth(WritingMode aWM) const {
931 return LogicalMargin(aWM, StyleBorder()->GetComputedBorder());
934 void nsTableCellFrame::AppendDirectlyOwnedAnonBoxes(
935 nsTArray<OwnedAnonBox>& aResult) {
936 nsIFrame* kid = mFrames.FirstChild();
937 MOZ_ASSERT(kid && !kid->GetNextSibling(),
938 "Table cells should have just one child");
939 aResult.AppendElement(OwnedAnonBox(kid));
942 #ifdef DEBUG_FRAME_DUMP
943 nsresult nsTableCellFrame::GetFrameName(nsAString& aResult) const {
944 return MakeFrameName(u"TableCell"_ns, aResult);
946 #endif
948 // nsBCTableCellFrame
950 nsBCTableCellFrame::nsBCTableCellFrame(ComputedStyle* aStyle,
951 nsTableFrame* aTableFrame)
952 : nsTableCellFrame(aStyle, aTableFrame, kClassID) {
953 mBStartBorder = mIEndBorder = mBEndBorder = mIStartBorder = 0;
956 nsBCTableCellFrame::~nsBCTableCellFrame() = default;
958 /* virtual */
959 nsMargin nsBCTableCellFrame::GetUsedBorder() const {
960 WritingMode wm = GetWritingMode();
961 return GetBorderWidth(wm).GetPhysicalMargin(wm);
964 #ifdef DEBUG_FRAME_DUMP
965 nsresult nsBCTableCellFrame::GetFrameName(nsAString& aResult) const {
966 return MakeFrameName(u"BCTableCell"_ns, aResult);
968 #endif
970 LogicalMargin nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const {
971 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
972 return LogicalMargin(aWM, BC_BORDER_END_HALF_COORD(d2a, mBStartBorder),
973 BC_BORDER_START_HALF_COORD(d2a, mIEndBorder),
974 BC_BORDER_START_HALF_COORD(d2a, mBEndBorder),
975 BC_BORDER_END_HALF_COORD(d2a, mIStartBorder));
978 BCPixelSize nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const {
979 switch (aSide) {
980 case eLogicalSideBStart:
981 return BC_BORDER_END_HALF(mBStartBorder);
982 case eLogicalSideIEnd:
983 return BC_BORDER_START_HALF(mIEndBorder);
984 case eLogicalSideBEnd:
985 return BC_BORDER_START_HALF(mBEndBorder);
986 default:
987 return BC_BORDER_END_HALF(mIStartBorder);
991 void nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) {
992 switch (aSide) {
993 case eLogicalSideBStart:
994 mBStartBorder = aValue;
995 break;
996 case eLogicalSideIEnd:
997 mIEndBorder = aValue;
998 break;
999 case eLogicalSideBEnd:
1000 mBEndBorder = aValue;
1001 break;
1002 default:
1003 mIStartBorder = aValue;
1007 /* virtual */
1008 nsMargin nsBCTableCellFrame::GetBorderOverflow() {
1009 WritingMode wm = GetWritingMode();
1010 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
1011 LogicalMargin halfBorder(wm, BC_BORDER_START_HALF_COORD(d2a, mBStartBorder),
1012 BC_BORDER_END_HALF_COORD(d2a, mIEndBorder),
1013 BC_BORDER_END_HALF_COORD(d2a, mBEndBorder),
1014 BC_BORDER_START_HALF_COORD(d2a, mIStartBorder));
1015 return halfBorder.GetPhysicalMargin(wm);
1018 namespace mozilla {
1020 class nsDisplayTableCellSelection final : public nsPaintedDisplayItem {
1021 public:
1022 nsDisplayTableCellSelection(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1023 : nsPaintedDisplayItem(aBuilder, aFrame) {
1024 MOZ_COUNT_CTOR(nsDisplayTableCellSelection);
1026 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTableCellSelection)
1028 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
1029 static_cast<nsTableCellFrame*>(mFrame)->DecorateForSelection(
1030 aCtx->GetDrawTarget(), ToReferenceFrame());
1032 NS_DISPLAY_DECL_NAME("TableCellSelection", TYPE_TABLE_CELL_SELECTION)
1034 bool CreateWebRenderCommands(
1035 mozilla::wr::DisplayListBuilder& aBuilder,
1036 mozilla::wr::IpcResourceUpdateQueue& aResources,
1037 const StackingContextHelper& aSc,
1038 mozilla::layers::RenderRootStateManager* aManager,
1039 nsDisplayListBuilder* aDisplayListBuilder) override {
1040 RefPtr<nsFrameSelection> frameSelection =
1041 mFrame->PresShell()->FrameSelection();
1042 return !frameSelection->IsInTableSelectionMode();
1046 } // namespace mozilla
1048 void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1049 const nsDisplayListSet& aLists) {
1050 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
1051 if (ShouldPaintBordersAndBackgrounds()) {
1052 // display outset box-shadows if we need to.
1053 bool hasBoxShadow = !StyleEffects()->mBoxShadow.IsEmpty();
1054 if (hasBoxShadow) {
1055 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
1056 aBuilder, this);
1059 nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
1060 nsRect bgRectInsideBorder = bgRect;
1062 // If we're doing collapsed borders, and this element forms a new stacking
1063 // context or has position:relative (which paints as though it did), inset
1064 // the background rect so that we don't overpaint the inset part of our
1065 // borders.
1066 nsTableFrame* tableFrame = GetTableFrame();
1067 if (tableFrame->IsBorderCollapse() &&
1068 (IsStackingContext() ||
1069 StyleDisplay()->mPosition == StylePositionProperty::Relative)) {
1070 bgRectInsideBorder.Deflate(GetUsedBorder());
1073 // display background if we need to.
1074 const AppendedBackgroundType result =
1075 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1076 aBuilder, this, bgRectInsideBorder, aLists.BorderBackground(), true,
1077 bgRect);
1078 if (result == AppendedBackgroundType::None) {
1079 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
1080 aLists.BorderBackground());
1083 // display inset box-shadows if we need to.
1084 if (hasBoxShadow) {
1085 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>(
1086 aBuilder, this);
1089 // display borders if we need to
1090 ProcessBorders(tableFrame, aBuilder, aLists);
1092 // and display the selection border if we need to
1093 if (IsSelected()) {
1094 aLists.BorderBackground()->AppendNewToTop<nsDisplayTableCellSelection>(
1095 aBuilder, this);
1098 // This can be null if display list building initiated in the middle
1099 // of the table, which can happen with background-clip:text and
1100 // -moz-element.
1101 nsDisplayTableBackgroundSet* backgrounds =
1102 aBuilder->GetTableBackgroundSet();
1103 if (backgrounds) {
1104 // Compute bgRect relative to reference frame, but using the
1105 // normal (without position:relative offsets) positions for the
1106 // cell, row and row group.
1107 bgRect = GetRectRelativeToSelf() + GetNormalPosition();
1109 nsTableRowFrame* row = GetTableRowFrame();
1110 bgRect += row->GetNormalPosition();
1112 nsTableRowGroupFrame* rowGroup = row->GetTableRowGroupFrame();
1113 bgRect += rowGroup->GetNormalPosition();
1115 bgRect += backgrounds->TableToReferenceFrame();
1117 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
1118 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
1119 aBuilder);
1120 if (IsStackingContext() || row->IsStackingContext() ||
1121 rowGroup->IsStackingContext() || tableFrame->IsStackingContext()) {
1122 // The col/colgroup items we create below will be inserted directly into
1123 // the BorderBackgrounds list of the table frame. That means that
1124 // they'll be moved *outside* of any wrapper items created for any
1125 // frames between this table cell frame and the table wrapper frame, and
1126 // will not participate in those frames's opacity / transform / filter /
1127 // mask effects. If one of those frames is a stacking context, then we
1128 // may have one or more of those wrapper items, and one of them may have
1129 // captured a clip. In order to ensure correct clipping and scrolling of
1130 // the col/colgroup items, restore the clip and ASR that we observed
1131 // when we entered the table frame. If that frame is a stacking context
1132 // but doesn't have any clip capturing wrapper items, then we'll
1133 // double-apply the clip. That's ok.
1134 clipState.SetClipChainForContainingBlockDescendants(
1135 backgrounds->GetTableClipChain());
1136 asrSetter.SetCurrentActiveScrolledRoot(backgrounds->GetTableASR());
1139 // Create backgrounds items as needed for the column and column
1140 // group that this cell occupies.
1141 nsTableColFrame* col = backgrounds->GetColForIndex(ColIndex());
1142 nsTableColGroupFrame* colGroup = col->GetTableColGroupFrame();
1144 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForColGroup;
1145 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1146 aBuilder, colGroup, bgRect, backgrounds->ColGroupBackgrounds(), false,
1147 colGroup->GetRect() + backgrounds->TableToReferenceFrame(), this,
1148 &buildingForColGroup);
1150 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForCol;
1151 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1152 aBuilder, col, bgRect, backgrounds->ColBackgrounds(), false,
1153 col->GetRect() + colGroup->GetPosition() +
1154 backgrounds->TableToReferenceFrame(),
1155 this, &buildingForCol);
1159 // the 'empty-cells' property has no effect on 'outline'
1160 DisplayOutline(aBuilder, aLists);
1162 nsIFrame* kid = mFrames.FirstChild();
1163 NS_ASSERTION(kid && !kid->GetNextSibling(),
1164 "Table cells should have just one child");
1165 // The child's background will go in our BorderBackground() list.
1166 // This isn't a problem since it won't have a real background except for
1167 // event handling. We do not call BuildDisplayListForNonBlockChildren
1168 // because that/ would put the child's background in the Content() list
1169 // which isn't right (e.g., would end up on top of our child floats for
1170 // event handling).
1171 BuildDisplayListForChild(aBuilder, kid, aLists);