tor-browser

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

nsTableColGroupFrame.cpp (17457B)


      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 #include "nsTableColGroupFrame.h"
      6 
      7 #include "mozilla/ComputedStyle.h"
      8 #include "mozilla/PresShell.h"
      9 #include "mozilla/StaticPrefs_layout.h"
     10 #include "nsCOMPtr.h"
     11 #include "nsCSSRendering.h"
     12 #include "nsGkAtoms.h"
     13 #include "nsHTMLParts.h"
     14 #include "nsPresContext.h"
     15 #include "nsStyleConsts.h"
     16 #include "nsTableColFrame.h"
     17 #include "nsTableFrame.h"
     18 
     19 using namespace mozilla;
     20 
     21 #define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30)
     22 
     23 bool nsTableColGroupFrame::IsSynthetic() const {
     24  return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT);
     25 }
     26 
     27 void nsTableColGroupFrame::SetIsSynthetic() {
     28  AddStateBits(COLGROUP_SYNTHETIC_BIT);
     29 }
     30 
     31 void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup,
     32                                           int32_t aFirstColIndex,
     33                                           nsIFrame* aStartColFrame) {
     34  nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup;
     35  int32_t colIndex = aFirstColIndex;
     36  while (colGroupFrame) {
     37    if (colGroupFrame->IsTableColGroupFrame()) {
     38      // reset the starting col index for the first cg only if we should reset
     39      // the whole colgroup (aStartColFrame defaults to nullptr) or if
     40      // aFirstColIndex is smaller than the existing starting col index
     41      if ((colIndex != aFirstColIndex) ||
     42          (colIndex < colGroupFrame->GetStartColumnIndex()) ||
     43          !aStartColFrame) {
     44        colGroupFrame->SetStartColumnIndex(colIndex);
     45      }
     46      nsIFrame* colFrame = aStartColFrame;
     47      if (!colFrame || (colIndex != aFirstColIndex)) {
     48        colFrame = colGroupFrame->PrincipalChildList().FirstChild();
     49      }
     50      while (colFrame) {
     51        if (colFrame->IsTableColFrame()) {
     52          ((nsTableColFrame*)colFrame)->SetColIndex(colIndex);
     53          colIndex++;
     54        }
     55        colFrame = colFrame->GetNextSibling();
     56      }
     57    }
     58    colGroupFrame =
     59        static_cast<nsTableColGroupFrame*>(colGroupFrame->GetNextSibling());
     60  }
     61 }
     62 
     63 nsresult nsTableColGroupFrame::AddColsToTable(int32_t aFirstColIndex,
     64                                              bool aResetSubsequentColIndices,
     65                                              const nsFrameList::Slice& aCols) {
     66  nsTableFrame* tableFrame = GetTableFrame();
     67 
     68  tableFrame->InvalidateFrameSubtree();
     69 
     70  // set the col indices of the col frames and and add col info to the table
     71  int32_t colIndex = aFirstColIndex;
     72 
     73  // XXX: We cannot use range-based for loop because InsertCol() can destroy the
     74  // nsTableColFrame in the slice we're traversing! Need to check the validity
     75  // of *colIter.
     76  auto colIter = aCols.begin();
     77  for (auto colIterEnd = aCols.end(); *colIter && colIter != colIterEnd;
     78       ++colIter) {
     79    auto* colFrame = static_cast<nsTableColFrame*>(*colIter);
     80    colFrame->SetColIndex(colIndex);
     81    mColCount++;
     82    tableFrame->InsertCol(*colFrame, colIndex);
     83    colIndex++;
     84  }
     85 
     86  for (; *colIter; ++colIter) {
     87    auto* colFrame = static_cast<nsTableColFrame*>(*colIter);
     88    colFrame->SetColIndex(colIndex);
     89    colIndex++;
     90  }
     91 
     92  // We have already set the colindex for all the colframes in this
     93  // colgroup that come after the first inserted colframe, but there could
     94  // be other colgroups following this one and their colframes need
     95  // correct colindices too.
     96  if (aResetSubsequentColIndices && GetNextSibling()) {
     97    ResetColIndices(GetNextSibling(), colIndex);
     98  }
     99 
    100  return NS_OK;
    101 }
    102 
    103 nsTableColGroupFrame* nsTableColGroupFrame::GetLastRealColGroup(
    104    nsTableFrame* aTableFrame) {
    105  const nsFrameList& colGroups = aTableFrame->GetColGroups();
    106 
    107  auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild());
    108  if (!lastColGroup) {
    109    return nullptr;
    110  }
    111 
    112  if (!lastColGroup->IsSynthetic()) {
    113    return lastColGroup;
    114  }
    115 
    116  return static_cast<nsTableColGroupFrame*>(lastColGroup->GetPrevSibling());
    117 }
    118 
    119 // don't set mColCount here, it is done in AddColsToTable
    120 void nsTableColGroupFrame::SetInitialChildList(ChildListID aListID,
    121                                               nsFrameList&& aChildList) {
    122  MOZ_ASSERT(mFrames.IsEmpty(),
    123             "unexpected second call to SetInitialChildList");
    124  MOZ_ASSERT(aListID == FrameChildListID::Principal, "unexpected child list");
    125 #ifdef DEBUG
    126  for (nsIFrame* f : aChildList) {
    127    MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
    128  }
    129 #endif
    130  if (aChildList.IsEmpty()) {
    131    GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
    132                                              eColAnonymousColGroup, false);
    133    return;
    134  }
    135 
    136  mFrames.AppendFrames(this, std::move(aChildList));
    137 }
    138 
    139 /* virtual */
    140 void nsTableColGroupFrame::DidSetComputedStyle(
    141    ComputedStyle* aOldComputedStyle) {
    142  nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
    143 
    144  if (!aOldComputedStyle) {  // avoid this on init
    145    return;
    146  }
    147 
    148  nsTableFrame* tableFrame = GetTableFrame();
    149  if (tableFrame->IsBorderCollapse() &&
    150      tableFrame->BCRecalcNeeded(aOldComputedStyle, Style())) {
    151    int32_t colCount = GetColCount();
    152    if (!colCount) {
    153      return;  // this is a degenerated colgroup
    154    }
    155    TableArea damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
    156                         tableFrame->GetRowCount());
    157    tableFrame->AddBCDamageArea(damageArea);
    158  }
    159 }
    160 
    161 void nsTableColGroupFrame::AppendFrames(ChildListID aListID,
    162                                        nsFrameList&& aFrameList) {
    163  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
    164 
    165  nsTableColFrame* col = GetFirstColumn();
    166  nsTableColFrame* nextCol;
    167  while (col && col->GetColType() == eColAnonymousColGroup) {
    168    // this colgroup spans one or more columns but now that there is a
    169    // real column below, spanned anonymous columns should be removed,
    170    // since the HTML spec says to ignore the span of a colgroup if it
    171    // has content columns in it.
    172    nextCol = col->GetNextCol();
    173    DestroyContext context(PresShell());
    174    RemoveFrame(context, FrameChildListID::Principal, col);
    175    col = nextCol;
    176  }
    177 
    178  // Our next colframe should be an eColContent.  We've removed all the
    179  // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
    180  // eColContent ones, and eColAnonymousCell colframes only appear in a
    181  // synthetic colgroup, which never gets AppendFrames() called on it.
    182  MOZ_ASSERT(!col || col->GetColType() == eColContent,
    183             "What's going on with our columns?");
    184 
    185  const nsFrameList::Slice& newFrames =
    186      mFrames.AppendFrames(this, std::move(aFrameList));
    187  InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames);
    188 }
    189 
    190 void nsTableColGroupFrame::InsertFrames(
    191    ChildListID aListID, nsIFrame* aPrevFrame,
    192    const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) {
    193  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
    194  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
    195               "inserting after sibling frame with different parent");
    196 
    197  nsTableColFrame* col = GetFirstColumn();
    198  nsTableColFrame* nextCol;
    199  while (col && col->GetColType() == eColAnonymousColGroup) {
    200    // this colgroup spans one or more columns but now that there is a
    201    // real column below, spanned anonymous columns should be removed,
    202    // since the HTML spec says to ignore the span of a colgroup if it
    203    // has content columns in it.
    204    nextCol = col->GetNextCol();
    205    if (col == aPrevFrame) {
    206      // This can happen when we're being appended to
    207      NS_ASSERTION(!nextCol || nextCol->GetColType() != eColAnonymousColGroup,
    208                   "Inserting in the middle of our anonymous cols?");
    209      // We'll want to insert at the beginning
    210      aPrevFrame = nullptr;
    211    }
    212    DestroyContext context(PresShell());
    213    RemoveFrame(context, FrameChildListID::Principal, col);
    214    col = nextCol;
    215  }
    216 
    217  // Our next colframe should be an eColContent.  We've removed all the
    218  // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow
    219  // eColContent ones, and eColAnonymousCell colframes only appear in a
    220  // synthetic colgroup, which never gets InsertFrames() called on it.
    221  MOZ_ASSERT(!col || col->GetColType() == eColContent,
    222             "What's going on with our columns?");
    223 
    224  NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->LastContinuation(),
    225               "Prev frame should be last in continuation chain");
    226  NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) ||
    227                   GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol,
    228               "Shouldn't be inserting before a spanned colframe");
    229 
    230  const nsFrameList::Slice& newFrames =
    231      mFrames.InsertFrames(this, aPrevFrame, std::move(aFrameList));
    232  nsIFrame* prevFrame = nsTableFrame::GetFrameAtOrBefore(
    233      this, aPrevFrame, LayoutFrameType::TableCol);
    234 
    235  int32_t colIndex = (prevFrame)
    236                         ? ((nsTableColFrame*)prevFrame)->GetColIndex() + 1
    237                         : GetStartColumnIndex();
    238  InsertColsReflow(colIndex, newFrames);
    239 }
    240 
    241 void nsTableColGroupFrame::InsertColsReflow(int32_t aColIndex,
    242                                            const nsFrameList::Slice& aCols) {
    243  AddColsToTable(aColIndex, true, aCols);
    244 
    245  PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
    246                                NS_FRAME_HAS_DIRTY_CHILDREN);
    247 }
    248 
    249 void nsTableColGroupFrame::RemoveChild(DestroyContext& aContext,
    250                                       nsTableColFrame& aChild,
    251                                       bool aResetSubsequentColIndices) {
    252  int32_t colIndex = 0;
    253  nsIFrame* nextChild = nullptr;
    254  if (aResetSubsequentColIndices) {
    255    colIndex = aChild.GetColIndex();
    256    nextChild = aChild.GetNextSibling();
    257  }
    258  mFrames.DestroyFrame(aContext, &aChild);
    259  mColCount--;
    260  if (aResetSubsequentColIndices) {
    261    if (nextChild) {  // reset inside this and all following colgroups
    262      ResetColIndices(this, colIndex, nextChild);
    263    } else {
    264      nsIFrame* nextGroup = GetNextSibling();
    265      if (nextGroup) {  // reset next and all following colgroups
    266        ResetColIndices(nextGroup, colIndex);
    267      }
    268    }
    269  }
    270 
    271  PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
    272                                NS_FRAME_HAS_DIRTY_CHILDREN);
    273 }
    274 
    275 void nsTableColGroupFrame::RemoveFrame(DestroyContext& aContext,
    276                                       ChildListID aListID,
    277                                       nsIFrame* aOldFrame) {
    278  NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list");
    279 
    280  if (!aOldFrame) {
    281    return;
    282  }
    283  bool contentRemoval = false;
    284 
    285  if (aOldFrame->IsTableColFrame()) {
    286    nsTableColFrame* colFrame = (nsTableColFrame*)aOldFrame;
    287    if (colFrame->GetColType() == eColContent) {
    288      contentRemoval = true;
    289      // Remove any anonymous column frames this <col> produced via a colspan
    290      nsTableColFrame* col = colFrame->GetNextCol();
    291      nsTableColFrame* nextCol;
    292      while (col && col->GetColType() == eColAnonymousCol) {
    293        nextCol = col->GetNextCol();
    294        RemoveFrame(aContext, FrameChildListID::Principal, col);
    295        col = nextCol;
    296      }
    297    }
    298 
    299    int32_t colIndex = colFrame->GetColIndex();
    300    // The RemoveChild call handles calling FrameNeedsReflow on us.
    301    RemoveChild(aContext, *colFrame, true);
    302 
    303    nsTableFrame* tableFrame = GetTableFrame();
    304    tableFrame->RemoveCol(this, colIndex, true, true);
    305    if (mFrames.IsEmpty() && contentRemoval && !IsSynthetic()) {
    306      tableFrame->AppendAnonymousColFrames(this, GetSpan(),
    307                                           eColAnonymousColGroup, true);
    308    }
    309  } else {
    310    mFrames.DestroyFrame(aContext, aOldFrame);
    311  }
    312 }
    313 
    314 nsIFrame::LogicalSides nsTableColGroupFrame::GetLogicalSkipSides() const {
    315  LogicalSides skip(mWritingMode);
    316  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
    317                   StyleBoxDecorationBreak::Clone)) {
    318    return skip;
    319  }
    320 
    321  if (GetPrevInFlow()) {
    322    skip += LogicalSide::BStart;
    323  }
    324  if (GetNextInFlow()) {
    325    skip += LogicalSide::BEnd;
    326  }
    327  return skip;
    328 }
    329 
    330 void nsTableColGroupFrame::Reflow(nsPresContext* aPresContext,
    331                                  ReflowOutput& aDesiredSize,
    332                                  const ReflowInput& aReflowInput,
    333                                  nsReflowStatus& aStatus) {
    334  MarkInReflow();
    335  DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
    336  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    337  NS_ASSERTION(nullptr != mContent, "bad state -- null content for frame");
    338 
    339  const nsStyleVisibility* groupVis = StyleVisibility();
    340  bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
    341  if (collapseGroup) {
    342    GetTableFrame()->SetNeedToCollapse(true);
    343  }
    344 
    345  const WritingMode wm = GetWritingMode();
    346  for (nsIFrame* kidFrame : mFrames) {
    347    // Give the child frame a chance to reflow, even though we know it'll have 0
    348    // size
    349    ReflowOutput kidSize(aReflowInput);
    350    ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
    351                               LogicalSize(kidFrame->GetWritingMode()));
    352    const LogicalPoint dummyPos(wm);
    353    const nsSize dummyContainerSize;
    354    nsReflowStatus status;
    355    ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, wm, dummyPos,
    356                dummyContainerSize, ReflowChildFlags::Default, status);
    357    FinishReflowChild(kidFrame, aPresContext, kidSize, &kidReflowInput, wm,
    358                      dummyPos, dummyContainerSize, ReflowChildFlags::Default);
    359  }
    360 
    361  aDesiredSize.ClearSize();
    362 }
    363 
    364 void nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    365                                            const nsDisplayListSet& aLists) {
    366  // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
    367  // "All css properties of table-column and table-column-group boxes are
    368  // ignored, except when explicitly specified by this specification."
    369  // CSS outlines and box-shadows fall into this category, so we skip them
    370  // on these boxes.
    371  MOZ_ASSERT_UNREACHABLE("Colgroups don't paint themselves");
    372 }
    373 
    374 nsTableColFrame* nsTableColGroupFrame::GetFirstColumn() {
    375  return GetNextColumn(nullptr);
    376 }
    377 
    378 nsTableColFrame* nsTableColGroupFrame::GetNextColumn(nsIFrame* aChildFrame) {
    379  nsTableColFrame* result = nullptr;
    380  nsIFrame* childFrame = aChildFrame;
    381  if (!childFrame) {
    382    childFrame = mFrames.FirstChild();
    383  } else {
    384    childFrame = childFrame->GetNextSibling();
    385  }
    386  while (childFrame) {
    387    if (mozilla::StyleDisplay::TableColumn ==
    388        childFrame->StyleDisplay()->mDisplay) {
    389      result = (nsTableColFrame*)childFrame;
    390      break;
    391    }
    392    childFrame = childFrame->GetNextSibling();
    393  }
    394  return result;
    395 }
    396 
    397 int32_t nsTableColGroupFrame::GetSpan() { return StyleTable()->mXSpan; }
    398 
    399 /* ----- global methods ----- */
    400 
    401 nsTableColGroupFrame* NS_NewTableColGroupFrame(PresShell* aPresShell,
    402                                               ComputedStyle* aStyle) {
    403  return new (aPresShell)
    404      nsTableColGroupFrame(aStyle, aPresShell->GetPresContext());
    405 }
    406 
    407 NS_IMPL_FRAMEARENA_HELPERS(nsTableColGroupFrame)
    408 
    409 void nsTableColGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey,
    410                                           bool aRebuildDisplayItems) {
    411  nsIFrame::InvalidateFrame(aDisplayItemKey, aRebuildDisplayItems);
    412  if (GetTableFrame()->IsBorderCollapse()) {
    413    const bool rebuild = StaticPrefs::layout_display_list_retain_sc();
    414    GetParent()->InvalidateFrameWithRect(InkOverflowRect() + GetPosition(),
    415                                         aDisplayItemKey, rebuild);
    416  }
    417 }
    418 
    419 void nsTableColGroupFrame::InvalidateFrameWithRect(const nsRect& aRect,
    420                                                   uint32_t aDisplayItemKey,
    421                                                   bool aRebuildDisplayItems) {
    422  nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey,
    423                                    aRebuildDisplayItems);
    424  // If we have filters applied that would affects our bounds, then
    425  // we get an inactive layer created and this is computed
    426  // within FrameLayerBuilder
    427  GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey,
    428                                       aRebuildDisplayItems);
    429 }
    430 
    431 #ifdef DEBUG_FRAME_DUMP
    432 nsresult nsTableColGroupFrame::GetFrameName(nsAString& aResult) const {
    433  return MakeFrameName(u"TableColGroup"_ns, aResult);
    434 }
    435 
    436 void nsTableColGroupFrame::Dump(int32_t aIndent) {
    437  char* indent = new char[aIndent + 1];
    438  if (!indent) {
    439    return;
    440  }
    441  for (int32_t i = 0; i < aIndent + 1; i++) {
    442    indent[i] = ' ';
    443  }
    444  indent[aIndent] = 0;
    445 
    446  printf(
    447      "%s**START COLGROUP DUMP**\n%s startcolIndex=%d  colcount=%d span=%d "
    448      "isSynthetic=%s",
    449      indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan(),
    450      IsSynthetic() ? "true" : "false");
    451 
    452  // verify the colindices
    453  DebugOnly<int32_t> j = GetStartColumnIndex();
    454  nsTableColFrame* col = GetFirstColumn();
    455  while (col) {
    456    NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame");
    457    col = col->GetNextCol();
    458    j++;
    459  }
    460  NS_ASSERTION((j - GetStartColumnIndex()) == GetColCount(),
    461               "number of cols out of sync");
    462  printf("\n%s**END COLGROUP DUMP** ", indent);
    463  delete[] indent;
    464 }
    465 #endif