tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsTableCellFrame.cpp (44525B)


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