tor-browser

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

nsTableWrapperFrame.cpp (33220B)


      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 "nsTableWrapperFrame.h"
      7 
      8 #include <algorithm>
      9 
     10 #include "LayoutConstants.h"
     11 #include "mozilla/ComputedStyle.h"
     12 #include "mozilla/PresShell.h"
     13 #include "nsCSSRendering.h"
     14 #include "nsDisplayList.h"
     15 #include "nsFrameManager.h"
     16 #include "nsGkAtoms.h"
     17 #include "nsGridContainerFrame.h"
     18 #include "nsHTMLParts.h"
     19 #include "nsIContent.h"
     20 #include "nsIFrameInlines.h"
     21 #include "nsLayoutUtils.h"
     22 #include "nsPresContext.h"
     23 #include "nsStyleConsts.h"
     24 #include "nsTableCellFrame.h"
     25 #include "nsTableFrame.h"
     26 #include "prinrval.h"
     27 
     28 using namespace mozilla;
     29 using namespace mozilla::layout;
     30 
     31 nscoord nsTableWrapperFrame::SynthesizeFallbackBaseline(
     32    mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
     33  const auto marginBlockEnd = GetLogicalUsedMargin(aWM).BEnd(aWM);
     34  if (aWM.IsCentralBaseline()) {
     35    return (BSize(aWM) + marginBlockEnd) / 2;
     36  }
     37  // Our fallback baseline is the block-end margin-edge, with respect to the
     38  // given writing mode.
     39  if (aBaselineGroup == BaselineSharingGroup::Last) {
     40    return -marginBlockEnd;
     41  }
     42  return BSize(aWM) + marginBlockEnd;
     43 }
     44 
     45 Maybe<nscoord> nsTableWrapperFrame::GetNaturalBaselineBOffset(
     46    WritingMode aWM, BaselineSharingGroup aBaselineGroup,
     47    BaselineExportContext aExportContext) const {
     48  // Baseline is determined by row
     49  // (https://drafts.csswg.org/css-align-3/#baseline-export). If the row
     50  // direction is going to be orthogonal to the parent's writing mode, the
     51  // resulting baseline wouldn't be valid, so we use the fallback baseline
     52  // instead.
     53  if (StyleDisplay()->IsContainLayout() ||
     54      GetWritingMode().IsOrthogonalTo(aWM)) {
     55    return Nothing{};
     56  }
     57  auto* innerTable = InnerTableFrame();
     58  return innerTable
     59      ->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext)
     60      .map([this, aWM, aBaselineGroup, innerTable](nscoord aBaseline) {
     61        auto bStart = innerTable->BStart(aWM, mRect.Size());
     62        if (aBaselineGroup == BaselineSharingGroup::First) {
     63          return aBaseline + bStart;
     64        }
     65        auto bEnd = bStart + innerTable->BSize(aWM);
     66        return BSize(aWM) - (bEnd - aBaseline);
     67      });
     68 }
     69 
     70 nsTableWrapperFrame::nsTableWrapperFrame(ComputedStyle* aStyle,
     71                                         nsPresContext* aPresContext,
     72                                         ClassID aID)
     73    : nsContainerFrame(aStyle, aPresContext, aID) {}
     74 
     75 nsTableWrapperFrame::~nsTableWrapperFrame() = default;
     76 
     77 NS_QUERYFRAME_HEAD(nsTableWrapperFrame)
     78  NS_QUERYFRAME_ENTRY(nsTableWrapperFrame)
     79 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     80 
     81 #ifdef ACCESSIBILITY
     82 a11y::AccType nsTableWrapperFrame::AccessibleType() {
     83  return a11y::eHTMLTableType;
     84 }
     85 #endif
     86 
     87 void nsTableWrapperFrame::Destroy(DestroyContext& aContext) {
     88  DestroyAbsoluteFrames(aContext);
     89  nsContainerFrame::Destroy(aContext);
     90 }
     91 
     92 void nsTableWrapperFrame::AppendFrames(ChildListID aListID,
     93                                       nsFrameList&& aFrameList) {
     94  // We only have two child frames: the inner table and a caption frame.
     95  // The inner frame is provided when we're initialized, and it cannot change
     96  MOZ_ASSERT(FrameChildListID::Principal == aListID, "unexpected child list");
     97  MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
     98             "Why are we appending non-caption frames?");
     99  nsContainerFrame::AppendFrames(aListID, std::move(aFrameList));
    100  // The presence of caption frames makes us sort our display list differently,
    101  // so mark us as changed for the new ordering.
    102  MarkNeedsDisplayItemRebuild();
    103 }
    104 
    105 void nsTableWrapperFrame::InsertFrames(
    106    ChildListID aListID, nsIFrame* aPrevFrame,
    107    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
    108  MOZ_ASSERT(FrameChildListID::Principal == aListID, "unexpected child list");
    109  MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
    110             "Why are we inserting non-caption frames?");
    111  MOZ_ASSERT(!aPrevFrame || aPrevFrame->GetParent() == this,
    112             "inserting after sibling frame with different parent");
    113  nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
    114                                 std::move(aFrameList));
    115  MarkNeedsDisplayItemRebuild();
    116 }
    117 
    118 void nsTableWrapperFrame::RemoveFrame(DestroyContext& aContext,
    119                                      ChildListID aListID,
    120                                      nsIFrame* aOldFrame) {
    121  // We only have two child frames: the inner table and one caption frame.
    122  // The inner frame can't be removed so this should be the caption
    123  MOZ_ASSERT(aOldFrame->IsTableCaption(), "can't remove inner frame");
    124  nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame);
    125  MarkNeedsDisplayItemRebuild();
    126 }
    127 
    128 void nsTableWrapperFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    129                                           const nsDisplayListSet& aLists) {
    130  // No border or background is painted because they belong to the inner table.
    131  // The outline belongs to the wrapper frame so it can contain the caption.
    132 
    133  // If there's no caption, take a short cut to avoid having to create
    134  // the special display list set and then sort it.
    135  if (nsIFrame* inner = mFrames.OnlyChild()) {
    136    BuildDisplayListForChild(aBuilder, inner, aLists);
    137    DisplayOutline(aBuilder, aLists);
    138    return;
    139  }
    140 
    141  MOZ_ASSERT(mFrames.FirstChild());
    142  MOZ_ASSERT(mFrames.FirstChild()->IsTableFrame());
    143 
    144  nsDisplayListCollection set(aBuilder);
    145  nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
    146  for (auto* frame : mFrames) {
    147    const bool isTable = frame->IsTableFrame();
    148    auto& setForFrame = isTable ? set : captionSet;
    149    BuildDisplayListForChild(aBuilder, frame, setForFrame);
    150    if (!isTable) {
    151      // FIXME(emilio, bug 144517): Historically we haven't displayed / laid
    152      // out multiple captions. This preserves that behavior.
    153      break;
    154    }
    155  }
    156 
    157  // Now we have to sort everything by content order, since the caption
    158  // may be somewhere inside the table.
    159  // We don't sort BlockBorderBackgrounds and BorderBackgrounds because the
    160  // display items in those lists should stay out of content order in order to
    161  // follow the rules in https://www.w3.org/TR/CSS21/zindex.html#painting-order
    162  // and paint the caption background after all of the rest.
    163  set.Floats()->SortByContentOrder(GetContent());
    164  set.Content()->SortByContentOrder(GetContent());
    165  set.PositionedDescendants()->SortByContentOrder(GetContent());
    166  set.Outlines()->SortByContentOrder(GetContent());
    167  set.MoveTo(aLists);
    168 
    169  DisplayOutline(aBuilder, aLists);
    170 }
    171 
    172 ComputedStyle* nsTableWrapperFrame::GetParentComputedStyle(
    173    nsIFrame** aProviderFrame) const {
    174  // The table wrapper frame and the (inner) table frame split the style
    175  // data by giving the table frame the ComputedStyle associated with
    176  // the table content node and creating a ComputedStyle for the wrapper
    177  // frame that is a *child* of the table frame's ComputedStyle,
    178  // matching the ::-moz-table-wrapper pseudo-element. html.css has a
    179  // rule that causes that pseudo-element (and thus the wrapper table)
    180  // to inherit *some* style properties from the table frame.  The
    181  // children of the table inherit directly from the inner table, and
    182  // the table wrapper's ComputedStyle is a leaf.
    183 
    184  return (*aProviderFrame = InnerTableFrame())->Style();
    185 }
    186 
    187 nscoord nsTableWrapperFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    188                                            IntrinsicISizeType aType) {
    189  nscoord iSize = nsLayoutUtils::IntrinsicForContainer(
    190      aInput.mContext, InnerTableFrame(), aType);
    191 
    192  {
    193    // If aFrame is a container for font size inflation, then shrink
    194    // wrapping inside of it should not apply font size inflation.
    195    AutoMaybeDisableFontInflation an(this);
    196 
    197    // Tables can't shrink smaller than their intrinsic minimum inline size,
    198    // no matter what.
    199    const IntrinsicSizeInput input(aInput.mContext, Nothing(), Nothing());
    200 
    201    // GetMinISize() returns a content-box inline size, but we need the
    202    // margin-box inline size as the contribution in the inline axis.
    203    const IntrinsicSizeOffsetData offset =
    204        InnerTableFrame()->IntrinsicISizeOffsets();
    205    const nscoord innerTableMinISize =
    206        InnerTableFrame()->GetMinISize(input) + offset.MarginBorderPadding();
    207    iSize = std::max(iSize, innerTableMinISize);
    208  }
    209 
    210  if (nsIFrame* caption = GetCaption()) {
    211    // The table wrapper's intrinsic inline size should be as least as large as
    212    // caption's min inline size.
    213    const nscoord capMinISize = nsLayoutUtils::IntrinsicForContainer(
    214        aInput.mContext, caption, IntrinsicISizeType::MinISize);
    215    iSize = std::max(iSize, capMinISize);
    216  }
    217  return iSize;
    218 }
    219 
    220 LogicalSize nsTableWrapperFrame::InnerTableShrinkWrapSize(
    221    const SizeComputationInput& aSizingInput, nsTableFrame* aTableFrame,
    222    WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize,
    223    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) const {
    224  MOZ_ASSERT(InnerTableFrame() == aTableFrame);
    225 
    226  AutoMaybeDisableFontInflation an(aTableFrame);
    227 
    228  Maybe<LogicalMargin> collapseBorder;
    229  Maybe<LogicalMargin> collapsePadding;
    230  aTableFrame->GetCollapsedBorderPadding(collapseBorder, collapsePadding);
    231 
    232  SizeComputationInput input(aTableFrame, aSizingInput.mRenderingContext, aWM,
    233                             aCBSize.ISize(aWM), collapseBorder,
    234                             collapsePadding);
    235  LogicalSize marginSize(aWM);  // Inner table doesn't have any margin
    236  LogicalSize bpSize = input.ComputedLogicalBorderPadding(aWM).Size(aWM);
    237 
    238  // Note that we pass an empty caption-area here (rather than the caption's
    239  // actual size). This is fine because:
    240  //
    241  // 1) nsTableWrapperFrame::ComputeSize() only uses the size returned by this
    242  //    method (indirectly via calling nsTableWrapperFrame::ComputeAutoSize())
    243  //    if it get a aSizeOverrides arg containing any size overrides with
    244  //    mApplyOverridesVerbatim=true. The aSizeOverrides arg is passed to this
    245  //    method without any modifications.
    246  //
    247  // 2) With 1), that means the aSizeOverrides passing into this method should
    248  //    be applied to the inner table directly, so we don't need to subtract
    249  //    caption-area when preparing innerOverrides for
    250  //    nsTableFrame::ComputeSize().
    251  StyleSizeOverrides innerOverrides = ComputeSizeOverridesForInnerTable(
    252      aTableFrame, aSizeOverrides, bpSize, /* aBSizeOccupiedByCaption = */ 0);
    253  auto size = aTableFrame
    254                  ->ComputeSize(input, aWM, aCBSize, aAvailableISize,
    255                                marginSize, bpSize, innerOverrides, aFlags)
    256                  .mLogicalSize;
    257  size.ISize(aWM) += bpSize.ISize(aWM);
    258  if (size.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
    259    size.BSize(aWM) += bpSize.BSize(aWM);
    260  }
    261  return size;
    262 }
    263 
    264 LogicalSize nsTableWrapperFrame::CaptionShrinkWrapSize(
    265    const SizeComputationInput& aSizingInput, nsIFrame* aCaptionFrame,
    266    WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize,
    267    ComputeSizeFlags aFlags) const {
    268  MOZ_ASSERT(aCaptionFrame != mFrames.FirstChild());
    269 
    270  AutoMaybeDisableFontInflation an(aCaptionFrame);
    271 
    272  SizeComputationInput input(aCaptionFrame, aSizingInput.mRenderingContext, aWM,
    273                             aCBSize.ISize(aWM));
    274  LogicalSize marginSize = input.ComputedLogicalMargin(aWM).Size(aWM);
    275  LogicalSize bpSize = input.ComputedLogicalBorderPadding(aWM).Size(aWM);
    276 
    277  auto size = aCaptionFrame
    278                  ->ComputeSize(input, aWM, aCBSize, aAvailableISize,
    279                                marginSize, bpSize, {}, aFlags)
    280                  .mLogicalSize;
    281  size.ISize(aWM) += (marginSize.ISize(aWM) + bpSize.ISize(aWM));
    282  if (size.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
    283    size.BSize(aWM) += (marginSize.BSize(aWM) + bpSize.BSize(aWM));
    284  }
    285  return size;
    286 }
    287 
    288 StyleSize nsTableWrapperFrame::ReduceStyleSizeBy(
    289    const StyleSize& aStyleSize, const nscoord aAmountToReduce) const {
    290  MOZ_ASSERT(aStyleSize.ConvertsToLength(), "Only handles 'Length' StyleSize!");
    291  const nscoord size = std::max(0, aStyleSize.ToLength() - aAmountToReduce);
    292  return StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(size));
    293 }
    294 
    295 StyleSizeOverrides nsTableWrapperFrame::ComputeSizeOverridesForInnerTable(
    296    const nsTableFrame* aTableFrame,
    297    const StyleSizeOverrides& aWrapperSizeOverrides,
    298    const LogicalSize& aBorderPadding, nscoord aBSizeOccupiedByCaption) const {
    299  if (aWrapperSizeOverrides.mApplyOverridesVerbatim ||
    300      !aWrapperSizeOverrides.HasAnyLengthOverrides()) {
    301    // We are asked to apply the size overrides directly to the inner table, or
    302    // there's no 'Length' size overrides. No need to tweak the size overrides.
    303    return aWrapperSizeOverrides;
    304  }
    305 
    306  const auto wm = aTableFrame->GetWritingMode();
    307  LogicalSize areaOccupied(wm, 0, aBSizeOccupiedByCaption);
    308  if (aTableFrame->StylePosition()->mBoxSizing == StyleBoxSizing::Content) {
    309    // If the inner table frame has 'box-sizing: content', enlarge the occupied
    310    // area by adding border & padding because they should also be subtracted
    311    // from the size overrides.
    312    areaOccupied += aBorderPadding;
    313  }
    314 
    315  StyleSizeOverrides innerSizeOverrides;
    316  const auto& wrapperISize = aWrapperSizeOverrides.mStyleISize;
    317  if (wrapperISize) {
    318    MOZ_ASSERT(!wrapperISize->HasPercent(),
    319               "Table doesn't support size overrides containing percentages!");
    320    innerSizeOverrides.mStyleISize.emplace(
    321        wrapperISize->ConvertsToLength()
    322            ? ReduceStyleSizeBy(*wrapperISize, areaOccupied.ISize(wm))
    323            : *wrapperISize);
    324  }
    325 
    326  const auto& wrapperBSize = aWrapperSizeOverrides.mStyleBSize;
    327  if (wrapperBSize) {
    328    MOZ_ASSERT(!wrapperBSize->HasPercent(),
    329               "Table doesn't support size overrides containing percentages!");
    330    innerSizeOverrides.mStyleBSize.emplace(
    331        wrapperBSize->ConvertsToLength()
    332            ? ReduceStyleSizeBy(*wrapperBSize, areaOccupied.BSize(wm))
    333            : *wrapperBSize);
    334  }
    335 
    336  return innerSizeOverrides;
    337 }
    338 
    339 /* virtual */
    340 nsIFrame::SizeComputationResult nsTableWrapperFrame::ComputeSize(
    341    const SizeComputationInput& aSizingInput, WritingMode aWM,
    342    const LogicalSize& aCBSize, nscoord aAvailableISize,
    343    const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
    344    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
    345  auto result = nsContainerFrame::ComputeSize(
    346      aSizingInput, aWM, aCBSize, aAvailableISize, aMargin, aBorderPadding,
    347      aSizeOverrides, aFlags);
    348 
    349  if (aSizeOverrides.mApplyOverridesVerbatim &&
    350      aSizeOverrides.HasAnyOverrides()) {
    351    // We are asked to apply the size overrides directly to the inner table, but
    352    // we still want ourselves to remain 'auto'-sized and shrink-wrapping our
    353    // children's sizes. (Table wrapper frames always have 'auto' inline-size
    354    // and block-size, since we don't inherit those properties from inner table,
    355    // and authors can't target them with styling.)
    356    auto size =
    357        ComputeAutoSize(aSizingInput, aWM, aCBSize, aAvailableISize, aMargin,
    358                        aBorderPadding, aSizeOverrides, aFlags);
    359    result.mLogicalSize = size;
    360  }
    361 
    362  return result;
    363 }
    364 
    365 /* virtual */
    366 LogicalSize nsTableWrapperFrame::ComputeAutoSize(
    367    const SizeComputationInput& aSizingInput, WritingMode aWM,
    368    const LogicalSize& aCBSize, nscoord aAvailableISize,
    369    const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
    370    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
    371  nscoord kidAvailableISize = aAvailableISize - aMargin.ISize(aWM);
    372  NS_ASSERTION(aBorderPadding.IsAllZero(),
    373               "Table wrapper frames cannot have borders or paddings");
    374 
    375  // When we're shrink-wrapping, our auto size needs to wrap around the
    376  // actual size of the table, which (if it is specified as a percent)
    377  // could be something that is not reflected in our GetMinISize and
    378  // GetPrefISize.  See bug 349457 for an example.
    379  const ComputeSizeFlags flags = CreateComputeSizeFlagsForChild();
    380 
    381  // Match the logic in Reflow() that sets aside space for the caption.
    382  Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
    383 
    384  const LogicalSize innerTableSize =
    385      InnerTableShrinkWrapSize(aSizingInput, InnerTableFrame(), aWM, aCBSize,
    386                               kidAvailableISize, aSizeOverrides, flags);
    387  if (!captionSide) {
    388    return innerTableSize;
    389  }
    390  const LogicalSize captionSize =
    391      CaptionShrinkWrapSize(aSizingInput, GetCaption(), aWM, aCBSize,
    392                            innerTableSize.ISize(aWM), flags);
    393  const nscoord iSize =
    394      std::max(innerTableSize.ISize(aWM), captionSize.ISize(aWM));
    395  nscoord bSize = NS_UNCONSTRAINEDSIZE;
    396  if (innerTableSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
    397      captionSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
    398    bSize = innerTableSize.BSize(aWM) + captionSize.BSize(aWM);
    399  }
    400  return LogicalSize(aWM, iSize, bSize);
    401 }
    402 
    403 Maybe<StyleCaptionSide> nsTableWrapperFrame::GetCaptionSide() const {
    404  if (!HasCaption()) {
    405    return Nothing();
    406  }
    407  return Some(GetCaption()->StyleTableBorder()->mCaptionSide);
    408 }
    409 
    410 nscoord nsTableWrapperFrame::ComputeFinalBSize(
    411    const LogicalSize& aInnerSize, const LogicalSize& aCaptionSize,
    412    const LogicalMargin& aCaptionMargin, const WritingMode aWM) const {
    413  // negative sizes can upset overflow-area code
    414  return std::max(0, aInnerSize.BSize(aWM) +
    415                         std::max(0, aCaptionSize.BSize(aWM) +
    416                                         aCaptionMargin.BStartEnd(aWM)));
    417 }
    418 
    419 void nsTableWrapperFrame::GetCaptionOrigin(StyleCaptionSide aCaptionSide,
    420                                           const LogicalSize& aInnerSize,
    421                                           const LogicalSize& aCaptionSize,
    422                                           LogicalMargin& aCaptionMargin,
    423                                           LogicalPoint& aOrigin,
    424                                           WritingMode aWM) const {
    425  aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
    426  if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
    427      (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
    428      (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
    429      (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
    430    return;
    431  }
    432  if (!HasCaption()) {
    433    return;
    434  }
    435 
    436  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
    437                   NS_AUTOMARGIN != aCaptionMargin.BStart(aWM) &&
    438                   NS_AUTOMARGIN != aCaptionMargin.BEnd(aWM),
    439               "The computed caption margin is auto?");
    440 
    441  aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
    442 
    443  // block-dir computation
    444  switch (aCaptionSide) {
    445    case StyleCaptionSide::Bottom:
    446      aOrigin.B(aWM) = aInnerSize.BSize(aWM) + aCaptionMargin.BStart(aWM);
    447      break;
    448    case StyleCaptionSide::Top:
    449      aOrigin.B(aWM) = aCaptionMargin.BStart(aWM);
    450      break;
    451  }
    452 }
    453 
    454 void nsTableWrapperFrame::GetInnerOrigin(const MaybeCaptionSide& aCaptionSide,
    455                                         const LogicalSize& aCaptionSize,
    456                                         const LogicalMargin& aCaptionMargin,
    457                                         const LogicalSize& aInnerSize,
    458                                         LogicalPoint& aOrigin,
    459                                         WritingMode aWM) const {
    460  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
    461                   NS_AUTOMARGIN != aCaptionMargin.IEnd(aWM),
    462               "The computed caption margin is auto?");
    463 
    464  aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
    465  if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
    466      (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
    467      (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
    468      (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
    469    return;
    470  }
    471 
    472  // block-dir computation
    473  if (aCaptionSide) {
    474    switch (*aCaptionSide) {
    475      case StyleCaptionSide::Bottom:
    476        // Leave at zero.
    477        break;
    478      case StyleCaptionSide::Top:
    479        aOrigin.B(aWM) =
    480            aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM);
    481        break;
    482    }
    483  }
    484 }
    485 
    486 ComputeSizeFlags nsTableWrapperFrame::CreateComputeSizeFlagsForChild() const {
    487  // Shrink-wrap child frames by default, except if we're a stretched grid item.
    488  if (MOZ_UNLIKELY(IsGridItem())) {
    489    auto* gridContainer = static_cast<nsGridContainerFrame*>(GetParent());
    490    if (gridContainer->GridItemShouldStretch(this, LogicalAxis::Inline)) {
    491      return {};
    492    }
    493  }
    494  return {ComputeSizeFlag::ShrinkWrap};
    495 }
    496 
    497 void nsTableWrapperFrame::CreateReflowInputForInnerTable(
    498    nsPresContext* aPresContext, nsTableFrame* aTableFrame,
    499    const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI,
    500    const nscoord aAvailISize, nscoord aBSizeOccupiedByCaption) const {
    501  MOZ_ASSERT(InnerTableFrame() == aTableFrame);
    502 
    503  const WritingMode wm = aTableFrame->GetWritingMode();
    504  // If we have a caption occupied our content-box area, reduce the available
    505  // block-size by the amount.
    506  nscoord availBSize = aOuterRI.AvailableBSize();
    507  if (availBSize != NS_UNCONSTRAINEDSIZE) {
    508    availBSize = std::max(0, availBSize - aBSizeOccupiedByCaption);
    509  }
    510  const LogicalSize availSize(wm, aAvailISize, availBSize);
    511 
    512  // For inner table frames, the containing block is the same as for the outer
    513  // table frame.
    514  Maybe<LogicalSize> cbSize = Some(aOuterRI.mContainingBlockSize);
    515 
    516  // However, if we are a grid item, the CB size needs to subtract our margins
    517  // and the area occupied by the caption.
    518  //
    519  // Note that inner table computed margins are always zero, they're inherited
    520  // by the table wrapper, so we need to get our margin from aOuterRI.
    521  if (IsGridItem()) {
    522    const LogicalMargin margin = aOuterRI.ComputedLogicalMargin(wm);
    523    cbSize->ISize(wm) = std::max(0, cbSize->ISize(wm) - margin.IStartEnd(wm));
    524    if (cbSize->BSize(wm) != NS_UNCONSTRAINEDSIZE) {
    525      cbSize->BSize(wm) = std::max(0, cbSize->BSize(wm) - margin.BStartEnd(wm) -
    526                                          aBSizeOccupiedByCaption);
    527    }
    528  }
    529 
    530  ComputeSizeFlags csFlags = CreateComputeSizeFlagsForChild();
    531  if (!aTableFrame->IsBorderCollapse() &&
    532      !aOuterRI.mStyleSizeOverrides.HasAnyOverrides()) {
    533    // We are not border-collapsed and not given any size overrides. It's
    534    // sufficient to call the standard ReflowInput constructor.
    535    aChildRI.emplace(aPresContext, aOuterRI, aTableFrame, availSize, cbSize,
    536                     ReflowInput::InitFlags{}, StyleSizeOverrides{}, csFlags);
    537    return;
    538  }
    539 
    540  Maybe<LogicalMargin> borderPadding;
    541  Maybe<LogicalMargin> padding;
    542  {
    543    // Compute inner table frame's border & padding because we may need to
    544    // reduce the size for inner table's size overrides. We won't waste time if
    545    // they are not used, because we can use them directly by passing them into
    546    // ReflowInput::Init().
    547    Maybe<LogicalMargin> collapseBorder;
    548    Maybe<LogicalMargin> collapsePadding;
    549    aTableFrame->GetCollapsedBorderPadding(collapseBorder, collapsePadding);
    550    SizeComputationInput input(aTableFrame, aOuterRI.mRenderingContext, wm,
    551                               cbSize->ISize(wm), collapseBorder,
    552                               collapsePadding);
    553    borderPadding.emplace(input.ComputedLogicalBorderPadding(wm));
    554    padding.emplace(input.ComputedLogicalPadding(wm));
    555  }
    556 
    557  StyleSizeOverrides innerOverrides = ComputeSizeOverridesForInnerTable(
    558      aTableFrame, aOuterRI.mStyleSizeOverrides, borderPadding->Size(wm),
    559      aBSizeOccupiedByCaption);
    560 
    561  aChildRI.emplace(aPresContext, aOuterRI, aTableFrame, availSize, Nothing(),
    562                   ReflowInput::InitFlag::CallerWillInit, innerOverrides,
    563                   csFlags);
    564  aChildRI->Init(aPresContext, cbSize, Some(*borderPadding - *padding),
    565                 padding);
    566 }
    567 
    568 void nsTableWrapperFrame::CreateReflowInputForCaption(
    569    nsPresContext* aPresContext, nsIFrame* aCaptionFrame,
    570    const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI,
    571    const nscoord aAvailISize) const {
    572  MOZ_ASSERT(aCaptionFrame == GetCaption());
    573 
    574  const WritingMode wm = aCaptionFrame->GetWritingMode();
    575 
    576  // Use unconstrained available block-size so that the caption is always
    577  // fully-complete.
    578  const LogicalSize availSize(wm, aAvailISize, NS_UNCONSTRAINEDSIZE);
    579  aChildRI.emplace(aPresContext, aOuterRI, aCaptionFrame, availSize);
    580 
    581  // See if we need to reset mIsTopOfPage flag.
    582  if (aChildRI->mFlags.mIsTopOfPage) {
    583    if (auto captionSide = GetCaptionSide()) {
    584      if (*captionSide == StyleCaptionSide::Bottom) {
    585        aChildRI->mFlags.mIsTopOfPage = false;
    586      }
    587    }
    588  }
    589 }
    590 
    591 void nsTableWrapperFrame::ReflowChild(nsPresContext* aPresContext,
    592                                      nsIFrame* aChildFrame,
    593                                      const ReflowInput& aChildRI,
    594                                      ReflowOutput& aMetrics,
    595                                      nsReflowStatus& aStatus) {
    596  // Using zero as containerSize here because we want consistency between
    597  // the GetLogicalPosition and ReflowChild calls, to avoid unnecessarily
    598  // changing the frame's coordinates; but we don't yet know its final
    599  // position anyway so the actual value is unimportant.
    600  const nsSize zeroCSize;
    601  WritingMode wm = aChildRI.GetWritingMode();
    602 
    603  // Use the current position as a best guess for placement.
    604  LogicalPoint childPt = aChildFrame->GetLogicalPosition(wm, zeroCSize);
    605  ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame;
    606 
    607  // We don't want to delete our next-in-flow's child if it's an inner table
    608  // frame, because table wrapper frames always assume that their inner table
    609  // frames don't go away. If a table wrapper frame is removed because it is
    610  // a next-in-flow of an already complete table wrapper frame, then it will
    611  // take care of removing it's inner table frame.
    612  if (aChildFrame == InnerTableFrame()) {
    613    flags |= ReflowChildFlags::NoDeleteNextInFlowChild;
    614  }
    615 
    616  nsContainerFrame::ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRI,
    617                                wm, childPt, zeroCSize, flags, aStatus);
    618 }
    619 
    620 void nsTableWrapperFrame::UpdateOverflowAreas(ReflowOutput& aMet) {
    621  aMet.SetOverflowAreasToDesiredBounds();
    622  for (auto* frame : mFrames) {
    623    ConsiderChildOverflow(aMet.mOverflowAreas, frame);
    624  }
    625 }
    626 
    627 void nsTableWrapperFrame::Reflow(nsPresContext* aPresContext,
    628                                 ReflowOutput& aDesiredSize,
    629                                 const ReflowInput& aOuterRI,
    630                                 nsReflowStatus& aStatus) {
    631  MarkInReflow();
    632  DO_GLOBAL_REFLOW_COUNT("nsTableWrapperFrame");
    633  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    634 
    635  // Initialize out parameters
    636  aDesiredSize.ClearSize();
    637 
    638  if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    639    // Set up our kids.  They're already present, on an overflow list,
    640    // or there are none so we'll create them now
    641    MoveOverflowToChildList();
    642  }
    643 
    644  Maybe<ReflowInput> captionRI;
    645  Maybe<ReflowInput> innerRI;
    646 
    647  nsRect origCaptionRect;
    648  nsRect origCaptionInkOverflow;
    649  bool captionFirstReflow = false;
    650  if (nsIFrame* caption = GetCaption()) {
    651    origCaptionRect = caption->GetRect();
    652    origCaptionInkOverflow = caption->InkOverflowRect();
    653    captionFirstReflow = caption->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
    654  }
    655 
    656  // ComputeAutoSize has to match this logic.
    657  WritingMode wm = aOuterRI.GetWritingMode();
    658  Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
    659  const nscoord contentBoxISize = aOuterRI.ComputedSize(wm).ISize(wm);
    660 
    661  MOZ_ASSERT(HasCaption() == captionSide.isSome());
    662 
    663  // Compute the table's size first, and then prevent the caption from
    664  // being larger in the inline dir unless it has to be.
    665  //
    666  // Note that CSS 2.1 (but not 2.0) says:
    667  //   The width of the anonymous box is the border-edge width of the
    668  //   table box inside it
    669  // We don't actually make our anonymous box that isize (if we did,
    670  // it would break 'auto' margins), but this effectively does that.
    671  CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
    672                                 innerRI, contentBoxISize);
    673 
    674  // First reflow the caption.
    675  ReflowOutput captionMet(wm);
    676  LogicalSize captionSize(wm);
    677  LogicalMargin captionMargin(wm);
    678  if (captionSide) {
    679    // It's good that CSS 2.1 says not to include margins, since we can't, since
    680    // they already been converted so they exactly fill the available isize
    681    // (ignoring the margin on one side if neither are auto).  (We take
    682    // advantage of that later when we call GetCaptionOrigin, though.)
    683    nscoord innerBorderISize =
    684        innerRI->ComputedSizeWithBorderPadding(wm).ISize(wm);
    685    CreateReflowInputForCaption(aPresContext, GetCaption(), aOuterRI, captionRI,
    686                                innerBorderISize);
    687 
    688    // We intentionally don't merge capStatus into aStatus, since we currently
    689    // can't handle caption continuations, but we probably should.
    690    nsReflowStatus capStatus;
    691    ReflowChild(aPresContext, GetCaption(), *captionRI, captionMet, capStatus);
    692    captionSize = captionMet.Size(wm);
    693    captionMargin = captionRI->ComputedLogicalMargin(wm);
    694    nscoord bSizeOccupiedByCaption =
    695        captionSize.BSize(wm) + captionMargin.BStartEnd(wm);
    696    if (bSizeOccupiedByCaption) {
    697      // Reset the inner table's ReflowInput to reduce various sizes because of
    698      // the area occupied by caption.
    699      innerRI.reset();
    700      CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
    701                                     innerRI, contentBoxISize,
    702                                     bSizeOccupiedByCaption);
    703    }
    704  }
    705 
    706  // Now we know how much to reduce the block-size for the inner table to
    707  // account for captions. Reflow the inner table.
    708  ReflowOutput innerMet(innerRI->GetWritingMode());
    709  ReflowChild(aPresContext, InnerTableFrame(), *innerRI, innerMet, aStatus);
    710  LogicalSize innerSize(wm, innerMet.ISize(wm), innerMet.BSize(wm));
    711 
    712  // Now that we've reflowed both we can place them.
    713  // Compute the desiredSize so that we can use it as the containerSize
    714  // for the FinishReflowChild calls below.
    715  LogicalSize desiredSize(wm);
    716 
    717  // We have zero border and padding, so content-box inline-size is our desired
    718  // border-box inline-size.
    719  desiredSize.ISize(wm) = contentBoxISize;
    720  desiredSize.BSize(wm) =
    721      ComputeFinalBSize(innerSize, captionSize, captionMargin, wm);
    722 
    723  aDesiredSize.SetSize(wm, desiredSize);
    724  nsSize containerSize = aDesiredSize.PhysicalSize();
    725 
    726  MOZ_ASSERT(HasCaption() == captionSide.isSome());
    727  if (nsIFrame* caption = GetCaption()) {
    728    LogicalPoint captionOrigin(wm);
    729    GetCaptionOrigin(*captionSide, innerSize, captionSize, captionMargin,
    730                     captionOrigin, wm);
    731    FinishReflowChild(caption, aPresContext, captionMet, captionRI.ptr(), wm,
    732                      captionOrigin, containerSize,
    733                      ReflowChildFlags::ApplyRelativePositioning);
    734    captionRI.reset();
    735  }
    736  // XXX If the bsize is constrained then we need to check whether
    737  // everything still fits...
    738 
    739  LogicalPoint innerOrigin(wm);
    740  GetInnerOrigin(captionSide, captionSize, captionMargin, innerSize,
    741                 innerOrigin, wm);
    742  // NOTE: Relative positioning on the table applies to the whole table wrapper.
    743  FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRI.ptr(),
    744                    wm, innerOrigin, containerSize, ReflowChildFlags::Default);
    745  innerRI.reset();
    746 
    747  if (HasCaption()) {
    748    nsTableFrame::InvalidateTableFrame(GetCaption(), origCaptionRect,
    749                                       origCaptionInkOverflow,
    750                                       captionFirstReflow);
    751  }
    752 
    753  UpdateOverflowAreas(aDesiredSize);
    754 
    755  if (GetPrevInFlow()) {
    756    ReflowOverflowContainerChildren(aPresContext, aOuterRI,
    757                                    aDesiredSize.mOverflowAreas,
    758                                    ReflowChildFlags::Default, aStatus);
    759  }
    760 
    761  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRI, aStatus);
    762 }
    763 
    764 /* ----- global methods ----- */
    765 
    766 nsIContent* nsTableWrapperFrame::GetCellAt(uint32_t aRowIdx,
    767                                           uint32_t aColIdx) const {
    768  nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
    769  if (!cellMap) {
    770    return nullptr;
    771  }
    772 
    773  nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
    774  if (!cell) {
    775    return nullptr;
    776  }
    777 
    778  return cell->GetContent();
    779 }
    780 
    781 nsTableWrapperFrame* NS_NewTableWrapperFrame(PresShell* aPresShell,
    782                                             ComputedStyle* aStyle) {
    783  return new (aPresShell)
    784      nsTableWrapperFrame(aStyle, aPresShell->GetPresContext());
    785 }
    786 
    787 NS_IMPL_FRAMEARENA_HELPERS(nsTableWrapperFrame)
    788 
    789 #ifdef DEBUG_FRAME_DUMP
    790 nsresult nsTableWrapperFrame::GetFrameName(nsAString& aResult) const {
    791  return MakeFrameName(u"TableWrapper"_ns, aResult);
    792 }
    793 #endif