Bug 1885565 - Part 1: Add mozac_ic_avatar_circle_24 to ui-icons r=android-reviewers...
[gecko.git] / layout / tables / nsTableCellFrame.cpp
blob9a09dfbf286ee060b7b5a9bf72a9bf168b40241a
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 SetContentEmpty(false);
54 nsTableCellFrame::~nsTableCellFrame() = default;
56 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame)
58 void nsTableCellFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
59 nsIFrame* aPrevInFlow) {
60 // Let the base class do its initialization
61 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
63 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
64 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
67 if (aPrevInFlow) {
68 // Set the column index
69 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
70 uint32_t colIndex = cellFrame->ColIndex();
71 SetColIndex(colIndex);
72 } else {
73 // Although the spec doesn't say that writing-mode is not applied to
74 // table-cells, we still override style value here because we want to
75 // make effective writing mode of table structure frames consistent
76 // within a table. The content inside table cells is reflowed by an
77 // anonymous block, hence their writing mode is not affected.
78 mWritingMode = GetTableFrame()->GetWritingMode();
82 void nsTableCellFrame::Destroy(DestroyContext& aContext) {
83 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
84 nsContainerFrame::Destroy(aContext);
87 // nsIPercentBSizeObserver methods
89 void nsTableCellFrame::NotifyPercentBSize(const ReflowInput& aReflowInput) {
90 // ReflowInput ensures the mCBReflowInput of blocks inside a
91 // cell is the cell frame, not the inner-cell block, and that the
92 // containing block of an inner table is the containing block of its
93 // table wrapper.
94 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
95 // these tests are probably unnecessary.
97 // Maybe the cell reflow input; we sure if we're inside the |if|.
98 const ReflowInput* cellRI = aReflowInput.mCBReflowInput;
100 if (cellRI && cellRI->mFrame == this &&
101 (cellRI->ComputedBSize() == NS_UNCONSTRAINEDSIZE ||
102 cellRI->ComputedBSize() == 0)) { // XXXldb Why 0?
103 // This is a percentage bsize on a frame whose percentage bsizes
104 // are based on the bsize of the cell, since its containing block
105 // is the inner cell frame.
107 // We'll only honor the percent bsize if sibling-cells/ancestors
108 // have specified/pct bsize. (Also, siblings only count for this if
109 // both this cell and the sibling cell span exactly 1 row.)
111 if (nsTableFrame::AncestorsHaveStyleBSize(*cellRI) ||
112 (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 &&
113 cellRI->mParentReflowInput->mFrame->HasAnyStateBits(
114 NS_ROW_HAS_CELL_WITH_STYLE_BSIZE))) {
115 for (const ReflowInput* rs = aReflowInput.mParentReflowInput;
116 rs != cellRI; rs = rs->mParentReflowInput) {
117 rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
120 nsTableFrame::RequestSpecialBSizeReflow(*cellRI);
125 // The cell needs to observe its block and things inside its block but nothing
126 // below that
127 bool nsTableCellFrame::NeedsToObserve(const ReflowInput& aReflowInput) {
128 const ReflowInput* rs = aReflowInput.mParentReflowInput;
129 if (!rs) return false;
130 if (rs->mFrame == this) {
131 // We always observe the child block. It will never send any
132 // notifications, but we need this so that the observer gets
133 // propagated to its kids.
134 return true;
136 rs = rs->mParentReflowInput;
137 if (!rs) {
138 return false;
141 // We always need to let the percent bsize observer be propagated
142 // from a table wrapper frame to an inner table frame.
143 LayoutFrameType fType = aReflowInput.mFrame->Type();
144 if (fType == LayoutFrameType::Table) {
145 return true;
148 // We need the observer to be propagated to all children of the cell
149 // (i.e., children of the child block) in quirks mode, but only to
150 // tables in standards mode.
151 // XXX This may not be true in the case of orthogonal flows within
152 // the cell (bug 1174711 comment 8); we may need to observe isizes
153 // instead of bsizes for orthogonal children.
154 return rs->mFrame == this &&
155 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
156 fType == LayoutFrameType::TableWrapper);
159 nsresult nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID,
160 nsAtom* aAttribute,
161 int32_t aModType) {
162 // We need to recalculate in this case because of the nowrap quirk in
163 // BasicTableLayoutStrategy
164 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
165 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
166 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
167 NS_FRAME_IS_DIRTY);
170 const nsAtom* colSpanAttribute =
171 MOZ_UNLIKELY(mContent->AsElement()->IsMathMLElement())
172 ? nsGkAtoms::columnspan_
173 : nsGkAtoms::colspan;
174 if (aAttribute == nsGkAtoms::rowspan || aAttribute == colSpanAttribute) {
175 nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
176 nsChangeHint_UpdateTableCellSpans);
178 return NS_OK;
181 /* virtual */
182 void nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
183 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
184 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle);
186 if (!aOldComputedStyle) {
187 return; // avoid the following on init
190 #ifdef ACCESSIBILITY
191 if (nsAccessibilityService* accService = GetAccService()) {
192 if (StyleBorder()->GetComputedBorder() !=
193 aOldComputedStyle->StyleBorder()->GetComputedBorder()) {
194 // If a table cell's computed border changes, it can change whether or
195 // not its parent table is classified as a layout or data table. We
196 // send a notification here to invalidate the a11y cache on the table
197 // so the next fetch of IsProbablyLayoutTable() is accurate.
198 accService->TableLayoutGuessMaybeChanged(PresShell(), mContent);
201 #endif
203 nsTableFrame* tableFrame = GetTableFrame();
204 if (tableFrame->IsBorderCollapse() &&
205 tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
206 uint32_t colIndex = ColIndex();
207 uint32_t rowIndex = RowIndex();
208 // row span needs to be clamped as we do not create rows in the cellmap
209 // which do not have cells originating in them
210 TableArea damageArea(colIndex, rowIndex, GetColSpan(),
211 std::min(static_cast<uint32_t>(GetRowSpan()),
212 tableFrame->GetRowCount() - rowIndex));
213 tableFrame->AddBCDamageArea(damageArea);
217 #ifdef DEBUG
218 void nsTableCellFrame::AppendFrames(ChildListID aListID,
219 nsFrameList&& aFrameList) {
220 MOZ_CRASH("unsupported operation");
223 void nsTableCellFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
224 const nsLineList::iterator* aPrevFrameLine,
225 nsFrameList&& aFrameList) {
226 MOZ_CRASH("unsupported operation");
229 void nsTableCellFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) {
230 MOZ_CRASH("unsupported operation");
232 #endif
234 void nsTableCellFrame::SetColIndex(int32_t aColIndex) { mColIndex = aColIndex; }
236 /* virtual */
237 nsMargin nsTableCellFrame::GetUsedMargin() const {
238 return nsMargin(0, 0, 0, 0);
241 // ASSURE DIFFERENT COLORS for selection
242 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) {
243 if (colorA == colorB) {
244 nscolor res;
245 res = NS_RGB(NS_GET_R(colorA) ^ 0xff, NS_GET_G(colorA) ^ 0xff,
246 NS_GET_B(colorA) ^ 0xff);
247 return res;
249 return colorA;
252 void nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget,
253 nsPoint aPt) {
254 NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
255 int16_t displaySelection;
256 displaySelection = DetermineDisplaySelection();
257 if (displaySelection) {
258 RefPtr<nsFrameSelection> frameSelection = PresShell()->FrameSelection();
260 if (frameSelection->IsInTableSelectionMode()) {
261 nscolor bordercolor;
262 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
263 bordercolor = NS_RGB(176, 176, 176); // disabled color
264 } else {
265 bordercolor = LookAndFeel::Color(LookAndFeel::ColorID::Highlight, this);
267 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3);
268 if ((mRect.width > threePx) && (mRect.height > threePx)) {
269 // compare bordercolor to background-color
270 bordercolor = EnsureDifferentColors(
271 bordercolor, StyleBackground()->BackgroundColor(this));
273 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
274 Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel);
276 AutoRestoreTransform autoRestoreTransform(aDrawTarget);
277 aDrawTarget->SetTransform(
278 aDrawTarget->GetTransform().PreTranslate(devPixelOffset));
280 ColorPattern color(ToDeviceColor(bordercolor));
282 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
284 StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0),
285 appUnitsPerDevPixel, *aDrawTarget, color);
286 StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height),
287 appUnitsPerDevPixel, *aDrawTarget, color);
288 StrokeLineWithSnapping(nsPoint(onePixel, mRect.height),
289 nsPoint(mRect.width, mRect.height),
290 appUnitsPerDevPixel, *aDrawTarget, color);
291 StrokeLineWithSnapping(nsPoint(mRect.width, onePixel),
292 nsPoint(mRect.width, mRect.height),
293 appUnitsPerDevPixel, *aDrawTarget, color);
294 // middle
295 nsRect r(onePixel, onePixel, mRect.width - onePixel,
296 mRect.height - onePixel);
297 Rect devPixelRect =
298 NSRectToSnappedRect(r, appUnitsPerDevPixel, *aDrawTarget);
299 aDrawTarget->StrokeRect(devPixelRect, color);
300 // shading
301 StrokeLineWithSnapping(
302 nsPoint(2 * onePixel, mRect.height - 2 * onePixel),
303 nsPoint(mRect.width - onePixel, mRect.height - (2 * onePixel)),
304 appUnitsPerDevPixel, *aDrawTarget, color);
305 StrokeLineWithSnapping(
306 nsPoint(mRect.width - (2 * onePixel), 2 * onePixel),
307 nsPoint(mRect.width - (2 * onePixel), mRect.height - onePixel),
308 appUnitsPerDevPixel, *aDrawTarget, color);
314 void nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame,
315 nsDisplayListBuilder* aBuilder,
316 const nsDisplayListSet& aLists) {
317 const nsStyleBorder* borderStyle = StyleBorder();
318 if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) {
319 return;
322 if (!GetContentEmpty() ||
323 StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show) {
324 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
328 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey,
329 bool aRebuildDisplayItems) {
330 nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
331 if (GetTableFrame()->IsBorderCollapse()) {
332 const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
333 GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
334 aDisplayItemKey, rebuild);
338 void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect,
339 uint32_t aDisplayItemKey,
340 bool aRebuildDisplayItems) {
341 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
342 aRebuildDisplayItems);
343 // If we have filters applied that would affects our bounds, then
344 // we get an inactive layer created and this is computed
345 // within FrameLayerBuilder
346 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
347 aRebuildDisplayItems);
350 bool nsTableCellFrame::ShouldPaintBordersAndBackgrounds() const {
351 // If we're not visible, we don't paint.
352 if (!StyleVisibility()->IsVisible()) {
353 return false;
356 // Consider 'empty-cells', but only in separated borders mode.
357 if (!GetContentEmpty()) {
358 return true;
361 nsTableFrame* tableFrame = GetTableFrame();
362 if (tableFrame->IsBorderCollapse()) {
363 return true;
366 return StyleTableBorder()->mEmptyCells == StyleEmptyCells::Show;
369 bool nsTableCellFrame::ShouldPaintBackground(nsDisplayListBuilder* aBuilder) {
370 return ShouldPaintBordersAndBackgrounds();
373 LogicalSides nsTableCellFrame::GetLogicalSkipSides() const {
374 LogicalSides skip(mWritingMode);
375 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
376 StyleBoxDecorationBreak::Clone)) {
377 return skip;
380 if (GetPrevInFlow()) {
381 skip |= eLogicalSideBitsBStart;
383 if (GetNextInFlow()) {
384 skip |= eLogicalSideBitsBEnd;
386 return skip;
389 /* virtual */
390 nsMargin nsTableCellFrame::GetBorderOverflow() { return nsMargin(0, 0, 0, 0); }
392 void nsTableCellFrame::BlockDirAlignChild(
393 WritingMode aWM, nscoord aMaxAscent,
394 ForceAlignTopForTableCell aForceAlignTop) {
395 MOZ_ASSERT(aForceAlignTop != ForceAlignTopForTableCell::Yes ||
396 PresContext()->IsPaginated(),
397 "We shouldn't force table-cells to do 'vertical-align:top' if "
398 "we're not in printing!");
400 /* It's the 'border-collapse' on the table that matters */
401 const LogicalMargin border = GetLogicalUsedBorder(GetWritingMode())
402 .ApplySkipSides(GetLogicalSkipSides())
403 .ConvertTo(aWM, GetWritingMode());
405 nscoord bStartInset = border.BStart(aWM);
406 nscoord bEndInset = border.BEnd(aWM);
408 nscoord bSize = BSize(aWM);
409 nsIFrame* firstKid = mFrames.FirstChild();
410 nsSize containerSize = mRect.Size();
411 NS_ASSERTION(firstKid,
412 "Frame construction error, a table cell always has "
413 "an inner cell frame");
414 LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize);
415 nscoord childBSize = kidRect.BSize(aWM);
417 // Vertically align the child
418 const auto verticalAlign = aForceAlignTop == ForceAlignTopForTableCell::Yes
419 ? StyleVerticalAlignKeyword::Top
420 : GetVerticalAlign();
421 nscoord kidBStart = 0;
422 switch (verticalAlign) {
423 case StyleVerticalAlignKeyword::Baseline:
424 if (!GetContentEmpty()) {
425 // Align the baselines of the child frame with the baselines of
426 // other children in the same row which have 'vertical-align: baseline'
427 kidBStart = bStartInset + aMaxAscent - GetCellBaseline();
428 break;
430 // Empty cells don't participate in baseline alignment -
431 // fallback to start alignment.
432 [[fallthrough]];
433 case StyleVerticalAlignKeyword::Top:
434 // Align the top of the child frame with the top of the content area,
435 kidBStart = bStartInset;
436 break;
438 case StyleVerticalAlignKeyword::Bottom:
439 // Align the bottom of the child frame with the bottom of the content
440 // area,
441 kidBStart = bSize - childBSize - bEndInset;
442 break;
444 default:
445 case StyleVerticalAlignKeyword::Middle:
446 // Align the middle of the child frame with the middle of the content
447 // area,
448 kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2;
450 // If the content is larger than the cell bsize, align from bStartInset
451 // (cell's content-box bstart edge).
452 kidBStart = std::max(bStartInset, kidBStart);
454 if (kidBStart != kidRect.BStart(aWM)) {
455 // Invalidate at the old position first
456 firstKid->InvalidateFrameSubtree();
459 firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), kidBStart),
460 containerSize);
461 ReflowOutput desiredSize(aWM);
462 desiredSize.SetSize(aWM, GetLogicalSize(aWM));
464 nsRect overflow(nsPoint(), GetSize());
465 overflow.Inflate(GetBorderOverflow());
466 desiredSize.mOverflowAreas.SetAllTo(overflow);
467 ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid);
468 FinishAndStoreOverflow(&desiredSize);
469 if (kidBStart != kidRect.BStart(aWM)) {
470 // Make sure any child views are correctly positioned. We know the inner
471 // table cell won't have a view
472 nsContainerFrame::PositionChildViews(firstKid);
474 // Invalidate new overflow rect
475 firstKid->InvalidateFrameSubtree();
477 if (HasView()) {
478 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
479 desiredSize.InkOverflow(),
480 ReflowChildFlags::Default);
484 bool nsTableCellFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
485 nsRect bounds(nsPoint(0, 0), GetSize());
486 bounds.Inflate(GetBorderOverflow());
488 aOverflowAreas.UnionAllWith(bounds);
489 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
492 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom',
493 // length, percentage, and calc() values to 'baseline'.
494 StyleVerticalAlignKeyword nsTableCellFrame::GetVerticalAlign() const {
495 const StyleVerticalAlign& verticalAlign = StyleDisplay()->mVerticalAlign;
496 if (verticalAlign.IsKeyword()) {
497 auto value = verticalAlign.AsKeyword();
498 if (value == StyleVerticalAlignKeyword::Top ||
499 value == StyleVerticalAlignKeyword::Middle ||
500 value == StyleVerticalAlignKeyword::Bottom) {
501 return value;
504 return StyleVerticalAlignKeyword::Baseline;
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;
514 if (aTableFrame->IsBorderCollapse()) {
515 return true;
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;
524 } else if (LayoutFrameType::Placeholder != frameType) {
525 return true;
526 } else if (nsLayoutUtils::GetFloatFromPlaceholder(innerFrame)) {
527 return true;
530 return false;
533 nsIFrame* nsTableCellFrame::CellContentFrame() const {
534 nsIFrame* inner = mFrames.FirstChild();
535 if (nsIScrollableFrame* sf = do_QueryFrame(inner)) {
536 return sf->GetScrolledFrame();
538 return inner;
541 nscoord nsTableCellFrame::GetCellBaseline() const {
542 // Ignore the position of the inner frame relative to the cell frame
543 // since we want the position as though the inner were top-aligned.
544 nsIFrame* inner = mFrames.FirstChild();
545 const auto wm = GetWritingMode();
546 nscoord result;
547 if (!StyleDisplay()->IsContainLayout() &&
548 nsLayoutUtils::GetFirstLineBaseline(wm, inner, &result)) {
549 // `result` already includes the padding-start from the inner frame.
550 return result + GetLogicalUsedBorder(wm).BStart(wm);
552 return CellContentFrame()->ContentBSize(wm) +
553 GetLogicalUsedBorderAndPadding(wm).BStart(wm);
556 int32_t nsTableCellFrame::GetRowSpan() {
557 int32_t rowSpan = 1;
559 // Don't look at the content's rowspan if we're a pseudo cell
560 if (!Style()->IsPseudoOrAnonBox()) {
561 dom::Element* elem = mContent->AsElement();
562 const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan);
563 // Note that we don't need to check the tag name, because only table cells
564 // (including MathML <mtd>) and table headers parse the "rowspan" attribute
565 // into an integer.
566 if (attr && attr->Type() == nsAttrValue::eInteger) {
567 rowSpan = attr->GetIntegerValue();
570 return rowSpan;
573 int32_t nsTableCellFrame::GetColSpan() {
574 int32_t colSpan = 1;
576 // Don't look at the content's colspan if we're a pseudo cell
577 if (!Style()->IsPseudoOrAnonBox()) {
578 dom::Element* elem = mContent->AsElement();
579 const nsAttrValue* attr = elem->GetParsedAttr(
580 MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_
581 : nsGkAtoms::colspan);
582 // Note that we don't need to check the tag name, because only table cells
583 // (including MathML <mtd>) and table headers parse the "colspan" attribute
584 // into an integer.
585 if (attr && attr->Type() == nsAttrValue::eInteger) {
586 colSpan = attr->GetIntegerValue();
589 return colSpan;
592 nsIScrollableFrame* nsTableCellFrame::GetScrollTargetFrame() const {
593 return do_QueryFrame(mFrames.FirstChild());
596 /* virtual */
597 nscoord nsTableCellFrame::GetMinISize(gfxContext* aRenderingContext) {
598 nscoord result = 0;
599 DISPLAY_MIN_INLINE_SIZE(this, result);
601 nsIFrame* inner = mFrames.FirstChild();
602 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
603 IntrinsicISizeType::MinISize,
604 nsLayoutUtils::IGNORE_PADDING);
605 return result;
608 /* virtual */
609 nscoord nsTableCellFrame::GetPrefISize(gfxContext* aRenderingContext) {
610 nscoord result = 0;
611 DISPLAY_PREF_INLINE_SIZE(this, result);
613 nsIFrame* inner = mFrames.FirstChild();
614 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
615 IntrinsicISizeType::PrefISize,
616 nsLayoutUtils::IGNORE_PADDING);
617 return result;
620 /* virtual */ nsIFrame::IntrinsicSizeOffsetData
621 nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) {
622 IntrinsicSizeOffsetData result =
623 nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
625 result.margin = 0;
627 WritingMode wm = GetWritingMode();
628 result.border = GetBorderWidth(wm).IStartEnd(wm);
630 return result;
633 #ifdef DEBUG
634 # define PROBABLY_TOO_LARGE 1000000
635 static void DebugCheckChildSize(nsIFrame* aChild, ReflowOutput& aMet) {
636 WritingMode wm = aMet.GetWritingMode();
637 if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) {
638 printf("WARNING: cell content %p has large inline size %d \n",
639 static_cast<void*>(aChild), int32_t(aMet.ISize(wm)));
642 #endif
644 // the computed bsize for the cell, which descendants use for percent bsize
645 // calculations it is the bsize (minus border, padding) of the cell's first in
646 // flow during its final reflow without an unconstrained bsize.
647 static nscoord CalcUnpaginatedBSize(nsTableCellFrame& aCellFrame,
648 nsTableFrame& aTableFrame,
649 nscoord aBlockDirBorderPadding) {
650 const nsTableCellFrame* firstCellInFlow =
651 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow());
652 nsTableFrame* firstTableInFlow =
653 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
654 nsTableRowFrame* row =
655 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
656 nsTableRowGroupFrame* firstRGInFlow =
657 static_cast<nsTableRowGroupFrame*>(row->GetParent());
659 uint32_t rowIndex = firstCellInFlow->RowIndex();
660 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
662 nscoord computedBSize =
663 firstTableInFlow->GetRowSpacing(rowIndex, rowIndex + rowSpan - 1);
664 computedBSize -= aBlockDirBorderPadding;
665 uint32_t rowX;
666 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row;
667 row = row->GetNextRow(), rowX++) {
668 if (rowX > rowIndex + rowSpan - 1) {
669 break;
670 } else if (rowX >= rowIndex) {
671 computedBSize += row->GetUnpaginatedBSize();
674 return computedBSize;
677 void nsTableCellFrame::Reflow(nsPresContext* aPresContext,
678 ReflowOutput& aDesiredSize,
679 const ReflowInput& aReflowInput,
680 nsReflowStatus& aStatus) {
681 MarkInReflow();
682 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
683 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
684 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
686 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
687 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
690 // see if a special bsize reflow needs to occur due to having a pct height
691 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
693 WritingMode wm = aReflowInput.GetWritingMode();
694 LogicalSize availSize = aReflowInput.AvailableSize();
696 // @note |this| frame applies borders but not any padding. Our anonymous
697 // inner frame applies the padding (but not borders).
698 LogicalMargin border = GetBorderWidth(wm);
700 ReflowOutput kidSize(wm);
701 SetPriorAvailISize(aReflowInput.AvailableISize());
702 nsIFrame* firstKid = mFrames.FirstChild();
703 NS_ASSERTION(
704 firstKid,
705 "Frame construction error, a table cell always has an inner cell frame");
706 nsTableFrame* tableFrame = GetTableFrame();
708 if (aReflowInput.mFlags.mSpecialBSizeReflow || aPresContext->IsPaginated()) {
709 // Here, we're changing our own reflow input, so we need to account for our
710 // padding, even though we don't apply it anywhere else, to get the correct
711 // percentage resolution on children.
712 const LogicalMargin bp = border + aReflowInput.ComputedLogicalPadding(wm);
713 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
714 const_cast<ReflowInput&>(aReflowInput)
715 .SetComputedBSize(BSize(wm) - bp.BStartEnd(wm));
716 DISPLAY_REFLOW_CHANGE();
717 } else {
718 const nscoord computedUnpaginatedBSize =
719 CalcUnpaginatedBSize(*this, *tableFrame, bp.BStartEnd(wm));
720 if (computedUnpaginatedBSize > 0) {
721 const_cast<ReflowInput&>(aReflowInput)
722 .SetComputedBSize(computedUnpaginatedBSize);
723 DISPLAY_REFLOW_CHANGE();
728 // We need to apply the skip sides for current fragmentainer's border after
729 // we finish calculating the special block-size or unpaginated block-size to
730 // prevent the skip sides from affecting the results.
732 // We assume we are the last fragment by using
733 // PreReflowBlockLevelLogicalSkipSides(), i.e. the block-end border and
734 // padding is not skipped.
735 border.ApplySkipSides(PreReflowBlockLevelLogicalSkipSides());
737 availSize.ISize(wm) -= border.IStartEnd(wm);
739 // If we have a constrained available block-size, shrink it by subtracting our
740 // block-direction border and padding for our children.
741 if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
742 availSize.BSize(wm) -= border.BStart(wm);
744 if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
745 StyleBoxDecorationBreak::Clone) {
746 // We have box-decoration-break:clone. Subtract block-end border from the
747 // available block-size as well.
748 availSize.BSize(wm) -= border.BEnd(wm);
752 // Available block-size can became negative after subtracting block-direction
753 // border and padding. Per spec, to guarantee progress, fragmentainers are
754 // assumed to have a minimum block size of 1px regardless of their used size.
755 // https://drafts.csswg.org/css-break/#breaking-rules
756 availSize.BSize(wm) =
757 std::max(availSize.BSize(wm), nsPresContext::CSSPixelsToAppUnits(1));
759 WritingMode kidWM = firstKid->GetWritingMode();
760 ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid,
761 availSize.ConvertTo(kidWM, wm), Nothing(),
762 ReflowInput::InitFlag::CallerWillInit);
763 // Override computed padding, in case it's percentage padding
765 const auto padding = aReflowInput.ComputedLogicalPadding(kidWM);
766 kidReflowInput.Init(aPresContext, Nothing(), Nothing(), Some(padding));
767 if (firstKid->IsScrollFrame()) {
768 // Propagate explicit block sizes to our inner frame, if it's a scroll
769 // frame. Note that in table layout, explicit heights act as a minimum
770 // height, see nsTableRowFrame::CalcCellActualBSize.
772 // Table cells don't respect box-sizing, so we need to remove the
773 // padding, so that the scroll-frame sizes properly (since the
774 // scrollbars also add to the padding area).
775 auto ToScrolledBSize = [&](const nscoord aBSize) {
776 return std::max(0, aBSize - padding.BStartEnd(kidWM));
778 nscoord minBSize = aReflowInput.ComputedMinBSize();
779 if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
780 minBSize = std::max(minBSize, aReflowInput.ComputedBSize());
782 if (minBSize > 0) {
783 kidReflowInput.SetComputedMinBSize(ToScrolledBSize(minBSize));
788 // Don't be a percent height observer if we're in the middle of
789 // special-bsize reflow, in case we get an accidental NotifyPercentBSize()
790 // call (which we shouldn't honor during special-bsize reflow)
791 if (!aReflowInput.mFlags.mSpecialBSizeReflow) {
792 // mPercentBSizeObserver is for children of cells in quirks mode,
793 // but only those than are tables in standards mode. NeedsToObserve
794 // will determine how far this is propagated to descendants.
795 kidReflowInput.mPercentBSizeObserver = this;
797 // Don't propagate special bsize reflow input to our kids
798 kidReflowInput.mFlags.mSpecialBSizeReflow = false;
800 if (aReflowInput.mFlags.mSpecialBSizeReflow ||
801 FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
802 // We need to force the kid to have mBResize set if we've had a
803 // special reflow in the past, since the non-special reflow needs to
804 // resize back to what it was without the special bsize reflow.
805 kidReflowInput.SetBResize(true);
808 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
810 const LogicalPoint kidOrigin = border.StartOffset(wm);
811 const nsRect origRect = firstKid->GetRect();
812 const nsRect origInkOverflow = firstKid->InkOverflowRect();
813 const bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
815 ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput, wm, kidOrigin,
816 containerSize, ReflowChildFlags::Default, aStatus);
817 if (aStatus.IsOverflowIncomplete()) {
818 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually
819 // handle it
820 // XXX should paginate overflow as overflow, but not in this patch (bug
821 // 379349)
822 aStatus.SetIncomplete();
823 NS_WARNING(nsPrintfCString("Set table cell incomplete %p", this).get());
826 // XXXbz is this invalidate actually needed, really?
827 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
828 InvalidateFrameSubtree();
831 #ifdef DEBUG
832 DebugCheckChildSize(firstKid, kidSize);
833 #endif
835 // Place the child
836 FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput, wm,
837 kidOrigin, containerSize, ReflowChildFlags::Default);
840 nsIFrame* prevInFlow = GetPrevInFlow();
841 const bool isEmpty =
842 prevInFlow
843 ? static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty()
844 : !CellHasVisibleContent(tableFrame, this);
845 SetContentEmpty(isEmpty);
848 if (tableFrame->IsBorderCollapse()) {
849 nsTableFrame::InvalidateTableFrame(firstKid, origRect, origInkOverflow,
850 firstReflow);
852 // first, compute the bsize which can be set w/o being restricted by
853 // available bsize
854 LogicalSize cellSize(wm);
855 cellSize.BSize(wm) = kidSize.BSize(wm);
857 if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) {
858 cellSize.BSize(wm) += border.BStart(wm);
860 if (aStatus.IsComplete() ||
861 aReflowInput.mStyleBorder->mBoxDecorationBreak ==
862 StyleBoxDecorationBreak::Clone) {
863 cellSize.BSize(wm) += border.BEnd(wm);
867 // next determine the cell's isize. At this point, we've factored in the
868 // cell's style attributes.
869 cellSize.ISize(wm) = kidSize.ISize(wm);
871 // factor in border (and disregard padding, which is handled by our child).
872 if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) {
873 cellSize.ISize(wm) += border.IStartEnd(wm);
876 // set the cell's desired size and max element size
877 aDesiredSize.SetSize(wm, cellSize);
879 // the overflow area will be computed when BlockDirAlignChild() gets called
881 if (aReflowInput.mFlags.mSpecialBSizeReflow &&
882 NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
883 aDesiredSize.BSize(wm) = BSize(wm);
886 // If our parent is in initial reflow, it'll handle invalidating our
887 // entire overflow rect.
888 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
889 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
890 InvalidateFrame();
893 // remember the desired size for this reflow
894 SetDesiredSize(aDesiredSize);
896 // Any absolutely-positioned children will get reflowed in
897 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
898 // dirtiness to them before our parent clears our dirty bits.
899 PushDirtyBitToAbsoluteFrames();
902 /* ----- global methods ----- */
904 NS_QUERYFRAME_HEAD(nsTableCellFrame)
905 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
906 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
907 NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver)
908 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
910 #ifdef ACCESSIBILITY
911 a11y::AccType nsTableCellFrame::AccessibleType() {
912 return a11y::eHTMLTableCellType;
914 #endif
916 /* This is primarily for editor access via nsITableLayout */
917 NS_IMETHODIMP
918 nsTableCellFrame::GetCellIndexes(int32_t& aRowIndex, int32_t& aColIndex) {
919 aRowIndex = RowIndex();
920 aColIndex = mColIndex;
921 return NS_OK;
924 nsTableCellFrame* NS_NewTableCellFrame(PresShell* aPresShell,
925 ComputedStyle* aStyle,
926 nsTableFrame* aTableFrame) {
927 if (aTableFrame->IsBorderCollapse())
928 return new (aPresShell) nsBCTableCellFrame(aStyle, aTableFrame);
929 else
930 return new (aPresShell) nsTableCellFrame(aStyle, aTableFrame);
933 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
935 LogicalMargin nsTableCellFrame::GetBorderWidth(WritingMode aWM) const {
936 return LogicalMargin(aWM, StyleBorder()->GetComputedBorder());
939 void nsTableCellFrame::AppendDirectlyOwnedAnonBoxes(
940 nsTArray<OwnedAnonBox>& aResult) {
941 nsIFrame* kid = mFrames.FirstChild();
942 MOZ_ASSERT(kid && !kid->GetNextSibling(),
943 "Table cells should have just one child");
944 aResult.AppendElement(OwnedAnonBox(kid));
947 #ifdef DEBUG_FRAME_DUMP
948 nsresult nsTableCellFrame::GetFrameName(nsAString& aResult) const {
949 return MakeFrameName(u"TableCell"_ns, aResult);
951 #endif
953 // nsBCTableCellFrame
955 nsBCTableCellFrame::nsBCTableCellFrame(ComputedStyle* aStyle,
956 nsTableFrame* aTableFrame)
957 : nsTableCellFrame(aStyle, aTableFrame, kClassID) {
958 mBStartBorder = mIEndBorder = mBEndBorder = mIStartBorder = 0;
961 nsBCTableCellFrame::~nsBCTableCellFrame() = default;
963 /* virtual */
964 nsMargin nsBCTableCellFrame::GetUsedBorder() const {
965 WritingMode wm = GetWritingMode();
966 return GetBorderWidth(wm).GetPhysicalMargin(wm);
969 #ifdef DEBUG_FRAME_DUMP
970 nsresult nsBCTableCellFrame::GetFrameName(nsAString& aResult) const {
971 return MakeFrameName(u"BCTableCell"_ns, aResult);
973 #endif
975 LogicalMargin nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const {
976 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
977 return LogicalMargin(aWM, BC_BORDER_END_HALF_COORD(d2a, mBStartBorder),
978 BC_BORDER_START_HALF_COORD(d2a, mIEndBorder),
979 BC_BORDER_START_HALF_COORD(d2a, mBEndBorder),
980 BC_BORDER_END_HALF_COORD(d2a, mIStartBorder));
983 BCPixelSize nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const {
984 switch (aSide) {
985 case LogicalSide::BStart:
986 return BC_BORDER_END_HALF(mBStartBorder);
987 case LogicalSide::IEnd:
988 return BC_BORDER_START_HALF(mIEndBorder);
989 case LogicalSide::BEnd:
990 return BC_BORDER_START_HALF(mBEndBorder);
991 default:
992 return BC_BORDER_END_HALF(mIStartBorder);
996 void nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) {
997 switch (aSide) {
998 case LogicalSide::BStart:
999 mBStartBorder = aValue;
1000 break;
1001 case LogicalSide::IEnd:
1002 mIEndBorder = aValue;
1003 break;
1004 case LogicalSide::BEnd:
1005 mBEndBorder = aValue;
1006 break;
1007 default:
1008 mIStartBorder = aValue;
1012 /* virtual */
1013 nsMargin nsBCTableCellFrame::GetBorderOverflow() {
1014 WritingMode wm = GetWritingMode();
1015 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
1016 LogicalMargin halfBorder(wm, BC_BORDER_START_HALF_COORD(d2a, mBStartBorder),
1017 BC_BORDER_END_HALF_COORD(d2a, mIEndBorder),
1018 BC_BORDER_END_HALF_COORD(d2a, mBEndBorder),
1019 BC_BORDER_START_HALF_COORD(d2a, mIStartBorder));
1020 return halfBorder.GetPhysicalMargin(wm);
1023 namespace mozilla {
1025 class nsDisplayTableCellSelection final : public nsPaintedDisplayItem {
1026 public:
1027 nsDisplayTableCellSelection(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1028 : nsPaintedDisplayItem(aBuilder, aFrame) {
1029 MOZ_COUNT_CTOR(nsDisplayTableCellSelection);
1031 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTableCellSelection)
1033 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
1034 static_cast<nsTableCellFrame*>(mFrame)->DecorateForSelection(
1035 aCtx->GetDrawTarget(), ToReferenceFrame());
1037 NS_DISPLAY_DECL_NAME("TableCellSelection", TYPE_TABLE_CELL_SELECTION)
1039 bool CreateWebRenderCommands(
1040 mozilla::wr::DisplayListBuilder& aBuilder,
1041 mozilla::wr::IpcResourceUpdateQueue& aResources,
1042 const StackingContextHelper& aSc,
1043 mozilla::layers::RenderRootStateManager* aManager,
1044 nsDisplayListBuilder* aDisplayListBuilder) override {
1045 RefPtr<nsFrameSelection> frameSelection =
1046 mFrame->PresShell()->FrameSelection();
1047 return !frameSelection->IsInTableSelectionMode();
1051 } // namespace mozilla
1053 void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1054 const nsDisplayListSet& aLists) {
1055 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
1056 if (ShouldPaintBordersAndBackgrounds()) {
1057 // display outset box-shadows if we need to.
1058 bool hasBoxShadow = !StyleEffects()->mBoxShadow.IsEmpty();
1059 if (hasBoxShadow) {
1060 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
1061 aBuilder, this);
1064 nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
1065 nsRect bgRectInsideBorder = bgRect;
1067 // If we're doing collapsed borders, and this element forms a new stacking
1068 // context or has position:relative (which paints as though it did), inset
1069 // the background rect so that we don't overpaint the inset part of our
1070 // borders.
1071 nsTableFrame* tableFrame = GetTableFrame();
1072 if (tableFrame->IsBorderCollapse() &&
1073 (IsStackingContext() ||
1074 StyleDisplay()->mPosition == StylePositionProperty::Relative)) {
1075 bgRectInsideBorder.Deflate(GetUsedBorder());
1078 // display background if we need to.
1079 const AppendedBackgroundType result =
1080 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1081 aBuilder, this, bgRectInsideBorder, aLists.BorderBackground(), true,
1082 bgRect);
1083 if (result == AppendedBackgroundType::None) {
1084 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
1085 aLists.BorderBackground());
1088 // display inset box-shadows if we need to.
1089 if (hasBoxShadow) {
1090 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>(
1091 aBuilder, this);
1094 // display borders if we need to
1095 ProcessBorders(tableFrame, aBuilder, aLists);
1097 // and display the selection border if we need to
1098 if (IsSelected()) {
1099 aLists.BorderBackground()->AppendNewToTop<nsDisplayTableCellSelection>(
1100 aBuilder, this);
1103 // This can be null if display list building initiated in the middle
1104 // of the table, which can happen with background-clip:text and
1105 // -moz-element.
1106 nsDisplayTableBackgroundSet* backgrounds =
1107 aBuilder->GetTableBackgroundSet();
1108 if (backgrounds) {
1109 // Compute bgRect relative to reference frame, but using the
1110 // normal (without position:relative offsets) positions for the
1111 // cell, row and row group.
1112 bgRect = GetRectRelativeToSelf() + GetNormalPosition();
1114 nsTableRowFrame* row = GetTableRowFrame();
1115 bgRect += row->GetNormalPosition();
1117 nsTableRowGroupFrame* rowGroup = row->GetTableRowGroupFrame();
1118 bgRect += rowGroup->GetNormalPosition();
1120 bgRect += backgrounds->TableToReferenceFrame();
1122 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
1123 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
1124 aBuilder);
1125 if (IsStackingContext() || row->IsStackingContext() ||
1126 rowGroup->IsStackingContext() || tableFrame->IsStackingContext()) {
1127 // The col/colgroup items we create below will be inserted directly into
1128 // the BorderBackgrounds list of the table frame. That means that
1129 // they'll be moved *outside* of any wrapper items created for any
1130 // frames between this table cell frame and the table wrapper frame, and
1131 // will not participate in those frames's opacity / transform / filter /
1132 // mask effects. If one of those frames is a stacking context, then we
1133 // may have one or more of those wrapper items, and one of them may have
1134 // captured a clip. In order to ensure correct clipping and scrolling of
1135 // the col/colgroup items, restore the clip and ASR that we observed
1136 // when we entered the table frame. If that frame is a stacking context
1137 // but doesn't have any clip capturing wrapper items, then we'll
1138 // double-apply the clip. That's ok.
1139 clipState.SetClipChainForContainingBlockDescendants(
1140 backgrounds->GetTableClipChain());
1141 asrSetter.SetCurrentActiveScrolledRoot(backgrounds->GetTableASR());
1144 // Create backgrounds items as needed for the column and column
1145 // group that this cell occupies.
1146 nsTableColFrame* col = backgrounds->GetColForIndex(ColIndex());
1147 nsTableColGroupFrame* colGroup = col->GetTableColGroupFrame();
1149 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForColGroup;
1150 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1151 aBuilder, colGroup, bgRect, backgrounds->ColGroupBackgrounds(), false,
1152 colGroup->GetRect() + backgrounds->TableToReferenceFrame(), this,
1153 &buildingForColGroup);
1155 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForCol;
1156 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1157 aBuilder, col, bgRect, backgrounds->ColBackgrounds(), false,
1158 col->GetRect() + colGroup->GetPosition() +
1159 backgrounds->TableToReferenceFrame(),
1160 this, &buildingForCol);
1164 // the 'empty-cells' property has no effect on 'outline'
1165 DisplayOutline(aBuilder, aLists);
1167 nsIFrame* kid = mFrames.FirstChild();
1168 NS_ASSERTION(kid && !kid->GetNextSibling(),
1169 "Table cells should have just one child");
1170 // The child's background will go in our BorderBackground() list.
1171 // This isn't a problem since it won't have a real background except for
1172 // event handling. We do not call BuildDisplayListForNonBlockChildren
1173 // because that/ would put the child's background in the Content() list
1174 // which isn't right (e.g., would end up on top of our child floats for
1175 // event handling).
1176 BuildDisplayListForChild(aBuilder, kid, aLists);