Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / tables / nsTableCellFrame.cpp
blobe14ab978d32964136cf0ae49e8efb0eaca895446
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 "nsGenericHTMLElement.h"
26 #include "nsAttrValueInlines.h"
27 #include "nsHTMLParts.h"
28 #include "nsGkAtoms.h"
29 #include "nsDisplayList.h"
30 #include "nsLayoutUtils.h"
31 #include "nsTextFrame.h"
32 #include <algorithm>
34 // TABLECELL SELECTION
35 #include "nsFrameSelection.h"
36 #include "mozilla/LookAndFeel.h"
38 #ifdef ACCESSIBILITY
39 # include "nsAccessibilityService.h"
40 #endif
42 using namespace mozilla;
43 using namespace mozilla::gfx;
44 using namespace mozilla::image;
46 nsTableCellFrame::nsTableCellFrame(ComputedStyle* aStyle,
47 nsTableFrame* aTableFrame, ClassID aID)
48 : nsContainerFrame(aStyle, aTableFrame->PresContext(), aID),
49 mDesiredSize(aTableFrame->GetWritingMode()) {
50 mColIndex = 0;
51 mPriorAvailISize = 0;
53 SetContentEmpty(false);
56 nsTableCellFrame::~nsTableCellFrame() = default;
58 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame)
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);
65 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
66 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
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();
84 void nsTableCellFrame::Destroy(DestroyContext& aContext) {
85 nsTableFrame::MaybeUnregisterPositionedTablePart(this);
86 nsContainerFrame::Destroy(aContext);
89 // nsIPercentBSizeObserver methods
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.
99 // Maybe the cell reflow input; we sure if we're inside the |if|.
100 const ReflowInput* cellRI = aReflowInput.mCBReflowInput;
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.
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.)
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);
122 nsTableFrame::RequestSpecialBSizeReflow(*cellRI);
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) return false;
132 if (rs->mFrame == this) {
133 // We always observe the child block. It will never send any
134 // notifications, but we need this so that the observer gets
135 // propagated to its kids.
136 return true;
138 rs = rs->mParentReflowInput;
139 if (!rs) {
140 return false;
143 // We always need to let the percent bsize observer be propagated
144 // from a table wrapper frame to an inner table frame.
145 LayoutFrameType fType = aReflowInput.mFrame->Type();
146 if (fType == LayoutFrameType::Table) {
147 return true;
150 // We need the observer to be propagated to all children of the cell
151 // (i.e., children of the child block) in quirks mode, but only to
152 // tables in standards mode.
153 // XXX This may not be true in the case of orthogonal flows within
154 // the cell (bug 1174711 comment 8); we may need to observe isizes
155 // instead of bsizes for orthogonal children.
156 return rs->mFrame == this &&
157 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
158 fType == LayoutFrameType::TableWrapper);
161 nsresult nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID,
162 nsAtom* aAttribute,
163 int32_t aModType) {
164 // We need to recalculate in this case because of the nowrap quirk in
165 // BasicTableLayoutStrategy
166 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
167 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
168 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
169 NS_FRAME_IS_DIRTY);
172 if (aAttribute == nsGkAtoms::rowspan || aAttribute == nsGkAtoms::colspan) {
173 nsLayoutUtils::PostRestyleEvent(mContent->AsElement(), RestyleHint{0},
174 nsChangeHint_UpdateTableCellSpans);
176 return NS_OK;
179 /* virtual */
180 void nsTableCellFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
181 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
182 nsTableFrame::PositionedTablePartMaybeChanged(this, aOldComputedStyle);
184 if (!aOldComputedStyle) {
185 return; // avoid the following on init
188 #ifdef ACCESSIBILITY
189 if (nsAccessibilityService* accService = GetAccService()) {
190 if (StyleBorder()->GetComputedBorder() !=
191 aOldComputedStyle->StyleBorder()->GetComputedBorder()) {
192 // If a table cell's computed border changes, it can change whether or
193 // not its parent table is classified as a layout or data table. We
194 // send a notification here to invalidate the a11y cache on the table
195 // so the next fetch of IsProbablyLayoutTable() is accurate.
196 accService->TableLayoutGuessMaybeChanged(PresShell(), mContent);
199 #endif
201 nsTableFrame* tableFrame = GetTableFrame();
202 if (tableFrame->IsBorderCollapse() &&
203 tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
204 uint32_t colIndex = ColIndex();
205 uint32_t rowIndex = RowIndex();
206 // row span needs to be clamped as we do not create rows in the cellmap
207 // which do not have cells originating in them
208 TableArea damageArea(colIndex, rowIndex, GetColSpan(),
209 std::min(static_cast<uint32_t>(GetRowSpan()),
210 tableFrame->GetRowCount() - rowIndex));
211 tableFrame->AddBCDamageArea(damageArea);
215 #ifdef DEBUG
216 void nsTableCellFrame::AppendFrames(ChildListID aListID,
217 nsFrameList&& aFrameList) {
218 MOZ_CRASH("unsupported operation");
221 void nsTableCellFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
222 const nsLineList::iterator* aPrevFrameLine,
223 nsFrameList&& aFrameList) {
224 MOZ_CRASH("unsupported operation");
227 void nsTableCellFrame::RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) {
228 MOZ_CRASH("unsupported operation");
230 #endif
232 void nsTableCellFrame::SetColIndex(int32_t aColIndex) { mColIndex = aColIndex; }
234 /* virtual */
235 nsMargin nsTableCellFrame::GetUsedMargin() const {
236 return nsMargin(0, 0, 0, 0);
239 // ASSURE DIFFERENT COLORS for selection
240 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) {
241 if (colorA == colorB) {
242 nscolor res;
243 res = NS_RGB(NS_GET_R(colorA) ^ 0xff, NS_GET_G(colorA) ^ 0xff,
244 NS_GET_B(colorA) ^ 0xff);
245 return res;
247 return colorA;
250 void nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget,
251 nsPoint aPt) {
252 NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
253 int16_t displaySelection;
254 nsPresContext* presContext = PresContext();
255 displaySelection = DetermineDisplaySelection();
256 if (displaySelection) {
257 RefPtr<nsFrameSelection> frameSelection =
258 presContext->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 // Align the cell's child frame within the cell
394 void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) {
395 /* It's the 'border-collapse' on the table that matters */
396 const LogicalMargin borderPadding =
397 GetLogicalUsedBorderAndPadding(GetWritingMode())
398 .ApplySkipSides(GetLogicalSkipSides())
399 .ConvertTo(aWM, GetWritingMode());
401 nscoord bStartInset = borderPadding.BStart(aWM);
402 nscoord bEndInset = borderPadding.BEnd(aWM);
404 nscoord bSize = BSize(aWM);
405 nsIFrame* firstKid = mFrames.FirstChild();
406 nsSize containerSize = mRect.Size();
407 NS_ASSERTION(firstKid,
408 "Frame construction error, a table cell always has "
409 "an inner cell frame");
410 LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize);
411 nscoord childBSize = kidRect.BSize(aWM);
413 // Vertically align the child
414 nscoord kidBStart = 0;
415 switch (GetVerticalAlign()) {
416 case StyleVerticalAlignKeyword::Baseline:
417 if (!GetContentEmpty()) {
418 // Align the baselines of the child frame with the baselines of
419 // other children in the same row which have 'vertical-align: baseline'
420 kidBStart = bStartInset + aMaxAscent - GetCellBaseline();
421 break;
423 // Empty cells don't participate in baseline alignment -
424 // fallback to start alignment.
425 [[fallthrough]];
426 case StyleVerticalAlignKeyword::Top:
427 // Align the top of the child frame with the top of the content area,
428 kidBStart = bStartInset;
429 break;
431 case StyleVerticalAlignKeyword::Bottom:
432 // Align the bottom of the child frame with the bottom of the content
433 // area,
434 kidBStart = bSize - childBSize - bEndInset;
435 break;
437 default:
438 case StyleVerticalAlignKeyword::Middle:
439 // Align the middle of the child frame with the middle of the content
440 // area,
441 kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2;
443 // If the content is larger than the cell bsize, align from bStartInset
444 // (cell's content-box bstart edge).
445 kidBStart = std::max(bStartInset, kidBStart);
447 if (kidBStart != kidRect.BStart(aWM)) {
448 // Invalidate at the old position first
449 firstKid->InvalidateFrameSubtree();
452 firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), kidBStart),
453 containerSize);
454 ReflowOutput desiredSize(aWM);
455 desiredSize.SetSize(aWM, GetLogicalSize(aWM));
457 nsRect overflow(nsPoint(0, 0), GetSize());
458 overflow.Inflate(GetBorderOverflow());
459 desiredSize.mOverflowAreas.SetAllTo(overflow);
460 ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid);
461 FinishAndStoreOverflow(&desiredSize);
462 if (kidBStart != kidRect.BStart(aWM)) {
463 // Make sure any child views are correctly positioned. We know the inner
464 // table cell won't have a view
465 nsContainerFrame::PositionChildViews(firstKid);
467 // Invalidate new overflow rect
468 firstKid->InvalidateFrameSubtree();
470 if (HasView()) {
471 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
472 desiredSize.InkOverflow(),
473 ReflowChildFlags::Default);
477 bool nsTableCellFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
478 nsRect bounds(nsPoint(0, 0), GetSize());
479 bounds.Inflate(GetBorderOverflow());
481 aOverflowAreas.UnionAllWith(bounds);
482 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
485 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom',
486 // length, percentage, and calc() values to 'baseline'.
487 StyleVerticalAlignKeyword nsTableCellFrame::GetVerticalAlign() const {
488 const StyleVerticalAlign& verticalAlign = StyleDisplay()->mVerticalAlign;
489 if (verticalAlign.IsKeyword()) {
490 auto value = verticalAlign.AsKeyword();
491 if (value == StyleVerticalAlignKeyword::Top ||
492 value == StyleVerticalAlignKeyword::Middle ||
493 value == StyleVerticalAlignKeyword::Bottom) {
494 return value;
497 return StyleVerticalAlignKeyword::Baseline;
500 bool nsTableCellFrame::CellHasVisibleContent(nscoord height,
501 nsTableFrame* tableFrame,
502 nsIFrame* kidFrame) {
503 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
504 if (height > 0) return true;
505 if (tableFrame->IsBorderCollapse()) return true;
506 for (nsIFrame* innerFrame : kidFrame->PrincipalChildList()) {
507 LayoutFrameType frameType = innerFrame->Type();
508 if (LayoutFrameType::Text == frameType) {
509 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
510 if (textFrame->HasNoncollapsedCharacters()) return true;
511 } else if (LayoutFrameType::Placeholder != frameType) {
512 return true;
513 } else {
514 nsIFrame* floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame);
515 if (floatFrame) return true;
518 return false;
521 nscoord nsTableCellFrame::GetCellBaseline() const {
522 // Ignore the position of the inner frame relative to the cell frame
523 // since we want the position as though the inner were top-aligned.
524 nsIFrame* inner = mFrames.FirstChild();
525 const auto wm = GetWritingMode();
526 const auto borderPadding = GetLogicalUsedBorderAndPadding(wm);
527 nscoord result;
528 if (!StyleDisplay()->IsContainLayout() &&
529 nsLayoutUtils::GetFirstLineBaseline(wm, inner, &result)) {
530 return result + borderPadding.BStart(wm);
532 const auto logicalSize = inner->GetLogicalSize(wm);
533 // ::-moz-cell-content shouldn't have any border/padding.
534 return logicalSize.BSize(wm) + borderPadding.BStart(wm);
537 int32_t nsTableCellFrame::GetRowSpan() {
538 int32_t rowSpan = 1;
540 // Don't look at the content's rowspan if we're a pseudo cell
541 if (!Style()->IsPseudoOrAnonBox()) {
542 dom::Element* elem = mContent->AsElement();
543 const nsAttrValue* attr = elem->GetParsedAttr(nsGkAtoms::rowspan);
544 // Note that we don't need to check the tag name, because only table cells
545 // (including MathML <mtd>) and table headers parse the "rowspan" attribute
546 // into an integer.
547 if (attr && attr->Type() == nsAttrValue::eInteger) {
548 rowSpan = attr->GetIntegerValue();
551 return rowSpan;
554 int32_t nsTableCellFrame::GetColSpan() {
555 int32_t colSpan = 1;
557 // Don't look at the content's colspan if we're a pseudo cell
558 if (!Style()->IsPseudoOrAnonBox()) {
559 dom::Element* elem = mContent->AsElement();
560 const nsAttrValue* attr = elem->GetParsedAttr(
561 MOZ_UNLIKELY(elem->IsMathMLElement()) ? nsGkAtoms::columnspan_
562 : nsGkAtoms::colspan);
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 "colspan" attribute
565 // into an integer.
566 if (attr && attr->Type() == nsAttrValue::eInteger) {
567 colSpan = attr->GetIntegerValue();
570 return colSpan;
573 /* virtual */
574 nscoord nsTableCellFrame::GetMinISize(gfxContext* aRenderingContext) {
575 nscoord result = 0;
576 DISPLAY_MIN_INLINE_SIZE(this, result);
578 nsIFrame* inner = mFrames.FirstChild();
579 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
580 IntrinsicISizeType::MinISize);
581 return result;
584 /* virtual */
585 nscoord nsTableCellFrame::GetPrefISize(gfxContext* aRenderingContext) {
586 nscoord result = 0;
587 DISPLAY_PREF_INLINE_SIZE(this, result);
589 nsIFrame* inner = mFrames.FirstChild();
590 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
591 IntrinsicISizeType::PrefISize);
592 return result;
595 /* virtual */ nsIFrame::IntrinsicSizeOffsetData
596 nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) {
597 IntrinsicSizeOffsetData result =
598 nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
600 result.margin = 0;
602 WritingMode wm = GetWritingMode();
603 result.border = GetBorderWidth(wm).IStartEnd(wm);
605 return result;
608 #ifdef DEBUG
609 # define PROBABLY_TOO_LARGE 1000000
610 static void DebugCheckChildSize(nsIFrame* aChild, ReflowOutput& aMet) {
611 WritingMode wm = aMet.GetWritingMode();
612 if ((aMet.ISize(wm) < 0) || (aMet.ISize(wm) > PROBABLY_TOO_LARGE)) {
613 printf("WARNING: cell content %p has large inline size %d \n",
614 static_cast<void*>(aChild), int32_t(aMet.ISize(wm)));
617 #endif
619 // the computed bsize for the cell, which descendants use for percent bsize
620 // calculations it is the bsize (minus border, padding) of the cell's first in
621 // flow during its final reflow without an unconstrained bsize.
622 static nscoord CalcUnpaginatedBSize(nsTableCellFrame& aCellFrame,
623 nsTableFrame& aTableFrame,
624 nscoord aBlockDirBorderPadding) {
625 const nsTableCellFrame* firstCellInFlow =
626 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow());
627 nsTableFrame* firstTableInFlow =
628 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
629 nsTableRowFrame* row =
630 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
631 nsTableRowGroupFrame* firstRGInFlow =
632 static_cast<nsTableRowGroupFrame*>(row->GetParent());
634 uint32_t rowIndex = firstCellInFlow->RowIndex();
635 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
637 nscoord computedBSize =
638 firstTableInFlow->GetRowSpacing(rowIndex, rowIndex + rowSpan - 1);
639 computedBSize -= aBlockDirBorderPadding;
640 uint32_t rowX;
641 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row;
642 row = row->GetNextRow(), rowX++) {
643 if (rowX > rowIndex + rowSpan - 1) {
644 break;
645 } else if (rowX >= rowIndex) {
646 computedBSize += row->GetUnpaginatedBSize();
649 return computedBSize;
652 void nsTableCellFrame::Reflow(nsPresContext* aPresContext,
653 ReflowOutput& aDesiredSize,
654 const ReflowInput& aReflowInput,
655 nsReflowStatus& aStatus) {
656 MarkInReflow();
657 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
658 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
659 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
661 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
662 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
665 // see if a special bsize reflow needs to occur due to having a pct height
666 nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
668 WritingMode wm = aReflowInput.GetWritingMode();
669 LogicalSize availSize = aReflowInput.AvailableSize();
671 LogicalMargin borderPadding =
672 aReflowInput.ComputedLogicalPadding(wm) + GetBorderWidth(wm);
674 ReflowOutput kidSize(wm);
675 SetPriorAvailISize(aReflowInput.AvailableISize());
676 nsIFrame* firstKid = mFrames.FirstChild();
677 NS_ASSERTION(
678 firstKid,
679 "Frame construction error, a table cell always has an inner cell frame");
680 nsTableFrame* tableFrame = GetTableFrame();
682 if (aReflowInput.mFlags.mSpecialBSizeReflow) {
683 const_cast<ReflowInput&>(aReflowInput)
684 .SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm));
685 DISPLAY_REFLOW_CHANGE();
686 } else if (aPresContext->IsPaginated()) {
687 nscoord computedUnpaginatedBSize = CalcUnpaginatedBSize(
688 (nsTableCellFrame&)*this, *tableFrame, borderPadding.BStartEnd(wm));
689 if (computedUnpaginatedBSize > 0) {
690 const_cast<ReflowInput&>(aReflowInput)
691 .SetComputedBSize(computedUnpaginatedBSize);
692 DISPLAY_REFLOW_CHANGE();
696 // We need to apply the skip sides for current fragmentainer's border and
697 // padding after we finish calculating the special block-size or unpaginated
698 // block-size to prevent the skip sides from affecting the results.
700 // We assume we are the last fragment by using
701 // PreReflowBlockLevelLogicalSkipSides(), i.e. the block-end border and
702 // padding is not skipped.
703 borderPadding.ApplySkipSides(PreReflowBlockLevelLogicalSkipSides());
705 availSize.ISize(wm) -= borderPadding.IStartEnd(wm);
707 // If we have a constrained available block-size, shrink it by subtracting our
708 // block-direction border and padding for our children.
709 if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
710 availSize.BSize(wm) -= borderPadding.BStart(wm);
712 if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
713 StyleBoxDecorationBreak::Clone) {
714 // We have box-decoration-break:clone. Subtract block-end border and
715 // padding from the available block-size as well.
716 availSize.BSize(wm) -= borderPadding.BEnd(wm);
720 // Available block-size can became negative after subtracting block-direction
721 // border and padding. Per spec, to guarantee progress, fragmentainers are
722 // assumed to have a minimum block size of 1px regardless of their used size.
723 // https://drafts.csswg.org/css-break/#breaking-rules
724 availSize.BSize(wm) =
725 std::max(availSize.BSize(wm), nsPresContext::CSSPixelsToAppUnits(1));
727 WritingMode kidWM = firstKid->GetWritingMode();
728 ReflowInput kidReflowInput(aPresContext, aReflowInput, firstKid,
729 availSize.ConvertTo(kidWM, wm));
731 // Don't be a percent height observer if we're in the middle of
732 // special-bsize reflow, in case we get an accidental NotifyPercentBSize()
733 // call (which we shouldn't honor during special-bsize reflow)
734 if (!aReflowInput.mFlags.mSpecialBSizeReflow) {
735 // mPercentBSizeObserver is for children of cells in quirks mode,
736 // but only those than are tables in standards mode. NeedsToObserve
737 // will determine how far this is propagated to descendants.
738 kidReflowInput.mPercentBSizeObserver = this;
740 // Don't propagate special bsize reflow input to our kids
741 kidReflowInput.mFlags.mSpecialBSizeReflow = false;
743 if (aReflowInput.mFlags.mSpecialBSizeReflow ||
744 FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
745 // We need to force the kid to have mBResize set if we've had a
746 // special reflow in the past, since the non-special reflow needs to
747 // resize back to what it was without the special bsize reflow.
748 kidReflowInput.SetBResize(true);
751 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
753 LogicalPoint kidOrigin(wm, borderPadding.IStart(wm),
754 borderPadding.BStart(wm));
755 nsRect origRect = firstKid->GetRect();
756 nsRect origInkOverflow = firstKid->InkOverflowRect();
757 bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
759 ReflowChild(firstKid, aPresContext, kidSize, kidReflowInput, wm, kidOrigin,
760 containerSize, ReflowChildFlags::Default, aStatus);
761 if (aStatus.IsOverflowIncomplete()) {
762 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually
763 // handle it
764 // XXX should paginate overflow as overflow, but not in this patch (bug
765 // 379349)
766 aStatus.SetIncomplete();
767 printf("Set table cell incomplete %p\n", static_cast<void*>(this));
770 // XXXbz is this invalidate actually needed, really?
771 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
772 InvalidateFrameSubtree();
775 #ifdef DEBUG
776 DebugCheckChildSize(firstKid, kidSize);
777 #endif
779 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
780 // see testcase "emptyCells.html"
781 nsIFrame* prevInFlow = GetPrevInFlow();
782 bool isEmpty;
783 if (prevInFlow) {
784 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
785 } else {
786 isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid);
788 SetContentEmpty(isEmpty);
790 // Place the child
791 FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowInput, wm,
792 kidOrigin, containerSize, ReflowChildFlags::Default);
794 if (tableFrame->IsBorderCollapse()) {
795 nsTableFrame::InvalidateTableFrame(firstKid, origRect, origInkOverflow,
796 firstReflow);
798 // first, compute the bsize which can be set w/o being restricted by
799 // available bsize
800 LogicalSize cellSize(wm);
801 cellSize.BSize(wm) = kidSize.BSize(wm);
803 if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) {
804 cellSize.BSize(wm) += borderPadding.BStart(wm);
806 if (aStatus.IsComplete() ||
807 aReflowInput.mStyleBorder->mBoxDecorationBreak ==
808 StyleBoxDecorationBreak::Clone) {
809 cellSize.BSize(wm) += borderPadding.BEnd(wm);
813 // next determine the cell's isize
814 cellSize.ISize(wm) = kidSize.ISize(
815 wm); // at this point, we've factored in the cell's style attributes
817 // factor in border and padding
818 if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) {
819 cellSize.ISize(wm) += borderPadding.IStartEnd(wm);
822 // set the cell's desired size and max element size
823 aDesiredSize.SetSize(wm, cellSize);
825 // the overflow area will be computed when BlockDirAlignChild() gets called
827 if (aReflowInput.mFlags.mSpecialBSizeReflow &&
828 NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
829 aDesiredSize.BSize(wm) = BSize(wm);
832 // If our parent is in initial reflow, it'll handle invalidating our
833 // entire overflow rect.
834 if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
835 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
836 InvalidateFrame();
839 // remember the desired size for this reflow
840 SetDesiredSize(aDesiredSize);
842 // Any absolutely-positioned children will get reflowed in
843 // nsIFrame::FixupPositionedTableParts in another pass, so propagate our
844 // dirtiness to them before our parent clears our dirty bits.
845 PushDirtyBitToAbsoluteFrames();
848 /* ----- global methods ----- */
850 NS_QUERYFRAME_HEAD(nsTableCellFrame)
851 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
852 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
853 NS_QUERYFRAME_ENTRY(nsIPercentBSizeObserver)
854 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
856 #ifdef ACCESSIBILITY
857 a11y::AccType nsTableCellFrame::AccessibleType() {
858 return a11y::eHTMLTableCellType;
860 #endif
862 /* This is primarily for editor access via nsITableLayout */
863 NS_IMETHODIMP
864 nsTableCellFrame::GetCellIndexes(int32_t& aRowIndex, int32_t& aColIndex) {
865 aRowIndex = RowIndex();
866 aColIndex = mColIndex;
867 return NS_OK;
870 nsTableCellFrame* NS_NewTableCellFrame(PresShell* aPresShell,
871 ComputedStyle* aStyle,
872 nsTableFrame* aTableFrame) {
873 if (aTableFrame->IsBorderCollapse())
874 return new (aPresShell) nsBCTableCellFrame(aStyle, aTableFrame);
875 else
876 return new (aPresShell) nsTableCellFrame(aStyle, aTableFrame);
879 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
881 LogicalMargin nsTableCellFrame::GetBorderWidth(WritingMode aWM) const {
882 return LogicalMargin(aWM, StyleBorder()->GetComputedBorder());
885 void nsTableCellFrame::AppendDirectlyOwnedAnonBoxes(
886 nsTArray<OwnedAnonBox>& aResult) {
887 nsIFrame* kid = mFrames.FirstChild();
888 MOZ_ASSERT(kid && !kid->GetNextSibling(),
889 "Table cells should have just one child");
890 aResult.AppendElement(OwnedAnonBox(kid));
893 #ifdef DEBUG_FRAME_DUMP
894 nsresult nsTableCellFrame::GetFrameName(nsAString& aResult) const {
895 return MakeFrameName(u"TableCell"_ns, aResult);
897 #endif
899 // nsBCTableCellFrame
901 nsBCTableCellFrame::nsBCTableCellFrame(ComputedStyle* aStyle,
902 nsTableFrame* aTableFrame)
903 : nsTableCellFrame(aStyle, aTableFrame, kClassID) {
904 mBStartBorder = mIEndBorder = mBEndBorder = mIStartBorder = 0;
907 nsBCTableCellFrame::~nsBCTableCellFrame() = default;
909 /* virtual */
910 nsMargin nsBCTableCellFrame::GetUsedBorder() const {
911 WritingMode wm = GetWritingMode();
912 return GetBorderWidth(wm).GetPhysicalMargin(wm);
915 #ifdef DEBUG_FRAME_DUMP
916 nsresult nsBCTableCellFrame::GetFrameName(nsAString& aResult) const {
917 return MakeFrameName(u"BCTableCell"_ns, aResult);
919 #endif
921 LogicalMargin nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const {
922 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
923 return LogicalMargin(aWM, BC_BORDER_END_HALF_COORD(d2a, mBStartBorder),
924 BC_BORDER_START_HALF_COORD(d2a, mIEndBorder),
925 BC_BORDER_START_HALF_COORD(d2a, mBEndBorder),
926 BC_BORDER_END_HALF_COORD(d2a, mIStartBorder));
929 BCPixelSize nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const {
930 switch (aSide) {
931 case eLogicalSideBStart:
932 return BC_BORDER_END_HALF(mBStartBorder);
933 case eLogicalSideIEnd:
934 return BC_BORDER_START_HALF(mIEndBorder);
935 case eLogicalSideBEnd:
936 return BC_BORDER_START_HALF(mBEndBorder);
937 default:
938 return BC_BORDER_END_HALF(mIStartBorder);
942 void nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) {
943 switch (aSide) {
944 case eLogicalSideBStart:
945 mBStartBorder = aValue;
946 break;
947 case eLogicalSideIEnd:
948 mIEndBorder = aValue;
949 break;
950 case eLogicalSideBEnd:
951 mBEndBorder = aValue;
952 break;
953 default:
954 mIStartBorder = aValue;
958 /* virtual */
959 nsMargin nsBCTableCellFrame::GetBorderOverflow() {
960 WritingMode wm = GetWritingMode();
961 int32_t d2a = PresContext()->AppUnitsPerDevPixel();
962 LogicalMargin halfBorder(wm, BC_BORDER_START_HALF_COORD(d2a, mBStartBorder),
963 BC_BORDER_END_HALF_COORD(d2a, mIEndBorder),
964 BC_BORDER_END_HALF_COORD(d2a, mBEndBorder),
965 BC_BORDER_START_HALF_COORD(d2a, mIStartBorder));
966 return halfBorder.GetPhysicalMargin(wm);
969 namespace mozilla {
971 class nsDisplayTableCellSelection final : public nsPaintedDisplayItem {
972 public:
973 nsDisplayTableCellSelection(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
974 : nsPaintedDisplayItem(aBuilder, aFrame) {
975 MOZ_COUNT_CTOR(nsDisplayTableCellSelection);
977 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTableCellSelection)
979 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override {
980 static_cast<nsTableCellFrame*>(mFrame)->DecorateForSelection(
981 aCtx->GetDrawTarget(), ToReferenceFrame());
983 NS_DISPLAY_DECL_NAME("TableCellSelection", TYPE_TABLE_CELL_SELECTION)
985 bool CreateWebRenderCommands(
986 mozilla::wr::DisplayListBuilder& aBuilder,
987 mozilla::wr::IpcResourceUpdateQueue& aResources,
988 const StackingContextHelper& aSc,
989 mozilla::layers::RenderRootStateManager* aManager,
990 nsDisplayListBuilder* aDisplayListBuilder) override {
991 RefPtr<nsFrameSelection> frameSelection =
992 mFrame->PresShell()->FrameSelection();
993 return !frameSelection->IsInTableSelectionMode();
997 } // namespace mozilla
999 void nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1000 const nsDisplayListSet& aLists) {
1001 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
1002 if (ShouldPaintBordersAndBackgrounds()) {
1003 // display outset box-shadows if we need to.
1004 bool hasBoxShadow = !StyleEffects()->mBoxShadow.IsEmpty();
1005 if (hasBoxShadow) {
1006 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowOuter>(
1007 aBuilder, this);
1010 nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
1011 nsRect bgRectInsideBorder = bgRect;
1013 // If we're doing collapsed borders, and this element forms a new stacking
1014 // context or has position:relative (which paints as though it did), inset
1015 // the background rect so that we don't overpaint the inset part of our
1016 // borders.
1017 nsTableFrame* tableFrame = GetTableFrame();
1018 if (tableFrame->IsBorderCollapse() &&
1019 (IsStackingContext() ||
1020 StyleDisplay()->mPosition == StylePositionProperty::Relative)) {
1021 bgRectInsideBorder.Deflate(GetUsedBorder());
1024 // display background if we need to.
1025 const AppendedBackgroundType result =
1026 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1027 aBuilder, this, bgRectInsideBorder, aLists.BorderBackground(), true,
1028 bgRect);
1029 if (result == AppendedBackgroundType::None) {
1030 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
1031 aLists.BorderBackground());
1034 // display inset box-shadows if we need to.
1035 if (hasBoxShadow) {
1036 aLists.BorderBackground()->AppendNewToTop<nsDisplayBoxShadowInner>(
1037 aBuilder, this);
1040 // display borders if we need to
1041 ProcessBorders(tableFrame, aBuilder, aLists);
1043 // and display the selection border if we need to
1044 if (IsSelected()) {
1045 aLists.BorderBackground()->AppendNewToTop<nsDisplayTableCellSelection>(
1046 aBuilder, this);
1049 // This can be null if display list building initiated in the middle
1050 // of the table, which can happen with background-clip:text and
1051 // -moz-element.
1052 nsDisplayTableBackgroundSet* backgrounds =
1053 aBuilder->GetTableBackgroundSet();
1054 if (backgrounds) {
1055 // Compute bgRect relative to reference frame, but using the
1056 // normal (without position:relative offsets) positions for the
1057 // cell, row and row group.
1058 bgRect = GetRectRelativeToSelf() + GetNormalPosition();
1060 nsTableRowFrame* row = GetTableRowFrame();
1061 bgRect += row->GetNormalPosition();
1063 nsTableRowGroupFrame* rowGroup = row->GetTableRowGroupFrame();
1064 bgRect += rowGroup->GetNormalPosition();
1066 bgRect += backgrounds->TableToReferenceFrame();
1068 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
1069 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
1070 aBuilder);
1071 if (IsStackingContext() || row->IsStackingContext() ||
1072 rowGroup->IsStackingContext() || tableFrame->IsStackingContext()) {
1073 // The col/colgroup items we create below will be inserted directly into
1074 // the BorderBackgrounds list of the table frame. That means that
1075 // they'll be moved *outside* of any wrapper items created for any
1076 // frames between this table cell frame and the table wrapper frame, and
1077 // will not participate in those frames's opacity / transform / filter /
1078 // mask effects. If one of those frames is a stacking context, then we
1079 // may have one or more of those wrapper items, and one of them may have
1080 // captured a clip. In order to ensure correct clipping and scrolling of
1081 // the col/colgroup items, restore the clip and ASR that we observed
1082 // when we entered the table frame. If that frame is a stacking context
1083 // but doesn't have any clip capturing wrapper items, then we'll
1084 // double-apply the clip. That's ok.
1085 clipState.SetClipChainForContainingBlockDescendants(
1086 backgrounds->GetTableClipChain());
1087 asrSetter.SetCurrentActiveScrolledRoot(backgrounds->GetTableASR());
1090 // Create backgrounds items as needed for the column and column
1091 // group that this cell occupies.
1092 nsTableColFrame* col = backgrounds->GetColForIndex(ColIndex());
1093 nsTableColGroupFrame* colGroup = col->GetTableColGroupFrame();
1095 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForColGroup;
1096 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1097 aBuilder, colGroup, bgRect, backgrounds->ColGroupBackgrounds(), false,
1098 colGroup->GetRect() + backgrounds->TableToReferenceFrame(), this,
1099 &buildingForColGroup);
1101 Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForCol;
1102 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
1103 aBuilder, col, bgRect, backgrounds->ColBackgrounds(), false,
1104 col->GetRect() + colGroup->GetPosition() +
1105 backgrounds->TableToReferenceFrame(),
1106 this, &buildingForCol);
1110 // the 'empty-cells' property has no effect on 'outline'
1111 DisplayOutline(aBuilder, aLists);
1113 nsIFrame* kid = mFrames.FirstChild();
1114 NS_ASSERTION(kid && !kid->GetNextSibling(),
1115 "Table cells should have just one child");
1116 // The child's background will go in our BorderBackground() list.
1117 // This isn't a problem since it won't have a real background except for
1118 // event handling. We do not call BuildDisplayListForNonBlockChildren
1119 // because that/ would put the child's background in the Content() list
1120 // which isn't right (e.g., would end up on top of our child floats for
1121 // event handling).
1122 BuildDisplayListForChild(aBuilder, kid, aLists);