LCOV - code coverage report
Current view: top level - layout/tables - nsTableFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 3787 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 274 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 sw=2 et tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/Likely.h"
       8             : #include "mozilla/MathAlgorithms.h"
       9             : #include "mozilla/IntegerRange.h"
      10             : #include "mozilla/WritingModes.h"
      11             : 
      12             : #include "gfxContext.h"
      13             : #include "nsCOMPtr.h"
      14             : #include "nsTableFrame.h"
      15             : #include "nsStyleContext.h"
      16             : #include "nsStyleConsts.h"
      17             : #include "nsIContent.h"
      18             : #include "nsCellMap.h"
      19             : #include "nsTableCellFrame.h"
      20             : #include "nsHTMLParts.h"
      21             : #include "nsTableColFrame.h"
      22             : #include "nsTableColGroupFrame.h"
      23             : #include "nsTableRowFrame.h"
      24             : #include "nsTableRowGroupFrame.h"
      25             : #include "nsTableWrapperFrame.h"
      26             : 
      27             : #include "BasicTableLayoutStrategy.h"
      28             : #include "FixedTableLayoutStrategy.h"
      29             : 
      30             : #include "nsPresContext.h"
      31             : #include "nsContentUtils.h"
      32             : #include "nsCSSRendering.h"
      33             : #include "nsGkAtoms.h"
      34             : #include "nsCSSAnonBoxes.h"
      35             : #include "nsIPresShell.h"
      36             : #include "nsIDOMElement.h"
      37             : #include "nsIDOMHTMLElement.h"
      38             : #include "nsIScriptError.h"
      39             : #include "nsFrameManager.h"
      40             : #include "nsError.h"
      41             : #include "nsCSSFrameConstructor.h"
      42             : #include "mozilla/Range.h"
      43             : #include "mozilla/ServoRestyleManager.h"
      44             : #include "mozilla/ServoStyleSet.h"
      45             : #include "mozilla/StyleSetHandle.h"
      46             : #include "mozilla/StyleSetHandleInlines.h"
      47             : #include "nsDisplayList.h"
      48             : #include "nsIScrollableFrame.h"
      49             : #include "nsCSSProps.h"
      50             : #include "RestyleTracker.h"
      51             : #include "nsStyleChangeList.h"
      52             : #include <algorithm>
      53             : 
      54             : #include "gfxPrefs.h"
      55             : #include "mozilla/layers/StackingContextHelper.h"
      56             : #include "mozilla/layers/WebRenderDisplayItemLayer.h"
      57             : 
      58             : using namespace mozilla;
      59             : using namespace mozilla::image;
      60             : using namespace mozilla::layout;
      61             : 
      62             : /********************************************************************************
      63             :  ** TableReflowInput                                                         **
      64             :  ********************************************************************************/
      65             : 
      66             : namespace mozilla {
      67             : 
      68             : struct TableReflowInput {
      69             : 
      70             :   // the real reflow state
      71             :   const ReflowInput& reflowInput;
      72             : 
      73             :   // The table's available size (in reflowInput's writing mode)
      74             :   LogicalSize availSize;
      75             : 
      76             :   // Stationary inline-offset
      77             :   nscoord iCoord;
      78             : 
      79             :   // Running block-offset
      80             :   nscoord bCoord;
      81             : 
      82           0 :   TableReflowInput(const ReflowInput& aReflowInput,
      83             :                      const LogicalSize& aAvailSize)
      84           0 :     : reflowInput(aReflowInput)
      85           0 :     , availSize(aAvailSize)
      86             :   {
      87           0 :     MOZ_ASSERT(reflowInput.mFrame->IsTableFrame(),
      88             :                "TableReflowInput should only be created for nsTableFrame");
      89             :     nsTableFrame* table =
      90           0 :       static_cast<nsTableFrame*>(reflowInput.mFrame->FirstInFlow());
      91           0 :     WritingMode wm = aReflowInput.GetWritingMode();
      92           0 :     LogicalMargin borderPadding = table->GetChildAreaOffset(wm, &reflowInput);
      93             : 
      94           0 :     iCoord = borderPadding.IStart(wm) + table->GetColSpacing(-1);
      95           0 :     bCoord = borderPadding.BStart(wm); //cellspacing added during reflow
      96             : 
      97             :     // XXX do we actually need to check for unconstrained inline-size here?
      98           0 :     if (NS_UNCONSTRAINEDSIZE != availSize.ISize(wm)) {
      99           0 :       int32_t colCount = table->GetColCount();
     100           0 :       availSize.ISize(wm) -= borderPadding.IStartEnd(wm) +
     101           0 :                              table->GetColSpacing(-1) +
     102           0 :                              table->GetColSpacing(colCount);
     103           0 :       availSize.ISize(wm) = std::max(0, availSize.ISize(wm));
     104             :     }
     105             : 
     106           0 :     if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
     107           0 :       availSize.BSize(wm) -= borderPadding.BStartEnd(wm) +
     108           0 :                              table->GetRowSpacing(-1) +
     109           0 :                              table->GetRowSpacing(table->GetRowCount());
     110           0 :       availSize.BSize(wm) = std::max(0, availSize.BSize(wm));
     111             :     }
     112           0 :   }
     113             : };
     114             : 
     115             : } // namespace mozilla
     116             : 
     117             : /********************************************************************************
     118             :  ** nsTableFrame                                                               **
     119             :  ********************************************************************************/
     120             : 
     121             : struct BCPropertyData
     122             : {
     123           0 :   BCPropertyData() : mBStartBorderWidth(0), mIEndBorderWidth(0),
     124             :                      mBEndBorderWidth(0), mIStartBorderWidth(0),
     125           0 :                      mIStartCellBorderWidth(0), mIEndCellBorderWidth(0) {}
     126             :   TableArea mDamageArea;
     127             :   BCPixelSize mBStartBorderWidth;
     128             :   BCPixelSize mIEndBorderWidth;
     129             :   BCPixelSize mBEndBorderWidth;
     130             :   BCPixelSize mIStartBorderWidth;
     131             :   BCPixelSize mIStartCellBorderWidth;
     132             :   BCPixelSize mIEndCellBorderWidth;
     133             : };
     134             : 
     135             : nsStyleContext*
     136           0 : nsTableFrame::GetParentStyleContext(nsIFrame** aProviderFrame) const
     137             : {
     138             :   // Since our parent, the table wrapper frame, returned this frame, we
     139             :   // must return whatever our parent would normally have returned.
     140             : 
     141           0 :   NS_PRECONDITION(GetParent(), "table constructed without table wrapper");
     142           0 :   if (!mContent->GetParent() && !StyleContext()->GetPseudo()) {
     143             :     // We're the root.  We have no style context parent.
     144           0 :     *aProviderFrame = nullptr;
     145           0 :     return nullptr;
     146             :   }
     147             : 
     148           0 :   return GetParent()->DoGetParentStyleContext(aProviderFrame);
     149             : }
     150             : 
     151           0 : nsTableFrame::nsTableFrame(nsStyleContext* aContext, ClassID aID)
     152             :   : nsContainerFrame(aContext, aID)
     153             :   , mCellMap(nullptr)
     154           0 :   , mTableLayoutStrategy(nullptr)
     155             : {
     156           0 :   memset(&mBits, 0, sizeof(mBits));
     157           0 : }
     158             : 
     159             : void
     160           0 : nsTableFrame::Init(nsIContent*       aContent,
     161             :                    nsContainerFrame* aParent,
     162             :                    nsIFrame*         aPrevInFlow)
     163             : {
     164           0 :   NS_PRECONDITION(!mCellMap, "Init called twice");
     165           0 :   NS_PRECONDITION(!mTableLayoutStrategy, "Init called twice");
     166           0 :   NS_PRECONDITION(!aPrevInFlow || aPrevInFlow->IsTableFrame(),
     167             :                   "prev-in-flow must be of same type");
     168             : 
     169             :   // Let the base class do its processing
     170           0 :   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
     171             : 
     172             :   // see if border collapse is on, if so set it
     173           0 :   const nsStyleTableBorder* tableStyle = StyleTableBorder();
     174           0 :   bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
     175           0 :   SetBorderCollapse(borderCollapse);
     176             : 
     177           0 :   if (!aPrevInFlow) {
     178             :     // If we're the first-in-flow, we manage the cell map & layout strategy that
     179             :     // get used by our continuation chain:
     180           0 :     mCellMap = new nsTableCellMap(*this, borderCollapse);
     181           0 :     if (IsAutoLayout()) {
     182           0 :       mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
     183             :     } else {
     184           0 :       mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
     185             :     }
     186             :   } else {
     187             :     // Set my isize, because all frames in a table flow are the same isize and
     188             :     // code in nsTableWrapperFrame depends on this being set.
     189           0 :     WritingMode wm = GetWritingMode();
     190           0 :     SetSize(LogicalSize(wm, aPrevInFlow->ISize(wm), BSize(wm)));
     191             :   }
     192           0 : }
     193             : 
     194           0 : nsTableFrame::~nsTableFrame()
     195             : {
     196           0 :   delete mCellMap;
     197           0 :   delete mTableLayoutStrategy;
     198           0 : }
     199             : 
     200             : void
     201           0 : nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
     202             : {
     203           0 :   mColGroups.DestroyFramesFrom(aDestructRoot);
     204           0 :   nsContainerFrame::DestroyFrom(aDestructRoot);
     205           0 : }
     206             : 
     207             : // Make sure any views are positioned properly
     208             : void
     209           0 : nsTableFrame::RePositionViews(nsIFrame* aFrame)
     210             : {
     211           0 :   nsContainerFrame::PositionFrameView(aFrame);
     212           0 :   nsContainerFrame::PositionChildViews(aFrame);
     213           0 : }
     214             : 
     215             : static bool
     216           0 : IsRepeatedFrame(nsIFrame* kidFrame)
     217             : {
     218           0 :   return (kidFrame->IsTableRowFrame() || kidFrame->IsTableRowGroupFrame()) &&
     219           0 :          kidFrame->HasAnyStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
     220             : }
     221             : 
     222             : bool
     223           0 : nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
     224             :                              nsIFrame* aNextFrame)
     225             : {
     226           0 :   const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
     227           0 :   nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
     228             :   // don't allow a page break after a repeated element ...
     229           0 :   if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
     230           0 :       !IsRepeatedFrame(aSourceFrame)) {
     231           0 :     return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
     232             :   }
     233             : 
     234           0 :   if (aNextFrame) {
     235           0 :     display = aNextFrame->StyleDisplay();
     236             :     // don't allow a page break before a repeated element ...
     237           0 :      nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
     238           0 :     if ((display->mBreakBefore ||
     239           0 :         (nextRg && nextRg->HasInternalBreakBefore())) &&
     240           0 :         !IsRepeatedFrame(aNextFrame)) {
     241           0 :       return !IsRepeatedFrame(aSourceFrame); // or after
     242             :     }
     243             :   }
     244           0 :   return false;
     245             : }
     246             : 
     247             : /* static */ void
     248           0 : nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
     249             : {
     250             :   // Supporting relative positioning for table parts other than table cells has
     251             :   // the potential to break sites that apply 'position: relative' to those
     252             :   // parts, expecting nothing to happen. We warn at the console to make tracking
     253             :   // down the issue easy.
     254           0 :   if (!IS_TABLE_CELL(aFrame->Type())) {
     255           0 :     nsIContent* content = aFrame->GetContent();
     256           0 :     nsPresContext* presContext = aFrame->PresContext();
     257           0 :     if (content && !presContext->HasWarnedAboutPositionedTableParts()) {
     258           0 :       presContext->SetHasWarnedAboutPositionedTableParts();
     259           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     260           0 :                                       NS_LITERAL_CSTRING("Layout: Tables"),
     261           0 :                                       content->OwnerDoc(),
     262             :                                       nsContentUtils::eLAYOUT_PROPERTIES,
     263           0 :                                       "TablePartRelPosWarning");
     264             :     }
     265             :   }
     266             : 
     267           0 :   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
     268           0 :   MOZ_ASSERT(tableFrame, "Should have a table frame here");
     269           0 :   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
     270             : 
     271             :   // Retrieve the positioned parts array for this table.
     272           0 :   FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
     273             : 
     274             :   // Lazily create the array if it doesn't exist yet.
     275           0 :   if (!positionedParts) {
     276           0 :     positionedParts = new FrameTArray;
     277           0 :     tableFrame->SetProperty(PositionedTablePartArray(), positionedParts);
     278             :   }
     279             : 
     280             :   // Add this frame to the list.
     281           0 :   positionedParts->AppendElement(aFrame);
     282           0 : }
     283             : 
     284             : /* static */ void
     285           0 : nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
     286             :                                             nsIFrame* aDestructRoot)
     287             : {
     288             :   // Retrieve the table frame, and check if we hit aDestructRoot on the way.
     289             :   bool didPassThrough;
     290           0 :   nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame,
     291           0 :       &didPassThrough);
     292           0 :   if (!didPassThrough && !tableFrame->GetPrevContinuation()) {
     293             :     // The table frame will be destroyed, and it's the first im flow (and thus
     294             :     // owning the PositionedTablePartArray), so we don't need to do
     295             :     // anything.
     296           0 :     return;
     297             :   }
     298           0 :   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
     299             : 
     300             :   // Retrieve the positioned parts array for this table.
     301           0 :   FrameTArray* positionedParts = tableFrame->GetProperty(PositionedTablePartArray());
     302             : 
     303             :   // Remove the frame.
     304           0 :   MOZ_ASSERT(positionedParts && positionedParts->Contains(aFrame),
     305             :              "Asked to unregister a positioned table part that wasn't registered");
     306           0 :   if (positionedParts) {
     307           0 :     positionedParts->RemoveElement(aFrame);
     308             :   }
     309             : }
     310             : 
     311             : // XXX this needs to be cleaned up so that the frame constructor breaks out col group
     312             : // frames into a separate child list, bug 343048.
     313             : void
     314           0 : nsTableFrame::SetInitialChildList(ChildListID     aListID,
     315             :                                   nsFrameList&    aChildList)
     316             : {
     317           0 :   if (aListID != kPrincipalList) {
     318           0 :     nsContainerFrame::SetInitialChildList(aListID, aChildList);
     319           0 :     return;
     320             :   }
     321             : 
     322           0 :   MOZ_ASSERT(mFrames.IsEmpty() && mColGroups.IsEmpty(),
     323             :              "unexpected second call to SetInitialChildList");
     324             : 
     325             :   // XXXbz the below code is an icky cesspit that's only needed in its current
     326             :   // form for two reasons:
     327             :   // 1) Both rowgroups and column groups come in on the principal child list.
     328           0 :   while (aChildList.NotEmpty()) {
     329           0 :     nsIFrame* childFrame = aChildList.FirstChild();
     330           0 :     aChildList.RemoveFirstChild();
     331           0 :     const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
     332             : 
     333           0 :     if (mozilla::StyleDisplay::TableColumnGroup == childDisplay->mDisplay) {
     334           0 :       NS_ASSERTION(childFrame->IsTableColGroupFrame(),
     335             :                    "This is not a colgroup");
     336           0 :       mColGroups.AppendFrame(nullptr, childFrame);
     337             :     }
     338             :     else { // row groups and unknown frames go on the main list for now
     339           0 :       mFrames.AppendFrame(nullptr, childFrame);
     340             :     }
     341             :   }
     342             : 
     343             :   // If we have a prev-in-flow, then we're a table that has been split and
     344             :   // so don't treat this like an append
     345           0 :   if (!GetPrevInFlow()) {
     346             :     // process col groups first so that real cols get constructed before
     347             :     // anonymous ones due to cells in rows.
     348           0 :     InsertColGroups(0, mColGroups);
     349           0 :     InsertRowGroups(mFrames);
     350             :     // calc collapsing borders
     351           0 :     if (IsBorderCollapse()) {
     352           0 :       SetFullBCDamageArea();
     353             :     }
     354             :   }
     355             : }
     356             : 
     357             : void
     358           0 : nsTableFrame::AttributeChangedFor(nsIFrame*       aFrame,
     359             :                                   nsIContent*     aContent,
     360             :                                   nsIAtom*        aAttribute)
     361             : {
     362           0 :   nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
     363           0 :   if (cellFrame) {
     364           0 :     if ((nsGkAtoms::rowspan == aAttribute) ||
     365           0 :         (nsGkAtoms::colspan == aAttribute)) {
     366           0 :       nsTableCellMap* cellMap = GetCellMap();
     367           0 :       if (cellMap) {
     368             :         // for now just remove the cell from the map and reinsert it
     369             :         int32_t rowIndex, colIndex;
     370           0 :         cellFrame->GetRowIndex(rowIndex);
     371           0 :         cellFrame->GetColIndex(colIndex);
     372           0 :         RemoveCell(cellFrame, rowIndex);
     373           0 :         AutoTArray<nsTableCellFrame*, 1> cells;
     374           0 :         cells.AppendElement(cellFrame);
     375           0 :         InsertCells(cells, rowIndex, colIndex - 1);
     376             : 
     377             :         // XXX Should this use eStyleChange?  It currently doesn't need
     378             :         // to, but it might given more optimization.
     379           0 :         PresContext()->PresShell()->
     380           0 :           FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
     381             :       }
     382             :     }
     383             :   }
     384           0 : }
     385             : 
     386             : 
     387             : /* ****** CellMap methods ******* */
     388             : 
     389             : /* return the effective col count */
     390             : int32_t
     391           0 : nsTableFrame::GetEffectiveColCount() const
     392             : {
     393           0 :   int32_t colCount = GetColCount();
     394           0 :   if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
     395           0 :     nsTableCellMap* cellMap = GetCellMap();
     396           0 :     if (!cellMap) {
     397           0 :       return 0;
     398             :     }
     399             :     // don't count cols at the end that don't have originating cells
     400           0 :     for (int32_t colIdx = colCount - 1; colIdx >= 0; colIdx--) {
     401           0 :       if (cellMap->GetNumCellsOriginatingInCol(colIdx) > 0) {
     402           0 :         break;
     403             :       }
     404           0 :       colCount--;
     405             :     }
     406             :   }
     407           0 :   return colCount;
     408             : }
     409             : 
     410             : int32_t
     411           0 : nsTableFrame::GetIndexOfLastRealCol()
     412             : {
     413           0 :   int32_t numCols = mColFrames.Length();
     414           0 :   if (numCols > 0) {
     415           0 :     for (int32_t colIdx = numCols - 1; colIdx >= 0; colIdx--) {
     416           0 :       nsTableColFrame* colFrame = GetColFrame(colIdx);
     417           0 :       if (colFrame) {
     418           0 :         if (eColAnonymousCell != colFrame->GetColType()) {
     419           0 :           return colIdx;
     420             :         }
     421             :       }
     422             :     }
     423             :   }
     424           0 :   return -1;
     425             : }
     426             : 
     427             : nsTableColFrame*
     428           0 : nsTableFrame::GetColFrame(int32_t aColIndex) const
     429             : {
     430           0 :   NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
     431           0 :   int32_t numCols = mColFrames.Length();
     432           0 :   if ((aColIndex >= 0) && (aColIndex < numCols)) {
     433           0 :     return mColFrames.ElementAt(aColIndex);
     434             :   }
     435             :   else {
     436           0 :     NS_ERROR("invalid col index");
     437           0 :     return nullptr;
     438             :   }
     439             : }
     440             : 
     441             : int32_t
     442           0 : nsTableFrame::GetEffectiveRowSpan(int32_t                 aRowIndex,
     443             :                                   const nsTableCellFrame& aCell) const
     444             : {
     445           0 :   nsTableCellMap* cellMap = GetCellMap();
     446           0 :   NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated.");
     447             : 
     448             :   int32_t colIndex;
     449           0 :   aCell.GetColIndex(colIndex);
     450           0 :   return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
     451             : }
     452             : 
     453             : int32_t
     454           0 : nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
     455             :                                   nsCellMap*              aCellMap)
     456             : {
     457           0 :   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
     458             : 
     459             :   int32_t colIndex, rowIndex;
     460           0 :   aCell.GetColIndex(colIndex);
     461           0 :   aCell.GetRowIndex(rowIndex);
     462             : 
     463           0 :   if (aCellMap)
     464           0 :     return aCellMap->GetRowSpan(rowIndex, colIndex, true);
     465             :   else
     466           0 :     return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
     467             : }
     468             : 
     469             : int32_t
     470           0 : nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
     471             :                                   nsCellMap*              aCellMap) const
     472             : {
     473           0 :   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
     474             : 
     475             :   int32_t colIndex, rowIndex;
     476           0 :   aCell.GetColIndex(colIndex);
     477           0 :   aCell.GetRowIndex(rowIndex);
     478             : 
     479           0 :   if (aCellMap)
     480           0 :     return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex);
     481             :   else
     482           0 :     return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
     483             : }
     484             : 
     485             : bool
     486           0 : nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const
     487             : {
     488           0 :   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
     489           0 :   return tableCellMap->HasMoreThanOneCell(aRowIndex);
     490             : }
     491             : 
     492             : void
     493           0 : nsTableFrame::AdjustRowIndices(int32_t         aRowIndex,
     494             :                                int32_t         aAdjustment)
     495             : {
     496             :   // Iterate over the row groups and adjust the row indices of all rows
     497             :   // whose index is >= aRowIndex.
     498           0 :   RowGroupArray rowGroups;
     499           0 :   OrderRowGroups(rowGroups);
     500             : 
     501           0 :   for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
     502           0 :     rowGroups[rgIdx]->AdjustRowIndices(aRowIndex, aAdjustment);
     503             :   }
     504           0 : }
     505             : 
     506             : 
     507             : void
     508           0 : nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
     509             : {
     510             :   // Iterate over the row groups and adjust the row indices of all rows
     511             :   // omit the rowgroups that will be inserted later
     512           0 :   mDeletedRowIndexRanges.clear();
     513             : 
     514           0 :   RowGroupArray rowGroups;
     515           0 :   OrderRowGroups(rowGroups);
     516             : 
     517           0 :   nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
     518           0 :   nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
     519           0 :   while (!excludeRowGroupsEnumerator.AtEnd()) {
     520           0 :     excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
     521             : #ifdef DEBUG
     522             :     {
     523             :       // Check to make sure that the row indices of all rows in excluded row
     524             :       // groups are '0' (i.e. the initial value since they haven't been added yet)
     525             :       const nsFrameList& rowFrames =
     526           0 :         excludeRowGroupsEnumerator.get()->PrincipalChildList();
     527           0 :       for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
     528           0 :         nsTableRowFrame* row = static_cast<nsTableRowFrame*>(rows.get());
     529           0 :         MOZ_ASSERT(row->GetRowIndex() == 0,
     530             :                    "exclusions cannot be used for rows that were already added,"
     531             :                    "because we'd need to process mDeletedRowIndexRanges");
     532             :       }
     533             :     }
     534             : #endif
     535           0 :     excludeRowGroupsEnumerator.Next();
     536             :   }
     537             : 
     538           0 :   int32_t rowIndex = 0;
     539           0 :   for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
     540           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
     541           0 :     if (!excludeRowGroups.GetEntry(rgFrame)) {
     542           0 :       const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
     543           0 :       for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
     544           0 :         if (mozilla::StyleDisplay::TableRow ==
     545           0 :             rows.get()->StyleDisplay()->mDisplay) {
     546           0 :           nsTableRowFrame* row = static_cast<nsTableRowFrame*>(rows.get());
     547           0 :           row->SetRowIndex(rowIndex);
     548           0 :           rowIndex++;
     549             :         }
     550             :       }
     551             :     }
     552             :   }
     553           0 : }
     554             : 
     555             : void
     556           0 : nsTableFrame::InsertColGroups(int32_t                   aStartColIndex,
     557             :                               const nsFrameList::Slice& aColGroups)
     558             : {
     559           0 :   int32_t colIndex = aStartColIndex;
     560           0 :   nsFrameList::Enumerator colGroups(aColGroups);
     561           0 :   for (; !colGroups.AtEnd(); colGroups.Next()) {
     562           0 :     MOZ_ASSERT(colGroups.get()->IsTableColGroupFrame());
     563             :     nsTableColGroupFrame* cgFrame =
     564           0 :       static_cast<nsTableColGroupFrame*>(colGroups.get());
     565           0 :     cgFrame->SetStartColumnIndex(colIndex);
     566             :     // XXXbz this sucks.  AddColsToTable will actually remove colgroups from
     567             :     // the list we're traversing!  Need to fix things here.  :( I guess this is
     568             :     // why the old code used pointer-to-last-frame as opposed to
     569             :     // pointer-to-frame-after-last....
     570             : 
     571             :     // How about dealing with this by storing a const reference to the
     572             :     // mNextSibling of the framelist's last frame, instead of storing a pointer
     573             :     // to the first-after-next frame?  Will involve making nsFrameList friend
     574             :     // of nsIFrame, but it's time for that anyway.
     575           0 :     cgFrame->AddColsToTable(colIndex, false,
     576           0 :                               colGroups.get()->PrincipalChildList());
     577           0 :     int32_t numCols = cgFrame->GetColCount();
     578           0 :     colIndex += numCols;
     579             :   }
     580             : 
     581           0 :   nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
     582           0 :   if (!remainingColgroups.AtEnd()) {
     583           0 :     nsTableColGroupFrame::ResetColIndices(
     584           0 :       static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
     585             :   }
     586           0 : }
     587             : 
     588             : void
     589           0 : nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
     590             :                         int32_t          aColIndex)
     591             : {
     592           0 :   mColFrames.InsertElementAt(aColIndex, &aColFrame);
     593           0 :   nsTableColType insertedColType = aColFrame.GetColType();
     594           0 :   int32_t numCacheCols = mColFrames.Length();
     595           0 :   nsTableCellMap* cellMap = GetCellMap();
     596           0 :   if (cellMap) {
     597           0 :     int32_t numMapCols = cellMap->GetColCount();
     598           0 :     if (numCacheCols > numMapCols) {
     599           0 :       bool removedFromCache = false;
     600           0 :       if (eColAnonymousCell != insertedColType) {
     601           0 :         nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
     602           0 :         if (lastCol) {
     603           0 :           nsTableColType lastColType = lastCol->GetColType();
     604           0 :           if (eColAnonymousCell == lastColType) {
     605             :             // remove the col from the cache
     606           0 :             mColFrames.RemoveElementAt(numCacheCols - 1);
     607             :             // remove the col from the eColGroupAnonymousCell col group
     608           0 :             nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
     609           0 :             if (lastColGroup) {
     610           0 :               lastColGroup->RemoveChild(*lastCol, false);
     611             : 
     612             :               // remove the col group if it is empty
     613           0 :               if (lastColGroup->GetColCount() <= 0) {
     614           0 :                 mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
     615             :               }
     616             :             }
     617           0 :             removedFromCache = true;
     618             :           }
     619             :         }
     620             :       }
     621           0 :       if (!removedFromCache) {
     622           0 :         cellMap->AddColsAtEnd(1);
     623             :       }
     624             :     }
     625             :   }
     626             :   // for now, just bail and recalc all of the collapsing borders
     627           0 :   if (IsBorderCollapse()) {
     628           0 :     TableArea damageArea(aColIndex, 0, 1, GetRowCount());
     629           0 :     AddBCDamageArea(damageArea);
     630             :   }
     631           0 : }
     632             : 
     633             : void
     634           0 : nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
     635             :                         int32_t               aColIndex,
     636             :                         bool                  aRemoveFromCache,
     637             :                         bool                  aRemoveFromCellMap)
     638             : {
     639           0 :   if (aRemoveFromCache) {
     640           0 :     mColFrames.RemoveElementAt(aColIndex);
     641             :   }
     642           0 :   if (aRemoveFromCellMap) {
     643           0 :     nsTableCellMap* cellMap = GetCellMap();
     644           0 :     if (cellMap) {
     645             :       // If we have some anonymous cols at the end already, we just
     646             :       // add a new anonymous col.
     647           0 :       if (!mColFrames.IsEmpty() &&
     648           0 :           mColFrames.LastElement() && // XXXbz is this ever null?
     649           0 :           mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
     650           0 :         AppendAnonymousColFrames(1);
     651             :       } else {
     652             :         // All of our colframes correspond to actual <col> tags.  It's possible
     653             :         // that we still have at least as many <col> tags as we have logical
     654             :         // columns from cells, but we might have one less.  Handle the latter
     655             :         // case as follows: First ask the cellmap to drop its last col if it
     656             :         // doesn't have any actual cells in it.  Then call
     657             :         // MatchCellMapToColCache to append an anonymous column if it's needed;
     658             :         // this needs to be after RemoveColsAtEnd, since it will determine the
     659             :         // need for a new column frame based on the width of the cell map.
     660           0 :         cellMap->RemoveColsAtEnd();
     661           0 :         MatchCellMapToColCache(cellMap);
     662             :       }
     663             :     }
     664             :   }
     665             :   // for now, just bail and recalc all of the collapsing borders
     666           0 :   if (IsBorderCollapse()) {
     667           0 :     TableArea damageArea(0, 0, GetColCount(), GetRowCount());
     668           0 :     AddBCDamageArea(damageArea);
     669             :   }
     670           0 : }
     671             : 
     672             : /** Get the cell map for this table frame.  It is not always mCellMap.
     673             :   * Only the first-in-flow has a legit cell map.
     674             :   */
     675             : nsTableCellMap*
     676           0 : nsTableFrame::GetCellMap() const
     677             : {
     678           0 :   return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
     679             : }
     680             : 
     681             : // XXX this needs to be moved to nsCSSFrameConstructor
     682             : nsTableColGroupFrame*
     683           0 : nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
     684             : {
     685           0 :   nsIContent* colGroupContent = GetContent();
     686           0 :   nsPresContext* presContext = PresContext();
     687           0 :   nsIPresShell *shell = presContext->PresShell();
     688             : 
     689           0 :   RefPtr<nsStyleContext> colGroupStyle;
     690           0 :   colGroupStyle = shell->StyleSet()->
     691           0 :     ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup,
     692           0 :                                        mStyleContext);
     693             :   // Create a col group frame
     694           0 :   nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
     695           0 :   ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
     696           0 :   newFrame->Init(colGroupContent, this, nullptr);
     697           0 :   return (nsTableColGroupFrame *)newFrame;
     698             : }
     699             : 
     700             : void
     701           0 : nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
     702             : {
     703             :   // get the last col group frame
     704             :   nsTableColGroupFrame* colGroupFrame =
     705           0 :     static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
     706             : 
     707           0 :   if (!colGroupFrame ||
     708           0 :       (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
     709           0 :     int32_t colIndex = (colGroupFrame) ?
     710           0 :                         colGroupFrame->GetStartColumnIndex() +
     711           0 :                         colGroupFrame->GetColCount() : 0;
     712           0 :     colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
     713           0 :     if (!colGroupFrame) {
     714           0 :       return;
     715             :     }
     716             :     // add the new frame to the child list
     717           0 :     mColGroups.AppendFrame(this, colGroupFrame);
     718           0 :     colGroupFrame->SetStartColumnIndex(colIndex);
     719             :   }
     720             :   AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
     721           0 :                            true);
     722             : 
     723             : }
     724             : 
     725             : // XXX this needs to be moved to nsCSSFrameConstructor
     726             : // Right now it only creates the col frames at the end
     727             : void
     728           0 : nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
     729             :                                        int32_t               aNumColsToAdd,
     730             :                                        nsTableColType        aColType,
     731             :                                        bool                  aAddToTable)
     732             : {
     733           0 :   NS_PRECONDITION(aColGroupFrame, "null frame");
     734           0 :   NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
     735             : 
     736           0 :   nsIPresShell *shell = PresContext()->PresShell();
     737             : 
     738             :   // Get the last col frame
     739           0 :   nsFrameList newColFrames;
     740             : 
     741           0 :   int32_t startIndex = mColFrames.Length();
     742           0 :   int32_t lastIndex  = startIndex + aNumColsToAdd - 1;
     743             : 
     744           0 :   for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
     745             :     nsIContent* iContent;
     746           0 :     RefPtr<nsStyleContext> styleContext;
     747             :     nsStyleContext* parentStyleContext;
     748             : 
     749             :     // all anonymous cols that we create here use a pseudo style context of the
     750             :     // col group
     751           0 :     iContent = aColGroupFrame->GetContent();
     752           0 :     parentStyleContext = aColGroupFrame->StyleContext();
     753           0 :     styleContext = shell->StyleSet()->
     754           0 :       ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableCol,
     755           0 :                                          parentStyleContext);
     756             :     // ASSERTION to check for bug 54454 sneaking back in...
     757           0 :     NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
     758             : 
     759             :     // create the new col frame
     760           0 :     nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
     761           0 :     ((nsTableColFrame *) colFrame)->SetColType(aColType);
     762           0 :     colFrame->Init(iContent, aColGroupFrame, nullptr);
     763             : 
     764           0 :     newColFrames.AppendFrame(nullptr, colFrame);
     765             :   }
     766           0 :   nsFrameList& cols = aColGroupFrame->GetWritableChildList();
     767           0 :   nsIFrame* oldLastCol = cols.LastChild();
     768             :   const nsFrameList::Slice& newCols =
     769           0 :     cols.InsertFrames(nullptr, oldLastCol, newColFrames);
     770           0 :   if (aAddToTable) {
     771             :     // get the starting col index in the cache
     772             :     int32_t startColIndex;
     773           0 :     if (oldLastCol) {
     774           0 :       startColIndex =
     775           0 :         static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
     776             :     } else {
     777           0 :       startColIndex = aColGroupFrame->GetStartColumnIndex();
     778             :     }
     779             : 
     780           0 :     aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
     781             :   }
     782           0 : }
     783             : 
     784             : void
     785           0 : nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
     786             : {
     787           0 :   int32_t numColsInMap   = GetColCount();
     788           0 :   int32_t numColsInCache = mColFrames.Length();
     789           0 :   int32_t numColsToAdd = numColsInMap - numColsInCache;
     790           0 :   if (numColsToAdd > 0) {
     791             :     // this sets the child list, updates the col cache and cell map
     792           0 :     AppendAnonymousColFrames(numColsToAdd);
     793             :   }
     794           0 :   if (numColsToAdd < 0) {
     795           0 :     int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
     796             :     // if the cell map has fewer cols than the cache, correct it
     797           0 :     if (numColsNotRemoved > 0) {
     798           0 :       aCellMap->AddColsAtEnd(numColsNotRemoved);
     799             :     }
     800             :   }
     801           0 : }
     802             : 
     803             : void
     804           0 : nsTableFrame::DidResizeColumns()
     805             : {
     806           0 :   NS_PRECONDITION(!GetPrevInFlow(),
     807             :                   "should only be called on first-in-flow");
     808           0 :   if (mBits.mResizedColumns)
     809           0 :     return; // already marked
     810             : 
     811           0 :   for (nsTableFrame *f = this; f;
     812           0 :        f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
     813           0 :     f->mBits.mResizedColumns = true;
     814             : }
     815             : 
     816             : void
     817           0 : nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
     818             :                          int32_t           aRowIndex)
     819             : {
     820           0 :   nsTableCellMap* cellMap = GetCellMap();
     821           0 :   if (cellMap) {
     822           0 :     TableArea damageArea(0, 0, 0, 0);
     823           0 :     cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
     824           0 :     MatchCellMapToColCache(cellMap);
     825           0 :     if (IsBorderCollapse()) {
     826           0 :       AddBCDamageArea(damageArea);
     827             :     }
     828             :   }
     829           0 : }
     830             : 
     831             : void
     832           0 : nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
     833             :                           int32_t                      aRowIndex,
     834             :                           int32_t                      aColIndexBefore)
     835             : {
     836           0 :   nsTableCellMap* cellMap = GetCellMap();
     837           0 :   if (cellMap) {
     838           0 :     TableArea damageArea(0, 0, 0, 0);
     839           0 :     cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
     840           0 :     MatchCellMapToColCache(cellMap);
     841           0 :     if (IsBorderCollapse()) {
     842           0 :       AddBCDamageArea(damageArea);
     843             :     }
     844             :   }
     845           0 : }
     846             : 
     847             : // this removes the frames from the col group and table, but not the cell map
     848             : int32_t
     849           0 : nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames)
     850             : {
     851             :   // only remove cols that are of type eTypeAnonymous cell (they are at the end)
     852           0 :   int32_t endIndex   = mColFrames.Length() - 1;
     853           0 :   int32_t startIndex = (endIndex - aNumFrames) + 1;
     854           0 :   int32_t numColsRemoved = 0;
     855           0 :   for (int32_t colIdx = endIndex; colIdx >= startIndex; colIdx--) {
     856           0 :     nsTableColFrame* colFrame = GetColFrame(colIdx);
     857           0 :     if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
     858             :       nsTableColGroupFrame* cgFrame =
     859           0 :         static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
     860             :       // remove the frame from the colgroup
     861           0 :       cgFrame->RemoveChild(*colFrame, false);
     862             :       // remove the frame from the cache, but not the cell map
     863           0 :       RemoveCol(nullptr, colIdx, true, false);
     864           0 :       numColsRemoved++;
     865             :     }
     866             :     else {
     867           0 :       break;
     868             :     }
     869             :   }
     870           0 :   return (aNumFrames - numColsRemoved);
     871             : }
     872             : 
     873             : void
     874           0 : nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
     875             :                          int32_t           aRowIndex)
     876             : {
     877           0 :   nsTableCellMap* cellMap = GetCellMap();
     878           0 :   if (cellMap) {
     879           0 :     TableArea damageArea(0, 0, 0, 0);
     880           0 :     cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
     881           0 :     MatchCellMapToColCache(cellMap);
     882           0 :     if (IsBorderCollapse()) {
     883           0 :       AddBCDamageArea(damageArea);
     884             :     }
     885             :   }
     886           0 : }
     887             : 
     888             : int32_t
     889           0 : nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
     890             : {
     891           0 :   RowGroupArray orderedRowGroups;
     892           0 :   OrderRowGroups(orderedRowGroups);
     893             : 
     894           0 :   int32_t rowIndex = 0;
     895           0 :   for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
     896           0 :     nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
     897           0 :     if (rgFrame == aRowGroupFrame) {
     898           0 :       break;
     899             :     }
     900           0 :     int32_t numRows = rgFrame->GetRowCount();
     901           0 :     rowIndex += numRows;
     902             :   }
     903           0 :   return rowIndex;
     904             : }
     905             : 
     906             : // this cannot extend beyond a single row group
     907             : void
     908           0 : nsTableFrame::AppendRows(nsTableRowGroupFrame*       aRowGroupFrame,
     909             :                          int32_t                     aRowIndex,
     910             :                          nsTArray<nsTableRowFrame*>& aRowFrames)
     911             : {
     912           0 :   nsTableCellMap* cellMap = GetCellMap();
     913           0 :   if (cellMap) {
     914           0 :     int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
     915           0 :     InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
     916             :   }
     917           0 : }
     918             : 
     919             : // this cannot extend beyond a single row group
     920             : int32_t
     921           0 : nsTableFrame::InsertRows(nsTableRowGroupFrame*       aRowGroupFrame,
     922             :                          nsTArray<nsTableRowFrame*>& aRowFrames,
     923             :                          int32_t                     aRowIndex,
     924             :                          bool                        aConsiderSpans)
     925             : {
     926             : #ifdef DEBUG_TABLE_CELLMAP
     927             :   printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
     928             :   Dump(true, false, true);
     929             : #endif
     930             : 
     931           0 :   int32_t numColsToAdd = 0;
     932           0 :   nsTableCellMap* cellMap = GetCellMap();
     933           0 :   if (cellMap) {
     934           0 :     TableArea damageArea(0, 0, 0, 0);
     935           0 :     bool shouldRecalculateIndex = !IsDeletedRowIndexRangesEmpty();
     936           0 :     if (shouldRecalculateIndex) {
     937           0 :       ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
     938             :     }
     939           0 :     int32_t origNumRows = cellMap->GetRowCount();
     940           0 :     int32_t numNewRows = aRowFrames.Length();
     941           0 :     cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
     942           0 :     MatchCellMapToColCache(cellMap);
     943             : 
     944             :     // Perform row index adjustment only if row indices were not
     945             :     // reset above
     946           0 :     if (!shouldRecalculateIndex) {
     947           0 :       if (aRowIndex < origNumRows) {
     948           0 :         AdjustRowIndices(aRowIndex, numNewRows);
     949             :       }
     950             : 
     951             :       // assign the correct row indices to the new rows. If they were recalculated
     952             :       // above it may not have been done correctly because each row is constructed
     953             :       // with index 0
     954           0 :       for (int32_t rowB = 0; rowB < numNewRows; rowB++) {
     955           0 :         nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowB);
     956           0 :         rowFrame->SetRowIndex(aRowIndex + rowB);
     957             :       }
     958             :     }
     959             : 
     960           0 :     if (IsBorderCollapse()) {
     961           0 :       AddBCDamageArea(damageArea);
     962             :     }
     963             :   }
     964             : #ifdef DEBUG_TABLE_CELLMAP
     965             :   printf("=== insertRowsAfter \n");
     966             :   Dump(true, false, true);
     967             : #endif
     968             : 
     969           0 :   return numColsToAdd;
     970             : }
     971             : 
     972             : void
     973           0 : nsTableFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex)
     974             : {
     975           0 :   if (mDeletedRowIndexRanges.size() == 0) {
     976           0 :     mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>
     977             :                                     (aDeletedRowStoredIndex,
     978           0 :                                      aDeletedRowStoredIndex));
     979           0 :     return;
     980             :   }
     981             : 
     982             :   // Find the position of the current deleted row's stored index
     983             :   // among the previous deleted row index ranges and merge ranges if
     984             :   // they are consecutive, else add a new (disjoint) range to the map.
     985             :   // Call to mDeletedRowIndexRanges.upper_bound is
     986             :   // O(log(mDeletedRowIndexRanges.size())) therefore call to
     987             :   // AddDeletedRowIndex is also ~O(log(mDeletedRowIndexRanges.size()))
     988             : 
     989             :   // greaterIter = will point to smallest range in the map with lower value
     990             :   //              greater than the aDeletedRowStoredIndex.
     991             :   //              If no such value exists, point to end of map.
     992             :   // smallerIter = will point to largest range in the map with higher value
     993             :   //              smaller than the aDeletedRowStoredIndex
     994             :   //              If no such value exists, point to beginning of map.
     995             :   // i.e. when both values exist below is true:
     996             :   // smallerIter->second < aDeletedRowStoredIndex < greaterIter->first
     997           0 :   auto greaterIter = mDeletedRowIndexRanges.upper_bound(aDeletedRowStoredIndex);
     998           0 :   auto smallerIter = greaterIter;
     999             : 
    1000           0 :   if (smallerIter != mDeletedRowIndexRanges.begin()) {
    1001           0 :     smallerIter--;
    1002             :     // While greaterIter might be out-of-bounds (by being equal to end()),
    1003             :     // smallerIter now cannot be, since we returned early above for a 0-size map.
    1004             :   }
    1005             : 
    1006             :   // Note: smallerIter can only be equal to greaterIter when both
    1007             :   // of them point to the beginning of the map and in that case smallerIter
    1008             :   // does not "exist" but we clip smallerIter to point to beginning of map
    1009             :   // so that it doesn't point to something unknown or outside the map boundry.
    1010             :   // Note: When greaterIter is not the end (i.e. it "exists") upper_bound()
    1011             :   // ensures aDeletedRowStoredIndex < greaterIter->first so no need to
    1012             :   // assert that.
    1013           0 :   MOZ_ASSERT(smallerIter == greaterIter ||
    1014             :                aDeletedRowStoredIndex > smallerIter->second,
    1015             :              "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
    1016             :              "Trying to delete an already deleted row?");
    1017             : 
    1018           0 :   if (smallerIter->second == aDeletedRowStoredIndex - 1) {
    1019           0 :     if (greaterIter != mDeletedRowIndexRanges.end() &&
    1020           0 :         greaterIter->first == aDeletedRowStoredIndex + 1) {
    1021             :       // merge current index with smaller and greater range as they are consecutive
    1022           0 :       smallerIter->second = greaterIter->second;
    1023           0 :       mDeletedRowIndexRanges.erase(greaterIter);
    1024             :     }
    1025             :     else {
    1026             :       // add aDeletedRowStoredIndex in the smaller range as it is consecutive
    1027           0 :       smallerIter->second = aDeletedRowStoredIndex;
    1028             :     }
    1029           0 :   } else if (greaterIter != mDeletedRowIndexRanges.end() &&
    1030           0 :              greaterIter->first == aDeletedRowStoredIndex + 1) {
    1031             :     // add aDeletedRowStoredIndex in the greater range as it is consecutive
    1032           0 :     mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>
    1033             :                                    (aDeletedRowStoredIndex,
    1034           0 :                                     greaterIter->second));
    1035           0 :     mDeletedRowIndexRanges.erase(greaterIter);
    1036             :   } else {
    1037             :     // add new range as aDeletedRowStoredIndex is disjoint from existing ranges
    1038           0 :     mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>
    1039             :                                    (aDeletedRowStoredIndex,
    1040           0 :                                     aDeletedRowStoredIndex));
    1041             :   }
    1042             : }
    1043             : 
    1044             : int32_t
    1045           0 : nsTableFrame::GetAdjustmentForStoredIndex(int32_t aStoredIndex)
    1046             : {
    1047           0 :   if (mDeletedRowIndexRanges.size() == 0)
    1048           0 :     return 0;
    1049             : 
    1050           0 :   int32_t adjustment = 0;
    1051             : 
    1052             :   // O(log(mDeletedRowIndexRanges.size()))
    1053           0 :   auto endIter = mDeletedRowIndexRanges.upper_bound(aStoredIndex);
    1054           0 :   for (auto iter = mDeletedRowIndexRanges.begin(); iter != endIter; ++iter) {
    1055           0 :     adjustment += iter->second - iter->first + 1;
    1056             :   }
    1057             : 
    1058           0 :   return adjustment;
    1059             : }
    1060             : 
    1061             : // this cannot extend beyond a single row group
    1062             : void
    1063           0 : nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
    1064             :                          int32_t          aNumRowsToRemove,
    1065             :                          bool             aConsiderSpans)
    1066             : {
    1067             : #ifdef TBD_OPTIMIZATION
    1068             :   // decide if we need to rebalance. we have to do this here because the row group
    1069             :   // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
    1070             :   bool stopTelling = false;
    1071             :   for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
    1072             :        kidFrame = kidFrame->GetNextSibling()) {
    1073             :     nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
    1074             :     if (cellFrame) {
    1075             :       stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
    1076             :                                                  cellFrame->GetMaximumWidth(), true);
    1077             :     }
    1078             :   }
    1079             :   // XXX need to consider what happens if there are cells that have rowspans
    1080             :   // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
    1081             : #endif
    1082             : 
    1083           0 :   int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
    1084             : #ifdef DEBUG_TABLE_CELLMAP
    1085             :   printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
    1086             :   Dump(true, false, true);
    1087             : #endif
    1088           0 :   nsTableCellMap* cellMap = GetCellMap();
    1089           0 :   if (cellMap) {
    1090           0 :     TableArea damageArea(0, 0, 0, 0);
    1091             : 
    1092             :     // Mark rows starting from aFirstRowFrame to the next 'aNumRowsToRemove-1'
    1093             :     // number of rows as deleted.
    1094           0 :     nsTableRowGroupFrame* parentFrame = aFirstRowFrame.GetTableRowGroupFrame();
    1095           0 :     parentFrame->MarkRowsAsDeleted(aFirstRowFrame, aNumRowsToRemove);
    1096             : 
    1097           0 :     cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
    1098           0 :     MatchCellMapToColCache(cellMap);
    1099           0 :     if (IsBorderCollapse()) {
    1100           0 :       AddBCDamageArea(damageArea);
    1101             :     }
    1102             :   }
    1103             : 
    1104             : #ifdef DEBUG_TABLE_CELLMAP
    1105             :   printf("=== removeRowsAfter\n");
    1106             :   Dump(true, true, true);
    1107             : #endif
    1108           0 : }
    1109             : 
    1110             : // collect the rows ancestors of aFrame
    1111             : int32_t
    1112           0 : nsTableFrame::CollectRows(nsIFrame*                   aFrame,
    1113             :                           nsTArray<nsTableRowFrame*>& aCollection)
    1114             : {
    1115           0 :   NS_PRECONDITION(aFrame, "null frame");
    1116           0 :   int32_t numRows = 0;
    1117           0 :   for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
    1118           0 :     aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
    1119           0 :     numRows++;
    1120             :   }
    1121           0 :   return numRows;
    1122             : }
    1123             : 
    1124             : void
    1125           0 : nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
    1126             : {
    1127             : #ifdef DEBUG_TABLE_CELLMAP
    1128             :   printf("=== insertRowGroupsBefore\n");
    1129             :   Dump(true, false, true);
    1130             : #endif
    1131           0 :   nsTableCellMap* cellMap = GetCellMap();
    1132           0 :   if (cellMap) {
    1133           0 :     RowGroupArray orderedRowGroups;
    1134           0 :     OrderRowGroups(orderedRowGroups);
    1135             : 
    1136           0 :     AutoTArray<nsTableRowFrame*, 8> rows;
    1137             :     // Loop over the rowgroups and check if some of them are new, if they are
    1138             :     // insert cellmaps in the order that is predefined by OrderRowGroups,
    1139             :     // XXXbz this code is O(N*M) where N is number of new rowgroups
    1140             :     // and M is number of rowgroups we have!
    1141             :     uint32_t rgIndex;
    1142           0 :     for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
    1143           0 :       for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
    1144           0 :            rowgroups.Next()) {
    1145           0 :         if (orderedRowGroups[rgIndex] == rowgroups.get()) {
    1146             :           nsTableRowGroupFrame* priorRG =
    1147           0 :             (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
    1148             :           // create and add the cell map for the row group
    1149           0 :           cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
    1150             : 
    1151           0 :           break;
    1152             :         }
    1153             :       }
    1154             :     }
    1155           0 :     cellMap->Synchronize(this);
    1156           0 :     ResetRowIndices(aRowGroups);
    1157             : 
    1158             :     //now that the cellmaps are reordered too insert the rows
    1159           0 :     for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
    1160           0 :       for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
    1161           0 :            rowgroups.Next()) {
    1162           0 :         if (orderedRowGroups[rgIndex] == rowgroups.get()) {
    1163             :           nsTableRowGroupFrame* priorRG =
    1164           0 :             (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
    1165             :           // collect the new row frames in an array and add them to the table
    1166           0 :           int32_t numRows = CollectRows(rowgroups.get(), rows);
    1167           0 :           if (numRows > 0) {
    1168           0 :             int32_t rowIndex = 0;
    1169           0 :             if (priorRG) {
    1170           0 :               int32_t priorNumRows = priorRG->GetRowCount();
    1171           0 :               rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
    1172             :             }
    1173           0 :             InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
    1174           0 :             rows.Clear();
    1175             :           }
    1176           0 :           break;
    1177             :         }
    1178             :       }
    1179             :     }
    1180             : 
    1181             :   }
    1182             : #ifdef DEBUG_TABLE_CELLMAP
    1183             :   printf("=== insertRowGroupsAfter\n");
    1184             :   Dump(true, true, true);
    1185             : #endif
    1186           0 : }
    1187             : 
    1188             : 
    1189             : /////////////////////////////////////////////////////////////////////////////
    1190             : // Child frame enumeration
    1191             : 
    1192             : const nsFrameList&
    1193           0 : nsTableFrame::GetChildList(ChildListID aListID) const
    1194             : {
    1195           0 :   if (aListID == kColGroupList) {
    1196           0 :     return mColGroups;
    1197             :   }
    1198           0 :   return nsContainerFrame::GetChildList(aListID);
    1199             : }
    1200             : 
    1201             : void
    1202           0 : nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
    1203             : {
    1204           0 :   nsContainerFrame::GetChildLists(aLists);
    1205           0 :   mColGroups.AppendIfNonempty(aLists, kColGroupList);
    1206           0 : }
    1207             : 
    1208             : nsRect
    1209           0 : nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
    1210           0 :   *aSnap = false;
    1211           0 :   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
    1212             : }
    1213             : 
    1214             : void
    1215           0 : nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
    1216             : {
    1217             :   nsStyleContext *bgSC;
    1218           0 :   if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
    1219           0 :     return;
    1220           0 :   if (!bgSC->StyleBackground()->HasFixedBackground(aFrame))
    1221           0 :     return;
    1222             : 
    1223           0 :   mPartHasFixedBackground = true;
    1224             : }
    1225             : 
    1226             : nsDisplayItemGeometry*
    1227           0 : nsDisplayTableItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
    1228             : {
    1229             :   return new nsDisplayTableItemGeometry(this, aBuilder,
    1230           0 :       mFrame->GetOffsetTo(mFrame->PresContext()->PresShell()->GetRootFrame()));
    1231             : }
    1232             : 
    1233             : void
    1234           0 : nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
    1235             :                                               const nsDisplayItemGeometry* aGeometry,
    1236             :                                               nsRegion *aInvalidRegion)
    1237             : {
    1238             :   auto geometry =
    1239           0 :     static_cast<const nsDisplayTableItemGeometry*>(aGeometry);
    1240             : 
    1241           0 :   bool invalidateForAttachmentFixed = false;
    1242           0 :   if (mDrawsBackground && mPartHasFixedBackground) {
    1243           0 :     nsPoint frameOffsetToViewport = mFrame->GetOffsetTo(
    1244           0 :         mFrame->PresContext()->PresShell()->GetRootFrame());
    1245             :     invalidateForAttachmentFixed =
    1246           0 :         frameOffsetToViewport != geometry->mFrameOffsetToViewport;
    1247             :   }
    1248             : 
    1249           0 :   if (invalidateForAttachmentFixed ||
    1250           0 :       (aBuilder->ShouldSyncDecodeImages() &&
    1251           0 :        geometry->ShouldInvalidateToSyncDecodeImages())) {
    1252             :     bool snap;
    1253           0 :     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
    1254             :   }
    1255             : 
    1256           0 :   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
    1257           0 : }
    1258             : 
    1259             : // A display item that draws all collapsed borders for a table.
    1260             : // At some point, we may want to find a nicer partitioning for dividing
    1261             : // border-collapse segments into their own display items.
    1262             : class nsDisplayTableBorderCollapse : public nsDisplayTableItem {
    1263             : public:
    1264           0 :   nsDisplayTableBorderCollapse(nsDisplayListBuilder* aBuilder,
    1265             :                                nsTableFrame* aFrame)
    1266           0 :     : nsDisplayTableItem(aBuilder, aFrame) {
    1267           0 :     MOZ_COUNT_CTOR(nsDisplayTableBorderCollapse);
    1268           0 :     }
    1269             : #ifdef NS_BUILD_REFCNT_LOGGING
    1270           0 :   virtual ~nsDisplayTableBorderCollapse() {
    1271           0 :     MOZ_COUNT_DTOR(nsDisplayTableBorderCollapse);
    1272           0 :   }
    1273             : #endif
    1274             : 
    1275             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1276             :                      gfxContext* aCtx) override;
    1277             :   virtual already_AddRefed<layers::Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
    1278             :                                                      LayerManager* aManager,
    1279             :                                                      const ContainerLayerParameters& aContainerParameters) override;
    1280             :   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
    1281             :                                        const StackingContextHelper& aSc,
    1282             :                                        nsTArray<WebRenderParentCommand>& aParentCommands,
    1283             :                                        mozilla::layers::WebRenderLayerManager* aManager,
    1284             :                                        nsDisplayListBuilder* aDisplayListBuilder) override;
    1285             :   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
    1286             :                                    LayerManager* aManager,
    1287             :                                    const ContainerLayerParameters& aParameters) override;
    1288           0 :   NS_DISPLAY_DECL_NAME("TableBorderCollapse", TYPE_TABLE_BORDER_COLLAPSE)
    1289             : };
    1290             : 
    1291             : void
    1292           0 : nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder,
    1293             :                                     gfxContext* aCtx)
    1294             : {
    1295           0 :   nsPoint pt = ToReferenceFrame();
    1296           0 :   DrawTarget* drawTarget = aCtx->GetDrawTarget();
    1297             : 
    1298             :   gfxPoint devPixelOffset =
    1299           0 :     nsLayoutUtils::PointToGfxPoint(pt, mFrame->PresContext()->AppUnitsPerDevPixel());
    1300             : 
    1301             :   // XXX we should probably get rid of this translation at some stage
    1302             :   // But that would mean modifying PaintBCBorders, ugh
    1303           0 :   AutoRestoreTransform autoRestoreTransform(drawTarget);
    1304             :   drawTarget->SetTransform(
    1305           0 :       drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
    1306             : 
    1307           0 :   static_cast<nsTableFrame*>(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt);
    1308           0 : }
    1309             : 
    1310             : already_AddRefed<layers::Layer>
    1311           0 : nsDisplayTableBorderCollapse::BuildLayer(nsDisplayListBuilder* aBuilder,
    1312             :                                          LayerManager* aManager,
    1313             :                                          const ContainerLayerParameters& aContainerParameters)
    1314             : {
    1315           0 :   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
    1316             : }
    1317             : 
    1318             : bool
    1319           0 : nsDisplayTableBorderCollapse::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
    1320             :                                                       const StackingContextHelper& aSc,
    1321             :                                                       nsTArray<WebRenderParentCommand>& aParentCommands,
    1322             :                                                       mozilla::layers::WebRenderLayerManager* aManager,
    1323             :                                                       nsDisplayListBuilder* aDisplayListBuilder)
    1324             : {
    1325           0 :   if (aManager->IsLayersFreeTransaction()) {
    1326           0 :     ContainerLayerParameters parameter;
    1327           0 :     if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
    1328           0 :       return false;
    1329             :     }
    1330             :   }
    1331             : 
    1332           0 :   static_cast<nsTableFrame *>(mFrame)->CreateWebRenderCommandsForBCBorders(aBuilder,
    1333             :                                                                           aSc,
    1334             :                                                                           aParentCommands,
    1335           0 :                                                                           ToReferenceFrame());
    1336           0 :   return true;
    1337             : }
    1338             : 
    1339             : LayerState
    1340           0 : nsDisplayTableBorderCollapse::GetLayerState(nsDisplayListBuilder* aBuilder,
    1341             :                                             LayerManager* aManager,
    1342             :                                             const ContainerLayerParameters& aParameters)
    1343             : {
    1344           0 :   if (gfxPrefs::LayersAllowTable()) {
    1345           0 :     return LAYER_ACTIVE;
    1346             :   }
    1347             : 
    1348           0 :   return LAYER_NONE;
    1349             : }
    1350             : 
    1351             : /* static */ void
    1352           0 : nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
    1353             :                                const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
    1354             : {
    1355             :   // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
    1356             :   // does, except that we allow the children's background and borders to go
    1357             :   // in our BorderBackground list. This doesn't really affect background
    1358             :   // painting --- the children won't actually draw their own backgrounds
    1359             :   // because the nsTableFrame already drew them, unless a child has its own
    1360             :   // stacking context, in which case the child won't use its passed-in
    1361             :   // BorderBackground list anyway. It does affect cell borders though; this
    1362             :   // lets us get cell borders into the nsTableFrame's BorderBackground list.
    1363           0 :   for (nsIFrame* kid : aFrame->GetChildList(kColGroupList)) {
    1364           0 :     aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
    1365             :   }
    1366             : 
    1367           0 :   for (nsIFrame* kid : aFrame->PrincipalChildList()) {
    1368           0 :     aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
    1369             :   }
    1370           0 : }
    1371             : 
    1372             : static void
    1373           0 : PaintRowBackground(nsTableRowFrame* aRow,
    1374             :                    nsIFrame* aFrame,
    1375             :                    nsDisplayListBuilder* aBuilder,
    1376             :                    const nsDisplayListSet& aLists,
    1377             :                    const nsRect& aDirtyRect,
    1378             :                    const nsPoint& aOffset = nsPoint())
    1379             : {
    1380             :   // Compute background rect by iterating all cell frame.
    1381           0 :   for (nsTableCellFrame* cell = aRow->GetFirstCell(); cell; cell = cell->GetNextCell()) {
    1382           0 :     auto cellRect = cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + aOffset;
    1383           0 :     if (!aDirtyRect.Intersects(cellRect)) {
    1384           0 :       continue;
    1385             :     }
    1386           0 :     nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect,
    1387             :                                                          aLists.BorderBackground(),
    1388             :                                                          true, nullptr,
    1389           0 :                                                          aFrame->GetRectRelativeToSelf(),
    1390           0 :                                                          cell);
    1391             :   }
    1392           0 : }
    1393             : 
    1394             : static void
    1395           0 : PaintRowGroupBackground(nsTableRowGroupFrame* aRowGroup,
    1396             :                         nsIFrame* aFrame,
    1397             :                         nsDisplayListBuilder* aBuilder,
    1398             :                         const nsDisplayListSet& aLists,
    1399             :                         const nsRect& aDirtyRect)
    1400             : {
    1401           0 :   for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
    1402           0 :     if (!aDirtyRect.Intersects(nsRect(row->GetNormalPosition(), row->GetSize()))) {
    1403           0 :       continue;
    1404             :     }
    1405           0 :     PaintRowBackground(row, aFrame, aBuilder, aLists, aDirtyRect, row->GetNormalPosition());
    1406             :   }
    1407           0 : }
    1408             : 
    1409             : static void
    1410           0 : PaintRowGroupBackgroundByColIdx(nsTableRowGroupFrame* aRowGroup,
    1411             :                                 nsIFrame* aFrame,
    1412             :                                 nsDisplayListBuilder* aBuilder,
    1413             :                                 const nsDisplayListSet& aLists,
    1414             :                                 const nsRect& aDirtyRect,
    1415             :                                 const nsTArray<int32_t>& aColIdx,
    1416             :                                 const nsPoint& aOffset)
    1417             : {
    1418           0 :   for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
    1419           0 :     auto rowPos = row->GetNormalPosition() + aOffset;
    1420           0 :     if (!aDirtyRect.Intersects(nsRect(rowPos, row->GetSize()))) {
    1421           0 :       continue;
    1422             :     }
    1423           0 :     for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
    1424             :       int32_t curColIdx;
    1425           0 :       cell->GetColIndex(curColIdx);
    1426           0 :       if (aColIdx.Contains(curColIdx)) {
    1427           0 :         auto cellPos = cell->GetNormalPosition() + rowPos;
    1428           0 :         auto cellRect = nsRect(cellPos, cell->GetSize());
    1429           0 :         if (!aDirtyRect.Intersects(cellRect)) {
    1430           0 :           continue;
    1431             :         }
    1432           0 :         nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect,
    1433             :                                                              aLists.BorderBackground(),
    1434             :                                                              true, nullptr,
    1435           0 :                                                              aFrame->GetRectRelativeToSelf(),
    1436           0 :                                                              cell);
    1437             :       }
    1438             :     }
    1439             :   }
    1440           0 : }
    1441             : 
    1442           0 : static inline bool FrameHasBorder(nsIFrame* f)
    1443             : {
    1444           0 :   if (!f->StyleVisibility()->IsVisible()) {
    1445           0 :     return false;
    1446             :   }
    1447             : 
    1448           0 :   if (f->StyleBorder()->HasBorder()) {
    1449           0 :     return true;
    1450             :   }
    1451             : 
    1452           0 :   return false;
    1453             : }
    1454             : 
    1455           0 : void nsTableFrame::CalcHasBCBorders()
    1456             : {
    1457           0 :   if (!IsBorderCollapse()) {
    1458           0 :     SetHasBCBorders(false);
    1459           0 :     return;
    1460             :   }
    1461             : 
    1462           0 :   if (FrameHasBorder(this)) {
    1463           0 :     SetHasBCBorders(true);
    1464           0 :     return;
    1465             :   }
    1466             : 
    1467             :   // Check col and col group has borders.
    1468           0 :   for (nsIFrame* f : this->GetChildList(kColGroupList)) {
    1469           0 :     if (FrameHasBorder(f)) {
    1470           0 :       SetHasBCBorders(true);
    1471           0 :       return;
    1472             :     }
    1473             : 
    1474           0 :     nsTableColGroupFrame *colGroup = static_cast<nsTableColGroupFrame*>(f);
    1475           0 :     for (nsTableColFrame* col = colGroup->GetFirstColumn(); col; col = col->GetNextCol()) {
    1476           0 :       if (FrameHasBorder(col)) {
    1477           0 :         SetHasBCBorders(true);
    1478           0 :         return;
    1479             :       }
    1480             :     }
    1481             :   }
    1482             : 
    1483             :   // check row group, row and cell has borders.
    1484           0 :   RowGroupArray rowGroups;
    1485           0 :   OrderRowGroups(rowGroups);
    1486           0 :   for (nsTableRowGroupFrame* rowGroup : rowGroups) {
    1487           0 :     if (FrameHasBorder(rowGroup)) {
    1488           0 :       SetHasBCBorders(true);
    1489           0 :       return;
    1490             :     }
    1491             : 
    1492           0 :     for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
    1493           0 :       if (FrameHasBorder(row)) {
    1494           0 :         SetHasBCBorders(true);
    1495           0 :         return;
    1496             :       }
    1497             : 
    1498           0 :       for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
    1499           0 :         if (FrameHasBorder(cell)) {
    1500           0 :           SetHasBCBorders(true);
    1501           0 :           return;
    1502             :         }
    1503             :       }
    1504             :     }
    1505             :   }
    1506             : 
    1507           0 :   SetHasBCBorders(false);
    1508             : }
    1509             : 
    1510             : /* static */ void
    1511           0 : nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
    1512             :                                       nsFrame* aFrame,
    1513             :                                       const nsRect& aDirtyRect,
    1514             :                                       const nsDisplayListSet& aLists,
    1515             :                                       DisplayGenericTablePartTraversal aTraversal)
    1516             : {
    1517           0 :   if (aFrame->IsVisibleForPainting(aBuilder)) {
    1518           0 :     nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
    1519             :     // currentItem may be null, when none of the table parts have a
    1520             :     // background or border
    1521           0 :     if (currentItem) {
    1522           0 :       currentItem->UpdateForFrameBackground(aFrame);
    1523             :     }
    1524             : 
    1525             :     // Paint the outset box-shadows for the table frames
    1526           0 :     bool hasBoxShadow = aFrame->StyleEffects()->mBoxShadow != nullptr;
    1527           0 :     if (hasBoxShadow) {
    1528           0 :       aLists.BorderBackground()->AppendNewToTop(
    1529           0 :         new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame));
    1530             :     }
    1531             : 
    1532           0 :     if (aFrame->IsTableRowGroupFrame()) {
    1533           0 :       nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame);
    1534           0 :       PaintRowGroupBackground(rowGroup, aFrame, aBuilder, aLists, aDirtyRect);
    1535           0 :     } else if (aFrame->IsTableRowFrame()) {
    1536           0 :       nsTableRowFrame* row = static_cast<nsTableRowFrame*>(aFrame);
    1537           0 :       PaintRowBackground(row, aFrame, aBuilder, aLists, aDirtyRect);
    1538           0 :     } else if (aFrame->IsTableColGroupFrame()) {
    1539             :       // Compute background rect by iterating all cell frame.
    1540           0 :       nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(aFrame);
    1541             :       // Collecting column index.
    1542           0 :       AutoTArray<int32_t, 1> colIdx;
    1543           0 :       for (nsTableColFrame* col = colGroup->GetFirstColumn(); col; col = col->GetNextCol()) {
    1544           0 :         colIdx.AppendElement(col->GetColIndex());
    1545             :       }
    1546             : 
    1547           0 :       nsTableFrame* table = colGroup->GetTableFrame();
    1548           0 :       RowGroupArray rowGroups;
    1549           0 :       table->OrderRowGroups(rowGroups);
    1550           0 :       for (nsTableRowGroupFrame* rowGroup : rowGroups) {
    1551           0 :         auto offset = rowGroup->GetNormalPosition() - colGroup->GetNormalPosition();
    1552           0 :         if (!aDirtyRect.Intersects(nsRect(offset, rowGroup->GetSize()))) {
    1553           0 :           continue;
    1554             :         }
    1555           0 :         PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, aDirtyRect, colIdx, offset);
    1556             :       }
    1557           0 :     } else if (aFrame->IsTableColFrame()) {
    1558             :       // Compute background rect by iterating all cell frame.
    1559           0 :       nsTableColFrame* col = static_cast<nsTableColFrame*>(aFrame);
    1560           0 :       AutoTArray<int32_t, 1> colIdx;
    1561           0 :       colIdx.AppendElement(col->GetColIndex());
    1562             : 
    1563           0 :       nsTableFrame* table = col->GetTableFrame();
    1564           0 :       RowGroupArray rowGroups;
    1565           0 :       table->OrderRowGroups(rowGroups);
    1566           0 :       for (nsTableRowGroupFrame* rowGroup : rowGroups) {
    1567           0 :         auto offset = rowGroup->GetNormalPosition() -
    1568           0 :                       col->GetNormalPosition() -
    1569           0 :                       col->GetTableColGroupFrame()->GetNormalPosition();
    1570           0 :         if (!aDirtyRect.Intersects(nsRect(offset, rowGroup->GetSize()))) {
    1571           0 :           continue;
    1572             :         }
    1573           0 :         PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, aDirtyRect, colIdx, offset);
    1574             :       }
    1575             :     } else {
    1576           0 :       nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame,
    1577           0 :                                                            aFrame->GetRectRelativeToSelf(),
    1578           0 :                                                            aLists.BorderBackground());
    1579             :     }
    1580             : 
    1581             :     // Paint the inset box-shadows for the table frames
    1582           0 :     if (hasBoxShadow) {
    1583           0 :       aLists.BorderBackground()->AppendNewToTop(
    1584           0 :         new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame));
    1585             :     }
    1586             :   }
    1587             : 
    1588           0 :   aTraversal(aBuilder, aFrame, aDirtyRect, aLists);
    1589             : 
    1590           0 :   if (aFrame->IsVisibleForPainting(aBuilder)) {
    1591           0 :     if (aFrame->IsTableFrame()) {
    1592           0 :       nsTableFrame* table = static_cast<nsTableFrame*>(aFrame);
    1593             :       // In the collapsed border model, overlay all collapsed borders.
    1594           0 :       if (table->IsBorderCollapse()) {
    1595           0 :         if (table->HasBCBorders()) {
    1596           0 :           aLists.BorderBackground()->AppendNewToTop(
    1597           0 :             new (aBuilder) nsDisplayTableBorderCollapse(aBuilder, table));
    1598             :         }
    1599             :       } else {
    1600           0 :         const nsStyleBorder* borderStyle = aFrame->StyleBorder();
    1601           0 :         if (borderStyle->HasBorder()) {
    1602           0 :           aLists.BorderBackground()->AppendNewToTop(
    1603           0 :             new (aBuilder) nsDisplayBorder(aBuilder, table));
    1604             :         }
    1605             :       }
    1606             :     }
    1607             :   }
    1608             : 
    1609           0 :   aFrame->DisplayOutline(aBuilder, aLists);
    1610           0 : }
    1611             : 
    1612             : // table paint code is concerned primarily with borders and bg color
    1613             : // SEC: TODO: adjust the rect for captions
    1614             : void
    1615           0 : nsTableFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    1616             :                                const nsRect&           aDirtyRect,
    1617             :                                const nsDisplayListSet& aLists)
    1618             : {
    1619           0 :   DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
    1620             : 
    1621           0 :   DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists);
    1622           0 : }
    1623             : 
    1624             : nsMargin
    1625           0 : nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
    1626             : {
    1627           0 :   if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
    1628           0 :       !IsBorderCollapse())
    1629           0 :     return nsMargin(0,0,0,0);
    1630             : 
    1631           0 :   WritingMode wm = GetWritingMode();
    1632           0 :   return GetOuterBCBorder(wm).GetPhysicalMargin(wm);
    1633             : }
    1634             : 
    1635             : nsIFrame::LogicalSides
    1636           0 : nsTableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
    1637             : {
    1638           0 :   if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
    1639             :                      StyleBoxDecorationBreak::Clone)) {
    1640           0 :     return LogicalSides();
    1641             :   }
    1642             : 
    1643           0 :   LogicalSides skip;
    1644             :   // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
    1645             :   // account for pagination
    1646           0 :   if (nullptr != GetPrevInFlow()) {
    1647           0 :     skip |= eLogicalSideBitsBStart;
    1648             :   }
    1649           0 :   if (nullptr != GetNextInFlow()) {
    1650           0 :     skip |= eLogicalSideBitsBEnd;
    1651             :   }
    1652           0 :   return skip;
    1653             : }
    1654             : 
    1655             : void
    1656           0 : nsTableFrame::SetColumnDimensions(nscoord aBSize, WritingMode aWM,
    1657             :                                   const LogicalMargin& aBorderPadding,
    1658             :                                   const nsSize& aContainerSize)
    1659             : {
    1660           0 :   const nscoord colBSize = aBSize - (aBorderPadding.BStartEnd(aWM) +
    1661           0 :                            GetRowSpacing(-1) + GetRowSpacing(GetRowCount()));
    1662           0 :   int32_t colIdx = 0;
    1663             :   LogicalPoint colGroupOrigin(aWM,
    1664           0 :                               aBorderPadding.IStart(aWM) + GetColSpacing(-1),
    1665           0 :                               aBorderPadding.BStart(aWM) + GetRowSpacing(-1));
    1666           0 :   nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
    1667           0 :   for (nsIFrame* colGroupFrame : mColGroups) {
    1668           0 :     MOZ_ASSERT(colGroupFrame->IsTableColGroupFrame());
    1669             :     // first we need to figure out the size of the colgroup
    1670           0 :     int32_t groupFirstCol = colIdx;
    1671           0 :     nscoord colGroupISize = 0;
    1672           0 :     nscoord cellSpacingI = 0;
    1673           0 :     const nsFrameList& columnList = colGroupFrame->PrincipalChildList();
    1674           0 :     for (nsIFrame* colFrame : columnList) {
    1675           0 :       if (mozilla::StyleDisplay::TableColumn ==
    1676           0 :           colFrame->StyleDisplay()->mDisplay) {
    1677           0 :         NS_ASSERTION(colIdx < GetColCount(), "invalid number of columns");
    1678           0 :         cellSpacingI = GetColSpacing(colIdx);
    1679           0 :         colGroupISize += fif->GetColumnISizeFromFirstInFlow(colIdx) +
    1680             :                          cellSpacingI;
    1681           0 :         ++colIdx;
    1682             :       }
    1683             :     }
    1684           0 :     if (colGroupISize) {
    1685           0 :       colGroupISize -= cellSpacingI;
    1686             :     }
    1687             : 
    1688           0 :     LogicalRect colGroupRect(aWM, colGroupOrigin.I(aWM), colGroupOrigin.B(aWM),
    1689           0 :                              colGroupISize, colBSize);
    1690           0 :     colGroupFrame->SetRect(aWM, colGroupRect, aContainerSize);
    1691           0 :     nsSize colGroupSize = colGroupFrame->GetSize();
    1692             : 
    1693             :     // then we can place the columns correctly within the group
    1694           0 :     colIdx = groupFirstCol;
    1695           0 :     LogicalPoint colOrigin(aWM);
    1696           0 :     for (nsIFrame* colFrame : columnList) {
    1697           0 :       if (mozilla::StyleDisplay::TableColumn ==
    1698           0 :           colFrame->StyleDisplay()->mDisplay) {
    1699           0 :         nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
    1700           0 :         LogicalRect colRect(aWM, colOrigin.I(aWM), colOrigin.B(aWM),
    1701           0 :                             colISize, colBSize);
    1702           0 :         colFrame->SetRect(aWM, colRect, colGroupSize);
    1703           0 :         cellSpacingI = GetColSpacing(colIdx);
    1704           0 :         colOrigin.I(aWM) += colISize + cellSpacingI;
    1705           0 :         ++colIdx;
    1706             :       }
    1707             :     }
    1708             : 
    1709           0 :     colGroupOrigin.I(aWM) += colGroupISize + cellSpacingI;
    1710             :   }
    1711           0 : }
    1712             : 
    1713             : // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
    1714             : 
    1715             : // XXX this could be made more general to handle row modifications that change the
    1716             : // table bsize, but first we need to scrutinize every Invalidate
    1717             : void
    1718           0 : nsTableFrame::ProcessRowInserted(nscoord aNewBSize)
    1719             : {
    1720           0 :   SetRowInserted(false); // reset the bit that got us here
    1721           0 :   nsTableFrame::RowGroupArray rowGroups;
    1722           0 :   OrderRowGroups(rowGroups);
    1723             :   // find the row group containing the inserted row
    1724           0 :   for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    1725           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    1726           0 :     NS_ASSERTION(rgFrame, "Must have rgFrame here");
    1727             :     // find the row that was inserted first
    1728           0 :     for (nsIFrame* childFrame : rgFrame->PrincipalChildList()) {
    1729           0 :       nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
    1730           0 :       if (rowFrame) {
    1731           0 :         if (rowFrame->IsFirstInserted()) {
    1732           0 :           rowFrame->SetFirstInserted(false);
    1733             :           // damage the table from the 1st row inserted to the end of the table
    1734           0 :           nsIFrame::InvalidateFrame();
    1735             :           // XXXbz didn't we do this up front?  Why do we need to do it again?
    1736           0 :           SetRowInserted(false);
    1737           0 :           return; // found it, so leave
    1738             :         }
    1739             :       }
    1740             :     }
    1741             :   }
    1742             : }
    1743             : 
    1744             : /* virtual */ void
    1745           0 : nsTableFrame::MarkIntrinsicISizesDirty()
    1746             : {
    1747           0 :   nsITableLayoutStrategy* tls = LayoutStrategy();
    1748           0 :   if (MOZ_UNLIKELY(!tls)) {
    1749             :     // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
    1750             :     // walking up the ancestor chain in a table next-in-flow.  In this case
    1751             :     // our original first-in-flow (which owns the TableLayoutStrategy) has
    1752             :     // already been destroyed and unhooked from the flow chain and thusly
    1753             :     // LayoutStrategy() returns null.  All the frames in the flow will be
    1754             :     // destroyed so no need to mark anything dirty here.  See bug 595758.
    1755           0 :     return;
    1756             :   }
    1757           0 :   tls->MarkIntrinsicISizesDirty();
    1758             : 
    1759             :   // XXXldb Call SetBCDamageArea?
    1760             : 
    1761           0 :   nsContainerFrame::MarkIntrinsicISizesDirty();
    1762             : }
    1763             : 
    1764             : /* virtual */ nscoord
    1765           0 : nsTableFrame::GetMinISize(gfxContext *aRenderingContext)
    1766             : {
    1767           0 :   if (NeedToCalcBCBorders())
    1768           0 :     CalcBCBorders();
    1769             : 
    1770           0 :   ReflowColGroups(aRenderingContext);
    1771             : 
    1772           0 :   return LayoutStrategy()->GetMinISize(aRenderingContext);
    1773             : }
    1774             : 
    1775             : /* virtual */ nscoord
    1776           0 : nsTableFrame::GetPrefISize(gfxContext *aRenderingContext)
    1777             : {
    1778           0 :   if (NeedToCalcBCBorders())
    1779           0 :     CalcBCBorders();
    1780             : 
    1781           0 :   ReflowColGroups(aRenderingContext);
    1782             : 
    1783           0 :   return LayoutStrategy()->GetPrefISize(aRenderingContext, false);
    1784             : }
    1785             : 
    1786             : /* virtual */ nsIFrame::IntrinsicISizeOffsetData
    1787           0 : nsTableFrame::IntrinsicISizeOffsets()
    1788             : {
    1789           0 :   IntrinsicISizeOffsetData result = nsContainerFrame::IntrinsicISizeOffsets();
    1790             : 
    1791           0 :   result.hMargin = 0;
    1792           0 :   result.hPctMargin = 0;
    1793             : 
    1794           0 :   if (IsBorderCollapse()) {
    1795           0 :     result.hPadding = 0;
    1796           0 :     result.hPctPadding = 0;
    1797             : 
    1798           0 :     WritingMode wm = GetWritingMode();
    1799           0 :     LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
    1800           0 :     result.hBorder = outerBC.IStartEnd(wm);
    1801             :   }
    1802             : 
    1803           0 :   return result;
    1804             : }
    1805             : 
    1806             : /* virtual */
    1807             : LogicalSize
    1808           0 : nsTableFrame::ComputeSize(gfxContext*         aRenderingContext,
    1809             :                           WritingMode         aWM,
    1810             :                           const LogicalSize&  aCBSize,
    1811             :                           nscoord             aAvailableISize,
    1812             :                           const LogicalSize&  aMargin,
    1813             :                           const LogicalSize&  aBorder,
    1814             :                           const LogicalSize&  aPadding,
    1815             :                           ComputeSizeFlags    aFlags)
    1816             : {
    1817             :   LogicalSize result =
    1818             :     nsContainerFrame::ComputeSize(aRenderingContext, aWM,
    1819             :                                   aCBSize, aAvailableISize,
    1820           0 :                                   aMargin, aBorder, aPadding, aFlags);
    1821             : 
    1822             :   // XXX The code below doesn't make sense if the caller's writing mode
    1823             :   // is orthogonal to this frame's. Not sure yet what should happen then;
    1824             :   // for now, just bail out.
    1825           0 :   if (aWM.IsVertical() != GetWritingMode().IsVertical()) {
    1826           0 :     return result;
    1827             :   }
    1828             : 
    1829             :   // If we're a container for font size inflation, then shrink
    1830             :   // wrapping inside of us should not apply font size inflation.
    1831           0 :   AutoMaybeDisableFontInflation an(this);
    1832             : 
    1833             :   // Tables never shrink below their min inline-size.
    1834           0 :   nscoord minISize = GetMinISize(aRenderingContext);
    1835           0 :   if (minISize > result.ISize(aWM)) {
    1836           0 :     result.ISize(aWM) = minISize;
    1837             :   }
    1838             : 
    1839           0 :   return result;
    1840             : }
    1841             : 
    1842             : nscoord
    1843           0 : nsTableFrame::TableShrinkISizeToFit(gfxContext *aRenderingContext,
    1844             :                                     nscoord aISizeInCB)
    1845             : {
    1846             :   // If we're a container for font size inflation, then shrink
    1847             :   // wrapping inside of us should not apply font size inflation.
    1848           0 :   AutoMaybeDisableFontInflation an(this);
    1849             : 
    1850             :   nscoord result;
    1851           0 :   nscoord minISize = GetMinISize(aRenderingContext);
    1852           0 :   if (minISize > aISizeInCB) {
    1853           0 :     result = minISize;
    1854             :   } else {
    1855             :     // Tables shrink inline-size to fit with a slightly different algorithm
    1856             :     // from the one they use for their intrinsic isize (the difference
    1857             :     // relates to handling of percentage isizes on columns).  So this
    1858             :     // function differs from nsFrame::ShrinkWidthToFit by only the
    1859             :     // following line.
    1860             :     // Since we've already called GetMinISize, we don't need to do any
    1861             :     // of the other stuff GetPrefISize does.
    1862             :     nscoord prefISize =
    1863           0 :       LayoutStrategy()->GetPrefISize(aRenderingContext, true);
    1864           0 :     if (prefISize > aISizeInCB) {
    1865           0 :       result = aISizeInCB;
    1866             :     } else {
    1867           0 :       result = prefISize;
    1868             :     }
    1869             :   }
    1870           0 :   return result;
    1871             : }
    1872             : 
    1873             : /* virtual */
    1874             : LogicalSize
    1875           0 : nsTableFrame::ComputeAutoSize(gfxContext*         aRenderingContext,
    1876             :                               WritingMode         aWM,
    1877             :                               const LogicalSize&  aCBSize,
    1878             :                               nscoord             aAvailableISize,
    1879             :                               const LogicalSize&  aMargin,
    1880             :                               const LogicalSize&  aBorder,
    1881             :                               const LogicalSize&  aPadding,
    1882             :                               ComputeSizeFlags    aFlags)
    1883             : {
    1884             :   // Tables always shrink-wrap.
    1885           0 :   nscoord cbBased = aAvailableISize - aMargin.ISize(aWM) - aBorder.ISize(aWM) -
    1886           0 :                     aPadding.ISize(aWM);
    1887           0 :   return LogicalSize(aWM, TableShrinkISizeToFit(aRenderingContext, cbBased),
    1888           0 :                      NS_UNCONSTRAINEDSIZE);
    1889             : }
    1890             : 
    1891             : // Return true if aParentReflowInput.frame or any of its ancestors within
    1892             : // the containing table have non-auto bsize. (e.g. pct or fixed bsize)
    1893             : bool
    1894           0 : nsTableFrame::AncestorsHaveStyleBSize(const ReflowInput& aParentReflowInput)
    1895             : {
    1896           0 :   WritingMode wm = aParentReflowInput.GetWritingMode();
    1897           0 :   for (const ReflowInput* rs = &aParentReflowInput;
    1898           0 :        rs && rs->mFrame; rs = rs->mParentReflowInput) {
    1899           0 :     LayoutFrameType frameType = rs->mFrame->Type();
    1900           0 :     if (IS_TABLE_CELL(frameType) ||
    1901           0 :         (LayoutFrameType::TableRow      == frameType) ||
    1902             :         (LayoutFrameType::TableRowGroup == frameType)) {
    1903           0 :       const nsStyleCoord &bsize = rs->mStylePosition->BSize(wm);
    1904             :       // calc() with percentages treated like 'auto' on internal table elements
    1905           0 :       if (bsize.GetUnit() != eStyleUnit_Auto &&
    1906           0 :           (!bsize.IsCalcUnit() || !bsize.HasPercent())) {
    1907           0 :         return true;
    1908             :       }
    1909           0 :     } else if (LayoutFrameType::Table == frameType) {
    1910             :       // we reached the containing table, so always return
    1911           0 :       return rs->mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto;
    1912             :     }
    1913             :   }
    1914           0 :   return false;
    1915             : }
    1916             : 
    1917             : // See if a special block-size reflow needs to occur and if so,
    1918             : // call RequestSpecialBSizeReflow
    1919             : void
    1920           0 : nsTableFrame::CheckRequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
    1921             : {
    1922           0 :   NS_ASSERTION(IS_TABLE_CELL(aReflowInput.mFrame->Type()) ||
    1923             :                aReflowInput.mFrame->IsTableRowFrame() ||
    1924             :                aReflowInput.mFrame->IsTableRowGroupFrame() ||
    1925             :                aReflowInput.mFrame->IsTableFrame(),
    1926             :                "unexpected frame type");
    1927           0 :   WritingMode wm = aReflowInput.GetWritingMode();
    1928           0 :   if (!aReflowInput.mFrame->GetPrevInFlow() &&  // 1st in flow
    1929           0 :       (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedBSize() ||  // no computed bsize
    1930           0 :        0                    == aReflowInput.ComputedBSize()) &&
    1931           0 :       eStyleUnit_Percent == aReflowInput.mStylePosition->BSize(wm).GetUnit() && // pct bsize
    1932           0 :       nsTableFrame::AncestorsHaveStyleBSize(*aReflowInput.mParentReflowInput)) {
    1933           0 :     nsTableFrame::RequestSpecialBSizeReflow(aReflowInput);
    1934             :   }
    1935           0 : }
    1936             : 
    1937             : // Notify the frame and its ancestors (up to the containing table) that a special
    1938             : // bsize reflow will occur. During a special bsize reflow, a table, row group,
    1939             : // row, or cell returns the last size it was reflowed at. However, the table may
    1940             : // change the bsize of row groups, rows, cells in DistributeBSizeToRows after.
    1941             : // And the row group can change the bsize of rows, cells in CalculateRowBSizes.
    1942             : void
    1943           0 : nsTableFrame::RequestSpecialBSizeReflow(const ReflowInput& aReflowInput)
    1944             : {
    1945             :   // notify the frame and its ancestors of the special reflow, stopping at the containing table
    1946           0 :   for (const ReflowInput* rs = &aReflowInput; rs && rs->mFrame; rs = rs->mParentReflowInput) {
    1947           0 :     LayoutFrameType frameType = rs->mFrame->Type();
    1948           0 :     NS_ASSERTION(IS_TABLE_CELL(frameType) ||
    1949             :                  LayoutFrameType::TableRow == frameType ||
    1950             :                  LayoutFrameType::TableRowGroup == frameType ||
    1951             :                  LayoutFrameType::Table == frameType,
    1952             :                  "unexpected frame type");
    1953             : 
    1954           0 :     rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    1955           0 :     if (LayoutFrameType::Table == frameType) {
    1956           0 :       NS_ASSERTION(rs != &aReflowInput,
    1957             :                    "should not request special bsize reflow for table");
    1958             :       // always stop when we reach a table
    1959           0 :       break;
    1960             :     }
    1961             :   }
    1962           0 : }
    1963             : 
    1964             : /******************************************************************************************
    1965             :  * Before reflow, intrinsic inline-size calculation is done using GetMinISize
    1966             :  * and GetPrefISize.  This used to be known as pass 1 reflow.
    1967             :  *
    1968             :  * After the intrinsic isize calculation, the table determines the
    1969             :  * column widths using BalanceColumnISizes() and
    1970             :  * then reflows each child again with a constrained avail isize. This reflow is referred to
    1971             :  * as the pass 2 reflow.
    1972             :  *
    1973             :  * A special bsize reflow (pass 3 reflow) can occur during an initial or resize reflow
    1974             :  * if (a) a row group, row, cell, or a frame inside a cell has a percent bsize but no computed
    1975             :  * bsize or (b) in paginated mode, a table has a bsize. (a) supports percent nested tables
    1976             :  * contained inside cells whose bsizes aren't known until after the pass 2 reflow. (b) is
    1977             :  * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
    1978             :  * the special bsize reflow (variety a) are as follows:
    1979             :  *
    1980             :  * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
    1981             :  *    to indicate that it should get the reflow. It does this when it has a percent bsize but
    1982             :  *    no computed bsize by calling CheckRequestSpecialBSizeReflow(). This method calls
    1983             :  *    RequestSpecialBSizeReflow() which calls SetNeedSpecialReflow() on its ancestors until
    1984             :  *    it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
    1985             :  *    percent bsize frames inside cells, during DidReflow(), the cell's NotifyPercentBSize()
    1986             :  *    is called (the cell is the reflow state's mPercentBSizeObserver in this case).
    1987             :  *    NotifyPercentBSize() calls RequestSpecialBSizeReflow().
    1988             :  *
    1989             :  * XXX (jfkthame) This comment appears to be out of date; it refers to methods/flags
    1990             :  *                that are no longer present in the code.
    1991             :  * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
    1992             :  *    will do the special bsize reflow, setting the reflow state's mFlags.mSpecialBSizeReflow
    1993             :  *    to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
    1994             :  *    returns true because in that case another special bsize reflow will be coming along with the
    1995             :  *    containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
    1996             :  *    the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
    1997             :  *    appropriate bsizes will not be known.
    1998             :  *
    1999             :  * 3) Since the bsizes of the table, row groups, rows, and cells was determined during the pass 2
    2000             :  *    reflow, they return their last desired sizes during the special bsize reflow. The reflow only
    2001             :  *    permits percent bsize frames inside the cells to resize based on the cells bsize and that bsize
    2002             :  *    was determined during the pass 2 reflow.
    2003             :  *
    2004             :  * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
    2005             :  * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
    2006             :  * until the current initiator is its containing table. Since these reflows are only received by
    2007             :  * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
    2008             :  *
    2009             :  * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
    2010             :  * that all frames will receive the reflow even if they don't really need them.
    2011             :  *
    2012             :  * Open issues with the special bsize reflow:
    2013             :  *
    2014             :  * 1) At some point there should be 2 kinds of special bsize reflows because (a) and (b) above are
    2015             :  *    really quite different. This would avoid unnecessary reflows during printing.
    2016             :  * 2) When a cell contains frames whose percent bsizes > 100%, there is data loss (see bug 115245).
    2017             :  *    However, this can also occur if a cell has a fixed bsize and there is no special bsize reflow.
    2018             :  *
    2019             :  * XXXldb Special bsize reflow should really be its own method, not
    2020             :  * part of nsIFrame::Reflow.  It should then call nsIFrame::Reflow on
    2021             :  * the contents of the cells to do the necessary block-axis resizing.
    2022             :  *
    2023             :  ******************************************************************************************/
    2024             : 
    2025             : /* Layout the entire inner table. */
    2026             : void
    2027           0 : nsTableFrame::Reflow(nsPresContext*           aPresContext,
    2028             :                      ReflowOutput&     aDesiredSize,
    2029             :                      const ReflowInput& aReflowInput,
    2030             :                      nsReflowStatus&          aStatus)
    2031             : {
    2032           0 :   MarkInReflow();
    2033           0 :   DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
    2034           0 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
    2035           0 :   bool isPaginated = aPresContext->IsPaginated();
    2036           0 :   WritingMode wm = aReflowInput.GetWritingMode();
    2037             : 
    2038           0 :   aStatus.Reset();
    2039           0 :   if (!GetPrevInFlow() && !mTableLayoutStrategy) {
    2040           0 :     NS_ERROR("strategy should have been created in Init");
    2041           0 :     return;
    2042             :   }
    2043             : 
    2044             :   // see if collapsing borders need to be calculated
    2045           0 :   if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
    2046           0 :     CalcBCBorders();
    2047             :   }
    2048             : 
    2049           0 :   aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
    2050             : 
    2051             :   // Check for an overflow list, and append any row group frames being pushed
    2052           0 :   MoveOverflowToChildList();
    2053             : 
    2054           0 :   bool haveDesiredBSize = false;
    2055           0 :   SetHaveReflowedColGroups(false);
    2056             : 
    2057             :   // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
    2058             :   // constrained initial reflow and other reflows which require either a strategy init or balance.
    2059             :   // This isn't done during an unconstrained reflow, because it will occur later when the parent
    2060             :   // reflows with a constrained isize.
    2061           0 :   bool fixupKidPositions = false;
    2062           0 :   if (NS_SUBTREE_DIRTY(this) ||
    2063           0 :       aReflowInput.ShouldReflowAllKids() ||
    2064           0 :       IsGeometryDirty() ||
    2065           0 :       isPaginated ||
    2066           0 :       aReflowInput.IsBResize()) {
    2067             : 
    2068           0 :     if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
    2069             :         // Also check IsBResize(), to handle the first Reflow preceding a
    2070             :         // special bsize Reflow, when we've already had a special bsize
    2071             :         // Reflow (where ComputedBSize() would not be
    2072             :         // NS_UNCONSTRAINEDSIZE, but without a style change in between).
    2073           0 :         aReflowInput.IsBResize()) {
    2074             :       // XXX Eventually, we should modify DistributeBSizeToRows to use
    2075             :       // nsTableRowFrame::GetInitialBSize instead of nsIFrame::BSize().
    2076             :       // That way, it will make its calculations based on internal table
    2077             :       // frame bsizes as they are before they ever had any extra bsize
    2078             :       // distributed to them.  In the meantime, this reflows all the
    2079             :       // internal table frames, which restores them to their state before
    2080             :       // DistributeBSizeToRows was called.
    2081           0 :       SetGeometryDirty();
    2082             :     }
    2083             : 
    2084           0 :     bool needToInitiateSpecialReflow = false;
    2085           0 :     if (isPaginated) {
    2086             :       // see if an extra reflow will be necessary in pagination mode
    2087             :       // when there is a specified table bsize
    2088           0 :       if (!GetPrevInFlow() &&
    2089           0 :           NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize()) {
    2090           0 :         nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
    2091           0 :         if ((tableSpecifiedBSize > 0) &&
    2092             :             (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE)) {
    2093           0 :           needToInitiateSpecialReflow = true;
    2094             :         }
    2095             :       }
    2096             :     } else {
    2097             :       needToInitiateSpecialReflow =
    2098           0 :         HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
    2099             :     }
    2100           0 :     nsIFrame* lastChildReflowed = nullptr;
    2101             : 
    2102           0 :     NS_ASSERTION(!aReflowInput.mFlags.mSpecialBSizeReflow,
    2103             :                  "Shouldn't be in special bsize reflow here!");
    2104             : 
    2105             :     // do the pass 2 reflow unless this is a special bsize reflow and we will be
    2106             :     // initiating a special bsize reflow
    2107             :     // XXXldb I changed this.  Should I change it back?
    2108             : 
    2109             :     // if we need to initiate a special bsize reflow, then don't constrain the
    2110             :     // bsize of the reflow before that
    2111             :     nscoord availBSize = needToInitiateSpecialReflow
    2112           0 :                          ? NS_UNCONSTRAINEDSIZE
    2113           0 :                          : aReflowInput.AvailableBSize();
    2114             : 
    2115             :     ReflowTable(aDesiredSize, aReflowInput, availBSize,
    2116           0 :                 lastChildReflowed, aStatus);
    2117             :     // If ComputedWidth is unconstrained, we may need to fix child positions
    2118             :     // later (in vertical-rl mode) due to use of 0 as a dummy
    2119             :     // containerSize.width during ReflowChildren.
    2120           0 :     fixupKidPositions = wm.IsVerticalRL() &&
    2121           0 :       aReflowInput.ComputedWidth() == NS_UNCONSTRAINEDSIZE;
    2122             : 
    2123             :     // reevaluate special bsize reflow conditions
    2124           0 :     if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
    2125           0 :       needToInitiateSpecialReflow = true;
    2126             :     }
    2127             : 
    2128             :     // XXXldb Are all these conditions correct?
    2129           0 :     if (needToInitiateSpecialReflow && aStatus.IsComplete()) {
    2130             :       // XXXldb Do we need to set the IsBResize flag on any reflow states?
    2131             : 
    2132             :       ReflowInput &mutable_rs =
    2133           0 :         const_cast<ReflowInput&>(aReflowInput);
    2134             : 
    2135             :       // distribute extra block-direction space to rows
    2136           0 :       CalcDesiredBSize(aReflowInput, aDesiredSize);
    2137           0 :       mutable_rs.mFlags.mSpecialBSizeReflow = true;
    2138             : 
    2139           0 :       ReflowTable(aDesiredSize, aReflowInput, aReflowInput.AvailableBSize(),
    2140           0 :                   lastChildReflowed, aStatus);
    2141             : 
    2142           0 :       if (lastChildReflowed && aStatus.IsIncomplete()) {
    2143             :         // if there is an incomplete child, then set the desired bsize
    2144             :         // to include it but not the next one
    2145           0 :         LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
    2146           0 :         aDesiredSize.BSize(wm) =
    2147           0 :           borderPadding.BEnd(wm) + GetRowSpacing(GetRowCount()) +
    2148           0 :           lastChildReflowed->GetNormalRect().YMost(); // XXX YMost should be B-flavored
    2149             :       }
    2150           0 :       haveDesiredBSize = true;
    2151             : 
    2152           0 :       mutable_rs.mFlags.mSpecialBSizeReflow = false;
    2153             :     }
    2154             :   }
    2155             : 
    2156           0 :   aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
    2157           0 :     aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
    2158           0 :   if (!haveDesiredBSize) {
    2159           0 :     CalcDesiredBSize(aReflowInput, aDesiredSize);
    2160             :   }
    2161           0 :   if (IsRowInserted()) {
    2162           0 :     ProcessRowInserted(aDesiredSize.BSize(wm));
    2163             :   }
    2164             : 
    2165           0 :   if (fixupKidPositions) {
    2166             :     // If we didn't already know the containerSize (and so used zero during
    2167             :     // ReflowChildren), then we need to update the block-position of our kids.
    2168           0 :     for (nsIFrame* kid : mFrames) {
    2169           0 :       kid->MovePositionBy(nsPoint(aDesiredSize.Width(), 0));
    2170           0 :       RePositionViews(kid);
    2171             :     }
    2172             :   }
    2173             : 
    2174             :   // Calculate the overflow area contribution from our children. We couldn't
    2175             :   // do this on the fly during ReflowChildren(), because in vertical-rl mode
    2176             :   // with unconstrained width, we weren't placing them in their final positions
    2177             :   // until the fixupKidPositions loop just above.
    2178           0 :   for (nsIFrame* kid : mFrames) {
    2179           0 :     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
    2180             :   }
    2181             : 
    2182           0 :   LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
    2183           0 :   SetColumnDimensions(aDesiredSize.BSize(wm), wm, borderPadding,
    2184           0 :                       aDesiredSize.PhysicalSize());
    2185           0 :   if (NeedToCollapse() &&
    2186           0 :       (NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableISize())) {
    2187           0 :     AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding);
    2188             :   }
    2189             : 
    2190             :   // If there are any relatively-positioned table parts, we need to reflow their
    2191             :   // absolutely-positioned descendants now that their dimensions are final.
    2192           0 :   FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowInput);
    2193             : 
    2194             :   // make sure the table overflow area does include the table rect.
    2195           0 :   nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
    2196             : 
    2197           0 :   if (!ShouldApplyOverflowClipping(this, aReflowInput.mStyleDisplay)) {
    2198             :     // collapsed border may leak out
    2199           0 :     LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
    2200           0 :     tableRect.Inflate(bcMargin.GetPhysicalMargin(wm));
    2201             :   }
    2202           0 :   aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
    2203             : 
    2204           0 :   if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
    2205           0 :       nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
    2206           0 :       nsIFrame::InvalidateFrame();
    2207             :   }
    2208             : 
    2209           0 :   FinishAndStoreOverflow(&aDesiredSize);
    2210           0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
    2211             : }
    2212             : 
    2213             : void
    2214           0 : nsTableFrame::FixupPositionedTableParts(nsPresContext*           aPresContext,
    2215             :                                         ReflowOutput&     aDesiredSize,
    2216             :                                         const ReflowInput& aReflowInput)
    2217             : {
    2218           0 :   FrameTArray* positionedParts = GetProperty(PositionedTablePartArray());
    2219           0 :   if (!positionedParts) {
    2220           0 :     return;
    2221             :   }
    2222             : 
    2223           0 :   OverflowChangedTracker overflowTracker;
    2224           0 :   overflowTracker.SetSubtreeRoot(this);
    2225             : 
    2226           0 :   for (size_t i = 0; i < positionedParts->Length(); ++i) {
    2227           0 :     nsIFrame* positionedPart = positionedParts->ElementAt(i);
    2228             : 
    2229             :     // As we've already finished reflow, positionedParts's size and overflow
    2230             :     // areas have already been assigned, so we just pull them back out.
    2231           0 :     nsSize size(positionedPart->GetSize());
    2232           0 :     ReflowOutput desiredSize(aReflowInput.GetWritingMode());
    2233           0 :     desiredSize.Width() = size.width;
    2234           0 :     desiredSize.Height() = size.height;
    2235           0 :     desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
    2236             : 
    2237             :     // Construct a dummy reflow state and reflow status.
    2238             :     // XXX(seth): Note that the dummy reflow state doesn't have a correct
    2239             :     // chain of parent reflow states. It also doesn't necessarily have a
    2240             :     // correct containing block.
    2241           0 :     WritingMode wm = positionedPart->GetWritingMode();
    2242           0 :     LogicalSize availSize(wm, size);
    2243           0 :     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    2244             :     ReflowInput reflowInput(aPresContext, positionedPart,
    2245           0 :                                   aReflowInput.mRenderingContext, availSize,
    2246           0 :                                   ReflowInput::DUMMY_PARENT_REFLOW_STATE);
    2247           0 :     nsReflowStatus reflowStatus;
    2248             : 
    2249             :     // Reflow absolutely-positioned descendants of the positioned part.
    2250             :     // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the bsize and
    2251             :     // ignoring any change to the reflow status aren't correct. We'll never
    2252             :     // paginate absolutely positioned frames.
    2253           0 :     nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
    2254           0 :     positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
    2255             :                                                     desiredSize,
    2256             :                                                     reflowInput,
    2257             :                                                     reflowStatus,
    2258           0 :                                                     true);
    2259             : 
    2260             :     // FinishReflowWithAbsoluteFrames has updated overflow on
    2261             :     // |positionedPart|.  We need to make sure that update propagates
    2262             :     // through the intermediate frames between it and this frame.
    2263           0 :     nsIFrame* positionedFrameParent = positionedPart->GetParent();
    2264           0 :     if (positionedFrameParent != this) {
    2265             :       overflowTracker.AddFrame(positionedFrameParent,
    2266           0 :         OverflowChangedTracker::CHILDREN_CHANGED);
    2267             :     }
    2268             :   }
    2269             : 
    2270             :   // Propagate updated overflow areas up the tree.
    2271           0 :   overflowTracker.Flush();
    2272             : 
    2273             :   // Update our own overflow areas. (OverflowChangedTracker doesn't update the
    2274             :   // subtree root itself.)
    2275           0 :   aDesiredSize.SetOverflowAreasToDesiredBounds();
    2276           0 :   nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
    2277             : }
    2278             : 
    2279             : bool
    2280           0 : nsTableFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
    2281             : {
    2282             :   // As above in Reflow, make sure the table overflow area includes the table
    2283             :   // rect, and check for collapsed borders leaking out.
    2284           0 :   if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
    2285           0 :     nsRect bounds(nsPoint(0, 0), GetSize());
    2286           0 :     WritingMode wm = GetWritingMode();
    2287           0 :     LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
    2288           0 :     bounds.Inflate(bcMargin.GetPhysicalMargin(wm));
    2289             : 
    2290           0 :     aOverflowAreas.UnionAllWith(bounds);
    2291             :   }
    2292           0 :   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
    2293             : }
    2294             : 
    2295             : void
    2296           0 : nsTableFrame::ReflowTable(ReflowOutput&     aDesiredSize,
    2297             :                           const ReflowInput& aReflowInput,
    2298             :                           nscoord                  aAvailBSize,
    2299             :                           nsIFrame*&               aLastChildReflowed,
    2300             :                           nsReflowStatus&          aStatus)
    2301             : {
    2302           0 :   aLastChildReflowed = nullptr;
    2303             : 
    2304           0 :   if (!GetPrevInFlow()) {
    2305           0 :     mTableLayoutStrategy->ComputeColumnISizes(aReflowInput);
    2306             :   }
    2307             :   // Constrain our reflow isize to the computed table isize (of the 1st in flow).
    2308             :   // and our reflow bsize to our avail bsize minus border, padding, cellspacing
    2309           0 :   WritingMode wm = aReflowInput.GetWritingMode();
    2310           0 :   aDesiredSize.ISize(wm) = aReflowInput.ComputedISize() +
    2311           0 :                      aReflowInput.ComputedLogicalBorderPadding().IStartEnd(wm);
    2312             :   TableReflowInput reflowInput(aReflowInput,
    2313           0 :                                  LogicalSize(wm, aDesiredSize.ISize(wm),
    2314           0 :                                              aAvailBSize));
    2315           0 :   ReflowChildren(reflowInput, aStatus, aLastChildReflowed,
    2316           0 :                  aDesiredSize.mOverflowAreas);
    2317             : 
    2318           0 :   ReflowColGroups(aReflowInput.mRenderingContext);
    2319           0 : }
    2320             : 
    2321             : nsIFrame*
    2322           0 : nsTableFrame::GetFirstBodyRowGroupFrame()
    2323             : {
    2324           0 :   nsIFrame* headerFrame = nullptr;
    2325           0 :   nsIFrame* footerFrame = nullptr;
    2326             : 
    2327           0 :   for (nsIFrame* kidFrame : mFrames) {
    2328           0 :     const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay();
    2329             : 
    2330             :     // We expect the header and footer row group frames to be first, and we only
    2331             :     // allow one header and one footer
    2332           0 :     if (mozilla::StyleDisplay::TableHeaderGroup == childDisplay->mDisplay) {
    2333           0 :       if (headerFrame) {
    2334             :         // We already have a header frame and so this header frame is treated
    2335             :         // like an ordinary body row group frame
    2336           0 :         return kidFrame;
    2337             :       }
    2338           0 :       headerFrame = kidFrame;
    2339             : 
    2340           0 :     } else if (mozilla::StyleDisplay::TableFooterGroup == childDisplay->mDisplay) {
    2341           0 :       if (footerFrame) {
    2342             :         // We already have a footer frame and so this footer frame is treated
    2343             :         // like an ordinary body row group frame
    2344           0 :         return kidFrame;
    2345             :       }
    2346           0 :       footerFrame = kidFrame;
    2347             : 
    2348           0 :     } else if (mozilla::StyleDisplay::TableRowGroup == childDisplay->mDisplay) {
    2349           0 :       return kidFrame;
    2350             :     }
    2351             :   }
    2352             : 
    2353           0 :   return nullptr;
    2354             : }
    2355             : 
    2356             : // Table specific version that takes into account repeated header and footer
    2357             : // frames when continuing table frames
    2358             : void
    2359           0 : nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
    2360             :                            int32_t aPushFrom)
    2361             : {
    2362           0 :   NS_PRECONDITION(aPushFrom > 0, "pushing first child");
    2363             : 
    2364             :   // extract the frames from the array into a sibling list
    2365           0 :   nsFrameList frames;
    2366             :   uint32_t childX;
    2367           0 :   for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
    2368           0 :     nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
    2369           0 :     if (!rgFrame->IsRepeatable()) {
    2370           0 :       mFrames.RemoveFrame(rgFrame);
    2371           0 :       frames.AppendFrame(nullptr, rgFrame);
    2372             :     }
    2373             :   }
    2374             : 
    2375           0 :   if (frames.IsEmpty()) {
    2376           0 :     return;
    2377             :   }
    2378             : 
    2379           0 :   nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
    2380           0 :   if (nextInFlow) {
    2381             :     // Insert the frames after any repeated header and footer frames.
    2382           0 :     nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
    2383           0 :     nsIFrame* prevSibling = nullptr;
    2384           0 :     if (firstBodyFrame) {
    2385           0 :       prevSibling = firstBodyFrame->GetPrevSibling();
    2386             :     }
    2387             :     // When pushing and pulling frames we need to check for whether any
    2388             :     // views need to be reparented.
    2389           0 :     ReparentFrameViewList(frames, this, nextInFlow);
    2390             :     nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
    2391           0 :                                      frames);
    2392             :   }
    2393             :   else {
    2394             :     // Add the frames to our overflow list.
    2395           0 :     SetOverflowFrames(frames);
    2396             :   }
    2397             : }
    2398             : 
    2399             : // collapsing row groups, rows, col groups and cols are accounted for after both passes of
    2400             : // reflow so that it has no effect on the calculations of reflow.
    2401             : void
    2402           0 : nsTableFrame::AdjustForCollapsingRowsCols(ReflowOutput& aDesiredSize,
    2403             :                                           const WritingMode aWM,
    2404             :                                           const LogicalMargin& aBorderPadding)
    2405             : {
    2406           0 :   nscoord bTotalOffset = 0; // total offset among all rows in all row groups
    2407             : 
    2408             :   // reset the bit, it will be set again if row/rowgroup or col/colgroup are
    2409             :   // collapsed
    2410           0 :   SetNeedToCollapse(false);
    2411             : 
    2412             :   // collapse the rows and/or row groups as necessary
    2413             :   // Get the ordered children
    2414           0 :   RowGroupArray rowGroups;
    2415           0 :   OrderRowGroups(rowGroups);
    2416             : 
    2417           0 :   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
    2418           0 :   nscoord iSize = firstInFlow->GetCollapsedISize(aWM, aBorderPadding);
    2419           0 :   nscoord rgISize = iSize - GetColSpacing(-1) -
    2420           0 :                     GetColSpacing(GetColCount());
    2421           0 :   nsOverflowAreas overflow;
    2422             :   // Walk the list of children
    2423           0 :   for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
    2424           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[childX];
    2425           0 :     NS_ASSERTION(rgFrame, "Must have row group frame here");
    2426           0 :     bTotalOffset += rgFrame->CollapseRowGroupIfNecessary(bTotalOffset, rgISize,
    2427             :                                                          aWM);
    2428           0 :     ConsiderChildOverflow(overflow, rgFrame);
    2429             :   }
    2430             : 
    2431           0 :   aDesiredSize.BSize(aWM) -= bTotalOffset;
    2432           0 :   aDesiredSize.ISize(aWM) = iSize;
    2433           0 :   overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
    2434           0 :   FinishAndStoreOverflow(overflow,
    2435           0 :                          nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
    2436           0 : }
    2437             : 
    2438             : 
    2439             : nscoord
    2440           0 : nsTableFrame::GetCollapsedISize(const WritingMode aWM,
    2441             :                                 const LogicalMargin& aBorderPadding)
    2442             : {
    2443           0 :   NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedISize called on next in flow");
    2444           0 :   nscoord iSize = GetColSpacing(GetColCount());
    2445           0 :   iSize += aBorderPadding.IStartEnd(aWM);
    2446           0 :   nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
    2447           0 :   for (nsIFrame* groupFrame : mColGroups) {
    2448           0 :     const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
    2449           0 :     bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
    2450           0 :     nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
    2451           0 :     for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
    2452             :          colFrame = colFrame->GetNextCol()) {
    2453           0 :       const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
    2454           0 :       nscoord colIdx = colFrame->GetColIndex();
    2455           0 :       if (mozilla::StyleDisplay::TableColumn == colDisplay->mDisplay) {
    2456           0 :         const nsStyleVisibility* colVis = colFrame->StyleVisibility();
    2457           0 :         bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
    2458           0 :         nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
    2459           0 :         if (!collapseGroup && !collapseCol) {
    2460           0 :           iSize += colISize;
    2461           0 :           if (ColumnHasCellSpacingBefore(colIdx)) {
    2462           0 :             iSize += GetColSpacing(colIdx - 1);
    2463             :           }
    2464             :         }
    2465             :         else {
    2466           0 :           SetNeedToCollapse(true);
    2467             :         }
    2468             :       }
    2469             :     }
    2470             :   }
    2471           0 :   return iSize;
    2472             : }
    2473             : 
    2474             : /* virtual */ void
    2475           0 : nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
    2476             : {
    2477           0 :   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
    2478             : 
    2479           0 :   if (!aOldStyleContext) //avoid this on init
    2480           0 :     return;
    2481             : 
    2482           0 :   if (IsBorderCollapse() &&
    2483           0 :       BCRecalcNeeded(aOldStyleContext, StyleContext())) {
    2484           0 :     SetFullBCDamageArea();
    2485             :   }
    2486             : 
    2487             :   //avoid this on init or nextinflow
    2488           0 :   if (!mTableLayoutStrategy || GetPrevInFlow())
    2489           0 :     return;
    2490             : 
    2491           0 :   bool isAuto = IsAutoLayout();
    2492           0 :   if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
    2493             :     nsITableLayoutStrategy* temp;
    2494           0 :     if (isAuto)
    2495           0 :       temp = new BasicTableLayoutStrategy(this);
    2496             :     else
    2497           0 :       temp = new FixedTableLayoutStrategy(this);
    2498             : 
    2499           0 :     if (temp) {
    2500           0 :       delete mTableLayoutStrategy;
    2501           0 :       mTableLayoutStrategy = temp;
    2502             :     }
    2503             :   }
    2504             : }
    2505             : 
    2506             : 
    2507             : 
    2508             : void
    2509           0 : nsTableFrame::AppendFrames(ChildListID     aListID,
    2510             :                            nsFrameList&    aFrameList)
    2511             : {
    2512           0 :   NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
    2513             :                "unexpected child list");
    2514             : 
    2515             :   // Because we actually have two child lists, one for col group frames and one
    2516             :   // for everything else, we need to look at each frame individually
    2517             :   // XXX The frame construction code should be separating out child frames
    2518             :   // based on the type, bug 343048.
    2519           0 :   while (!aFrameList.IsEmpty()) {
    2520           0 :     nsIFrame* f = aFrameList.FirstChild();
    2521           0 :     aFrameList.RemoveFrame(f);
    2522             : 
    2523             :     // See what kind of frame we have
    2524           0 :     const nsStyleDisplay* display = f->StyleDisplay();
    2525             : 
    2526           0 :     if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
    2527           0 :       if (MOZ_UNLIKELY(GetPrevInFlow())) {
    2528           0 :         nsFrameList colgroupFrame(f, f);
    2529           0 :         auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
    2530           0 :         firstInFlow->AppendFrames(aListID, colgroupFrame);
    2531           0 :         continue;
    2532             :       }
    2533             :       nsTableColGroupFrame* lastColGroup =
    2534           0 :         nsTableColGroupFrame::GetLastRealColGroup(this);
    2535             :       int32_t startColIndex = (lastColGroup)
    2536           0 :         ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
    2537           0 :       mColGroups.InsertFrame(this, lastColGroup, f);
    2538             :       // Insert the colgroup and its cols into the table
    2539             :       InsertColGroups(startColIndex,
    2540           0 :                       nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
    2541           0 :     } else if (IsRowGroup(display->mDisplay)) {
    2542           0 :       DrainSelfOverflowList(); // ensure the last frame is in mFrames
    2543             :       // Append the new row group frame to the sibling chain
    2544           0 :       mFrames.AppendFrame(nullptr, f);
    2545             : 
    2546             :       // insert the row group and its rows into the table
    2547           0 :       InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr));
    2548             :     } else {
    2549             :       // Nothing special to do, just add the frame to our child list
    2550           0 :       NS_NOTREACHED("How did we get here?  Frame construction screwed up");
    2551           0 :       mFrames.AppendFrame(nullptr, f);
    2552             :     }
    2553             :   }
    2554             : 
    2555             : #ifdef DEBUG_TABLE_CELLMAP
    2556             :   printf("=== TableFrame::AppendFrames\n");
    2557             :   Dump(true, true, true);
    2558             : #endif
    2559           0 :   PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    2560           0 :                                                NS_FRAME_HAS_DIRTY_CHILDREN);
    2561           0 :   SetGeometryDirty();
    2562           0 : }
    2563             : 
    2564             : // Needs to be at file scope or ArrayLength fails to compile.
    2565           0 : struct ChildListInsertions {
    2566             :   nsIFrame::ChildListID mID;
    2567             :   nsFrameList mList;
    2568             : };
    2569             : 
    2570             : void
    2571           0 : nsTableFrame::InsertFrames(ChildListID     aListID,
    2572             :                            nsIFrame*       aPrevFrame,
    2573             :                            nsFrameList&    aFrameList)
    2574             : {
    2575             :   // The frames in aFrameList can be a mix of row group frames and col group
    2576             :   // frames. The problem is that they should go in separate child lists so
    2577             :   // we need to deal with that here...
    2578             :   // XXX The frame construction code should be separating out child frames
    2579             :   // based on the type, bug 343048.
    2580             : 
    2581           0 :   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
    2582             :                "inserting after sibling frame with different parent");
    2583             : 
    2584           0 :   if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
    2585           0 :       (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
    2586             :     // Treat this like an append; still a workaround for bug 343048.
    2587           0 :     AppendFrames(aListID, aFrameList);
    2588           0 :     return;
    2589             :   }
    2590             : 
    2591             :   // Collect ColGroupFrames into a separate list and insert those separately
    2592             :   // from the other frames (bug 759249).
    2593           0 :   ChildListInsertions insertions[2]; // ColGroup, other
    2594           0 :   const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
    2595           0 :   nsFrameList::FrameLinkEnumerator e(aFrameList);
    2596           0 :   for (; !aFrameList.IsEmpty(); e.Next()) {
    2597           0 :     nsIFrame* next = e.NextFrame();
    2598           0 :     if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) {
    2599           0 :       nsFrameList head = aFrameList.ExtractHead(e);
    2600           0 :       if (display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) {
    2601           0 :         insertions[0].mID = kColGroupList;
    2602           0 :         insertions[0].mList.AppendFrames(nullptr, head);
    2603             :       } else {
    2604           0 :         insertions[1].mID = kPrincipalList;
    2605           0 :         insertions[1].mList.AppendFrames(nullptr, head);
    2606             :       }
    2607           0 :       if (!next) {
    2608           0 :         break;
    2609             :       }
    2610           0 :       display = next->StyleDisplay();
    2611             :     }
    2612             :   }
    2613           0 :   for (uint32_t i = 0; i < ArrayLength(insertions); ++i) {
    2614             :     // We pass aPrevFrame for both ColGroup and other frames since
    2615             :     // HomogenousInsertFrames will only use it if it's a suitable
    2616             :     // prev-sibling for the frames in the frame list.
    2617           0 :     if (!insertions[i].mList.IsEmpty()) {
    2618           0 :       HomogenousInsertFrames(insertions[i].mID, aPrevFrame,
    2619           0 :                              insertions[i].mList);
    2620             :     }
    2621             :   }
    2622             : }
    2623             : 
    2624             : void
    2625           0 : nsTableFrame::HomogenousInsertFrames(ChildListID     aListID,
    2626             :                                      nsIFrame*       aPrevFrame,
    2627             :                                      nsFrameList&    aFrameList)
    2628             : {
    2629             :   // See what kind of frame we have
    2630           0 :   const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
    2631           0 :   bool isColGroup = mozilla::StyleDisplay::TableColumnGroup == display->mDisplay;
    2632             : #ifdef DEBUG
    2633             :   // Verify that either all siblings have display:table-column-group, or they
    2634             :   // all have display values different from table-column-group.
    2635           0 :   for (nsIFrame* frame : aFrameList) {
    2636           0 :     auto nextDisplay = frame->StyleDisplay()->mDisplay;
    2637           0 :     MOZ_ASSERT(isColGroup ==
    2638             :                (nextDisplay == mozilla::StyleDisplay::TableColumnGroup),
    2639             :                "heterogenous childlist");
    2640             :   }
    2641             : #endif
    2642           0 :   if (MOZ_UNLIKELY(isColGroup && GetPrevInFlow())) {
    2643           0 :     auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
    2644           0 :     firstInFlow->AppendFrames(aListID, aFrameList);
    2645           0 :     return;
    2646             :   }
    2647           0 :   if (aPrevFrame) {
    2648           0 :     const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
    2649             :     // Make sure they belong on the same frame list
    2650           0 :     if ((display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) !=
    2651           0 :         (prevDisplay->mDisplay == mozilla::StyleDisplay::TableColumnGroup)) {
    2652             :       // the previous frame is not valid, see comment at ::AppendFrames
    2653             :       // XXXbz Using content indices here means XBL will get screwed
    2654             :       // over...  Oh, well.
    2655           0 :       nsIFrame* pseudoFrame = aFrameList.FirstChild();
    2656           0 :       nsIContent* parentContent = GetContent();
    2657           0 :       nsIContent* content = nullptr;
    2658           0 :       aPrevFrame = nullptr;
    2659           0 :       while (pseudoFrame  && (parentContent ==
    2660             :                               (content = pseudoFrame->GetContent()))) {
    2661           0 :         pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
    2662             :       }
    2663           0 :       nsCOMPtr<nsIContent> container = content->GetParent();
    2664           0 :       if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823.
    2665           0 :         int32_t newIndex = container->IndexOf(content);
    2666             :         nsIFrame* kidFrame;
    2667           0 :         nsTableColGroupFrame* lastColGroup = nullptr;
    2668           0 :         if (isColGroup) {
    2669           0 :           kidFrame = mColGroups.FirstChild();
    2670           0 :           lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
    2671             :         }
    2672             :         else {
    2673           0 :           kidFrame = mFrames.FirstChild();
    2674             :         }
    2675             :         // Important: need to start at a value smaller than all valid indices
    2676           0 :         int32_t lastIndex = -1;
    2677           0 :         while (kidFrame) {
    2678           0 :           if (isColGroup) {
    2679           0 :             if (kidFrame == lastColGroup) {
    2680           0 :               aPrevFrame = kidFrame; // there is no real colgroup after this one
    2681           0 :               break;
    2682             :             }
    2683             :           }
    2684           0 :           pseudoFrame = kidFrame;
    2685           0 :           while (pseudoFrame  && (parentContent ==
    2686             :                                   (content = pseudoFrame->GetContent()))) {
    2687           0 :             pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
    2688             :           }
    2689           0 :           int32_t index = container->IndexOf(content);
    2690           0 :           if (index > lastIndex && index < newIndex) {
    2691           0 :             lastIndex = index;
    2692           0 :             aPrevFrame = kidFrame;
    2693             :           }
    2694           0 :           kidFrame = kidFrame->GetNextSibling();
    2695             :         }
    2696             :       }
    2697             :     }
    2698             :   }
    2699           0 :   if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
    2700           0 :     NS_ASSERTION(aListID == kColGroupList, "unexpected child list");
    2701             :     // Insert the column group frames
    2702             :     const nsFrameList::Slice& newColgroups =
    2703           0 :       mColGroups.InsertFrames(this, aPrevFrame, aFrameList);
    2704             :     // find the starting col index for the first new col group
    2705           0 :     int32_t startColIndex = 0;
    2706           0 :     if (aPrevFrame) {
    2707             :       nsTableColGroupFrame* prevColGroup =
    2708           0 :         (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
    2709           0 :                                                   LayoutFrameType::TableColGroup);
    2710           0 :       if (prevColGroup) {
    2711           0 :         startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
    2712             :       }
    2713             :     }
    2714           0 :     InsertColGroups(startColIndex, newColgroups);
    2715           0 :   } else if (IsRowGroup(display->mDisplay)) {
    2716           0 :     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
    2717           0 :     DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
    2718             :     // Insert the frames in the sibling chain
    2719             :     const nsFrameList::Slice& newRowGroups =
    2720           0 :       mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
    2721             : 
    2722           0 :     InsertRowGroups(newRowGroups);
    2723             :   } else {
    2724           0 :     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
    2725           0 :     NS_NOTREACHED("How did we even get here?");
    2726             :     // Just insert the frame and don't worry about reflowing it
    2727           0 :     mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
    2728           0 :     return;
    2729             :   }
    2730             : 
    2731           0 :   PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    2732           0 :                                                NS_FRAME_HAS_DIRTY_CHILDREN);
    2733           0 :   SetGeometryDirty();
    2734             : #ifdef DEBUG_TABLE_CELLMAP
    2735             :   printf("=== TableFrame::InsertFrames\n");
    2736             :   Dump(true, true, true);
    2737             : #endif
    2738           0 :   return;
    2739             : }
    2740             : 
    2741             : void
    2742           0 : nsTableFrame::DoRemoveFrame(ChildListID     aListID,
    2743             :                             nsIFrame*       aOldFrame)
    2744             : {
    2745           0 :   if (aListID == kColGroupList) {
    2746           0 :     nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
    2747           0 :     nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
    2748           0 :     int32_t firstColIndex = colGroup->GetStartColumnIndex();
    2749           0 :     int32_t lastColIndex  = firstColIndex + colGroup->GetColCount() - 1;
    2750           0 :     mColGroups.DestroyFrame(aOldFrame);
    2751           0 :     nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
    2752             :     // remove the cols from the table
    2753             :     int32_t colIdx;
    2754           0 :     for (colIdx = lastColIndex; colIdx >= firstColIndex; colIdx--) {
    2755           0 :       nsTableColFrame* colFrame = mColFrames.SafeElementAt(colIdx);
    2756           0 :       if (colFrame) {
    2757           0 :         RemoveCol(colGroup, colIdx, true, false);
    2758             :       }
    2759             :     }
    2760             : 
    2761             :     // If we have some anonymous cols at the end already, we just
    2762             :     // add more of them.
    2763           0 :     if (!mColFrames.IsEmpty() &&
    2764           0 :         mColFrames.LastElement() && // XXXbz is this ever null?
    2765           0 :         mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
    2766           0 :       int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
    2767           0 :       if (numAnonymousColsToAdd > 0) {
    2768             :         // this sets the child list, updates the col cache and cell map
    2769           0 :         AppendAnonymousColFrames(numAnonymousColsToAdd);
    2770             :       }
    2771             :     } else {
    2772             :       // All of our colframes correspond to actual <col> tags.  It's possible
    2773             :       // that we still have at least as many <col> tags as we have logical
    2774             :       // columns from cells, but we might have one less.  Handle the latter case
    2775             :       // as follows: First ask the cellmap to drop its last col if it doesn't
    2776             :       // have any actual cells in it.  Then call MatchCellMapToColCache to
    2777             :       // append an anonymous column if it's needed; this needs to be after
    2778             :       // RemoveColsAtEnd, since it will determine the need for a new column
    2779             :       // frame based on the width of the cell map.
    2780           0 :       nsTableCellMap* cellMap = GetCellMap();
    2781           0 :       if (cellMap) { // XXXbz is this ever null?
    2782           0 :         cellMap->RemoveColsAtEnd();
    2783           0 :         MatchCellMapToColCache(cellMap);
    2784             :       }
    2785             :     }
    2786             : 
    2787             :   } else {
    2788           0 :     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
    2789             :     nsTableRowGroupFrame* rgFrame =
    2790           0 :       static_cast<nsTableRowGroupFrame*>(aOldFrame);
    2791             :     // remove the row group from the cell map
    2792           0 :     nsTableCellMap* cellMap = GetCellMap();
    2793           0 :     if (cellMap) {
    2794           0 :       cellMap->RemoveGroupCellMap(rgFrame);
    2795             :     }
    2796             : 
    2797             :     // remove the row group frame from the sibling chain
    2798           0 :     mFrames.DestroyFrame(aOldFrame);
    2799             : 
    2800             :     // the removal of a row group changes the cellmap, the columns might change
    2801           0 :     if (cellMap) {
    2802           0 :       cellMap->Synchronize(this);
    2803             :       // Create an empty slice
    2804           0 :       ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
    2805           0 :       TableArea damageArea;
    2806           0 :       cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea);
    2807             : 
    2808           0 :       static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap);
    2809             :     }
    2810             :   }
    2811           0 : }
    2812             : 
    2813             : void
    2814           0 : nsTableFrame::RemoveFrame(ChildListID     aListID,
    2815             :                           nsIFrame*       aOldFrame)
    2816             : {
    2817           0 :   NS_ASSERTION(aListID == kColGroupList ||
    2818             :                mozilla::StyleDisplay::TableColumnGroup !=
    2819             :                  aOldFrame->StyleDisplay()->mDisplay,
    2820             :                "Wrong list name; use kColGroupList iff colgroup");
    2821           0 :   nsIPresShell* shell = PresContext()->PresShell();
    2822           0 :   nsTableFrame* lastParent = nullptr;
    2823           0 :   while (aOldFrame) {
    2824           0 :     nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
    2825           0 :     nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
    2826           0 :     if (parent != lastParent) {
    2827           0 :       parent->DrainSelfOverflowList();
    2828             :     }
    2829           0 :     parent->DoRemoveFrame(aListID, aOldFrame);
    2830           0 :     aOldFrame = oldFrameNextContinuation;
    2831           0 :     if (parent != lastParent) {
    2832             :       // for now, just bail and recalc all of the collapsing borders
    2833             :       // as the cellmap changes we need to recalc
    2834           0 :       if (parent->IsBorderCollapse()) {
    2835           0 :         parent->SetFullBCDamageArea();
    2836             :       }
    2837           0 :       parent->SetGeometryDirty();
    2838             :       shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
    2839           0 :                               NS_FRAME_HAS_DIRTY_CHILDREN);
    2840           0 :       lastParent = parent;
    2841             :     }
    2842             :   }
    2843             : #ifdef DEBUG_TABLE_CELLMAP
    2844             :   printf("=== TableFrame::RemoveFrame\n");
    2845             :   Dump(true, true, true);
    2846             : #endif
    2847           0 : }
    2848             : 
    2849             : /* virtual */ nsMargin
    2850           0 : nsTableFrame::GetUsedBorder() const
    2851             : {
    2852           0 :   if (!IsBorderCollapse())
    2853           0 :     return nsContainerFrame::GetUsedBorder();
    2854             : 
    2855           0 :   WritingMode wm = GetWritingMode();
    2856           0 :   return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm);
    2857             : }
    2858             : 
    2859             : /* virtual */ nsMargin
    2860           0 : nsTableFrame::GetUsedPadding() const
    2861             : {
    2862           0 :   if (!IsBorderCollapse())
    2863           0 :     return nsContainerFrame::GetUsedPadding();
    2864             : 
    2865           0 :   return nsMargin(0,0,0,0);
    2866             : }
    2867             : 
    2868             : /* virtual */ nsMargin
    2869           0 : nsTableFrame::GetUsedMargin() const
    2870             : {
    2871             :   // The margin is inherited to the table wrapper frame via
    2872             :   // the ::-moz-table-wrapper rule in ua.css.
    2873           0 :   return nsMargin(0, 0, 0, 0);
    2874             : }
    2875             : 
    2876           0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCProperty, BCPropertyData)
    2877             : 
    2878             : BCPropertyData*
    2879           0 : nsTableFrame::GetBCProperty() const
    2880             : {
    2881           0 :   return GetProperty(TableBCProperty());
    2882             : }
    2883             : 
    2884             : BCPropertyData*
    2885           0 : nsTableFrame::GetOrCreateBCProperty()
    2886             : {
    2887           0 :   BCPropertyData* value = GetProperty(TableBCProperty());
    2888           0 :   if (!value) {
    2889           0 :     value = new BCPropertyData();
    2890           0 :     SetProperty(TableBCProperty(), value);
    2891             :   }
    2892             : 
    2893           0 :   return value;
    2894             : }
    2895             : 
    2896             : static void
    2897           0 : DivideBCBorderSize(BCPixelSize  aPixelSize,
    2898             :                    BCPixelSize& aSmallHalf,
    2899             :                    BCPixelSize& aLargeHalf)
    2900             : {
    2901           0 :   aSmallHalf = aPixelSize / 2;
    2902           0 :   aLargeHalf = aPixelSize - aSmallHalf;
    2903           0 : }
    2904             : 
    2905             : LogicalMargin
    2906           0 : nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const
    2907             : {
    2908           0 :   if (NeedToCalcBCBorders()) {
    2909           0 :     const_cast<nsTableFrame*>(this)->CalcBCBorders();
    2910             :   }
    2911             : 
    2912           0 :   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
    2913           0 :   BCPropertyData* propData = GetBCProperty();
    2914           0 :   if (propData) {
    2915             :     return LogicalMargin(aWM,
    2916           0 :                BC_BORDER_START_HALF_COORD(p2t, propData->mBStartBorderWidth),
    2917           0 :                BC_BORDER_END_HALF_COORD(p2t, propData->mIEndBorderWidth),
    2918           0 :                BC_BORDER_END_HALF_COORD(p2t, propData->mBEndBorderWidth),
    2919           0 :                BC_BORDER_START_HALF_COORD(p2t, propData->mIStartBorderWidth));
    2920             :   }
    2921           0 :   return LogicalMargin(aWM);
    2922             : }
    2923             : 
    2924             : LogicalMargin
    2925           0 : nsTableFrame::GetIncludedOuterBCBorder(const WritingMode aWM) const
    2926             : {
    2927           0 :   if (NeedToCalcBCBorders()) {
    2928           0 :     const_cast<nsTableFrame*>(this)->CalcBCBorders();
    2929             :   }
    2930             : 
    2931           0 :   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
    2932           0 :   BCPropertyData* propData = GetBCProperty();
    2933           0 :   if (propData) {
    2934             :     return LogicalMargin(aWM,
    2935           0 :                BC_BORDER_START_HALF_COORD(p2t, propData->mBStartBorderWidth),
    2936           0 :                BC_BORDER_END_HALF_COORD(p2t, propData->mIEndCellBorderWidth),
    2937           0 :                BC_BORDER_END_HALF_COORD(p2t, propData->mBEndBorderWidth),
    2938           0 :                BC_BORDER_START_HALF_COORD(p2t, propData->mIStartCellBorderWidth));
    2939             :   }
    2940           0 :   return LogicalMargin(aWM);
    2941             : }
    2942             : 
    2943             : LogicalMargin
    2944           0 : nsTableFrame::GetExcludedOuterBCBorder(const WritingMode aWM) const
    2945             : {
    2946           0 :   return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM);
    2947             : }
    2948             : 
    2949             : static LogicalMargin
    2950           0 : GetSeparateModelBorderPadding(const WritingMode aWM,
    2951             :                               const ReflowInput* aReflowInput,
    2952             :                               nsStyleContext* aStyleContext)
    2953             : {
    2954             :   // XXXbz Either we _do_ have a reflow state and then we can use its
    2955             :   // mComputedBorderPadding or we don't and then we get the padding
    2956             :   // wrong!
    2957           0 :   const nsStyleBorder* border = aStyleContext->StyleBorder();
    2958           0 :   LogicalMargin borderPadding(aWM, border->GetComputedBorder());
    2959           0 :   if (aReflowInput) {
    2960           0 :     borderPadding += aReflowInput->ComputedLogicalPadding();
    2961             :   }
    2962           0 :   return borderPadding;
    2963             : }
    2964             : 
    2965             : LogicalMargin
    2966           0 : nsTableFrame::GetChildAreaOffset(const WritingMode aWM,
    2967             :                                  const ReflowInput* aReflowInput) const
    2968             : {
    2969           0 :   return IsBorderCollapse() ? GetIncludedOuterBCBorder(aWM) :
    2970           0 :     GetSeparateModelBorderPadding(aWM, aReflowInput, mStyleContext);
    2971             : }
    2972             : 
    2973             : void
    2974           0 : nsTableFrame::InitChildReflowInput(ReflowInput& aReflowInput)
    2975             : {
    2976           0 :   nsMargin collapseBorder;
    2977           0 :   nsMargin padding(0,0,0,0);
    2978           0 :   nsMargin* pCollapseBorder = nullptr;
    2979           0 :   nsPresContext* presContext = PresContext();
    2980           0 :   if (IsBorderCollapse()) {
    2981             :     nsTableRowGroupFrame* rgFrame =
    2982           0 :        static_cast<nsTableRowGroupFrame*>(aReflowInput.mFrame);
    2983           0 :     WritingMode wm = GetWritingMode();
    2984           0 :     LogicalMargin border = rgFrame->GetBCBorderWidth(wm);
    2985           0 :     collapseBorder = border.GetPhysicalMargin(wm);
    2986           0 :     pCollapseBorder = &collapseBorder;
    2987             :   }
    2988           0 :   aReflowInput.Init(presContext, nullptr, pCollapseBorder, &padding);
    2989             : 
    2990           0 :   NS_ASSERTION(!mBits.mResizedColumns ||
    2991             :                !aReflowInput.mParentReflowInput->mFlags.mSpecialBSizeReflow,
    2992             :                "should not resize columns on special bsize reflow");
    2993           0 :   if (mBits.mResizedColumns) {
    2994           0 :     aReflowInput.SetIResize(true);
    2995             :   }
    2996           0 : }
    2997             : 
    2998             : // Position and size aKidFrame and update our reflow state. The origin of
    2999             : // aKidRect is relative to the upper-left origin of our frame
    3000             : void
    3001           0 : nsTableFrame::PlaceChild(TableReflowInput&  aReflowInput,
    3002             :                          nsIFrame*            aKidFrame,
    3003             :                          nsPoint              aKidPosition,
    3004             :                          ReflowOutput& aKidDesiredSize,
    3005             :                          const nsRect&        aOriginalKidRect,
    3006             :                          const nsRect&        aOriginalKidVisualOverflow)
    3007             : {
    3008           0 :   WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
    3009             :   bool isFirstReflow =
    3010           0 :     aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
    3011             : 
    3012             :   // Place and size the child
    3013           0 :   FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
    3014           0 :                     aKidPosition.x, aKidPosition.y, 0);
    3015             : 
    3016           0 :   InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
    3017           0 :                        isFirstReflow);
    3018             : 
    3019             :   // Adjust the running block-offset
    3020           0 :   aReflowInput.bCoord += aKidDesiredSize.BSize(wm);
    3021             : 
    3022             :   // If our bsize is constrained, then update the available bsize
    3023           0 :   if (NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm)) {
    3024           0 :     aReflowInput.availSize.BSize(wm) -= aKidDesiredSize.BSize(wm);
    3025             :   }
    3026           0 : }
    3027             : 
    3028             : void
    3029           0 : nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
    3030             :                              nsTableRowGroupFrame** aHead,
    3031             :                              nsTableRowGroupFrame** aFoot) const
    3032             : {
    3033           0 :   aChildren.Clear();
    3034           0 :   nsTableRowGroupFrame* head = nullptr;
    3035           0 :   nsTableRowGroupFrame* foot = nullptr;
    3036             : 
    3037           0 :   nsIFrame* kidFrame = mFrames.FirstChild();
    3038           0 :   while (kidFrame) {
    3039           0 :     const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
    3040             :     nsTableRowGroupFrame* rowGroup =
    3041           0 :       static_cast<nsTableRowGroupFrame*>(kidFrame);
    3042             : 
    3043           0 :     switch (kidDisplay->mDisplay) {
    3044             :     case mozilla::StyleDisplay::TableHeaderGroup:
    3045           0 :       if (head) { // treat additional thead like tbody
    3046           0 :         aChildren.AppendElement(rowGroup);
    3047             :       }
    3048             :       else {
    3049           0 :         head = rowGroup;
    3050             :       }
    3051           0 :       break;
    3052             :     case mozilla::StyleDisplay::TableFooterGroup:
    3053           0 :       if (foot) { // treat additional tfoot like tbody
    3054           0 :         aChildren.AppendElement(rowGroup);
    3055             :       }
    3056             :       else {
    3057           0 :         foot = rowGroup;
    3058             :       }
    3059           0 :       break;
    3060             :     case mozilla::StyleDisplay::TableRowGroup:
    3061           0 :       aChildren.AppendElement(rowGroup);
    3062           0 :       break;
    3063             :     default:
    3064           0 :       NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
    3065             :       // Just ignore it
    3066           0 :       break;
    3067             :     }
    3068             :     // Get the next sibling but skip it if it's also the next-in-flow, since
    3069             :     // a next-in-flow will not be part of the current table.
    3070           0 :     while (kidFrame) {
    3071           0 :       nsIFrame* nif = kidFrame->GetNextInFlow();
    3072           0 :       kidFrame = kidFrame->GetNextSibling();
    3073           0 :       if (kidFrame != nif)
    3074           0 :         break;
    3075             :     }
    3076             :   }
    3077             : 
    3078             :   // put the thead first
    3079           0 :   if (head) {
    3080           0 :     aChildren.InsertElementAt(0, head);
    3081             :   }
    3082           0 :   if (aHead)
    3083           0 :     *aHead = head;
    3084             :   // put the tfoot after the last tbody
    3085           0 :   if (foot) {
    3086           0 :     aChildren.AppendElement(foot);
    3087             :   }
    3088           0 :   if (aFoot)
    3089           0 :     *aFoot = foot;
    3090           0 : }
    3091             : 
    3092             : nsTableRowGroupFrame*
    3093           0 : nsTableFrame::GetTHead() const
    3094             : {
    3095           0 :   nsIFrame* kidFrame = mFrames.FirstChild();
    3096           0 :   while (kidFrame) {
    3097           0 :     if (kidFrame->StyleDisplay()->mDisplay ==
    3098             :           mozilla::StyleDisplay::TableHeaderGroup) {
    3099           0 :       return static_cast<nsTableRowGroupFrame*>(kidFrame);
    3100             :     }
    3101             : 
    3102             :     // Get the next sibling but skip it if it's also the next-in-flow, since
    3103             :     // a next-in-flow will not be part of the current table.
    3104           0 :     while (kidFrame) {
    3105           0 :       nsIFrame* nif = kidFrame->GetNextInFlow();
    3106           0 :       kidFrame = kidFrame->GetNextSibling();
    3107           0 :       if (kidFrame != nif)
    3108           0 :         break;
    3109             :     }
    3110             :   }
    3111             : 
    3112           0 :   return nullptr;
    3113             : }
    3114             : 
    3115             : nsTableRowGroupFrame*
    3116           0 : nsTableFrame::GetTFoot() const
    3117             : {
    3118           0 :   nsIFrame* kidFrame = mFrames.FirstChild();
    3119           0 :   while (kidFrame) {
    3120           0 :     if (kidFrame->StyleDisplay()->mDisplay ==
    3121             :           mozilla::StyleDisplay::TableFooterGroup) {
    3122           0 :       return static_cast<nsTableRowGroupFrame*>(kidFrame);
    3123             :     }
    3124             : 
    3125             :     // Get the next sibling but skip it if it's also the next-in-flow, since
    3126             :     // a next-in-flow will not be part of the current table.
    3127           0 :     while (kidFrame) {
    3128           0 :       nsIFrame* nif = kidFrame->GetNextInFlow();
    3129           0 :       kidFrame = kidFrame->GetNextSibling();
    3130           0 :       if (kidFrame != nif)
    3131           0 :         break;
    3132             :     }
    3133             :   }
    3134             : 
    3135           0 :   return nullptr;
    3136             : }
    3137             : 
    3138             : static bool
    3139           0 : IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
    3140             : {
    3141           0 :   return aFrameHeight < (aPageHeight / 4);
    3142             : }
    3143             : 
    3144             : nsresult
    3145           0 : nsTableFrame::SetupHeaderFooterChild(const TableReflowInput& aReflowInput,
    3146             :                                      nsTableRowGroupFrame* aFrame,
    3147             :                                      nscoord* aDesiredHeight)
    3148             : {
    3149           0 :   nsPresContext* presContext = PresContext();
    3150           0 :   nscoord pageHeight = presContext->GetPageSize().height;
    3151             : 
    3152             :   // Reflow the child with unconstrained height
    3153           0 :   WritingMode wm = aFrame->GetWritingMode();
    3154           0 :   LogicalSize availSize = aReflowInput.reflowInput.AvailableSize(wm);
    3155             : 
    3156           0 :   nsSize containerSize = availSize.GetPhysicalSize(wm);
    3157             :   // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
    3158             : 
    3159           0 :   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    3160             :   ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
    3161             :                                    aFrame, availSize, nullptr,
    3162           0 :                                    ReflowInput::CALLER_WILL_INIT);
    3163           0 :   InitChildReflowInput(kidReflowInput);
    3164           0 :   kidReflowInput.mFlags.mIsTopOfPage = true;
    3165           0 :   ReflowOutput desiredSize(aReflowInput.reflowInput);
    3166           0 :   desiredSize.ClearSize();
    3167           0 :   nsReflowStatus status;
    3168           0 :   ReflowChild(aFrame, presContext, desiredSize, kidReflowInput,
    3169           0 :               wm, LogicalPoint(wm, aReflowInput.iCoord, aReflowInput.bCoord),
    3170           0 :               containerSize, 0, status);
    3171             :   // The child will be reflowed again "for real" so no need to place it now
    3172             : 
    3173           0 :   aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight));
    3174           0 :   *aDesiredHeight = desiredSize.Height();
    3175           0 :   return NS_OK;
    3176             : }
    3177             : 
    3178             : void
    3179           0 : nsTableFrame::PlaceRepeatedFooter(TableReflowInput& aReflowInput,
    3180             :                                   nsTableRowGroupFrame *aTfoot,
    3181             :                                   nscoord aFooterHeight)
    3182             : {
    3183           0 :   nsPresContext* presContext = PresContext();
    3184           0 :   WritingMode wm = aTfoot->GetWritingMode();
    3185           0 :   LogicalSize kidAvailSize = aReflowInput.availSize;
    3186             : 
    3187           0 :   nsSize containerSize = kidAvailSize.GetPhysicalSize(wm);
    3188             :   // XXX check for containerSize.* == NS_UNCONSTRAINEDSIZE
    3189             : 
    3190           0 :   kidAvailSize.BSize(wm) = aFooterHeight;
    3191             :   ReflowInput footerReflowInput(presContext,
    3192             :                                       aReflowInput.reflowInput,
    3193             :                                       aTfoot, kidAvailSize,
    3194             :                                       nullptr,
    3195           0 :                                       ReflowInput::CALLER_WILL_INIT);
    3196           0 :   InitChildReflowInput(footerReflowInput);
    3197           0 :   aReflowInput.bCoord += GetRowSpacing(GetRowCount());
    3198             : 
    3199           0 :   nsRect origTfootRect = aTfoot->GetRect();
    3200           0 :   nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
    3201             : 
    3202           0 :   nsReflowStatus footerStatus;
    3203           0 :   ReflowOutput desiredSize(aReflowInput.reflowInput);
    3204           0 :   desiredSize.ClearSize();
    3205           0 :   LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
    3206           0 :   ReflowChild(aTfoot, presContext, desiredSize, footerReflowInput,
    3207           0 :               wm, kidPosition, containerSize, 0, footerStatus);
    3208           0 :   footerReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
    3209             : 
    3210             :   PlaceChild(aReflowInput, aTfoot,
    3211             :              // We subtract desiredSize.PhysicalSize() from containerSize here
    3212             :              // to account for the fact that in RTL modes, the origin is
    3213             :              // on the right-hand side so we're not simply converting a
    3214             :              // point, we're also swapping the child's origin side.
    3215           0 :              kidPosition.GetPhysicalPoint(wm, containerSize -
    3216           0 :                                               desiredSize.PhysicalSize()),
    3217           0 :              desiredSize, origTfootRect, origTfootVisualOverflow);
    3218           0 : }
    3219             : 
    3220             : // Reflow the children based on the avail size and reason in aReflowInput
    3221             : void
    3222           0 : nsTableFrame::ReflowChildren(TableReflowInput& aReflowInput,
    3223             :                              nsReflowStatus&     aStatus,
    3224             :                              nsIFrame*&          aLastChildReflowed,
    3225             :                              nsOverflowAreas&    aOverflowAreas)
    3226             : {
    3227           0 :   aStatus.Reset();
    3228           0 :   aLastChildReflowed = nullptr;
    3229             : 
    3230           0 :   nsIFrame* prevKidFrame = nullptr;
    3231           0 :   WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
    3232           0 :   NS_WARNING_ASSERTION(
    3233             :     wm.IsVertical() ||
    3234             :     NS_UNCONSTRAINEDSIZE != aReflowInput.reflowInput.ComputedWidth(),
    3235             :     "shouldn't have unconstrained width in horizontal mode");
    3236             :   nsSize containerSize =
    3237           0 :     aReflowInput.reflowInput.ComputedSizeAsContainerIfConstrained();
    3238             : 
    3239           0 :   nsPresContext* presContext = PresContext();
    3240             :   // XXXldb Should we be checking constrained height instead?
    3241             :   // tables are not able to pull back children from its next inflow, so even
    3242             :   // under paginated contexts tables are should not paginate if they are inside
    3243             :   // column set
    3244           0 :   bool isPaginated = presContext->IsPaginated() &&
    3245           0 :                        NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm) &&
    3246           0 :                        aReflowInput.reflowInput.mFlags.mTableIsSplittable;
    3247             : 
    3248             :   // Tables currently (though we ought to fix this) only fragment in
    3249             :   // paginated contexts, not in multicolumn contexts.  (See bug 888257.)
    3250             :   // This is partly because they don't correctly handle incremental
    3251             :   // layout when paginated.
    3252             :   //
    3253             :   // Since we propagate NS_FRAME_IS_DIRTY from parent to child at the
    3254             :   // start of the parent's reflow (behavior that's new as of bug
    3255             :   // 1308876), we can do things that are effectively incremental reflow
    3256             :   // during paginated layout.  Since the table code doesn't handle this
    3257             :   // correctly, we need to set the flag that says to reflow everything
    3258             :   // within the table structure.
    3259           0 :   if (presContext->IsPaginated()) {
    3260           0 :     SetGeometryDirty();
    3261             :   }
    3262             : 
    3263           0 :   aOverflowAreas.Clear();
    3264             : 
    3265           0 :   bool reflowAllKids = aReflowInput.reflowInput.ShouldReflowAllKids() ||
    3266           0 :                          mBits.mResizedColumns ||
    3267           0 :                          IsGeometryDirty();
    3268             : 
    3269           0 :   RowGroupArray rowGroups;
    3270             :   nsTableRowGroupFrame *thead, *tfoot;
    3271           0 :   OrderRowGroups(rowGroups, &thead, &tfoot);
    3272           0 :   bool pageBreak = false;
    3273           0 :   nscoord footerHeight = 0;
    3274             : 
    3275             :   // Determine the repeatablility of headers and footers, and also the desired
    3276             :   // height of any repeatable footer.
    3277             :   // The repeatability of headers on continued tables is handled
    3278             :   // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
    3279             :   // We handle the repeatability of footers again here because we need to
    3280             :   // determine the footer's height anyway. We could perhaps optimize by
    3281             :   // using the footer's prev-in-flow's height instead of reflowing it again,
    3282             :   // but there's no real need.
    3283           0 :   if (isPaginated) {
    3284           0 :     if (thead && !GetPrevInFlow()) {
    3285             :       nscoord desiredHeight;
    3286           0 :       nsresult rv = SetupHeaderFooterChild(aReflowInput, thead, &desiredHeight);
    3287           0 :       if (NS_FAILED(rv))
    3288           0 :         return;
    3289             :     }
    3290           0 :     if (tfoot) {
    3291           0 :       nsresult rv = SetupHeaderFooterChild(aReflowInput, tfoot, &footerHeight);
    3292           0 :       if (NS_FAILED(rv))
    3293           0 :         return;
    3294             :     }
    3295             :   }
    3296             :    // if the child is a tbody in paginated mode reduce the height by a repeated footer
    3297           0 :   bool allowRepeatedFooter = false;
    3298           0 :   for (size_t childX = 0; childX < rowGroups.Length(); childX++) {
    3299           0 :     nsIFrame* kidFrame = rowGroups[childX];
    3300           0 :     nsTableRowGroupFrame* rowGroupFrame = rowGroups[childX];
    3301           0 :     nscoord cellSpacingB = GetRowSpacing(rowGroupFrame->GetStartRowIndex()+
    3302           0 :                                          rowGroupFrame->GetRowCount());
    3303             :     // Get the frame state bits
    3304             :     // See if we should only reflow the dirty child frames
    3305           0 :     if (reflowAllKids ||
    3306           0 :         NS_SUBTREE_DIRTY(kidFrame) ||
    3307           0 :         (aReflowInput.reflowInput.mFlags.mSpecialBSizeReflow &&
    3308           0 :          (isPaginated || kidFrame->HasAnyStateBits(
    3309             :                           NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
    3310           0 :       if (pageBreak) {
    3311           0 :         if (allowRepeatedFooter) {
    3312           0 :           PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
    3313             :         }
    3314           0 :         else if (tfoot && tfoot->IsRepeatable()) {
    3315           0 :           tfoot->SetRepeatable(false);
    3316             :         }
    3317           0 :         PushChildren(rowGroups, childX);
    3318           0 :         aStatus.Reset();
    3319           0 :         aStatus.SetIncomplete();
    3320           0 :         break;
    3321             :       }
    3322             : 
    3323           0 :       LogicalSize kidAvailSize(aReflowInput.availSize);
    3324           0 :       allowRepeatedFooter = false;
    3325           0 :       if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.BSize(wm))) {
    3326             :         nsTableRowGroupFrame* kidRG =
    3327           0 :           static_cast<nsTableRowGroupFrame*>(kidFrame);
    3328           0 :         if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
    3329             :           // the child is a tbody and there is a repeatable footer
    3330           0 :           NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
    3331           0 :           if (footerHeight + cellSpacingB < kidAvailSize.BSize(wm)) {
    3332           0 :             allowRepeatedFooter = true;
    3333           0 :             kidAvailSize.BSize(wm) -= footerHeight + cellSpacingB;
    3334             :           }
    3335             :         }
    3336             :       }
    3337             : 
    3338           0 :       nsRect oldKidRect = kidFrame->GetRect();
    3339           0 :       nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
    3340             : 
    3341           0 :       ReflowOutput desiredSize(aReflowInput.reflowInput);
    3342           0 :       desiredSize.ClearSize();
    3343             : 
    3344             :       // Reflow the child into the available space
    3345             :       ReflowInput kidReflowInput(presContext, aReflowInput.reflowInput,
    3346             :                                        kidFrame,
    3347             :                                        kidAvailSize,
    3348             :                                        nullptr,
    3349           0 :                                        ReflowInput::CALLER_WILL_INIT);
    3350           0 :       InitChildReflowInput(kidReflowInput);
    3351             : 
    3352             :       // If this isn't the first row group, and the previous row group has a
    3353             :       // nonzero YMost, then we can't be at the top of the page.
    3354             :       // We ignore a repeated head row group in this check to avoid causing
    3355             :       // infinite loops in some circumstances - see bug 344883.
    3356           0 :       if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
    3357           0 :           (rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) {
    3358           0 :         kidReflowInput.mFlags.mIsTopOfPage = false;
    3359             :       }
    3360           0 :       aReflowInput.bCoord += cellSpacingB;
    3361           0 :       if (NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm)) {
    3362           0 :         aReflowInput.availSize.BSize(wm) -= cellSpacingB;
    3363             :       }
    3364             :       // record the presence of a next in flow, it might get destroyed so we
    3365             :       // need to reorder the row group array
    3366           0 :       bool reorder = false;
    3367           0 :       if (kidFrame->GetNextInFlow())
    3368           0 :         reorder = true;
    3369             : 
    3370           0 :       LogicalPoint kidPosition(wm, aReflowInput.iCoord, aReflowInput.bCoord);
    3371           0 :       ReflowChild(kidFrame, presContext, desiredSize, kidReflowInput,
    3372           0 :                   wm, kidPosition, containerSize, 0, aStatus);
    3373           0 :       kidReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
    3374             : 
    3375           0 :       if (reorder) {
    3376             :         // reorder row groups the reflow may have changed the nextinflows
    3377           0 :         OrderRowGroups(rowGroups, &thead, &tfoot);
    3378           0 :         childX = rowGroups.IndexOf(kidFrame);
    3379           0 :         if (childX == RowGroupArray::NoIndex) {
    3380             :           // XXXbz can this happen?
    3381           0 :           childX = rowGroups.Length();
    3382             :         }
    3383             :       }
    3384           0 :       if (isPaginated && !aStatus.IsFullyComplete() &&
    3385           0 :           ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
    3386           0 :         aStatus.SetInlineLineBreakBeforeAndReset();
    3387           0 :         break;
    3388             :       }
    3389             :       // see if the rowgroup did not fit on this page might be pushed on
    3390             :       // the next page
    3391           0 :       if (isPaginated &&
    3392           0 :           (aStatus.IsInlineBreakBefore() ||
    3393           0 :            (aStatus.IsComplete() &&
    3394           0 :             (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight()) &&
    3395           0 :             kidReflowInput.AvailableHeight() < desiredSize.Height()))) {
    3396           0 :         if (ShouldAvoidBreakInside(aReflowInput.reflowInput)) {
    3397           0 :           aStatus.SetInlineLineBreakBeforeAndReset();
    3398           0 :           break;
    3399             :         }
    3400             :         // if we are on top of the page place with dataloss
    3401           0 :         if (kidReflowInput.mFlags.mIsTopOfPage) {
    3402           0 :           if (childX+1 < rowGroups.Length()) {
    3403           0 :             nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
    3404           0 :             if (nextRowGroupFrame) {
    3405             :               PlaceChild(aReflowInput, kidFrame,
    3406           0 :                          kidPosition.GetPhysicalPoint(wm,
    3407           0 :                            containerSize - desiredSize.PhysicalSize()),
    3408           0 :                          desiredSize, oldKidRect, oldKidVisualOverflow);
    3409           0 :               if (allowRepeatedFooter) {
    3410           0 :                 PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
    3411             :               }
    3412           0 :               else if (tfoot && tfoot->IsRepeatable()) {
    3413           0 :                 tfoot->SetRepeatable(false);
    3414             :               }
    3415           0 :               aStatus.Reset();
    3416           0 :               aStatus.SetIncomplete();
    3417           0 :               PushChildren(rowGroups, childX + 1);
    3418           0 :               aLastChildReflowed = kidFrame;
    3419           0 :               break;
    3420             :             }
    3421             :           }
    3422             :         }
    3423             :         else { // we are not on top, push this rowgroup onto the next page
    3424           0 :           if (prevKidFrame) { // we had a rowgroup before so push this
    3425           0 :             if (allowRepeatedFooter) {
    3426           0 :               PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
    3427             :             }
    3428           0 :             else if (tfoot && tfoot->IsRepeatable()) {
    3429           0 :               tfoot->SetRepeatable(false);
    3430             :             }
    3431           0 :             aStatus.Reset();
    3432           0 :             aStatus.SetIncomplete();
    3433           0 :             PushChildren(rowGroups, childX);
    3434           0 :             aLastChildReflowed = prevKidFrame;
    3435           0 :             break;
    3436             :           }
    3437             :           else { // we can't push so lets make clear how much space we need
    3438             :             PlaceChild(aReflowInput, kidFrame,
    3439           0 :                        kidPosition.GetPhysicalPoint(wm,
    3440           0 :                          containerSize - desiredSize.PhysicalSize()),
    3441           0 :                        desiredSize, oldKidRect, oldKidVisualOverflow);
    3442           0 :             aLastChildReflowed = kidFrame;
    3443           0 :             if (allowRepeatedFooter) {
    3444           0 :               PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
    3445           0 :               aLastChildReflowed = tfoot;
    3446             :             }
    3447           0 :             break;
    3448             :           }
    3449             :         }
    3450             :       }
    3451             : 
    3452           0 :       aLastChildReflowed   = kidFrame;
    3453             : 
    3454           0 :       pageBreak = false;
    3455             :       // see if there is a page break after this row group or before the next one
    3456           0 :       if (aStatus.IsComplete() && isPaginated &&
    3457           0 :           (NS_UNCONSTRAINEDSIZE != kidReflowInput.AvailableHeight())) {
    3458             :         nsIFrame* nextKid =
    3459           0 :           (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
    3460           0 :         pageBreak = PageBreakAfter(kidFrame, nextKid);
    3461             :       }
    3462             : 
    3463             :       // Place the child
    3464             :       PlaceChild(aReflowInput, kidFrame,
    3465           0 :                  kidPosition.GetPhysicalPoint(wm, containerSize -
    3466           0 :                                                   desiredSize.PhysicalSize()),
    3467           0 :                  desiredSize, oldKidRect, oldKidVisualOverflow);
    3468             : 
    3469             :       // Remember where we just were in case we end up pushing children
    3470           0 :       prevKidFrame = kidFrame;
    3471             : 
    3472           0 :       MOZ_ASSERT(!aStatus.IsIncomplete() || isPaginated,
    3473             :                  "Table contents should only fragment in paginated contexts");
    3474             : 
    3475             :       // Special handling for incomplete children
    3476           0 :       if (isPaginated && aStatus.IsIncomplete()) {
    3477           0 :         nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
    3478           0 :         if (!kidNextInFlow) {
    3479             :           // The child doesn't have a next-in-flow so create a continuing
    3480             :           // frame. This hooks the child into the flow
    3481           0 :           kidNextInFlow = presContext->PresShell()->FrameConstructor()->
    3482           0 :             CreateContinuingFrame(presContext, kidFrame, this);
    3483             : 
    3484             :           // Insert the kid's new next-in-flow into our sibling list...
    3485           0 :           mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
    3486             :           // and in rowGroups after childX so that it will get pushed below.
    3487           0 :           rowGroups.InsertElementAt(childX + 1,
    3488           0 :                       static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
    3489           0 :         } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
    3490             :           // OrderRowGroups excludes NIFs in the child list from 'rowGroups'
    3491             :           // so we deal with that here to make sure they get pushed.
    3492           0 :           MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow),
    3493             :                      "OrderRowGroups must not put our NIF in 'rowGroups'");
    3494           0 :           rowGroups.InsertElementAt(childX + 1,
    3495           0 :                       static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
    3496             :         }
    3497             : 
    3498             :         // We've used up all of our available space so push the remaining
    3499             :         // children.
    3500           0 :         if (allowRepeatedFooter) {
    3501           0 :           PlaceRepeatedFooter(aReflowInput, tfoot, footerHeight);
    3502             :         }
    3503           0 :         else if (tfoot && tfoot->IsRepeatable()) {
    3504           0 :           tfoot->SetRepeatable(false);
    3505             :         }
    3506             : 
    3507           0 :         nsIFrame* nextSibling = kidFrame->GetNextSibling();
    3508           0 :         if (nextSibling) {
    3509           0 :           PushChildren(rowGroups, childX + 1);
    3510             :         }
    3511           0 :         break;
    3512             :       }
    3513             :     }
    3514             :     else { // it isn't being reflowed
    3515           0 :       aReflowInput.bCoord += cellSpacingB;
    3516           0 :       LogicalRect kidRect(wm, kidFrame->GetNormalRect(), containerSize);
    3517           0 :       if (kidRect.BStart(wm) != aReflowInput.bCoord) {
    3518             :         // invalidate the old position
    3519           0 :         kidFrame->InvalidateFrameSubtree();
    3520             :         // move to the new position
    3521           0 :         kidFrame->MovePositionBy(wm, LogicalPoint(wm, 0, aReflowInput.bCoord -
    3522           0 :                                                          kidRect.BStart(wm)));
    3523           0 :         RePositionViews(kidFrame);
    3524             :         // invalidate the new position
    3525           0 :         kidFrame->InvalidateFrameSubtree();
    3526             :       }
    3527           0 :       aReflowInput.bCoord += kidRect.BSize(wm);
    3528             : 
    3529             :       // If our bsize is constrained then update the available bsize.
    3530           0 :       if (NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm)) {
    3531           0 :         aReflowInput.availSize.BSize(wm) -= cellSpacingB + kidRect.BSize(wm);
    3532             :       }
    3533             :     }
    3534             :   }
    3535             : 
    3536             :   // We've now propagated the column resizes and geometry changes to all
    3537             :   // the children.
    3538           0 :   mBits.mResizedColumns = false;
    3539           0 :   ClearGeometryDirty();
    3540             : }
    3541             : 
    3542             : void
    3543           0 : nsTableFrame::ReflowColGroups(gfxContext *aRenderingContext)
    3544             : {
    3545           0 :   if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
    3546           0 :     ReflowOutput kidMet(GetWritingMode());
    3547           0 :     nsPresContext *presContext = PresContext();
    3548           0 :     for (nsIFrame* kidFrame : mColGroups) {
    3549           0 :       if (NS_SUBTREE_DIRTY(kidFrame)) {
    3550             :         // The column groups don't care about dimensions or reflow states.
    3551             :         ReflowInput
    3552             :           kidReflowInput(presContext, kidFrame, aRenderingContext,
    3553           0 :                          LogicalSize(kidFrame->GetWritingMode()));
    3554           0 :         nsReflowStatus cgStatus;
    3555           0 :         ReflowChild(kidFrame, presContext, kidMet, kidReflowInput, 0, 0, 0,
    3556           0 :                     cgStatus);
    3557           0 :         FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0);
    3558             :       }
    3559             :     }
    3560           0 :     SetHaveReflowedColGroups(true);
    3561             :   }
    3562           0 : }
    3563             : 
    3564             : void
    3565           0 : nsTableFrame::CalcDesiredBSize(const ReflowInput& aReflowInput,
    3566             :                                ReflowOutput& aDesiredSize)
    3567             : {
    3568           0 :   WritingMode wm = aReflowInput.GetWritingMode();
    3569           0 :   nsTableCellMap* cellMap = GetCellMap();
    3570           0 :   if (!cellMap) {
    3571           0 :     NS_ERROR("never ever call me until the cell map is built!");
    3572           0 :     aDesiredSize.BSize(wm) = 0;
    3573           0 :     return;
    3574             :   }
    3575           0 :   LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
    3576             : 
    3577             :   // get the natural bsize based on the last child's (row group) rect
    3578           0 :   RowGroupArray rowGroups;
    3579           0 :   OrderRowGroups(rowGroups);
    3580           0 :   if (rowGroups.IsEmpty()) {
    3581             :     // tables can be used as rectangular items without content
    3582           0 :     nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
    3583           0 :     if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedBSize) &&
    3584           0 :         (tableSpecifiedBSize > 0) &&
    3585           0 :         eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
    3586             :           // empty tables should not have a size in quirks mode
    3587           0 :       aDesiredSize.BSize(wm) = tableSpecifiedBSize;
    3588             :     } else {
    3589           0 :       aDesiredSize.BSize(wm) = 0;
    3590             :     }
    3591           0 :     return;
    3592             :   }
    3593           0 :   int32_t rowCount = cellMap->GetRowCount();
    3594           0 :   int32_t colCount = cellMap->GetColCount();
    3595           0 :   nscoord desiredBSize = borderPadding.BStartEnd(wm);
    3596           0 :   if (rowCount > 0 && colCount > 0) {
    3597           0 :     desiredBSize += GetRowSpacing(-1);
    3598           0 :     for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    3599           0 :       desiredBSize += rowGroups[rgIdx]->BSize(wm) +
    3600           0 :                        GetRowSpacing(rowGroups[rgIdx]->GetRowCount() +
    3601           0 :                                      rowGroups[rgIdx]->GetStartRowIndex());
    3602             :     }
    3603             :   }
    3604             : 
    3605             :   // see if a specified table bsize requires dividing additional space to rows
    3606           0 :   if (!GetPrevInFlow()) {
    3607           0 :     nscoord tableSpecifiedBSize = CalcBorderBoxBSize(aReflowInput);
    3608           0 :     if ((tableSpecifiedBSize > 0) &&
    3609           0 :         (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE) &&
    3610             :         (tableSpecifiedBSize > desiredBSize)) {
    3611             :       // proportionately distribute the excess bsize to unconstrained rows in each
    3612             :       // unconstrained row group.
    3613           0 :       DistributeBSizeToRows(aReflowInput, tableSpecifiedBSize - desiredBSize);
    3614             :       // this might have changed the overflow area incorporate the childframe overflow area.
    3615           0 :       for (nsIFrame* kidFrame : mFrames) {
    3616           0 :         ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
    3617             :       }
    3618           0 :       desiredBSize = tableSpecifiedBSize;
    3619             :     }
    3620             :   }
    3621           0 :   aDesiredSize.BSize(wm) = desiredBSize;
    3622             : }
    3623             : 
    3624             : static
    3625           0 : void ResizeCells(nsTableFrame& aTableFrame)
    3626             : {
    3627           0 :   nsTableFrame::RowGroupArray rowGroups;
    3628           0 :   aTableFrame.OrderRowGroups(rowGroups);
    3629           0 :   WritingMode wm = aTableFrame.GetWritingMode();
    3630           0 :   ReflowOutput tableDesiredSize(wm);
    3631           0 :   tableDesiredSize.SetSize(wm, aTableFrame.GetLogicalSize(wm));
    3632           0 :   tableDesiredSize.SetOverflowAreasToDesiredBounds();
    3633             : 
    3634           0 :   for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    3635           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    3636             : 
    3637           0 :     ReflowOutput groupDesiredSize(wm);
    3638           0 :     groupDesiredSize.SetSize(wm, rgFrame->GetLogicalSize(wm));
    3639           0 :     groupDesiredSize.SetOverflowAreasToDesiredBounds();
    3640             : 
    3641           0 :     nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3642           0 :     while (rowFrame) {
    3643           0 :       rowFrame->DidResize();
    3644           0 :       rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
    3645           0 :       rowFrame = rowFrame->GetNextRow();
    3646             :     }
    3647           0 :     rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
    3648           0 :     tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
    3649           0 :                                               rgFrame->GetPosition());
    3650             :   }
    3651           0 :   aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
    3652           0 : }
    3653             : 
    3654             : void
    3655           0 : nsTableFrame::DistributeBSizeToRows(const ReflowInput& aReflowInput,
    3656             :                                     nscoord                  aAmount)
    3657             : {
    3658           0 :   WritingMode wm = aReflowInput.GetWritingMode();
    3659           0 :   LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowInput);
    3660             : 
    3661             :   nsSize containerSize =
    3662           0 :     aReflowInput.ComputedSizeAsContainerIfConstrained();
    3663             : 
    3664           0 :   RowGroupArray rowGroups;
    3665           0 :   OrderRowGroups(rowGroups);
    3666             : 
    3667           0 :   nscoord amountUsed = 0;
    3668             :   // distribute space to each pct bsize row whose row group doesn't have a computed
    3669             :   // bsize, and base the pct on the table bsize. If the row group had a computed
    3670             :   // bsize, then this was already done in nsTableRowGroupFrame::CalculateRowBSizes
    3671           0 :   nscoord pctBasis = aReflowInput.ComputedBSize() - GetRowSpacing(-1, GetRowCount());
    3672           0 :   nscoord bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0);
    3673           0 :   nscoord bEndRG = bOriginRG;
    3674             :   uint32_t rgIdx;
    3675           0 :   for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    3676           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    3677           0 :     nscoord amountUsedByRG = 0;
    3678           0 :     nscoord bOriginRow = 0;
    3679           0 :     LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
    3680           0 :     if (!rgFrame->HasStyleBSize()) {
    3681           0 :       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3682           0 :       while (rowFrame) {
    3683             :         // We don't know the final width of the rowGroupFrame yet, so use 0,0
    3684             :         // as a dummy containerSize here; we'll adjust the row positions at
    3685             :         // the end, after the rowGroup size is finalized.
    3686           0 :         const nsSize dummyContainerSize;
    3687           0 :         LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
    3688           0 :                                   dummyContainerSize);
    3689           0 :         nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
    3690           0 :         if ((amountUsed < aAmount) && rowFrame->HasPctBSize()) {
    3691           0 :           nscoord pctBSize = rowFrame->GetInitialBSize(pctBasis);
    3692           0 :           nscoord amountForRow = std::min(aAmount - amountUsed,
    3693           0 :                                           pctBSize - rowNormalRect.BSize(wm));
    3694           0 :           if (amountForRow > 0) {
    3695             :             // XXXbz we don't need to move the row's b-position to bOriginRow?
    3696           0 :             nsRect origRowRect = rowFrame->GetRect();
    3697           0 :             nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
    3698           0 :             rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
    3699           0 :                               newRowBSize));
    3700           0 :             bOriginRow += newRowBSize + cellSpacingB;
    3701           0 :             bEndRG += newRowBSize + cellSpacingB;
    3702           0 :             amountUsed += amountForRow;
    3703           0 :             amountUsedByRG += amountForRow;
    3704             :             //rowFrame->DidResize();
    3705           0 :             nsTableFrame::RePositionViews(rowFrame);
    3706             : 
    3707           0 :             rgFrame->InvalidateFrameWithRect(origRowRect);
    3708           0 :             rgFrame->InvalidateFrame();
    3709             :           }
    3710             :         }
    3711             :         else {
    3712           0 :           if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm) &&
    3713           0 :               !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    3714           0 :             rowFrame->InvalidateFrameSubtree();
    3715           0 :             rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
    3716           0 :                                                     rowNormalRect.BStart(wm)));
    3717           0 :             nsTableFrame::RePositionViews(rowFrame);
    3718           0 :             rowFrame->InvalidateFrameSubtree();
    3719             :           }
    3720           0 :           bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
    3721           0 :           bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
    3722             :         }
    3723           0 :         rowFrame = rowFrame->GetNextRow();
    3724             :       }
    3725           0 :       if (amountUsed > 0) {
    3726           0 :         if (rgNormalRect.BStart(wm) != bOriginRG) {
    3727           0 :           rgFrame->InvalidateFrameSubtree();
    3728             :         }
    3729             : 
    3730           0 :         nsRect origRgNormalRect = rgFrame->GetRect();
    3731           0 :         nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
    3732             : 
    3733           0 :         rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
    3734           0 :                                                  rgNormalRect.BStart(wm)));
    3735           0 :         rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
    3736           0 :                                 rgNormalRect.BSize(wm) + amountUsedByRG));
    3737             : 
    3738             :         nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
    3739           0 :                                            origRgVisualOverflow, false);
    3740             :       }
    3741             :     }
    3742           0 :     else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
    3743           0 :       rgFrame->InvalidateFrameSubtree();
    3744           0 :       rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
    3745           0 :                                                rgNormalRect.BStart(wm)));
    3746             :       // Make sure child views are properly positioned
    3747           0 :       nsTableFrame::RePositionViews(rgFrame);
    3748           0 :       rgFrame->InvalidateFrameSubtree();
    3749             :     }
    3750           0 :     bOriginRG = bEndRG;
    3751             :   }
    3752             : 
    3753           0 :   if (amountUsed >= aAmount) {
    3754           0 :     ResizeCells(*this);
    3755           0 :     return;
    3756             :   }
    3757             : 
    3758             :   // get the first row without a style bsize where its row group has an
    3759             :   // unconstrained bsize
    3760           0 :   nsTableRowGroupFrame* firstUnStyledRG  = nullptr;
    3761           0 :   nsTableRowFrame*      firstUnStyledRow = nullptr;
    3762           0 :   for (rgIdx = 0; rgIdx < rowGroups.Length() && !firstUnStyledRG; rgIdx++) {
    3763           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    3764           0 :     if (!rgFrame->HasStyleBSize()) {
    3765           0 :       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3766           0 :       while (rowFrame) {
    3767           0 :         if (!rowFrame->HasStyleBSize()) {
    3768           0 :           firstUnStyledRG = rgFrame;
    3769           0 :           firstUnStyledRow = rowFrame;
    3770           0 :           break;
    3771             :         }
    3772           0 :         rowFrame = rowFrame->GetNextRow();
    3773             :       }
    3774             :     }
    3775             :   }
    3776             : 
    3777           0 :   nsTableRowFrame* lastEligibleRow = nullptr;
    3778             :   // Accumulate the correct divisor. This will be the total bsize of all
    3779             :   // unstyled rows inside unstyled row groups, unless there are none, in which
    3780             :   // case, it will be number of all rows. If the unstyled rows don't have a
    3781             :   // bsize, divide the space equally among them.
    3782           0 :   nscoord divisor = 0;
    3783           0 :   int32_t eligibleRows = 0;
    3784           0 :   bool expandEmptyRows = false;
    3785             : 
    3786           0 :   if (!firstUnStyledRow) {
    3787             :     // there is no unstyled row
    3788           0 :     divisor = GetRowCount();
    3789             :   }
    3790             :   else {
    3791           0 :     for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    3792           0 :       nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    3793           0 :       if (!firstUnStyledRG || !rgFrame->HasStyleBSize()) {
    3794           0 :         nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3795           0 :         while (rowFrame) {
    3796           0 :           if (!firstUnStyledRG || !rowFrame->HasStyleBSize()) {
    3797           0 :             NS_ASSERTION(rowFrame->BSize(wm) >= 0,
    3798             :                          "negative row frame block-size");
    3799           0 :             divisor += rowFrame->BSize(wm);
    3800           0 :             eligibleRows++;
    3801           0 :             lastEligibleRow = rowFrame;
    3802             :           }
    3803           0 :           rowFrame = rowFrame->GetNextRow();
    3804             :         }
    3805             :       }
    3806             :     }
    3807           0 :     if (divisor <= 0) {
    3808           0 :       if (eligibleRows > 0) {
    3809           0 :         expandEmptyRows = true;
    3810             :       }
    3811             :       else {
    3812           0 :         NS_ERROR("invalid divisor");
    3813           0 :         return;
    3814             :       }
    3815             :     }
    3816             :   }
    3817             :   // allocate the extra bsize to the unstyled row groups and rows
    3818           0 :   nscoord bSizeToDistribute = aAmount - amountUsed;
    3819           0 :   bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1);
    3820           0 :   bEndRG = bOriginRG;
    3821           0 :   for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    3822           0 :     nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    3823           0 :     nscoord amountUsedByRG = 0;
    3824           0 :     nscoord bOriginRow = 0;
    3825           0 :     LogicalRect rgNormalRect(wm, rgFrame->GetNormalRect(), containerSize);
    3826           0 :     nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
    3827             :     // see if there is an eligible row group or we distribute to all rows
    3828           0 :     if (!firstUnStyledRG || !rgFrame->HasStyleBSize() || !eligibleRows) {
    3829           0 :       for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3830           0 :            rowFrame; rowFrame = rowFrame->GetNextRow()) {
    3831           0 :         nscoord cellSpacingB = GetRowSpacing(rowFrame->GetRowIndex());
    3832             :         // We don't know the final width of the rowGroupFrame yet, so use 0,0
    3833             :         // as a dummy containerSize here; we'll adjust the row positions at
    3834             :         // the end, after the rowGroup size is finalized.
    3835           0 :         const nsSize dummyContainerSize;
    3836           0 :         LogicalRect rowNormalRect(wm, rowFrame->GetNormalRect(),
    3837           0 :                                   dummyContainerSize);
    3838           0 :         nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
    3839             :         // see if there is an eligible row or we distribute to all rows
    3840           0 :         if (!firstUnStyledRow || !rowFrame->HasStyleBSize() || !eligibleRows) {
    3841             :           float ratio;
    3842           0 :           if (eligibleRows) {
    3843           0 :             if (!expandEmptyRows) {
    3844             :               // The amount of additional space each row gets is proportional
    3845             :               // to its bsize
    3846           0 :               ratio = float(rowNormalRect.BSize(wm)) / float(divisor);
    3847             :             } else {
    3848             :               // empty rows get all the same additional space
    3849           0 :               ratio = 1.0f / float(eligibleRows);
    3850             :             }
    3851             :           }
    3852             :           else {
    3853             :             // all rows get the same additional space
    3854           0 :             ratio = 1.0f / float(divisor);
    3855             :           }
    3856             :           // give rows their additional space, except for the last row which
    3857             :           // gets the remainder
    3858             :           nscoord amountForRow =
    3859             :             (rowFrame == lastEligibleRow)
    3860           0 :               ? aAmount - amountUsed
    3861           0 :               : NSToCoordRound(((float)(bSizeToDistribute)) * ratio);
    3862           0 :           amountForRow = std::min(amountForRow, aAmount - amountUsed);
    3863             : 
    3864           0 :           if (bOriginRow != rowNormalRect.BStart(wm)) {
    3865           0 :             rowFrame->InvalidateFrameSubtree();
    3866             :           }
    3867             : 
    3868             :           // update the row bsize
    3869           0 :           nsRect origRowRect = rowFrame->GetRect();
    3870           0 :           nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
    3871           0 :           rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
    3872           0 :                                                     rowNormalRect.BStart(wm)));
    3873           0 :           rowFrame->SetSize(wm, LogicalSize(wm, rowNormalRect.ISize(wm),
    3874           0 :                                             newRowBSize));
    3875             : 
    3876           0 :           bOriginRow += newRowBSize + cellSpacingB;
    3877           0 :           bEndRG += newRowBSize + cellSpacingB;
    3878             : 
    3879           0 :           amountUsed += amountForRow;
    3880           0 :           amountUsedByRG += amountForRow;
    3881           0 :           NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
    3882             :           //rowFrame->DidResize();
    3883           0 :           nsTableFrame::RePositionViews(rowFrame);
    3884             : 
    3885             :           nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
    3886           0 :                                              rowVisualOverflow, false);
    3887             :         }
    3888             :         else {
    3889           0 :           if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm)) {
    3890           0 :             rowFrame->InvalidateFrameSubtree();
    3891           0 :             rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRow -
    3892           0 :                                                     rowNormalRect.BStart(wm)));
    3893           0 :             nsTableFrame::RePositionViews(rowFrame);
    3894           0 :             rowFrame->InvalidateFrameSubtree();
    3895             :           }
    3896           0 :           bOriginRow += rowNormalRect.BSize(wm) + cellSpacingB;
    3897           0 :           bEndRG += rowNormalRect.BSize(wm) + cellSpacingB;
    3898             :         }
    3899             :       }
    3900             : 
    3901           0 :       if (amountUsed > 0) {
    3902           0 :         if (rgNormalRect.BStart(wm) != bOriginRG) {
    3903           0 :           rgFrame->InvalidateFrameSubtree();
    3904             :         }
    3905             : 
    3906           0 :         nsRect origRgNormalRect = rgFrame->GetRect();
    3907           0 :         rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
    3908           0 :                                                  rgNormalRect.BStart(wm)));
    3909           0 :         rgFrame->SetSize(wm, LogicalSize(wm, rgNormalRect.ISize(wm),
    3910           0 :                                 rgNormalRect.BSize(wm) + amountUsedByRG));
    3911             : 
    3912             :         nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
    3913           0 :                                            rgVisualOverflow, false);
    3914             :       }
    3915             : 
    3916             :       // For vertical-rl mode, we needed to position the rows relative to the
    3917             :       // right-hand (block-start) side of the group; but we couldn't do that
    3918             :       // above, as we didn't know the rowGroupFrame's final block size yet.
    3919             :       // So we used a dummyContainerSize of 0,0 earlier, placing the rows to
    3920             :       // the left of the rowGroupFrame's (physical) origin. Now we move them
    3921             :       // all rightwards by its final width.
    3922           0 :       if (wm.IsVerticalRL()) {
    3923           0 :         nscoord rgWidth = rgFrame->GetSize().width;
    3924           0 :         for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
    3925           0 :              rowFrame; rowFrame = rowFrame->GetNextRow()) {
    3926           0 :           rowFrame->InvalidateFrameSubtree();
    3927           0 :           rowFrame->MovePositionBy(nsPoint(rgWidth, 0));
    3928           0 :           nsTableFrame::RePositionViews(rowFrame);
    3929           0 :           rowFrame->InvalidateFrameSubtree();
    3930             :         }
    3931             :       }
    3932             :     }
    3933           0 :     else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
    3934           0 :       rgFrame->InvalidateFrameSubtree();
    3935           0 :       rgFrame->MovePositionBy(wm, LogicalPoint(wm, 0, bOriginRG -
    3936           0 :                                                rgNormalRect.BStart(wm)));
    3937             :       // Make sure child views are properly positioned
    3938           0 :       nsTableFrame::RePositionViews(rgFrame);
    3939           0 :       rgFrame->InvalidateFrameSubtree();
    3940             :     }
    3941           0 :     bOriginRG = bEndRG;
    3942             :   }
    3943             : 
    3944           0 :   ResizeCells(*this);
    3945             : }
    3946             : 
    3947             : nscoord
    3948           0 : nsTableFrame::GetColumnISizeFromFirstInFlow(int32_t aColIndex)
    3949             : {
    3950           0 :   MOZ_ASSERT(this == FirstInFlow());
    3951           0 :   nsTableColFrame* colFrame = GetColFrame(aColIndex);
    3952           0 :   return colFrame ? colFrame->GetFinalISize() : 0;
    3953             : }
    3954             : 
    3955             : nscoord
    3956           0 : nsTableFrame::GetColSpacing()
    3957             : {
    3958           0 :   if (IsBorderCollapse())
    3959           0 :     return 0;
    3960             : 
    3961           0 :   return StyleTableBorder()->mBorderSpacingCol;
    3962             : }
    3963             : 
    3964             : // XXX: could cache this.  But be sure to check style changes if you do!
    3965             : nscoord
    3966           0 : nsTableFrame::GetColSpacing(int32_t aColIndex)
    3967             : {
    3968           0 :   NS_ASSERTION(aColIndex >= -1 && aColIndex <= GetColCount(),
    3969             :                "Column index exceeds the bounds of the table");
    3970             :   // Index is irrelevant for ordinary tables.  We check that it falls within
    3971             :   // appropriate bounds to increase confidence of correctness in situations
    3972             :   // where it does matter.
    3973           0 :   return GetColSpacing();
    3974             : }
    3975             : 
    3976             : nscoord
    3977           0 : nsTableFrame::GetColSpacing(int32_t aStartColIndex,
    3978             :                             int32_t aEndColIndex)
    3979             : {
    3980           0 :   NS_ASSERTION(aStartColIndex >= -1 && aStartColIndex <= GetColCount(),
    3981             :                "Start column index exceeds the bounds of the table");
    3982           0 :   NS_ASSERTION(aEndColIndex >= -1 && aEndColIndex <= GetColCount(),
    3983             :                "End column index exceeds the bounds of the table");
    3984           0 :   NS_ASSERTION(aStartColIndex <= aEndColIndex,
    3985             :                "End index must not be less than start index");
    3986             :   // Only one possible value so just multiply it out. Tables where index
    3987             :   // matters will override this function
    3988           0 :   return GetColSpacing() * (aEndColIndex - aStartColIndex);
    3989             : }
    3990             : 
    3991             : nscoord
    3992           0 : nsTableFrame::GetRowSpacing()
    3993             : {
    3994           0 :   if (IsBorderCollapse())
    3995           0 :     return 0;
    3996             : 
    3997           0 :   return StyleTableBorder()->mBorderSpacingRow;
    3998             : }
    3999             : 
    4000             : // XXX: could cache this. But be sure to check style changes if you do!
    4001             : nscoord
    4002           0 : nsTableFrame::GetRowSpacing(int32_t aRowIndex)
    4003             : {
    4004           0 :   NS_ASSERTION(aRowIndex >= -1 && aRowIndex <= GetRowCount(),
    4005             :                "Row index exceeds the bounds of the table");
    4006             :   // Index is irrelevant for ordinary tables.  We check that it falls within
    4007             :   // appropriate bounds to increase confidence of correctness in situations
    4008             :   // where it does matter.
    4009           0 :   return GetRowSpacing();
    4010             : }
    4011             : 
    4012             : nscoord
    4013           0 : nsTableFrame::GetRowSpacing(int32_t aStartRowIndex,
    4014             :                             int32_t aEndRowIndex)
    4015             : {
    4016           0 :   NS_ASSERTION(aStartRowIndex >= -1 && aStartRowIndex <= GetRowCount(),
    4017             :                "Start row index exceeds the bounds of the table");
    4018           0 :   NS_ASSERTION(aEndRowIndex >= -1 && aEndRowIndex <= GetRowCount(),
    4019             :                "End row index exceeds the bounds of the table");
    4020           0 :   NS_ASSERTION(aStartRowIndex <= aEndRowIndex,
    4021             :                "End index must not be less than start index");
    4022             :   // Only one possible value so just multiply it out. Tables where index
    4023             :   // matters will override this function
    4024           0 :   return GetRowSpacing() * (aEndRowIndex - aStartRowIndex);
    4025             : }
    4026             : 
    4027             : /* virtual */ nscoord
    4028           0 : nsTableFrame::GetLogicalBaseline(WritingMode aWM) const
    4029             : {
    4030             :   nscoord baseline;
    4031           0 :   if (!GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, &baseline)) {
    4032           0 :     baseline = BSize(aWM);
    4033             :   }
    4034           0 :   return baseline;
    4035             : }
    4036             : 
    4037             : /* virtual */ bool
    4038           0 : nsTableFrame::GetNaturalBaselineBOffset(WritingMode aWM,
    4039             :                                         BaselineSharingGroup aBaselineGroup,
    4040             :                                         nscoord*             aBaseline) const
    4041             : {
    4042           0 :   RowGroupArray orderedRowGroups;
    4043           0 :   OrderRowGroups(orderedRowGroups);
    4044             :   // XXX not sure if this should be the size of the containing block instead.
    4045           0 :   nsSize containerSize = mRect.Size();
    4046             :   auto TableBaseline = [aWM, containerSize] (nsTableRowGroupFrame* aRowGroup,
    4047           0 :                                              nsTableRowFrame* aRow) {
    4048           0 :     nscoord rgBStart = LogicalRect(aWM, aRowGroup->GetNormalRect(),
    4049           0 :                                    containerSize).BStart(aWM);
    4050           0 :     nscoord rowBStart = LogicalRect(aWM, aRow->GetNormalRect(),
    4051           0 :                                     containerSize).BStart(aWM);
    4052           0 :     return rgBStart + rowBStart + aRow->GetRowBaseline(aWM);
    4053           0 :   };
    4054           0 :   if (aBaselineGroup == BaselineSharingGroup::eFirst) {
    4055           0 :     for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
    4056           0 :       nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
    4057           0 :       nsTableRowFrame* row = rgFrame->GetFirstRow();
    4058           0 :       if (row) {
    4059           0 :         *aBaseline = TableBaseline(rgFrame, row);
    4060           0 :         return true;
    4061             :       }
    4062             :     }
    4063             :   } else {
    4064           0 :     for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) {
    4065           0 :       nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
    4066           0 :       nsTableRowFrame* row = rgFrame->GetLastRow();
    4067           0 :       if (row) {
    4068           0 :         *aBaseline = BSize(aWM) - TableBaseline(rgFrame, row);
    4069           0 :         return true;
    4070             :       }
    4071             :     }
    4072             :   }
    4073           0 :   return false;
    4074             : }
    4075             : 
    4076             : /* ----- global methods ----- */
    4077             : 
    4078             : nsTableFrame*
    4079           0 : NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    4080             : {
    4081           0 :   return new (aPresShell) nsTableFrame(aContext);
    4082             : }
    4083             : 
    4084           0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
    4085             : 
    4086             : nsTableFrame*
    4087           0 : nsTableFrame::GetTableFrame(nsIFrame* aFrame)
    4088             : {
    4089           0 :   for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
    4090             :        ancestor = ancestor->GetParent()) {
    4091           0 :     if (ancestor->IsTableFrame()) {
    4092           0 :       return static_cast<nsTableFrame*>(ancestor);
    4093             :     }
    4094             :   }
    4095           0 :   MOZ_CRASH("unable to find table parent");
    4096             :   return nullptr;
    4097             : }
    4098             : 
    4099             : nsTableFrame*
    4100           0 : nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
    4101             :                                           nsIFrame* aFrame,
    4102             :                                           bool* aDidPassThrough)
    4103             : {
    4104           0 :   MOZ_ASSERT(aMustPassThrough == aFrame ||
    4105             :              nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
    4106             :              "aMustPassThrough should be an ancestor");
    4107             : 
    4108             :   // Retrieve the table frame, and check if we hit aMustPassThrough on the
    4109             :   // way.
    4110           0 :   *aDidPassThrough = false;
    4111           0 :   nsTableFrame* tableFrame = nullptr;
    4112           0 :   for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
    4113           0 :     if (ancestor == aMustPassThrough) {
    4114           0 :       *aDidPassThrough = true;
    4115             :     }
    4116           0 :     if (ancestor->IsTableFrame()) {
    4117           0 :       tableFrame = static_cast<nsTableFrame*>(ancestor);
    4118           0 :       break;
    4119             :     }
    4120             :   }
    4121             : 
    4122           0 :   MOZ_ASSERT(tableFrame, "Should have a table frame here");
    4123           0 :   return tableFrame;
    4124             : }
    4125             : 
    4126             : bool
    4127           0 : nsTableFrame::IsAutoBSize(WritingMode aWM)
    4128             : {
    4129           0 :   const nsStyleCoord &bsize = StylePosition()->BSize(aWM);
    4130             :   // Don't consider calc() here like this quirk for percent.
    4131           0 :   return bsize.GetUnit() == eStyleUnit_Auto ||
    4132           0 :          (bsize.GetUnit() == eStyleUnit_Percent &&
    4133           0 :           bsize.GetPercentValue() <= 0.0f);
    4134             : }
    4135             : 
    4136             : nscoord
    4137           0 : nsTableFrame::CalcBorderBoxBSize(const ReflowInput& aState)
    4138             : {
    4139           0 :   nscoord bSize = aState.ComputedBSize();
    4140           0 :   if (NS_AUTOHEIGHT != bSize) {
    4141           0 :     WritingMode wm = aState.GetWritingMode();
    4142           0 :     LogicalMargin borderPadding = GetChildAreaOffset(wm, &aState);
    4143           0 :     bSize += borderPadding.BStartEnd(wm);
    4144             :   }
    4145           0 :   bSize = std::max(0, bSize);
    4146             : 
    4147           0 :   return bSize;
    4148             : }
    4149             : 
    4150             : bool
    4151           0 : nsTableFrame::IsAutoLayout()
    4152             : {
    4153           0 :   if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
    4154           0 :     return true;
    4155             :   // a fixed-layout inline-table must have a inline size
    4156             :   // and tables with inline size set to '-moz-max-content' must be
    4157             :   // auto-layout (at least as long as
    4158             :   // FixedTableLayoutStrategy::GetPrefISize returns nscoord_MAX)
    4159           0 :   const nsStyleCoord &iSize = StylePosition()->ISize(GetWritingMode());
    4160           0 :   return (iSize.GetUnit() == eStyleUnit_Auto) ||
    4161           0 :          (iSize.GetUnit() == eStyleUnit_Enumerated &&
    4162           0 :           iSize.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
    4163             : }
    4164             : 
    4165             : #ifdef DEBUG_FRAME_DUMP
    4166             : nsresult
    4167           0 : nsTableFrame::GetFrameName(nsAString& aResult) const
    4168             : {
    4169           0 :   return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
    4170             : }
    4171             : #endif
    4172             : 
    4173             : // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
    4174             : // is of type aChildType
    4175             : nsIFrame*
    4176           0 : nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
    4177             :                                  nsIFrame* aPriorChildFrame,
    4178             :                                  LayoutFrameType aChildType)
    4179             : {
    4180           0 :   nsIFrame* result = nullptr;
    4181           0 :   if (!aPriorChildFrame) {
    4182           0 :     return result;
    4183             :   }
    4184           0 :   if (aChildType == aPriorChildFrame->Type()) {
    4185           0 :     return aPriorChildFrame;
    4186             :   }
    4187             : 
    4188             :   // aPriorChildFrame is not of type aChildType, so we need start from
    4189             :   // the beginnng and find the closest one
    4190           0 :   nsIFrame* lastMatchingFrame = nullptr;
    4191           0 :   nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
    4192           0 :   while (childFrame && (childFrame != aPriorChildFrame)) {
    4193           0 :     if (aChildType == childFrame->Type()) {
    4194           0 :       lastMatchingFrame = childFrame;
    4195             :     }
    4196           0 :     childFrame = childFrame->GetNextSibling();
    4197             :   }
    4198           0 :   return lastMatchingFrame;
    4199             : }
    4200             : 
    4201             : #ifdef DEBUG
    4202             : void
    4203           0 : nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
    4204             : {
    4205           0 :   if (!aKidFrame)
    4206           0 :     return;
    4207             : 
    4208           0 :   for (nsIFrame* cFrame : aKidFrame->PrincipalChildList()) {
    4209           0 :     nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
    4210           0 :     if (rowFrame) {
    4211           0 :       printf("row(%d)=%p ", rowFrame->GetRowIndex(),
    4212           0 :              static_cast<void*>(rowFrame));
    4213           0 :       for (nsIFrame* childFrame : cFrame->PrincipalChildList()) {
    4214           0 :         nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
    4215           0 :         if (cellFrame) {
    4216             :           int32_t colIndex;
    4217           0 :           cellFrame->GetColIndex(colIndex);
    4218           0 :           printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame));
    4219             :         }
    4220             :       }
    4221           0 :       printf("\n");
    4222             :     }
    4223             :     else {
    4224           0 :       DumpRowGroup(rowFrame);
    4225             :     }
    4226             :   }
    4227             : }
    4228             : 
    4229             : void
    4230           0 : nsTableFrame::Dump(bool            aDumpRows,
    4231             :                    bool            aDumpCols,
    4232             :                    bool            aDumpCellMap)
    4233             : {
    4234           0 :   printf("***START TABLE DUMP*** \n");
    4235             :   // dump the columns widths array
    4236           0 :   printf("mColWidths=");
    4237           0 :   int32_t numCols = GetColCount();
    4238             :   int32_t colIdx;
    4239           0 :   nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
    4240           0 :   for (colIdx = 0; colIdx < numCols; colIdx++) {
    4241           0 :     printf("%d ", fif->GetColumnISizeFromFirstInFlow(colIdx));
    4242             :   }
    4243           0 :   printf("\n");
    4244             : 
    4245           0 :   if (aDumpRows) {
    4246           0 :     nsIFrame* kidFrame = mFrames.FirstChild();
    4247           0 :     while (kidFrame) {
    4248           0 :       DumpRowGroup(kidFrame);
    4249           0 :       kidFrame = kidFrame->GetNextSibling();
    4250             :     }
    4251             :   }
    4252             : 
    4253           0 :   if (aDumpCols) {
    4254             :           // output col frame cache
    4255           0 :     printf("\n col frame cache ->");
    4256           0 :            for (colIdx = 0; colIdx < numCols; colIdx++) {
    4257           0 :       nsTableColFrame* colFrame = mColFrames.ElementAt(colIdx);
    4258           0 :       if (0 == (colIdx % 8)) {
    4259           0 :         printf("\n");
    4260             :       }
    4261           0 :       printf ("%d=%p ", colIdx, static_cast<void*>(colFrame));
    4262           0 :       nsTableColType colType = colFrame->GetColType();
    4263           0 :       switch (colType) {
    4264             :       case eColContent:
    4265           0 :         printf(" content ");
    4266           0 :         break;
    4267             :       case eColAnonymousCol:
    4268           0 :         printf(" anonymous-column ");
    4269           0 :         break;
    4270             :       case eColAnonymousColGroup:
    4271           0 :         printf(" anonymous-colgroup ");
    4272           0 :         break;
    4273             :       case eColAnonymousCell:
    4274           0 :         printf(" anonymous-cell ");
    4275           0 :         break;
    4276             :       }
    4277             :     }
    4278           0 :     printf("\n colgroups->");
    4279           0 :     for (nsIFrame* childFrame : mColGroups) {
    4280           0 :       if (LayoutFrameType::TableColGroup == childFrame->Type()) {
    4281           0 :         nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
    4282           0 :         colGroupFrame->Dump(1);
    4283             :       }
    4284             :     }
    4285           0 :     for (colIdx = 0; colIdx < numCols; colIdx++) {
    4286           0 :       printf("\n");
    4287           0 :       nsTableColFrame* colFrame = GetColFrame(colIdx);
    4288           0 :       colFrame->Dump(1);
    4289             :     }
    4290             :   }
    4291           0 :   if (aDumpCellMap) {
    4292           0 :     nsTableCellMap* cellMap = GetCellMap();
    4293           0 :     cellMap->Dump();
    4294             :   }
    4295           0 :   printf(" ***END TABLE DUMP*** \n");
    4296           0 : }
    4297             : #endif
    4298             : 
    4299             : bool
    4300           0 : nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const
    4301             : {
    4302             :   // Since fixed-layout tables should not have their column sizes change
    4303             :   // as they load, we assume that all columns are significant.
    4304           0 :   if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
    4305           0 :     return true;
    4306             :   // the first column is always significant
    4307           0 :   if (aColIndex == 0)
    4308           0 :     return true;
    4309           0 :   nsTableCellMap* cellMap = GetCellMap();
    4310           0 :   if (!cellMap)
    4311           0 :     return false;
    4312           0 :   return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
    4313             : }
    4314             : 
    4315             : /********************************************************************************
    4316             :  * Collapsing Borders
    4317             :  *
    4318             :  *  The CSS spec says to resolve border conflicts in this order:
    4319             :  *  1) any border with the style HIDDEN wins
    4320             :  *  2) the widest border with a style that is not NONE wins
    4321             :  *  3) the border styles are ranked in this order, highest to lowest precedence:
    4322             :  *     double, solid, dashed, dotted, ridge, outset, groove, inset
    4323             :  *  4) borders that are of equal width and style (differ only in color) have this precedence:
    4324             :  *     cell, row, rowgroup, col, colgroup, table
    4325             :  *  5) if all border styles are NONE, then that's the computed border style.
    4326             :  *******************************************************************************/
    4327             : 
    4328             : #ifdef DEBUG
    4329             : #define VerifyNonNegativeDamageRect(r)                                  \
    4330             :   NS_ASSERTION((r).StartCol() >= 0, "negative col index");              \
    4331             :   NS_ASSERTION((r).StartRow() >= 0, "negative row index");              \
    4332             :   NS_ASSERTION((r).ColCount() >= 0, "negative cols damage");            \
    4333             :   NS_ASSERTION((r).RowCount() >= 0, "negative rows damage");
    4334             : #define VerifyDamageRect(r)                                             \
    4335             :   VerifyNonNegativeDamageRect(r);                                       \
    4336             :   NS_ASSERTION((r).EndCol() <= GetColCount(),                           \
    4337             :                "cols damage extends outside table");                    \
    4338             :   NS_ASSERTION((r).EndRow() <= GetRowCount(),                           \
    4339             :                "rows damage extends outside table");
    4340             : #endif
    4341             : 
    4342             : void
    4343           0 : nsTableFrame::AddBCDamageArea(const TableArea& aValue)
    4344             : {
    4345           0 :   NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
    4346             : #ifdef DEBUG
    4347           0 :   VerifyDamageRect(aValue);
    4348             : #endif
    4349             : 
    4350           0 :   SetNeedToCalcBCBorders(true);
    4351           0 :   SetNeedToCalcHasBCBorders(true);
    4352             :   // Get the property
    4353           0 :   BCPropertyData* value = GetOrCreateBCProperty();
    4354           0 :   if (value) {
    4355             : #ifdef DEBUG
    4356           0 :     VerifyNonNegativeDamageRect(value->mDamageArea);
    4357             : #endif
    4358             :     // Clamp the old damage area to the current table area in case it shrunk.
    4359           0 :     int32_t cols = GetColCount();
    4360           0 :     if (value->mDamageArea.EndCol() > cols) {
    4361           0 :       if (value->mDamageArea.StartCol() > cols) {
    4362           0 :         value->mDamageArea.StartCol() = cols;
    4363           0 :         value->mDamageArea.ColCount() = 0;
    4364             :       }
    4365             :       else {
    4366           0 :         value->mDamageArea.ColCount() = cols - value->mDamageArea.StartCol();
    4367             :       }
    4368             :     }
    4369           0 :     int32_t rows = GetRowCount();
    4370           0 :     if (value->mDamageArea.EndRow() > rows) {
    4371           0 :       if (value->mDamageArea.StartRow() > rows) {
    4372           0 :         value->mDamageArea.StartRow() = rows;
    4373           0 :         value->mDamageArea.RowCount() = 0;
    4374             :       }
    4375             :       else {
    4376           0 :         value->mDamageArea.RowCount() = rows - value->mDamageArea.StartRow();
    4377             :       }
    4378             :     }
    4379             : 
    4380             :     // Construct a union of the new and old damage areas.
    4381           0 :     value->mDamageArea.UnionArea(value->mDamageArea, aValue);
    4382             :   }
    4383           0 : }
    4384             : 
    4385             : 
    4386             : void
    4387           0 : nsTableFrame::SetFullBCDamageArea()
    4388             : {
    4389           0 :   NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
    4390             : 
    4391           0 :   SetNeedToCalcBCBorders(true);
    4392           0 :   SetNeedToCalcHasBCBorders(true);
    4393             : 
    4394           0 :   BCPropertyData* value = GetOrCreateBCProperty();
    4395           0 :   if (value) {
    4396           0 :     value->mDamageArea = TableArea(0, 0, GetColCount(), GetRowCount());
    4397             :   }
    4398           0 : }
    4399             : 
    4400             : 
    4401             : /* BCCellBorder represents a border segment which can be either an inline-dir
    4402             :  * or a block-dir segment. For each segment we need to know the color, width,
    4403             :  * style, who owns it and how long it is in cellmap coordinates.
    4404             :  * Ownership of these segments is important to calculate which corners should
    4405             :  * be bevelled. This structure has dual use, its used first to compute the
    4406             :  * dominant border for inline-dir and block-dir segments and to store the
    4407             :  * preliminary computed border results in the BCCellBorders structure.
    4408             :  * This temporary storage is not symmetric with respect to inline-dir and
    4409             :  * block-dir border segments, its always column oriented. For each column in
    4410             :  * the cellmap there is a temporary stored block-dir and inline-dir segment.
    4411             :  * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
    4412             :  */
    4413             : struct BCCellBorder
    4414             : {
    4415           0 :   BCCellBorder() { Reset(0, 1); }
    4416             :   void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
    4417             :   nscolor       color;    // border segment color
    4418             :   BCPixelSize   width;    // border segment width in pixel coordinates !!
    4419             :   uint8_t       style;    // border segment style, possible values are defined
    4420             :                           // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
    4421             :   BCBorderOwner owner;    // border segment owner, possible values are defined
    4422             :                           // in celldata.h. In the cellmap for each border
    4423             :                           // segment we store the owner and later when
    4424             :                           // painting we know the owner and can retrieve the
    4425             :                           // style info from the corresponding frame
    4426             :   int32_t       rowIndex; // rowIndex of temporary stored inline-dir border
    4427             :                           // segments relative to the table
    4428             :   int32_t       rowSpan;  // row span of temporary stored inline-dir border
    4429             :                           // segments
    4430             : };
    4431             : 
    4432             : void
    4433           0 : BCCellBorder::Reset(uint32_t aRowIndex,
    4434             :                     uint32_t aRowSpan)
    4435             : {
    4436           0 :   style = NS_STYLE_BORDER_STYLE_NONE;
    4437           0 :   color = 0;
    4438           0 :   width = 0;
    4439           0 :   owner = eTableOwner;
    4440           0 :   rowIndex = aRowIndex;
    4441           0 :   rowSpan  = aRowSpan;
    4442           0 : }
    4443             : 
    4444             : class BCMapCellIterator;
    4445             : 
    4446             : /*****************************************************************
    4447             :  *  BCMapCellInfo
    4448             :  * This structure stores information about the cellmap and all involved
    4449             :  * table related frames that are used during the computation of winning borders
    4450             :  * in CalcBCBorders so that they do need to be looked up again and again when
    4451             :  * iterating over the cells.
    4452             :  ****************************************************************/
    4453             : struct BCMapCellInfo
    4454             : {
    4455             :   explicit BCMapCellInfo(nsTableFrame* aTableFrame);
    4456             :   void ResetCellInfo();
    4457             :   void SetInfo(nsTableRowFrame*   aNewRow,
    4458             :                int32_t            aColIndex,
    4459             :                BCCellData*        aCellData,
    4460             :                BCMapCellIterator* aIter,
    4461             :                nsCellMap*         aCellMap = nullptr);
    4462             :   // The BCMapCellInfo has functions to set the continous
    4463             :   // border widths (see nsTablePainter.cpp for a description of the continous
    4464             :   // borders concept). The widths are computed inside these functions based on
    4465             :   // the current position inside the table and the cached frames that correspond
    4466             :   // to this position. The widths are stored in member variables of the internal
    4467             :   // table frames.
    4468             :   void SetTableBStartIStartContBCBorder();
    4469             :   void SetRowGroupIStartContBCBorder();
    4470             :   void SetRowGroupIEndContBCBorder();
    4471             :   void SetRowGroupBEndContBCBorder();
    4472             :   void SetRowIStartContBCBorder();
    4473             :   void SetRowIEndContBCBorder();
    4474             :   void SetColumnBStartIEndContBCBorder();
    4475             :   void SetColumnBEndContBCBorder();
    4476             :   void SetColGroupBEndContBCBorder();
    4477             :   void SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
    4478             :                                         nsTableRowFrame* aNextRow);
    4479             : 
    4480             :   // functions to set the border widths on the table related frames, where the
    4481             :   // knowledge about the current position in the table is used.
    4482             :   void SetTableBStartBorderWidth(BCPixelSize aWidth);
    4483             :   void SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth);
    4484             :   void SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth);
    4485             :   void SetTableBEndBorderWidth(BCPixelSize aWidth);
    4486             :   void SetIStartBorderWidths(BCPixelSize aWidth);
    4487             :   void SetIEndBorderWidths(BCPixelSize aWidth);
    4488             :   void SetBStartBorderWidths(BCPixelSize aWidth);
    4489             :   void SetBEndBorderWidths(BCPixelSize aWidth);
    4490             : 
    4491             :   // functions to compute the borders; they depend on the
    4492             :   // knowledge about the current position in the table. The edge functions
    4493             :   // should be called if a table edge is involved, otherwise the internal
    4494             :   // functions should be called.
    4495             :   BCCellBorder GetBStartEdgeBorder();
    4496             :   BCCellBorder GetBEndEdgeBorder();
    4497             :   BCCellBorder GetIStartEdgeBorder();
    4498             :   BCCellBorder GetIEndEdgeBorder();
    4499             :   BCCellBorder GetIEndInternalBorder();
    4500             :   BCCellBorder GetIStartInternalBorder();
    4501             :   BCCellBorder GetBStartInternalBorder();
    4502             :   BCCellBorder GetBEndInternalBorder();
    4503             : 
    4504             :   // functions to set the internal position information
    4505             :   void SetColumn(int32_t aColX);
    4506             :   // Increment the row as we loop over the rows of a rowspan
    4507             :   void IncrementRow(bool aResetToBStartRowOfCell = false);
    4508             : 
    4509             :   // Helper functions to get extent of the cell
    4510             :   int32_t GetCellEndRowIndex() const;
    4511             :   int32_t GetCellEndColIndex() const;
    4512             : 
    4513             :   // storage of table information
    4514             :   nsTableFrame*         mTableFrame;
    4515             :   int32_t               mNumTableRows;
    4516             :   int32_t               mNumTableCols;
    4517             :   BCPropertyData*       mTableBCData;
    4518             :   WritingMode           mTableWM;
    4519             : 
    4520             :   // a cell can only belong to one rowgroup
    4521             :   nsTableRowGroupFrame* mRowGroup;
    4522             : 
    4523             :   // a cell with a rowspan has a bstart and a bend row, and rows in between
    4524             :   nsTableRowFrame*      mStartRow;
    4525             :   nsTableRowFrame*      mEndRow;
    4526             :   nsTableRowFrame*      mCurrentRowFrame;
    4527             : 
    4528             :   // a cell with a colspan has an istart and iend column and columns in between
    4529             :   // they can belong to different colgroups
    4530             :   nsTableColGroupFrame* mColGroup;
    4531             :   nsTableColGroupFrame* mCurrentColGroupFrame;
    4532             : 
    4533             :   nsTableColFrame*      mStartCol;
    4534             :   nsTableColFrame*      mEndCol;
    4535             :   nsTableColFrame*      mCurrentColFrame;
    4536             : 
    4537             :   // cell information
    4538             :   BCCellData*           mCellData;
    4539             :   nsBCTableCellFrame*   mCell;
    4540             : 
    4541             :   int32_t               mRowIndex;
    4542             :   int32_t               mRowSpan;
    4543             :   int32_t               mColIndex;
    4544             :   int32_t               mColSpan;
    4545             : 
    4546             :   // flags to describe the position of the cell with respect to the row- and
    4547             :   // colgroups, for instance mRgAtStart documents that the bStart cell border hits
    4548             :   // a rowgroup border
    4549             :   bool                  mRgAtStart;
    4550             :   bool                  mRgAtEnd;
    4551             :   bool                  mCgAtStart;
    4552             :   bool                  mCgAtEnd;
    4553             : 
    4554             : };
    4555             : 
    4556             : 
    4557           0 : BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
    4558             :   : mTableFrame(aTableFrame)
    4559           0 :   , mNumTableRows(aTableFrame->GetRowCount())
    4560           0 :   , mNumTableCols(aTableFrame->GetColCount())
    4561           0 :   , mTableBCData(mTableFrame->GetProperty(TableBCProperty()))
    4562           0 :   , mTableWM(aTableFrame->StyleContext())
    4563             : {
    4564           0 :   ResetCellInfo();
    4565           0 : }
    4566             : 
    4567             : void
    4568           0 : BCMapCellInfo::ResetCellInfo()
    4569             : {
    4570           0 :   mCellData  = nullptr;
    4571           0 :   mRowGroup  = nullptr;
    4572           0 :   mStartRow  = nullptr;
    4573           0 :   mEndRow    = nullptr;
    4574           0 :   mColGroup  = nullptr;
    4575           0 :   mStartCol  = nullptr;
    4576           0 :   mEndCol    = nullptr;
    4577           0 :   mCell      = nullptr;
    4578           0 :   mRowIndex  = mRowSpan = mColIndex = mColSpan = 0;
    4579           0 :   mRgAtStart = mRgAtEnd = mCgAtStart = mCgAtEnd = false;
    4580           0 : }
    4581             : 
    4582             : inline int32_t
    4583           0 : BCMapCellInfo::GetCellEndRowIndex() const
    4584             : {
    4585           0 :   return mRowIndex + mRowSpan - 1;
    4586             : }
    4587             : 
    4588             : inline int32_t
    4589           0 : BCMapCellInfo::GetCellEndColIndex() const
    4590             : {
    4591           0 :   return mColIndex + mColSpan - 1;
    4592             : }
    4593             : 
    4594             : 
    4595           0 : class BCMapCellIterator
    4596             : {
    4597             : public:
    4598             :   BCMapCellIterator(nsTableFrame* aTableFrame,
    4599             :                     const TableArea& aDamageArea);
    4600             : 
    4601             :   void First(BCMapCellInfo& aMapCellInfo);
    4602             : 
    4603             :   void Next(BCMapCellInfo& aMapCellInfo);
    4604             : 
    4605             :   void PeekIEnd(BCMapCellInfo& aRefInfo,
    4606             :                 uint32_t       aRowIndex,
    4607             :                 BCMapCellInfo& aAjaInfo);
    4608             : 
    4609             :   void PeekBEnd(BCMapCellInfo& aRefInfo,
    4610             :                 uint32_t       aColIndex,
    4611             :                 BCMapCellInfo& aAjaInfo);
    4612             : 
    4613           0 :   bool IsNewRow() { return mIsNewRow; }
    4614             : 
    4615             :   nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
    4616           0 :   nsTableRowFrame* GetCurrentRow() const { return mRow; }
    4617           0 :   nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup; }
    4618             : 
    4619             :   int32_t    mRowGroupStart;
    4620             :   int32_t    mRowGroupEnd;
    4621             :   bool       mAtEnd;
    4622             :   nsCellMap* mCellMap;
    4623             : 
    4624             : private:
    4625             :   bool SetNewRow(nsTableRowFrame* row = nullptr);
    4626             :   bool SetNewRowGroup(bool aFindFirstDamagedRow);
    4627             : 
    4628             :   nsTableFrame*         mTableFrame;
    4629             :   nsTableCellMap*       mTableCellMap;
    4630             :   nsTableFrame::RowGroupArray mRowGroups;
    4631             :   nsTableRowGroupFrame* mRowGroup;
    4632             :   int32_t               mRowGroupIndex;
    4633             :   uint32_t              mNumTableRows;
    4634             :   nsTableRowFrame*      mRow;
    4635             :   nsTableRowFrame*      mPrevRow;
    4636             :   bool                  mIsNewRow;
    4637             :   int32_t               mRowIndex;
    4638             :   uint32_t              mNumTableCols;
    4639             :   int32_t               mColIndex;
    4640             :   nsPoint               mAreaStart; // These are not really points in the usual
    4641             :   nsPoint               mAreaEnd;   // sense; they're column/row coordinates
    4642             :                                     // in the cell map.
    4643             : };
    4644             : 
    4645           0 : BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
    4646           0 :                                      const TableArea& aDamageArea)
    4647           0 :   : mTableFrame(aTableFrame)
    4648             : {
    4649           0 :   mTableCellMap  = aTableFrame->GetCellMap();
    4650             : 
    4651           0 :   mAreaStart.x   = aDamageArea.StartCol();
    4652           0 :   mAreaStart.y   = aDamageArea.StartRow();
    4653           0 :   mAreaEnd.x     = aDamageArea.EndCol() - 1;
    4654           0 :   mAreaEnd.y     = aDamageArea.EndRow() - 1;
    4655             : 
    4656           0 :   mNumTableRows  = mTableFrame->GetRowCount();
    4657           0 :   mRow           = nullptr;
    4658           0 :   mRowIndex      = 0;
    4659           0 :   mNumTableCols  = mTableFrame->GetColCount();
    4660           0 :   mColIndex      = 0;
    4661           0 :   mRowGroupIndex = -1;
    4662             : 
    4663             :   // Get the ordered row groups
    4664           0 :   aTableFrame->OrderRowGroups(mRowGroups);
    4665             : 
    4666           0 :   mAtEnd = true; // gets reset when First() is called
    4667           0 : }
    4668             : 
    4669             : // fill fields that we need for border collapse computation on a given cell
    4670             : void
    4671           0 : BCMapCellInfo::SetInfo(nsTableRowFrame*   aNewRow,
    4672             :                        int32_t            aColIndex,
    4673             :                        BCCellData*        aCellData,
    4674             :                        BCMapCellIterator* aIter,
    4675             :                        nsCellMap*         aCellMap)
    4676             : {
    4677             :   // fill the cell information
    4678           0 :   mCellData = aCellData;
    4679           0 :   mColIndex = aColIndex;
    4680             : 
    4681             :   // initialize the row information if it was not previously set for cells in
    4682             :   // this row
    4683           0 :   mRowIndex = 0;
    4684           0 :   if (aNewRow) {
    4685           0 :     mStartRow = aNewRow;
    4686           0 :     mRowIndex = aNewRow->GetRowIndex();
    4687             :   }
    4688             : 
    4689             :   // fill cell frame info and row information
    4690           0 :   mCell      = nullptr;
    4691           0 :   mRowSpan   = 1;
    4692           0 :   mColSpan   = 1;
    4693           0 :   if (aCellData) {
    4694           0 :     mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
    4695           0 :     if (mCell) {
    4696           0 :       if (!mStartRow) {
    4697           0 :         mStartRow = mCell->GetTableRowFrame();
    4698           0 :         if (!mStartRow) ABORT0();
    4699           0 :         mRowIndex = mStartRow->GetRowIndex();
    4700             :       }
    4701           0 :       mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
    4702           0 :       mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
    4703             :     }
    4704             :   }
    4705             : 
    4706           0 :   if (!mStartRow) {
    4707           0 :     mStartRow = aIter->GetCurrentRow();
    4708             :   }
    4709           0 :   if (1 == mRowSpan) {
    4710           0 :     mEndRow = mStartRow;
    4711             :   }
    4712             :   else {
    4713           0 :     mEndRow = mStartRow->GetNextRow();
    4714           0 :     if (mEndRow) {
    4715           0 :       for (int32_t span = 2; mEndRow && span < mRowSpan; span++) {
    4716           0 :         mEndRow = mEndRow->GetNextRow();
    4717             :       }
    4718           0 :       NS_ASSERTION(mEndRow, "spanned row not found");
    4719             :     }
    4720             :     else {
    4721           0 :       NS_ERROR("error in cell map");
    4722           0 :       mRowSpan = 1;
    4723           0 :       mEndRow = mStartRow;
    4724             :     }
    4725             :   }
    4726             :   // row group frame info
    4727             :   // try to reuse the rgStart and rgEnd from the iterator as calls to
    4728             :   // GetRowCount() are computationally expensive and should be avoided if
    4729             :   // possible
    4730           0 :   uint32_t rgStart  = aIter->mRowGroupStart;
    4731           0 :   uint32_t rgEnd    = aIter->mRowGroupEnd;
    4732           0 :   mRowGroup = mStartRow->GetTableRowGroupFrame();
    4733           0 :   if (mRowGroup != aIter->GetCurrentRowGroup()) {
    4734           0 :     rgStart = mRowGroup->GetStartRowIndex();
    4735           0 :     rgEnd   = rgStart + mRowGroup->GetRowCount() - 1;
    4736             :   }
    4737           0 :   uint32_t rowIndex = mStartRow->GetRowIndex();
    4738           0 :   mRgAtStart = rgStart == rowIndex;
    4739           0 :   mRgAtEnd = rgEnd == rowIndex + mRowSpan - 1;
    4740             : 
    4741             :    // col frame info
    4742           0 :   mStartCol = mTableFrame->GetColFrame(aColIndex);
    4743           0 :   if (!mStartCol) ABORT0();
    4744             : 
    4745           0 :   mEndCol = mStartCol;
    4746           0 :   if (mColSpan > 1) {
    4747           0 :     nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
    4748           0 :                                                          mColSpan -1);
    4749           0 :     if (!colFrame) ABORT0();
    4750           0 :     mEndCol = colFrame;
    4751             :   }
    4752             : 
    4753             :   // col group frame info
    4754           0 :   mColGroup = mStartCol->GetTableColGroupFrame();
    4755           0 :   int32_t cgStart = mColGroup->GetStartColumnIndex();
    4756           0 :   int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
    4757           0 :   mCgAtStart = cgStart == aColIndex;
    4758           0 :   mCgAtEnd = cgEnd == aColIndex + mColSpan - 1;
    4759             : }
    4760             : 
    4761             : bool
    4762           0 : BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
    4763             : {
    4764           0 :   mAtEnd   = true;
    4765           0 :   mPrevRow = mRow;
    4766           0 :   if (aRow) {
    4767           0 :     mRow = aRow;
    4768             :   }
    4769           0 :   else if (mRow) {
    4770           0 :     mRow = mRow->GetNextRow();
    4771             :   }
    4772           0 :   if (mRow) {
    4773           0 :     mRowIndex = mRow->GetRowIndex();
    4774             :     // get to the first entry with an originating cell
    4775           0 :     int32_t rgRowIndex = mRowIndex - mRowGroupStart;
    4776           0 :     if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length())
    4777           0 :       ABORT1(false);
    4778           0 :     const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
    4779             : 
    4780           0 :     for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
    4781           0 :       CellData* cellData = row.SafeElementAt(mColIndex);
    4782           0 :       if (!cellData) { // add a dead cell data
    4783           0 :         TableArea damageArea;
    4784           0 :         cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
    4785           0 :                                         false, 0, damageArea);
    4786           0 :         if (!cellData) ABORT1(false);
    4787             :       }
    4788           0 :       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
    4789           0 :         break;
    4790             :       }
    4791             :     }
    4792           0 :     mIsNewRow = true;
    4793           0 :     mAtEnd    = false;
    4794             :   }
    4795           0 :   else ABORT1(false);
    4796             : 
    4797           0 :   return !mAtEnd;
    4798             : }
    4799             : 
    4800             : bool
    4801           0 : BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
    4802             : {
    4803           0 :   mAtEnd = true;
    4804           0 :   int32_t numRowGroups = mRowGroups.Length();
    4805           0 :   mCellMap = nullptr;
    4806           0 :   for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
    4807           0 :     mRowGroup = mRowGroups[mRowGroupIndex];
    4808           0 :     int32_t rowCount = mRowGroup->GetRowCount();
    4809           0 :     mRowGroupStart = mRowGroup->GetStartRowIndex();
    4810           0 :     mRowGroupEnd   = mRowGroupStart + rowCount - 1;
    4811           0 :     if (rowCount > 0) {
    4812           0 :       mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
    4813           0 :       if (!mCellMap) ABORT1(false);
    4814           0 :       nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
    4815           0 :       if (aFindFirstDamagedRow) {
    4816           0 :         if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
    4817             :           // the damage area starts in the row group
    4818           0 :           if (aFindFirstDamagedRow) {
    4819             :             // find the correct first damaged row
    4820           0 :             int32_t numRows = mAreaStart.y - mRowGroupStart;
    4821           0 :             for (int32_t i = 0; i < numRows; i++) {
    4822           0 :               firstRow = firstRow->GetNextRow();
    4823           0 :               if (!firstRow) ABORT1(false);
    4824             :             }
    4825           0 :           }
    4826             :         }
    4827             :         else {
    4828           0 :           continue;
    4829             :         }
    4830             :       }
    4831           0 :       if (SetNewRow(firstRow)) { // sets mAtEnd
    4832           0 :         break;
    4833             :       }
    4834             :     }
    4835             :   }
    4836             : 
    4837           0 :   return !mAtEnd;
    4838             : }
    4839             : 
    4840             : void
    4841           0 : BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
    4842             : {
    4843           0 :   aMapInfo.ResetCellInfo();
    4844             : 
    4845           0 :   SetNewRowGroup(true); // sets mAtEnd
    4846           0 :   while (!mAtEnd) {
    4847           0 :     if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
    4848             :       BCCellData* cellData =
    4849           0 :         static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
    4850           0 :                                                      mRowGroupStart,
    4851           0 :                                                      mAreaStart.x));
    4852           0 :       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
    4853           0 :         aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
    4854           0 :         return;
    4855             :       }
    4856             :       else {
    4857           0 :         NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
    4858             :                      "damage area expanded incorrectly");
    4859             :       }
    4860             :     }
    4861           0 :     SetNewRowGroup(true); // sets mAtEnd
    4862             :   }
    4863             : }
    4864             : 
    4865             : void
    4866           0 : BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
    4867             : {
    4868           0 :   if (mAtEnd) ABORT0();
    4869           0 :   aMapInfo.ResetCellInfo();
    4870             : 
    4871           0 :   mIsNewRow = false;
    4872           0 :   mColIndex++;
    4873           0 :   while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
    4874           0 :     for (; mColIndex <= mAreaEnd.x; mColIndex++) {
    4875           0 :       int32_t rgRowIndex = mRowIndex - mRowGroupStart;
    4876             :       BCCellData* cellData =
    4877           0 :          static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
    4878           0 :       if (!cellData) { // add a dead cell data
    4879           0 :         TableArea damageArea;
    4880             :         cellData =
    4881           0 :           static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
    4882             :                                                         rgRowIndex, false, 0,
    4883           0 :                                                         damageArea));
    4884           0 :         if (!cellData) ABORT0();
    4885             :       }
    4886           0 :       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
    4887           0 :         aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
    4888           0 :         return;
    4889             :       }
    4890             :     }
    4891           0 :     if (mRowIndex >= mRowGroupEnd) {
    4892           0 :       SetNewRowGroup(false); // could set mAtEnd
    4893             :     }
    4894             :     else {
    4895           0 :       SetNewRow(); // could set mAtEnd
    4896             :     }
    4897             :   }
    4898           0 :   mAtEnd = true;
    4899             : }
    4900             : 
    4901             : void
    4902           0 : BCMapCellIterator::PeekIEnd(BCMapCellInfo& aRefInfo,
    4903             :                             uint32_t       aRowIndex,
    4904             :                             BCMapCellInfo& aAjaInfo)
    4905             : {
    4906           0 :   aAjaInfo.ResetCellInfo();
    4907           0 :   int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
    4908           0 :   uint32_t rgRowIndex = aRowIndex - mRowGroupStart;
    4909             : 
    4910             :   BCCellData* cellData =
    4911           0 :     static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
    4912           0 :   if (!cellData) { // add a dead cell data
    4913           0 :     NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
    4914           0 :     TableArea damageArea;
    4915             :     cellData =
    4916           0 :       static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
    4917             :                                                     rgRowIndex, false, 0,
    4918           0 :                                                     damageArea));
    4919           0 :     if (!cellData) ABORT0();
    4920             :   }
    4921           0 :   nsTableRowFrame* row = nullptr;
    4922           0 :   if (cellData->IsRowSpan()) {
    4923           0 :     rgRowIndex -= cellData->GetRowSpanOffset();
    4924             :     cellData =
    4925           0 :       static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
    4926           0 :     if (!cellData)
    4927           0 :       ABORT0();
    4928             :   }
    4929             :   else {
    4930           0 :     row = mRow;
    4931             :   }
    4932           0 :   aAjaInfo.SetInfo(row, colIndex, cellData, this);
    4933             : }
    4934             : 
    4935             : void
    4936           0 : BCMapCellIterator::PeekBEnd(BCMapCellInfo& aRefInfo,
    4937             :                             uint32_t       aColIndex,
    4938             :                             BCMapCellInfo& aAjaInfo)
    4939             : {
    4940           0 :   aAjaInfo.ResetCellInfo();
    4941           0 :   int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
    4942           0 :   int32_t rgRowIndex = rowIndex - mRowGroupStart;
    4943           0 :   nsTableRowGroupFrame* rg = mRowGroup;
    4944           0 :   nsCellMap* cellMap = mCellMap;
    4945           0 :   nsTableRowFrame* nextRow = nullptr;
    4946           0 :   if (rowIndex > mRowGroupEnd) {
    4947           0 :     int32_t nextRgIndex = mRowGroupIndex;
    4948           0 :     do {
    4949           0 :       nextRgIndex++;
    4950           0 :       rg = mRowGroups.SafeElementAt(nextRgIndex);
    4951           0 :       if (rg) {
    4952           0 :         cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
    4953           0 :         rgRowIndex = 0;
    4954           0 :         nextRow = rg->GetFirstRow();
    4955             :       }
    4956             :     }
    4957           0 :     while (rg && !nextRow);
    4958           0 :     if(!rg) return;
    4959             :   }
    4960             :   else {
    4961             :     // get the row within the same row group
    4962           0 :     nextRow = mRow;
    4963           0 :     for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
    4964           0 :       nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
    4965             :     }
    4966             :   }
    4967             : 
    4968             :   BCCellData* cellData =
    4969           0 :     static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
    4970           0 :   if (!cellData) { // add a dead cell data
    4971           0 :     NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
    4972           0 :     TableArea damageArea;
    4973             :     cellData =
    4974           0 :       static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr,
    4975             :                                                    rgRowIndex, false, 0,
    4976           0 :                                                    damageArea));
    4977           0 :     if (!cellData) ABORT0();
    4978             :   }
    4979           0 :   if (cellData->IsColSpan()) {
    4980           0 :     aColIndex -= cellData->GetColSpanOffset();
    4981             :     cellData =
    4982           0 :       static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
    4983             :   }
    4984           0 :   aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
    4985             : }
    4986             : 
    4987             : // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
    4988             : // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
    4989             : static uint8_t styleToPriority[13] = { 0,  // NS_STYLE_BORDER_STYLE_NONE
    4990             :                                        2,  // NS_STYLE_BORDER_STYLE_GROOVE
    4991             :                                        4,  // NS_STYLE_BORDER_STYLE_RIDGE
    4992             :                                        5,  // NS_STYLE_BORDER_STYLE_DOTTED
    4993             :                                        6,  // NS_STYLE_BORDER_STYLE_DASHED
    4994             :                                        7,  // NS_STYLE_BORDER_STYLE_SOLID
    4995             :                                        8,  // NS_STYLE_BORDER_STYLE_DOUBLE
    4996             :                                        1,  // NS_STYLE_BORDER_STYLE_INSET
    4997             :                                        3,  // NS_STYLE_BORDER_STYLE_OUTSET
    4998             :                                        9 };// NS_STYLE_BORDER_STYLE_HIDDEN
    4999             : // priority rules follow CSS 2.1 spec
    5000             : // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
    5001             : // and the lowest: 'inset'. none is even weaker
    5002             : #define CELL_CORNER true
    5003             : 
    5004             : /** return the border style, border color and optionally the width in
    5005             :   * pixel for a given frame and side
    5006             :   * @param aFrame           - query the info for this frame
    5007             :   * @param aTableWM         - the writing-mode of the frame
    5008             :   * @param aSide            - the side of the frame
    5009             :   * @param aStyle           - the border style
    5010             :   * @param aColor           - the border color
    5011             :   * @param aWidth           - the border width in px
    5012             :   */
    5013             : static void
    5014           0 : GetColorAndStyle(const nsIFrame* aFrame,
    5015             :                  WritingMode aTableWM,
    5016             :                  LogicalSide aSide,
    5017             :                  uint8_t* aStyle,
    5018             :                  nscolor* aColor,
    5019             :                  BCPixelSize* aWidth = nullptr)
    5020             : {
    5021           0 :   NS_PRECONDITION(aFrame, "null frame");
    5022           0 :   NS_PRECONDITION(aStyle && aColor, "null argument");
    5023             :   // initialize out arg
    5024           0 :   *aColor = 0;
    5025           0 :   if (aWidth) {
    5026           0 :     *aWidth = 0;
    5027             :   }
    5028             : 
    5029           0 :   const nsStyleBorder* styleData = aFrame->StyleBorder();
    5030           0 :   mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
    5031           0 :   *aStyle = styleData->GetBorderStyle(physicalSide);
    5032             : 
    5033           0 :   if ((NS_STYLE_BORDER_STYLE_NONE == *aStyle) ||
    5034           0 :       (NS_STYLE_BORDER_STYLE_HIDDEN == *aStyle)) {
    5035           0 :     return;
    5036             :   }
    5037           0 :   *aColor = aFrame->StyleContext()->
    5038           0 :     GetVisitedDependentColor(nsStyleBorder::BorderColorFieldFor(physicalSide));
    5039             : 
    5040           0 :   if (aWidth) {
    5041           0 :     nscoord width = styleData->GetComputedBorderWidth(physicalSide);
    5042           0 :     *aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
    5043             :   }
    5044             : }
    5045             : 
    5046             : /** coerce the paint style as required by CSS2.1
    5047             :   * @param aFrame           - query the info for this frame
    5048             :   * @param aTableWM         - the writing mode of the frame
    5049             :   * @param aSide            - the side of the frame
    5050             :   * @param aStyle           - the border style
    5051             :   * @param aColor           - the border color
    5052             :   */
    5053             : static void
    5054           0 : GetPaintStyleInfo(const nsIFrame* aFrame,
    5055             :                   WritingMode aTableWM,
    5056             :                   LogicalSide aSide,
    5057             :                   uint8_t* aStyle,
    5058             :                   nscolor* aColor)
    5059             : {
    5060           0 :   GetColorAndStyle(aFrame, aTableWM, aSide, aStyle, aColor);
    5061           0 :   if (NS_STYLE_BORDER_STYLE_INSET == *aStyle) {
    5062           0 :     *aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
    5063           0 :   } else if (NS_STYLE_BORDER_STYLE_OUTSET == *aStyle) {
    5064           0 :     *aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
    5065             :   }
    5066           0 : }
    5067             : 
    5068           0 : class nsDelayedCalcBCBorders : public Runnable {
    5069             : public:
    5070           0 :   explicit nsDelayedCalcBCBorders(nsIFrame* aFrame)
    5071           0 :     : mozilla::Runnable("nsDelayedCalcBCBorders")
    5072           0 :     , mFrame(aFrame)
    5073             :   {
    5074           0 :   }
    5075             : 
    5076           0 :   NS_IMETHOD Run() override {
    5077           0 :     if (mFrame) {
    5078           0 :       nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
    5079           0 :       if (tableFrame->NeedToCalcBCBorders()) {
    5080           0 :         tableFrame->CalcBCBorders();
    5081             :       }
    5082             :     }
    5083           0 :     return NS_OK;
    5084             :   }
    5085             : private:
    5086             :   WeakFrame mFrame;
    5087             : };
    5088             : 
    5089             : bool
    5090           0 : nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
    5091             :                              nsStyleContext* aNewStyleContext)
    5092             : {
    5093             :   // Attention: the old style context is the one we're forgetting,
    5094             :   // and hence possibly completely bogus for GetStyle* purposes.
    5095             :   // We use PeekStyleData instead.
    5096             : 
    5097           0 :   const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
    5098           0 :   if (!oldStyleData)
    5099           0 :     return false;
    5100             : 
    5101           0 :   const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder();
    5102           0 :   nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
    5103           0 :   if (!change)
    5104           0 :     return false;
    5105           0 :   if (change & nsChangeHint_NeedReflow)
    5106           0 :     return true; // the caller only needs to mark the bc damage area
    5107           0 :   if (change & nsChangeHint_RepaintFrame) {
    5108             :     // we need to recompute the borders and the caller needs to mark
    5109             :     // the bc damage area
    5110             :     // XXX In principle this should only be necessary for border style changes
    5111             :     // However the bc painting code tries to maximize the drawn border segments
    5112             :     // so it stores in the cellmap where a new border segment starts and this
    5113             :     // introduces a unwanted cellmap data dependence on color
    5114           0 :     nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
    5115             :     nsresult rv =
    5116           0 :       GetContent()->OwnerDoc()->Dispatch("nsDelayedCalcBCBorders",
    5117           0 :                                          TaskCategory::Other, evt.forget());
    5118           0 :     return NS_SUCCEEDED(rv);
    5119             :   }
    5120           0 :   return false;
    5121             : }
    5122             : 
    5123             : 
    5124             : // Compare two border segments, this comparison depends whether the two
    5125             : // segments meet at a corner and whether the second segment is inline-dir.
    5126             : // The return value is whichever of aBorder1 or aBorder2 dominates.
    5127             : static const BCCellBorder&
    5128           0 : CompareBorders(bool                aIsCorner, // Pass true for corner calculations
    5129             :                const BCCellBorder& aBorder1,
    5130             :                const BCCellBorder& aBorder2,
    5131             :                bool                aSecondIsInlineDir,
    5132             :                bool*               aFirstDominates = nullptr)
    5133             : {
    5134           0 :   bool firstDominates = true;
    5135             : 
    5136           0 :   if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
    5137           0 :     firstDominates = (aIsCorner) ? false : true;
    5138             :   }
    5139           0 :   else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
    5140           0 :     firstDominates = (aIsCorner) ? true : false;
    5141             :   }
    5142           0 :   else if (aBorder1.width < aBorder2.width) {
    5143           0 :     firstDominates = false;
    5144             :   }
    5145           0 :   else if (aBorder1.width == aBorder2.width) {
    5146           0 :     if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
    5147           0 :       firstDominates = false;
    5148             :     }
    5149           0 :     else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
    5150           0 :       if (aBorder1.owner == aBorder2.owner) {
    5151           0 :         firstDominates = !aSecondIsInlineDir;
    5152             :       }
    5153           0 :       else if (aBorder1.owner < aBorder2.owner) {
    5154           0 :         firstDominates = false;
    5155             :       }
    5156             :     }
    5157             :   }
    5158             : 
    5159           0 :   if (aFirstDominates)
    5160           0 :     *aFirstDominates = firstDominates;
    5161             : 
    5162           0 :   if (firstDominates)
    5163           0 :     return aBorder1;
    5164           0 :   return aBorder2;
    5165             : }
    5166             : 
    5167             : /** calc the dominant border by considering the table, row/col group, row/col,
    5168             :   * cell.
    5169             :   * Depending on whether the side is block-dir or inline-dir and whether
    5170             :   * adjacent frames are taken into account the ownership of a single border
    5171             :   * segment is defined. The return value is the dominating border
    5172             :   * The cellmap stores only bstart and istart borders for each cellmap position.
    5173             :   * If the cell border is owned by the cell that is istart-wards of the border
    5174             :   * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
    5175             :   * scenarios with a adjacent owner.
    5176             :   * @param xxxFrame         - the frame for style information, might be zero if
    5177             :   *                           it should not be considered
    5178             :   * @param aTableWM         - the writing mode of the frame
    5179             :   * @param aSide            - side of the frames that should be considered
    5180             :   * @param aAja             - the border comparison takes place from the point of
    5181             :   *                           a frame that is adjacent to the cellmap entry, for
    5182             :   *                           when a cell owns its lower border it will be the
    5183             :   *                           adjacent owner as in the cellmap only bstart and
    5184             :   *                           istart borders are stored.
    5185             :   */
    5186             : static BCCellBorder
    5187           0 : CompareBorders(const nsIFrame*  aTableFrame,
    5188             :                const nsIFrame*  aColGroupFrame,
    5189             :                const nsIFrame*  aColFrame,
    5190             :                const nsIFrame*  aRowGroupFrame,
    5191             :                const nsIFrame*  aRowFrame,
    5192             :                const nsIFrame*  aCellFrame,
    5193             :                WritingMode      aTableWM,
    5194             :                LogicalSide      aSide,
    5195             :                bool             aAja)
    5196             : {
    5197           0 :   BCCellBorder border, tempBorder;
    5198           0 :   bool inlineAxis = IsBlock(aSide);
    5199             : 
    5200             :   // start with the table as dominant if present
    5201           0 :   if (aTableFrame) {
    5202             :     GetColorAndStyle(aTableFrame, aTableWM, aSide,
    5203           0 :                      &border.style, &border.color, &border.width);
    5204           0 :     border.owner = eTableOwner;
    5205           0 :     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
    5206           0 :       return border;
    5207             :     }
    5208             :   }
    5209             :   // see if the colgroup is dominant
    5210           0 :   if (aColGroupFrame) {
    5211             :     GetColorAndStyle(aColGroupFrame, aTableWM, aSide,
    5212           0 :                      &tempBorder.style, &tempBorder.color, &tempBorder.width);
    5213           0 :     tempBorder.owner = aAja && !inlineAxis ? eAjaColGroupOwner : eColGroupOwner;
    5214             :     // pass here and below false for aSecondIsInlineDir as it is only used for corner calculations.
    5215           0 :     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
    5216           0 :     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
    5217           0 :       return border;
    5218             :     }
    5219             :   }
    5220             :   // see if the col is dominant
    5221           0 :   if (aColFrame) {
    5222             :     GetColorAndStyle(aColFrame, aTableWM, aSide,
    5223           0 :                      &tempBorder.style, &tempBorder.color, &tempBorder.width);
    5224           0 :     tempBorder.owner = aAja && !inlineAxis ? eAjaColOwner : eColOwner;
    5225           0 :     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
    5226           0 :     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
    5227           0 :       return border;
    5228             :     }
    5229             :   }
    5230             :   // see if the rowgroup is dominant
    5231           0 :   if (aRowGroupFrame) {
    5232             :     GetColorAndStyle(aRowGroupFrame, aTableWM, aSide,
    5233           0 :                      &tempBorder.style, &tempBorder.color, &tempBorder.width);
    5234           0 :     tempBorder.owner = aAja && inlineAxis ? eAjaRowGroupOwner : eRowGroupOwner;
    5235           0 :     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
    5236           0 :     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
    5237           0 :       return border;
    5238             :     }
    5239             :   }
    5240             :   // see if the row is dominant
    5241           0 :   if (aRowFrame) {
    5242             :     GetColorAndStyle(aRowFrame, aTableWM, aSide,
    5243           0 :                      &tempBorder.style, &tempBorder.color, &tempBorder.width);
    5244           0 :     tempBorder.owner = aAja && inlineAxis ? eAjaRowOwner : eRowOwner;
    5245           0 :     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
    5246           0 :     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
    5247           0 :       return border;
    5248             :     }
    5249             :   }
    5250             :   // see if the cell is dominant
    5251           0 :   if (aCellFrame) {
    5252             :     GetColorAndStyle(aCellFrame, aTableWM, aSide,
    5253           0 :                      &tempBorder.style, &tempBorder.color, &tempBorder.width);
    5254           0 :     tempBorder.owner = aAja ? eAjaCellOwner : eCellOwner;
    5255           0 :     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
    5256             :   }
    5257           0 :   return border;
    5258             : }
    5259             : 
    5260             : static bool
    5261           0 : Perpendicular(mozilla::LogicalSide aSide1,
    5262             :               mozilla::LogicalSide aSide2)
    5263             : {
    5264           0 :   return IsInline(aSide1) != IsInline(aSide2);
    5265             : }
    5266             : 
    5267             : // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
    5268             : struct BCCornerInfo
    5269             : {
    5270           0 :   BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
    5271           0 :                    subElem = hasDashDot = numSegs = bevel = 0; ownerSide = eLogicalSideBStart;
    5272           0 :                    ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID;  }
    5273             :   void Set(mozilla::LogicalSide aSide,
    5274             :            BCCellBorder  border);
    5275             : 
    5276             :   void Update(mozilla::LogicalSide aSide,
    5277             :               BCCellBorder  border);
    5278             : 
    5279             :   nscolor   ownerColor;     // color of borderOwner
    5280             :   uint16_t  ownerWidth;     // pixel width of borderOwner
    5281             :   uint16_t  subWidth;       // pixel width of the largest border intersecting the border perpendicular
    5282             :                             // to ownerSide
    5283             :   uint32_t  ownerSide:2;    // LogicalSide (e.g eLogicalSideBStart, etc) of the border
    5284             :                             // owning the corner relative to the corner
    5285             :   uint32_t  ownerElem:3;    // elem type (e.g. eTable, eGroup, etc) owning the corner
    5286             :   uint32_t  ownerStyle:8;   // border style of ownerElem
    5287             :   uint32_t  subSide:2;      // side of border with subWidth relative to the corner
    5288             :   uint32_t  subElem:3;      // elem type (e.g. eTable, eGroup, etc) of sub owner
    5289             :   uint32_t  subStyle:8;     // border style of subElem
    5290             :   uint32_t  hasDashDot:1;   // does a dashed, dotted segment enter the corner, they cannot be beveled
    5291             :   uint32_t  numSegs:3;      // number of segments entering corner
    5292             :   uint32_t  bevel:1;        // is the corner beveled (uses the above two fields together with subWidth)
    5293             :   uint32_t  unused:1;
    5294             : };
    5295             : 
    5296             : void
    5297           0 : BCCornerInfo::Set(mozilla::LogicalSide aSide,
    5298             :                   BCCellBorder  aBorder)
    5299             : {
    5300           0 :   ownerElem  = aBorder.owner;
    5301           0 :   ownerStyle = aBorder.style;
    5302           0 :   ownerWidth = aBorder.width;
    5303           0 :   ownerColor = aBorder.color;
    5304           0 :   ownerSide  = aSide;
    5305           0 :   hasDashDot = 0;
    5306           0 :   numSegs    = 0;
    5307           0 :   if (aBorder.width > 0) {
    5308           0 :     numSegs++;
    5309           0 :     hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
    5310           0 :                  (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
    5311             :   }
    5312           0 :   bevel      = 0;
    5313           0 :   subWidth   = 0;
    5314             :   // the following will get set later
    5315           0 :   subSide    = IsInline(aSide) ? eLogicalSideBStart : eLogicalSideIStart;
    5316           0 :   subElem    = eTableOwner;
    5317           0 :   subStyle   = NS_STYLE_BORDER_STYLE_SOLID;
    5318           0 : }
    5319             : 
    5320             : void
    5321           0 : BCCornerInfo::Update(mozilla::LogicalSide aSide,
    5322             :                      BCCellBorder  aBorder)
    5323             : {
    5324           0 :   bool existingWins = false;
    5325           0 :   if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
    5326           0 :     Set(aSide, aBorder);
    5327             :   }
    5328             :   else {
    5329           0 :     bool isInline = IsInline(aSide); // relative to the corner
    5330           0 :     BCCellBorder oldBorder, tempBorder;
    5331           0 :     oldBorder.owner  = (BCBorderOwner) ownerElem;
    5332           0 :     oldBorder.style =  ownerStyle;
    5333           0 :     oldBorder.width =  ownerWidth;
    5334           0 :     oldBorder.color =  ownerColor;
    5335             : 
    5336           0 :     LogicalSide oldSide  = LogicalSide(ownerSide);
    5337             : 
    5338           0 :     tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, isInline, &existingWins);
    5339             : 
    5340           0 :     ownerElem  = tempBorder.owner;
    5341           0 :     ownerStyle = tempBorder.style;
    5342           0 :     ownerWidth = tempBorder.width;
    5343           0 :     ownerColor = tempBorder.color;
    5344           0 :     if (existingWins) { // existing corner is dominant
    5345           0 :       if (::Perpendicular(LogicalSide(ownerSide), aSide)) {
    5346             :         // see if the new sub info replaces the old
    5347           0 :         BCCellBorder subBorder;
    5348           0 :         subBorder.owner = (BCBorderOwner) subElem;
    5349           0 :         subBorder.style =  subStyle;
    5350           0 :         subBorder.width =  subWidth;
    5351           0 :         subBorder.color = 0; // we are not interested in subBorder color
    5352             :         bool firstWins;
    5353             : 
    5354           0 :         tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, isInline, &firstWins);
    5355             : 
    5356           0 :         subElem  = tempBorder.owner;
    5357           0 :         subStyle = tempBorder.style;
    5358           0 :         subWidth = tempBorder.width;
    5359           0 :         if (!firstWins) {
    5360           0 :           subSide = aSide;
    5361             :         }
    5362             :       }
    5363             :     }
    5364             :     else { // input args are dominant
    5365           0 :       ownerSide = aSide;
    5366           0 :       if (::Perpendicular(oldSide, LogicalSide(ownerSide))) {
    5367           0 :         subElem  = oldBorder.owner;
    5368           0 :         subStyle = oldBorder.style;
    5369           0 :         subWidth = oldBorder.width;
    5370           0 :         subSide  = oldSide;
    5371             :       }
    5372             :     }
    5373           0 :     if (aBorder.width > 0) {
    5374           0 :       numSegs++;
    5375           0 :       if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
    5376           0 :                           (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
    5377           0 :         hasDashDot = 1;
    5378             :       }
    5379             :     }
    5380             : 
    5381             :     // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
    5382           0 :     bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
    5383             :   }
    5384           0 : }
    5385             : 
    5386             : struct BCCorners
    5387             : {
    5388             :   BCCorners(int32_t aNumCorners,
    5389             :             int32_t aStartIndex);
    5390             : 
    5391           0 :   ~BCCorners() { delete [] corners; }
    5392             : 
    5393           0 :   BCCornerInfo& operator [](int32_t i) const
    5394           0 :   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
    5395           0 :     return corners[clamped(i, startIndex, endIndex) - startIndex]; }
    5396             : 
    5397             :   int32_t       startIndex;
    5398             :   int32_t       endIndex;
    5399             :   BCCornerInfo* corners;
    5400             : };
    5401             : 
    5402           0 : BCCorners::BCCorners(int32_t aNumCorners,
    5403           0 :                      int32_t aStartIndex)
    5404             : {
    5405           0 :   NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
    5406           0 :   startIndex = aStartIndex;
    5407           0 :   endIndex   = aStartIndex + aNumCorners - 1;
    5408           0 :   corners    = new BCCornerInfo[aNumCorners];
    5409           0 : }
    5410             : 
    5411             : 
    5412             : struct BCCellBorders
    5413             : {
    5414             :   BCCellBorders(int32_t aNumBorders,
    5415             :                 int32_t aStartIndex);
    5416             : 
    5417           0 :   ~BCCellBorders() { delete [] borders; }
    5418             : 
    5419           0 :   BCCellBorder& operator [](int32_t i) const
    5420           0 :   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
    5421           0 :     return borders[clamped(i, startIndex, endIndex) - startIndex]; }
    5422             : 
    5423             :   int32_t       startIndex;
    5424             :   int32_t       endIndex;
    5425             :   BCCellBorder* borders;
    5426             : };
    5427             : 
    5428           0 : BCCellBorders::BCCellBorders(int32_t aNumBorders,
    5429           0 :                              int32_t aStartIndex)
    5430             : {
    5431           0 :   NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
    5432           0 :   startIndex = aStartIndex;
    5433           0 :   endIndex   = aStartIndex + aNumBorders - 1;
    5434           0 :   borders    = new BCCellBorder[aNumBorders];
    5435           0 : }
    5436             : 
    5437             : // this function sets the new border properties and returns true if the border
    5438             : // segment will start a new segment and not be accumulated into the previous
    5439             : // segment.
    5440             : static bool
    5441           0 : SetBorder(const BCCellBorder&   aNewBorder,
    5442             :           BCCellBorder&         aBorder)
    5443             : {
    5444           0 :   bool changed = (aNewBorder.style != aBorder.style) ||
    5445           0 :                    (aNewBorder.width != aBorder.width) ||
    5446           0 :                    (aNewBorder.color != aBorder.color);
    5447           0 :   aBorder.color        = aNewBorder.color;
    5448           0 :   aBorder.width        = aNewBorder.width;
    5449           0 :   aBorder.style        = aNewBorder.style;
    5450           0 :   aBorder.owner        = aNewBorder.owner;
    5451             : 
    5452           0 :   return changed;
    5453             : }
    5454             : 
    5455             : // this function will set the inline-dir border. It will return true if the
    5456             : // existing segment will not be continued. Having a block-dir owner of a corner
    5457             : // should also start a new segment.
    5458             : static bool
    5459           0 : SetInlineDirBorder(const BCCellBorder& aNewBorder,
    5460             :                    const BCCornerInfo& aCorner,
    5461             :                    BCCellBorder&       aBorder)
    5462             : {
    5463           0 :   bool startSeg = ::SetBorder(aNewBorder, aBorder);
    5464           0 :   if (!startSeg) {
    5465           0 :     startSeg = !IsInline(LogicalSide(aCorner.ownerSide));
    5466             :   }
    5467           0 :   return startSeg;
    5468             : }
    5469             : 
    5470             : // Make the damage area larger on the top and bottom by at least one row and on the left and right
    5471             : // at least one column. This is done so that adjacent elements are part of the border calculations.
    5472             : // The extra segments and borders outside the actual damage area will not be updated in the cell map,
    5473             : // because they in turn would need info from adjacent segments outside the damage area to be accurate.
    5474             : void
    5475           0 : nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const
    5476             : {
    5477           0 :   int32_t numRows = GetRowCount();
    5478           0 :   int32_t numCols = GetColCount();
    5479             : 
    5480           0 :   int32_t dStartX = aArea.StartCol();
    5481           0 :   int32_t dEndX   = aArea.EndCol() - 1;
    5482           0 :   int32_t dStartY = aArea.StartRow();
    5483           0 :   int32_t dEndY   = aArea.EndRow() - 1;
    5484             : 
    5485             :   // expand the damage area in each direction
    5486           0 :   if (dStartX > 0) {
    5487           0 :     dStartX--;
    5488             :   }
    5489           0 :   if (dEndX < (numCols - 1)) {
    5490           0 :     dEndX++;
    5491             :   }
    5492           0 :   if (dStartY > 0) {
    5493           0 :     dStartY--;
    5494             :   }
    5495           0 :   if (dEndY < (numRows - 1)) {
    5496           0 :     dEndY++;
    5497             :   }
    5498             :   // Check the damage area so that there are no cells spanning in or out. If there are any then
    5499             :   // make the damage area as big as the table, similarly to the way the cell map decides whether
    5500             :   // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
    5501             :   // no spanners, but it may not be worth the effort in general, and it would need to be done in the
    5502             :   // cell map as well.
    5503           0 :   bool haveSpanner = false;
    5504           0 :   if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
    5505           0 :     nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
    5506             :     // Get the ordered row groups
    5507           0 :     RowGroupArray rowGroups;
    5508           0 :     OrderRowGroups(rowGroups);
    5509             : 
    5510             :     // Scope outside loop to be used as hint.
    5511           0 :     nsCellMap* cellMap = nullptr;
    5512           0 :     for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
    5513           0 :       nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
    5514           0 :       int32_t rgStartY = rgFrame->GetStartRowIndex();
    5515           0 :       int32_t rgEndY   = rgStartY + rgFrame->GetRowCount() - 1;
    5516           0 :       if (dEndY < rgStartY)
    5517           0 :         break;
    5518           0 :       cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
    5519           0 :       if (!cellMap) ABORT0();
    5520             :       // check for spanners from above and below
    5521           0 :       if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
    5522           0 :         if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length())
    5523           0 :           ABORT0();
    5524             :         const nsCellMap::CellDataArray& row =
    5525           0 :           cellMap->mRows[dStartY - rgStartY];
    5526           0 :         for (int32_t x = dStartX; x <= dEndX; x++) {
    5527           0 :           CellData* cellData = row.SafeElementAt(x);
    5528           0 :           if (cellData && (cellData->IsRowSpan())) {
    5529           0 :              haveSpanner = true;
    5530           0 :              break;
    5531             :           }
    5532             :         }
    5533           0 :         if (dEndY < rgEndY) {
    5534           0 :           if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
    5535           0 :             ABORT0();
    5536             :           const nsCellMap::CellDataArray& row2 =
    5537           0 :             cellMap->mRows[dEndY + 1 - rgStartY];
    5538           0 :           for (int32_t x = dStartX; x <= dEndX; x++) {
    5539           0 :             CellData* cellData = row2.SafeElementAt(x);
    5540           0 :             if (cellData && (cellData->IsRowSpan())) {
    5541           0 :               haveSpanner = true;
    5542           0 :               break;
    5543             :             }
    5544             :           }
    5545             :         }
    5546             :       }
    5547             :       // check for spanners on the left and right
    5548           0 :       int32_t iterStartY = -1;
    5549           0 :       int32_t iterEndY   = -1;
    5550           0 :       if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
    5551             :         // the damage area starts in the row group
    5552           0 :         iterStartY = dStartY;
    5553           0 :         iterEndY   = std::min(dEndY, rgEndY);
    5554             :       }
    5555           0 :       else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
    5556             :         // the damage area ends in the row group
    5557           0 :         iterStartY = rgStartY;
    5558           0 :         iterEndY   = dEndY;
    5559             :       }
    5560           0 :       else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
    5561             :         // the damage area contains the row group
    5562           0 :         iterStartY = rgStartY;
    5563           0 :         iterEndY   = rgEndY;
    5564             :       }
    5565           0 :       if ((iterStartY >= 0) && (iterEndY >= 0)) {
    5566           0 :         for (int32_t y = iterStartY; y <= iterEndY; y++) {
    5567           0 :           if (uint32_t(y - rgStartY) >= cellMap->mRows.Length())
    5568           0 :             ABORT0();
    5569             :           const nsCellMap::CellDataArray& row =
    5570           0 :             cellMap->mRows[y - rgStartY];
    5571           0 :           CellData* cellData = row.SafeElementAt(dStartX);
    5572           0 :           if (cellData && (cellData->IsColSpan())) {
    5573           0 :             haveSpanner = true;
    5574           0 :             break;
    5575             :           }
    5576           0 :           if (dEndX < (numCols - 1)) {
    5577           0 :             cellData = row.SafeElementAt(dEndX + 1);
    5578           0 :             if (cellData && (cellData->IsColSpan())) {
    5579           0 :               haveSpanner = true;
    5580           0 :               break;
    5581             :             }
    5582             :           }
    5583             :         }
    5584             :       }
    5585             :     }
    5586             :   }
    5587           0 :   if (haveSpanner) {
    5588             :     // make the damage area the whole table
    5589           0 :     aArea.StartCol() = 0;
    5590           0 :     aArea.StartRow() = 0;
    5591           0 :     aArea.ColCount() = numCols;
    5592           0 :     aArea.RowCount() = numRows;
    5593             :   }
    5594             :   else {
    5595           0 :     aArea.StartCol() = dStartX;
    5596           0 :     aArea.StartRow() = dStartY;
    5597           0 :     aArea.ColCount() = 1 + dEndX - dStartX;
    5598           0 :     aArea.RowCount() = 1 + dEndY - dStartY;
    5599             :   }
    5600             : }
    5601             : 
    5602             : 
    5603             : #define ADJACENT    true
    5604             : #define INLINE_DIR  true
    5605             : 
    5606             : void
    5607           0 : BCMapCellInfo::SetTableBStartIStartContBCBorder()
    5608             : {
    5609           0 :   BCCellBorder currentBorder;
    5610             :   //calculate continuous top first row & rowgroup border: special case
    5611             :   //because it must include the table in the collapse
    5612           0 :   if (mStartRow) {
    5613           0 :     currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
    5614           0 :                                    mStartRow, nullptr, mTableWM,
    5615           0 :                                    eLogicalSideBStart, !ADJACENT);
    5616           0 :     mStartRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
    5617           0 :                                           currentBorder.width);
    5618             :   }
    5619           0 :   if (mCgAtEnd && mColGroup) {
    5620             :     //calculate continuous top colgroup border once per colgroup
    5621           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
    5622           0 :                                    mStartRow, nullptr, mTableWM,
    5623           0 :                                    eLogicalSideBStart, !ADJACENT);
    5624           0 :     mColGroup->SetContinuousBCBorderWidth(eLogicalSideBStart,
    5625           0 :                                           currentBorder.width);
    5626             :   }
    5627           0 :   if (0 == mColIndex) {
    5628           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol, nullptr,
    5629             :                                    nullptr, nullptr, mTableWM,
    5630           0 :                                    eLogicalSideIStart, !ADJACENT);
    5631           0 :     mTableFrame->SetContinuousIStartBCBorderWidth(currentBorder.width);
    5632             :   }
    5633           0 : }
    5634             : 
    5635             : void
    5636           0 : BCMapCellInfo::SetRowGroupIStartContBCBorder()
    5637             : {
    5638           0 :   BCCellBorder currentBorder;
    5639             :   //get row group continuous borders
    5640           0 :   if (mRgAtEnd && mRowGroup) { //once per row group, so check for bottom
    5641           0 :      currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
    5642           0 :                                     mRowGroup, nullptr, nullptr, mTableWM,
    5643           0 :                                     eLogicalSideIStart, !ADJACENT);
    5644           0 :      mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIStart,
    5645           0 :                                            currentBorder.width);
    5646             :   }
    5647           0 : }
    5648             : 
    5649             : void
    5650           0 : BCMapCellInfo::SetRowGroupIEndContBCBorder()
    5651             : {
    5652           0 :   BCCellBorder currentBorder;
    5653             :   //get row group continuous borders
    5654           0 :   if (mRgAtEnd && mRowGroup) { //once per mRowGroup, so check for bottom
    5655           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
    5656             :                                    nullptr, nullptr, mTableWM, eLogicalSideIEnd,
    5657           0 :                                    ADJACENT);
    5658           0 :     mRowGroup->SetContinuousBCBorderWidth(eLogicalSideIEnd,
    5659           0 :                                           currentBorder.width);
    5660             :   }
    5661           0 : }
    5662             : 
    5663             : void
    5664           0 : BCMapCellInfo::SetColumnBStartIEndContBCBorder()
    5665             : {
    5666           0 :   BCCellBorder currentBorder;
    5667             :   //calculate column continuous borders
    5668             :   //we only need to do this once, so we'll do it only on the first row
    5669           0 :   currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
    5670           0 :                                  mCurrentColFrame, mRowGroup, mStartRow,
    5671             :                                  nullptr, mTableWM, eLogicalSideBStart,
    5672           0 :                                  !ADJACENT);
    5673           0 :   mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBStart,
    5674           0 :                                                currentBorder.width);
    5675           0 :   if (mNumTableCols == GetCellEndColIndex() + 1) {
    5676           0 :     currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
    5677           0 :                                    mCurrentColFrame, nullptr, nullptr, nullptr,
    5678           0 :                                    mTableWM, eLogicalSideIEnd, !ADJACENT);
    5679             :   }
    5680             :   else {
    5681           0 :     currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame,
    5682           0 :                                    mCurrentColFrame, nullptr,nullptr, nullptr,
    5683           0 :                                    mTableWM, eLogicalSideIEnd, !ADJACENT);
    5684             :   }
    5685           0 :   mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
    5686           0 :                                                currentBorder.width);
    5687           0 : }
    5688             : 
    5689             : void
    5690           0 : BCMapCellInfo::SetColumnBEndContBCBorder()
    5691             : {
    5692           0 :   BCCellBorder currentBorder;
    5693             :   //get col continuous border
    5694           0 :   currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
    5695           0 :                                  mCurrentColFrame, mRowGroup, mEndRow,
    5696           0 :                                  nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
    5697           0 :   mCurrentColFrame->SetContinuousBCBorderWidth(eLogicalSideBEnd,
    5698           0 :                                                currentBorder.width);
    5699           0 : }
    5700             : 
    5701             : void
    5702           0 : BCMapCellInfo::SetColGroupBEndContBCBorder()
    5703             : {
    5704           0 :   BCCellBorder currentBorder;
    5705           0 :   if (mColGroup) {
    5706           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
    5707           0 :                                    mEndRow, nullptr, mTableWM,
    5708           0 :                                    eLogicalSideBEnd, ADJACENT);
    5709           0 :     mColGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
    5710             :   }
    5711           0 : }
    5712             : 
    5713             : void
    5714           0 : BCMapCellInfo::SetRowGroupBEndContBCBorder()
    5715             : {
    5716           0 :   BCCellBorder currentBorder;
    5717           0 :   if (mRowGroup) {
    5718           0 :     currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
    5719           0 :                                    mEndRow, nullptr, mTableWM,
    5720           0 :                                    eLogicalSideBEnd, ADJACENT);
    5721           0 :     mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
    5722             :   }
    5723           0 : }
    5724             : 
    5725             : void
    5726           0 : BCMapCellInfo::SetInnerRowGroupBEndContBCBorder(const nsIFrame* aNextRowGroup,
    5727             :                                                 nsTableRowFrame* aNextRow)
    5728             : {
    5729           0 :   BCCellBorder currentBorder, adjacentBorder;
    5730             : 
    5731           0 :   const nsIFrame* rowgroup = mRgAtEnd ? mRowGroup : nullptr;
    5732           0 :   currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mEndRow,
    5733           0 :                                  nullptr, mTableWM, eLogicalSideBEnd, ADJACENT);
    5734             : 
    5735             :   adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup,
    5736             :                                   aNextRow, nullptr, mTableWM, eLogicalSideBStart,
    5737           0 :                                   !ADJACENT);
    5738           0 :   currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
    5739           0 :                                  INLINE_DIR);
    5740           0 :   if (aNextRow) {
    5741           0 :     aNextRow->SetContinuousBCBorderWidth(eLogicalSideBStart,
    5742           0 :                                          currentBorder.width);
    5743             :   }
    5744           0 :   if (mRgAtEnd && mRowGroup) {
    5745           0 :     mRowGroup->SetContinuousBCBorderWidth(eLogicalSideBEnd, currentBorder.width);
    5746             :   }
    5747           0 : }
    5748             : 
    5749             : void
    5750           0 : BCMapCellInfo::SetRowIStartContBCBorder()
    5751             : {
    5752             :   //get row continuous borders
    5753           0 :   if (mCurrentRowFrame) {
    5754           0 :     BCCellBorder currentBorder;
    5755           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, mStartCol,
    5756           0 :                                    mRowGroup, mCurrentRowFrame, nullptr,
    5757           0 :                                    mTableWM, eLogicalSideIStart, !ADJACENT);
    5758           0 :     mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIStart,
    5759           0 :                                                  currentBorder.width);
    5760             :   }
    5761           0 : }
    5762             : 
    5763             : void
    5764           0 : BCMapCellInfo::SetRowIEndContBCBorder()
    5765             : {
    5766           0 :   if (mCurrentRowFrame) {
    5767           0 :     BCCellBorder currentBorder;
    5768           0 :     currentBorder = CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
    5769           0 :                                    mCurrentRowFrame, nullptr, mTableWM,
    5770           0 :                                    eLogicalSideIEnd, ADJACENT);
    5771           0 :     mCurrentRowFrame->SetContinuousBCBorderWidth(eLogicalSideIEnd,
    5772           0 :                                                  currentBorder.width);
    5773             :   }
    5774           0 : }
    5775             : void
    5776           0 : BCMapCellInfo::SetTableBStartBorderWidth(BCPixelSize aWidth)
    5777             : {
    5778           0 :   mTableBCData->mBStartBorderWidth = std::max(mTableBCData->mBStartBorderWidth,
    5779           0 :                                               aWidth);
    5780           0 : }
    5781             : 
    5782             : void
    5783           0 : BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth)
    5784             : {
    5785             :   // update the iStart first cell border
    5786           0 :   if (aRowB == 0) {
    5787           0 :     mTableBCData->mIStartCellBorderWidth = aWidth;
    5788             :   }
    5789           0 :   mTableBCData->mIStartBorderWidth = std::max(mTableBCData->mIStartBorderWidth,
    5790           0 :                                               aWidth);
    5791           0 : }
    5792             : 
    5793             : void
    5794           0 : BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth)
    5795             : {
    5796             :   // update the iEnd first cell border
    5797           0 :   if (aRowB == 0) {
    5798           0 :     mTableBCData->mIEndCellBorderWidth = aWidth;
    5799             :   }
    5800           0 :   mTableBCData->mIEndBorderWidth = std::max(mTableBCData->mIEndBorderWidth,
    5801           0 :                                             aWidth);
    5802           0 : }
    5803             : 
    5804             : void
    5805           0 : BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth)
    5806             : {
    5807             :    // update the borders of the cells and cols affected
    5808           0 :   if (mCell) {
    5809           0 :     mCell->SetBorderWidth(eLogicalSideIEnd, std::max(aWidth,
    5810           0 :                           mCell->GetBorderWidth(eLogicalSideIEnd)));
    5811             :   }
    5812           0 :   if (mEndCol) {
    5813           0 :     BCPixelSize half = BC_BORDER_START_HALF(aWidth);
    5814           0 :     mEndCol->SetIEndBorderWidth(
    5815           0 :       std::max(nscoord(half), mEndCol->GetIEndBorderWidth()));
    5816             :   }
    5817           0 : }
    5818             : 
    5819             : void
    5820           0 : BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth)
    5821             : {
    5822             :   // update the borders of the affected cells and rows
    5823           0 :   if (mCell) {
    5824           0 :     mCell->SetBorderWidth(eLogicalSideBEnd, std::max(aWidth,
    5825           0 :                           mCell->GetBorderWidth(eLogicalSideBEnd)));
    5826             :   }
    5827           0 :   if (mEndRow) {
    5828           0 :     BCPixelSize half = BC_BORDER_START_HALF(aWidth);
    5829           0 :     mEndRow->SetBEndBCBorderWidth(
    5830           0 :       std::max(nscoord(half), mEndRow->GetBEndBCBorderWidth()));
    5831             :   }
    5832           0 : }
    5833             : void
    5834           0 : BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth)
    5835             : {
    5836           0 :  if (mCell) {
    5837           0 :      mCell->SetBorderWidth(eLogicalSideBStart, std::max(aWidth,
    5838           0 :                            mCell->GetBorderWidth(eLogicalSideBStart)));
    5839             :   }
    5840           0 :   if (mStartRow) {
    5841           0 :     BCPixelSize half = BC_BORDER_END_HALF(aWidth);
    5842           0 :     mStartRow->SetBStartBCBorderWidth(
    5843           0 :       std::max(nscoord(half), mStartRow->GetBStartBCBorderWidth()));
    5844             :   }
    5845           0 : }
    5846             : void
    5847           0 : BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth)
    5848             : {
    5849           0 :   if (mCell) {
    5850           0 :     mCell->SetBorderWidth(eLogicalSideIStart, std::max(aWidth,
    5851           0 :                           mCell->GetBorderWidth(eLogicalSideIStart)));
    5852             :   }
    5853           0 :   if (mStartCol) {
    5854           0 :     BCPixelSize half = BC_BORDER_END_HALF(aWidth);
    5855           0 :     mStartCol->SetIStartBorderWidth(
    5856           0 :       std::max(nscoord(half), mStartCol->GetIStartBorderWidth()));
    5857             :   }
    5858           0 : }
    5859             : 
    5860             : void
    5861           0 : BCMapCellInfo::SetTableBEndBorderWidth(BCPixelSize aWidth)
    5862             : {
    5863           0 :   mTableBCData->mBEndBorderWidth = std::max(mTableBCData->mBEndBorderWidth,
    5864           0 :                                             aWidth);
    5865           0 : }
    5866             : 
    5867             : void
    5868           0 : BCMapCellInfo::SetColumn(int32_t aColX)
    5869             : {
    5870           0 :   mCurrentColFrame = mTableFrame->GetColFrame(aColX);
    5871           0 :   if (!mCurrentColFrame) {
    5872           0 :     NS_ERROR("null mCurrentColFrame");
    5873             :   }
    5874           0 :   mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
    5875           0 :                             (mCurrentColFrame->GetParent());
    5876           0 :   if (!mCurrentColGroupFrame) {
    5877           0 :     NS_ERROR("null mCurrentColGroupFrame");
    5878             :   }
    5879           0 : }
    5880             : 
    5881             : void
    5882           0 : BCMapCellInfo::IncrementRow(bool aResetToBStartRowOfCell)
    5883             : {
    5884           0 :   mCurrentRowFrame =
    5885           0 :     aResetToBStartRowOfCell ? mStartRow : mCurrentRowFrame->GetNextRow();
    5886           0 : }
    5887             : 
    5888             : BCCellBorder
    5889           0 : BCMapCellInfo::GetBStartEdgeBorder()
    5890             : {
    5891           0 :   return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
    5892           0 :                         mRowGroup, mStartRow, mCell, mTableWM,
    5893           0 :                         eLogicalSideBStart, !ADJACENT);
    5894             : }
    5895             : 
    5896             : BCCellBorder
    5897           0 : BCMapCellInfo::GetBEndEdgeBorder()
    5898             : {
    5899           0 :   return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
    5900           0 :                         mRowGroup, mEndRow, mCell, mTableWM,
    5901           0 :                         eLogicalSideBEnd, ADJACENT);
    5902             : }
    5903             : BCCellBorder
    5904           0 : BCMapCellInfo::GetIStartEdgeBorder()
    5905             : {
    5906           0 :   return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup,
    5907           0 :                         mCurrentRowFrame, mCell, mTableWM, eLogicalSideIStart,
    5908           0 :                         !ADJACENT);
    5909             : }
    5910             : BCCellBorder
    5911           0 : BCMapCellInfo::GetIEndEdgeBorder()
    5912             : {
    5913           0 :   return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
    5914           0 :                         mCurrentRowFrame, mCell, mTableWM, eLogicalSideIEnd,
    5915           0 :                         ADJACENT);
    5916             : }
    5917             : BCCellBorder
    5918           0 : BCMapCellInfo::GetIEndInternalBorder()
    5919             : {
    5920           0 :   const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr;
    5921           0 :   return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell,
    5922           0 :                         mTableWM, eLogicalSideIEnd, ADJACENT);
    5923             : }
    5924             : 
    5925             : BCCellBorder
    5926           0 : BCMapCellInfo::GetIStartInternalBorder()
    5927             : {
    5928           0 :   const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr;
    5929           0 :   return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell,
    5930           0 :                         mTableWM, eLogicalSideIStart, !ADJACENT);
    5931             : }
    5932             : 
    5933             : BCCellBorder
    5934           0 : BCMapCellInfo::GetBEndInternalBorder()
    5935             : {
    5936           0 :   const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr;
    5937           0 :   return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell,
    5938           0 :                         mTableWM, eLogicalSideBEnd, ADJACENT);
    5939             : }
    5940             : 
    5941             : BCCellBorder
    5942           0 : BCMapCellInfo::GetBStartInternalBorder()
    5943             : {
    5944           0 :   const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr;
    5945           0 :   return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell,
    5946           0 :                         mTableWM, eLogicalSideBStart, !ADJACENT);
    5947             : }
    5948             : 
    5949             : /* XXX This comment is still written in physical (horizontal-tb) terms.
    5950             : 
    5951             :    Here is the order for storing border edges in the cell map as a cell is processed. There are
    5952             :    n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
    5953             : 
    5954             :    1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
    5955             :       a bottom edge from a cell above will take care of it.
    5956             :    2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
    5957             :       a right edge from a cell to the left will take care of it.
    5958             :    3) Store the right edge (or edges if a row span)
    5959             :    4) Store the bottom edge (or edges if a col span)
    5960             : 
    5961             :    Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
    5962             :    calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
    5963             :    would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
    5964             :    borders already have about an extra 8 byte per cell map entry overhead (this could be
    5965             :    reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
    5966             :    rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
    5967             :    left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
    5968             :    edges per cell and n=rowspan left and right border edges per cell.
    5969             : 
    5970             :    1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
    5971             :       Never store the top-right corner, since it will get stored as a right-top corner.
    5972             :    2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
    5973             :       since it will get stored as a bottom-left corner.
    5974             :    3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
    5975             :       the top edge of the table. Never store the right-bottom corner since it will get stored as a
    5976             :       bottom-right corner.
    5977             :    4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
    5978             :       otherwise, since it will get stored as either a right-top corner by a cell below or
    5979             :       a bottom-left corner from a cell to the right.
    5980             :    5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
    5981             :       the top side of a colspan in its interior. Never store the corner otherwise, since it will
    5982             :       get stored as a right-top corner by a cell from below.
    5983             : 
    5984             :    XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
    5985             :    In order to draw borders in rtl conditions somehow correct, the existing structure which relies
    5986             :    heavily on the assumption that the next cell sibling will be on the right side, has been modified.
    5987             :    We flip the border during painting and during style lookup. Look for tableIsLTR for places where
    5988             :    the flipping is done.
    5989             :  */
    5990             : 
    5991             : 
    5992             : 
    5993             : // Calc the dominant border at every cell edge and corner within the current damage area
    5994             : void
    5995           0 : nsTableFrame::CalcBCBorders()
    5996             : {
    5997           0 :   NS_ASSERTION(IsBorderCollapse(),
    5998             :                "calling CalcBCBorders on separated-border table");
    5999           0 :   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
    6000           0 :   int32_t numRows = GetRowCount();
    6001           0 :   int32_t numCols = GetColCount();
    6002           0 :   if (!numRows || !numCols)
    6003           0 :     return; // nothing to do
    6004             : 
    6005             :   // Get the property holding the table damage area and border widths
    6006           0 :   BCPropertyData* propData = GetBCProperty();
    6007           0 :   if (!propData) ABORT0();
    6008             : 
    6009             : 
    6010             : 
    6011             :   // calculate an expanded damage area
    6012           0 :   TableArea damageArea(propData->mDamageArea);
    6013           0 :   ExpandBCDamageArea(damageArea);
    6014             : 
    6015             :   // segments that are on the table border edges need
    6016             :   // to be initialized only once
    6017             :   bool tableBorderReset[4];
    6018           0 :   for (uint32_t sideX = 0; sideX < ArrayLength(tableBorderReset); sideX++) {
    6019           0 :     tableBorderReset[sideX] = false;
    6020             :   }
    6021             : 
    6022             :   // block-dir borders indexed in inline-direction (cols)
    6023           0 :   BCCellBorders lastBlockDirBorders(damageArea.ColCount() + 1,
    6024           0 :                                     damageArea.StartCol());
    6025           0 :   if (!lastBlockDirBorders.borders) ABORT0();
    6026           0 :   BCCellBorder  lastBStartBorder, lastBEndBorder;
    6027             :   // inline-dir borders indexed in inline-direction (cols)
    6028           0 :   BCCellBorders lastBEndBorders(damageArea.ColCount() + 1,
    6029           0 :                                 damageArea.StartCol());
    6030           0 :   if (!lastBEndBorders.borders) ABORT0();
    6031             :   bool startSeg;
    6032           0 :   bool gotRowBorder = false;
    6033             : 
    6034           0 :   BCMapCellInfo  info(this), ajaInfo(this);
    6035             : 
    6036           0 :   BCCellBorder currentBorder, adjacentBorder;
    6037           0 :   BCCorners bStartCorners(damageArea.ColCount() + 1, damageArea.StartCol());
    6038           0 :   if (!bStartCorners.corners) ABORT0();
    6039           0 :   BCCorners bEndCorners(damageArea.ColCount() + 1, damageArea.StartCol());
    6040           0 :   if (!bEndCorners.corners) ABORT0();
    6041             : 
    6042           0 :   BCMapCellIterator iter(this, damageArea);
    6043           0 :   for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
    6044             :     // see if lastBStartBorder, lastBEndBorder need to be reset
    6045           0 :     if (iter.IsNewRow()) {
    6046           0 :       gotRowBorder = false;
    6047           0 :       lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
    6048           0 :       lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
    6049             :     }
    6050           0 :     else if (info.mColIndex > damageArea.StartCol()) {
    6051           0 :       lastBEndBorder = lastBEndBorders[info.mColIndex - 1];
    6052           0 :       if (info.mRowIndex >
    6053           0 :           (lastBEndBorder.rowIndex - lastBEndBorder.rowSpan)) {
    6054             :         // the bStart border's iStart edge butts against the middle of a rowspan
    6055           0 :         lastBStartBorder.Reset(info.mRowIndex, info.mRowSpan);
    6056             :       }
    6057           0 :       if (lastBEndBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
    6058             :         // the bEnd border's iStart edge butts against the middle of a rowspan
    6059           0 :         lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
    6060             :       }
    6061             :     }
    6062             : 
    6063             :     // find the dominant border considering the cell's bStart border and the table,
    6064             :     // row group, row if the border is at the bStart of the table, otherwise it was
    6065             :     // processed in a previous row
    6066           0 :     if (0 == info.mRowIndex) {
    6067           0 :       if (!tableBorderReset[eLogicalSideBStart]) {
    6068           0 :         propData->mBStartBorderWidth = 0;
    6069           0 :         tableBorderReset[eLogicalSideBStart] = true;
    6070             :       }
    6071           0 :       for (int32_t colIdx = info.mColIndex;
    6072           0 :            colIdx <= info.GetCellEndColIndex(); colIdx++) {
    6073           0 :         info.SetColumn(colIdx);
    6074           0 :         currentBorder = info.GetBStartEdgeBorder();
    6075             :         // update/store the bStart-iStart & bStart-iEnd corners of the seg
    6076           0 :         BCCornerInfo& tlCorner = bStartCorners[colIdx]; // bStart-iStart
    6077           0 :         if (0 == colIdx) {
    6078             :           // we are on the iEnd side of the corner
    6079           0 :           tlCorner.Set(eLogicalSideIEnd, currentBorder);
    6080             :         }
    6081             :         else {
    6082           0 :           tlCorner.Update(eLogicalSideIEnd, currentBorder);
    6083           0 :           tableCellMap->SetBCBorderCorner(eLogicalCornerBStartIStart,
    6084           0 :                                           *iter.mCellMap, 0, 0, colIdx,
    6085           0 :                                           LogicalSide(tlCorner.ownerSide),
    6086           0 :                                           tlCorner.subWidth,
    6087           0 :                                           tlCorner.bevel);
    6088             :         }
    6089           0 :         bStartCorners[colIdx + 1].Set(eLogicalSideIStart, currentBorder); // bStart-iEnd
    6090             :         // update lastBStartBorder and see if a new segment starts
    6091           0 :         startSeg = SetInlineDirBorder(currentBorder, tlCorner, lastBStartBorder);
    6092             :         // store the border segment in the cell map
    6093           0 :         tableCellMap->SetBCBorderEdge(eLogicalSideBStart, *iter.mCellMap, 0, 0, colIdx,
    6094             :                                       1, currentBorder.owner,
    6095           0 :                                       currentBorder.width, startSeg);
    6096             : 
    6097           0 :         info.SetTableBStartBorderWidth(currentBorder.width);
    6098           0 :         info.SetBStartBorderWidths(currentBorder.width);
    6099           0 :         info.SetColumnBStartIEndContBCBorder();
    6100             :       }
    6101           0 :       info.SetTableBStartIStartContBCBorder();
    6102             :     }
    6103             :     else {
    6104             :       // see if the bStart border needs to be the start of a segment due to a
    6105             :       // block-dir border owning the corner
    6106           0 :       if (info.mColIndex > 0) {
    6107           0 :         BCData& data = info.mCellData->mData;
    6108           0 :         if (!data.IsBStartStart()) {
    6109             :           LogicalSide cornerSide;
    6110             :           bool bevel;
    6111           0 :           data.GetCorner(cornerSide, bevel);
    6112           0 :           if (IsBlock(cornerSide)) {
    6113           0 :             data.SetBStartStart(true);
    6114             :           }
    6115             :         }
    6116             :       }
    6117             :     }
    6118             : 
    6119             :     // find the dominant border considering the cell's iStart border and the
    6120             :     // table, col group, col if the border is at the iStart of the table,
    6121             :     // otherwise it was processed in a previous col
    6122           0 :     if (0 == info.mColIndex) {
    6123           0 :       if (!tableBorderReset[eLogicalSideIStart]) {
    6124           0 :         propData->mIStartBorderWidth = 0;
    6125           0 :         tableBorderReset[eLogicalSideIStart] = true;
    6126             :       }
    6127           0 :       info.mCurrentRowFrame = nullptr;
    6128           0 :       for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
    6129             :            rowB++) {
    6130           0 :         info.IncrementRow(rowB == info.mRowIndex);
    6131           0 :         currentBorder = info.GetIStartEdgeBorder();
    6132           0 :         BCCornerInfo& tlCorner = (0 == rowB) ? bStartCorners[0] : bEndCorners[0];
    6133           0 :         tlCorner.Update(eLogicalSideBEnd, currentBorder);
    6134           0 :         tableCellMap->SetBCBorderCorner(eLogicalCornerBStartIStart, *iter.mCellMap,
    6135           0 :                                         iter.mRowGroupStart, rowB, 0,
    6136           0 :                                         LogicalSide(tlCorner.ownerSide),
    6137           0 :                                         tlCorner.subWidth,
    6138           0 :                                         tlCorner.bevel);
    6139           0 :         bEndCorners[0].Set(eLogicalSideBStart, currentBorder); // bEnd-iStart
    6140             : 
    6141             :         // update lastBlockDirBorders and see if a new segment starts
    6142           0 :         startSeg = SetBorder(currentBorder, lastBlockDirBorders[0]);
    6143             :         // store the border segment in the cell map
    6144           0 :         tableCellMap->SetBCBorderEdge(eLogicalSideIStart, *iter.mCellMap,
    6145           0 :                                       iter.mRowGroupStart, rowB, info.mColIndex,
    6146             :                                       1, currentBorder.owner,
    6147           0 :                                       currentBorder.width, startSeg);
    6148           0 :         info.SetTableIStartBorderWidth(rowB , currentBorder.width);
    6149           0 :         info.SetIStartBorderWidths(currentBorder.width);
    6150           0 :         info.SetRowIStartContBCBorder();
    6151             :       }
    6152           0 :       info.SetRowGroupIStartContBCBorder();
    6153             :     }
    6154             : 
    6155             :     // find the dominant border considering the cell's iEnd border, adjacent
    6156             :     // cells and the table, row group, row
    6157           0 :     if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
    6158             :       // touches iEnd edge of table
    6159           0 :       if (!tableBorderReset[eLogicalSideIEnd]) {
    6160           0 :         propData->mIEndBorderWidth = 0;
    6161           0 :         tableBorderReset[eLogicalSideIEnd] = true;
    6162             :       }
    6163           0 :       info.mCurrentRowFrame = nullptr;
    6164           0 :       for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
    6165             :            rowB++) {
    6166           0 :         info.IncrementRow(rowB == info.mRowIndex);
    6167           0 :         currentBorder = info.GetIEndEdgeBorder();
    6168             :         // update/store the bStart-iEnd & bEnd-iEnd corners
    6169           0 :         BCCornerInfo& trCorner = (0 == rowB) ?
    6170           0 :                                  bStartCorners[info.GetCellEndColIndex() + 1] :
    6171           0 :                                  bEndCorners[info.GetCellEndColIndex() + 1];
    6172           0 :         trCorner.Update(eLogicalSideBEnd, currentBorder);   // bStart-iEnd
    6173           0 :         tableCellMap->SetBCBorderCorner(eLogicalCornerBStartIEnd, *iter.mCellMap,
    6174           0 :                                         iter.mRowGroupStart, rowB,
    6175           0 :                                         info.GetCellEndColIndex(),
    6176           0 :                                         LogicalSide(trCorner.ownerSide),
    6177           0 :                                         trCorner.subWidth,
    6178           0 :                                         trCorner.bevel);
    6179           0 :         BCCornerInfo& brCorner = bEndCorners[info.GetCellEndColIndex() + 1];
    6180           0 :         brCorner.Set(eLogicalSideBStart, currentBorder); // bEnd-iEnd
    6181           0 :         tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIEnd, *iter.mCellMap,
    6182           0 :                                         iter.mRowGroupStart, rowB,
    6183           0 :                                         info.GetCellEndColIndex(),
    6184           0 :                                         LogicalSide(brCorner.ownerSide),
    6185           0 :                                         brCorner.subWidth,
    6186           0 :                                         brCorner.bevel);
    6187             :         // update lastBlockDirBorders and see if a new segment starts
    6188           0 :         startSeg = SetBorder(currentBorder,
    6189           0 :                              lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
    6190             :         // store the border segment in the cell map and update cellBorders
    6191           0 :         tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
    6192           0 :                                       iter.mRowGroupStart, rowB,
    6193           0 :                                       info.GetCellEndColIndex(), 1,
    6194           0 :                                       currentBorder.owner, currentBorder.width,
    6195           0 :                                       startSeg);
    6196           0 :         info.SetTableIEndBorderWidth(rowB, currentBorder.width);
    6197           0 :         info.SetIEndBorderWidths(currentBorder.width);
    6198           0 :         info.SetRowIEndContBCBorder();
    6199             :       }
    6200           0 :       info.SetRowGroupIEndContBCBorder();
    6201             :     }
    6202             :     else {
    6203           0 :       int32_t segLength = 0;
    6204           0 :       BCMapCellInfo priorAjaInfo(this);
    6205           0 :       for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
    6206           0 :            rowB += segLength) {
    6207           0 :         iter.PeekIEnd(info, rowB, ajaInfo);
    6208           0 :         currentBorder = info.GetIEndInternalBorder();
    6209           0 :         adjacentBorder = ajaInfo.GetIStartInternalBorder();
    6210           0 :         currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
    6211           0 :                                         adjacentBorder, !INLINE_DIR);
    6212             : 
    6213           0 :         segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowB);
    6214           0 :         segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowB);
    6215             : 
    6216             :         // update lastBlockDirBorders and see if a new segment starts
    6217           0 :         startSeg = SetBorder(currentBorder,
    6218           0 :                              lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
    6219             :         // store the border segment in the cell map and update cellBorders
    6220           0 :         if (info.GetCellEndColIndex() < damageArea.EndCol() &&
    6221           0 :             rowB >= damageArea.StartRow() && rowB < damageArea.EndRow()) {
    6222           0 :           tableCellMap->SetBCBorderEdge(eLogicalSideIEnd, *iter.mCellMap,
    6223           0 :                                         iter.mRowGroupStart, rowB,
    6224           0 :                                         info.GetCellEndColIndex(), segLength,
    6225             :                                         currentBorder.owner,
    6226           0 :                                         currentBorder.width, startSeg);
    6227           0 :           info.SetIEndBorderWidths(currentBorder.width);
    6228           0 :           ajaInfo.SetIStartBorderWidths(currentBorder.width);
    6229             :         }
    6230             :         // update the bStart-iEnd corner
    6231           0 :         bool hitsSpanOnIEnd = (rowB > ajaInfo.mRowIndex) &&
    6232           0 :                                   (rowB < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
    6233           0 :         BCCornerInfo* trCorner = ((0 == rowB) || hitsSpanOnIEnd) ?
    6234           0 :                                  &bStartCorners[info.GetCellEndColIndex() + 1] :
    6235           0 :                                  &bEndCorners[info.GetCellEndColIndex() + 1];
    6236           0 :         trCorner->Update(eLogicalSideBEnd, currentBorder);
    6237             :         // if this is not the first time through,
    6238             :         // consider the segment to the iEnd side
    6239           0 :         if (rowB != info.mRowIndex) {
    6240           0 :           currentBorder = priorAjaInfo.GetBEndInternalBorder();
    6241           0 :           adjacentBorder = ajaInfo.GetBStartInternalBorder();
    6242           0 :           currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
    6243           0 :                                           adjacentBorder, INLINE_DIR);
    6244           0 :           trCorner->Update(eLogicalSideIEnd, currentBorder);
    6245             :         }
    6246             :         // store the bStart-iEnd corner in the cell map
    6247           0 :         if (info.GetCellEndColIndex() < damageArea.EndCol() &&
    6248           0 :             rowB >= damageArea.StartRow()) {
    6249           0 :           if (0 != rowB) {
    6250           0 :             tableCellMap->SetBCBorderCorner(eLogicalCornerBStartIEnd, *iter.mCellMap,
    6251           0 :                                             iter.mRowGroupStart, rowB,
    6252           0 :                                             info.GetCellEndColIndex(),
    6253           0 :                                             LogicalSide(trCorner->ownerSide),
    6254           0 :                                             trCorner->subWidth,
    6255           0 :                                             trCorner->bevel);
    6256             :           }
    6257             :           // store any corners this cell spans together with the aja cell
    6258           0 :           for (int32_t rX = rowB + 1; rX < rowB + segLength; rX++) {
    6259           0 :             tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIEnd, *iter.mCellMap,
    6260           0 :                                             iter.mRowGroupStart, rX,
    6261           0 :                                             info.GetCellEndColIndex(),
    6262           0 :                                             LogicalSide(trCorner->ownerSide),
    6263           0 :                                             trCorner->subWidth, false);
    6264             :           }
    6265             :         }
    6266             :         // update bEnd-iEnd corner, bStartCorners, bEndCorners
    6267           0 :         hitsSpanOnIEnd = (rowB + segLength <
    6268           0 :                            ajaInfo.mRowIndex + ajaInfo.mRowSpan);
    6269           0 :         BCCornerInfo& brCorner = (hitsSpanOnIEnd) ?
    6270           0 :                                  bStartCorners[info.GetCellEndColIndex() + 1] :
    6271           0 :                                  bEndCorners[info.GetCellEndColIndex() + 1];
    6272           0 :         brCorner.Set(eLogicalSideBStart, currentBorder);
    6273           0 :         priorAjaInfo = ajaInfo;
    6274             :       }
    6275             :     }
    6276           0 :     for (int32_t colIdx = info.mColIndex + 1;
    6277           0 :          colIdx <= info.GetCellEndColIndex(); colIdx++) {
    6278           0 :       lastBlockDirBorders[colIdx].Reset(0,1);
    6279             :     }
    6280             : 
    6281             :     // find the dominant border considering the cell's bEnd border, adjacent
    6282             :     // cells and the table, row group, row
    6283           0 :     if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
    6284             :       // touches bEnd edge of table
    6285           0 :       if (!tableBorderReset[eLogicalSideBEnd]) {
    6286           0 :         propData->mBEndBorderWidth = 0;
    6287           0 :         tableBorderReset[eLogicalSideBEnd] = true;
    6288             :       }
    6289           0 :       for (int32_t colIdx = info.mColIndex;
    6290           0 :            colIdx <= info.GetCellEndColIndex(); colIdx++) {
    6291           0 :         info.SetColumn(colIdx);
    6292           0 :         currentBorder = info.GetBEndEdgeBorder();
    6293             :         // update/store the bEnd-iStart & bEnd-IEnd corners
    6294           0 :         BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
    6295           0 :         blCorner.Update(eLogicalSideIEnd, currentBorder);
    6296           0 :         tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIStart, *iter.mCellMap,
    6297           0 :                                         iter.mRowGroupStart,
    6298           0 :                                         info.GetCellEndRowIndex(),
    6299             :                                         colIdx,
    6300           0 :                                         LogicalSide(blCorner.ownerSide),
    6301           0 :                                         blCorner.subWidth, blCorner.bevel);
    6302           0 :         BCCornerInfo& brCorner = bEndCorners[colIdx + 1]; // bEnd-iEnd
    6303           0 :         brCorner.Update(eLogicalSideIStart, currentBorder);
    6304           0 :         if (info.mNumTableCols == colIdx + 1) { // bEnd-IEnd corner of the table
    6305           0 :           tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIEnd, *iter.mCellMap,
    6306           0 :                                           iter.mRowGroupStart,
    6307           0 :                                           info.GetCellEndRowIndex(), colIdx,
    6308           0 :                                           LogicalSide(brCorner.ownerSide),
    6309           0 :                                           brCorner.subWidth,
    6310           0 :                                           brCorner.bevel, true);
    6311             :         }
    6312             :         // update lastBEndBorder and see if a new segment starts
    6313           0 :         startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
    6314           0 :         if (!startSeg) {
    6315             :            // make sure that we did not compare apples to oranges i.e. the
    6316             :            // current border should be a continuation of the lastBEndBorder,
    6317             :            // as it is a bEnd border
    6318             :            // add 1 to the info.GetCellEndRowIndex()
    6319           0 :            startSeg = (lastBEndBorder.rowIndex !=
    6320           0 :                        (info.GetCellEndRowIndex() + 1));
    6321             :         }
    6322             :         // store the border segment in the cell map and update cellBorders
    6323           0 :         tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
    6324           0 :                                       iter.mRowGroupStart,
    6325           0 :                                       info.GetCellEndRowIndex(),
    6326             :                                       colIdx, 1, currentBorder.owner,
    6327           0 :                                       currentBorder.width, startSeg);
    6328             :         // update lastBEndBorders
    6329           0 :         lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
    6330           0 :         lastBEndBorder.rowSpan = info.mRowSpan;
    6331           0 :         lastBEndBorders[colIdx] = lastBEndBorder;
    6332             : 
    6333           0 :         info.SetBEndBorderWidths(currentBorder.width);
    6334           0 :         info.SetTableBEndBorderWidth(currentBorder.width);
    6335           0 :         info.SetColumnBEndContBCBorder();
    6336             :       }
    6337           0 :       info.SetRowGroupBEndContBCBorder();
    6338           0 :       info.SetColGroupBEndContBCBorder();
    6339             :     }
    6340             :     else {
    6341           0 :       int32_t segLength = 0;
    6342           0 :       for (int32_t colIdx = info.mColIndex;
    6343           0 :            colIdx <= info.GetCellEndColIndex(); colIdx += segLength) {
    6344           0 :         iter.PeekBEnd(info, colIdx, ajaInfo);
    6345           0 :         currentBorder = info.GetBEndInternalBorder();
    6346           0 :         adjacentBorder = ajaInfo.GetBStartInternalBorder();
    6347           0 :         currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
    6348           0 :                                         adjacentBorder, INLINE_DIR);
    6349           0 :         segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colIdx);
    6350           0 :         segLength = std::min(segLength, info.mColIndex + info.mColSpan - colIdx);
    6351             : 
    6352             :         // update, store the bEnd-iStart corner
    6353           0 :         BCCornerInfo& blCorner = bEndCorners[colIdx]; // bEnd-iStart
    6354           0 :         bool hitsSpanBelow = (colIdx > ajaInfo.mColIndex) &&
    6355           0 :                                (colIdx < ajaInfo.mColIndex + ajaInfo.mColSpan);
    6356           0 :         bool update = true;
    6357           0 :         if (colIdx == info.mColIndex && colIdx > damageArea.StartCol()) {
    6358           0 :           int32_t prevRowIndex = lastBEndBorders[colIdx - 1].rowIndex;
    6359           0 :           if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
    6360             :             // hits a rowspan on the iEnd side
    6361           0 :             update = false;
    6362             :             // the corner was taken care of during the cell on the iStart side
    6363             :           }
    6364           0 :           else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
    6365             :             // spans below the cell to the iStart side
    6366           0 :             bStartCorners[colIdx] = blCorner;
    6367           0 :             blCorner.Set(eLogicalSideIEnd, currentBorder);
    6368           0 :             update = false;
    6369             :           }
    6370             :         }
    6371           0 :         if (update) {
    6372           0 :           blCorner.Update(eLogicalSideIEnd, currentBorder);
    6373             :         }
    6374           0 :         if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
    6375           0 :             colIdx >= damageArea.StartCol()) {
    6376           0 :           if (hitsSpanBelow) {
    6377           0 :             tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIStart, *iter.mCellMap,
    6378           0 :                                             iter.mRowGroupStart,
    6379           0 :                                             info.GetCellEndRowIndex(), colIdx,
    6380           0 :                                             LogicalSide(blCorner.ownerSide),
    6381           0 :                                             blCorner.subWidth, blCorner.bevel);
    6382             :           }
    6383             :           // store any corners this cell spans together with the aja cell
    6384           0 :           for (int32_t c = colIdx + 1; c < colIdx + segLength; c++) {
    6385           0 :             BCCornerInfo& corner = bEndCorners[c];
    6386           0 :             corner.Set(eLogicalSideIEnd, currentBorder);
    6387           0 :             tableCellMap->SetBCBorderCorner(eLogicalCornerBEndIStart, *iter.mCellMap,
    6388           0 :                                             iter.mRowGroupStart,
    6389           0 :                                             info.GetCellEndRowIndex(), c,
    6390           0 :                                             LogicalSide(corner.ownerSide),
    6391           0 :                                             corner.subWidth,
    6392           0 :                                             false);
    6393             :           }
    6394             :         }
    6395             :         // update lastBEndBorders and see if a new segment starts
    6396           0 :         startSeg = SetInlineDirBorder(currentBorder, blCorner, lastBEndBorder);
    6397           0 :         if (!startSeg) {
    6398             :            // make sure that we did not compare apples to oranges i.e. the
    6399             :            // current border should be a continuation of the lastBEndBorder,
    6400             :            // as it is a bEnd border
    6401             :            // add 1 to the info.GetCellEndRowIndex()
    6402           0 :            startSeg = (lastBEndBorder.rowIndex !=
    6403           0 :                        info.GetCellEndRowIndex() + 1);
    6404             :         }
    6405           0 :         lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
    6406           0 :         lastBEndBorder.rowSpan = info.mRowSpan;
    6407           0 :         for (int32_t c = colIdx; c < colIdx + segLength; c++) {
    6408           0 :           lastBEndBorders[c] = lastBEndBorder;
    6409             :         }
    6410             : 
    6411             :         // store the border segment the cell map and update cellBorders
    6412           0 :         if (info.GetCellEndRowIndex() < damageArea.EndRow() &&
    6413           0 :             colIdx >= damageArea.StartCol() && colIdx < damageArea.EndCol()) {
    6414           0 :           tableCellMap->SetBCBorderEdge(eLogicalSideBEnd, *iter.mCellMap,
    6415           0 :                                         iter.mRowGroupStart,
    6416           0 :                                         info.GetCellEndRowIndex(),
    6417             :                                         colIdx, segLength, currentBorder.owner,
    6418           0 :                                         currentBorder.width, startSeg);
    6419           0 :           info.SetBEndBorderWidths(currentBorder.width);
    6420           0 :           ajaInfo.SetBStartBorderWidths(currentBorder.width);
    6421             :         }
    6422             :         // update bEnd-iEnd corner
    6423           0 :         BCCornerInfo& brCorner = bEndCorners[colIdx + segLength];
    6424           0 :         brCorner.Update(eLogicalSideIStart, currentBorder);
    6425             :       }
    6426           0 :       if (!gotRowBorder && 1 == info.mRowSpan &&
    6427           0 :           (ajaInfo.mStartRow || info.mRgAtEnd)) {
    6428             :         //get continuous row/row group border
    6429             :         //we need to check the row group's bEnd border if this is
    6430             :         //the last row in the row group, but only a cell with rowspan=1
    6431             :         //will know whether *this* row is at the bEnd
    6432             :         const nsIFrame* nextRowGroup =
    6433           0 :           ajaInfo.mRgAtStart ? ajaInfo.mRowGroup : nullptr;
    6434           0 :         info.SetInnerRowGroupBEndContBCBorder(nextRowGroup, ajaInfo.mStartRow);
    6435           0 :         gotRowBorder = true;
    6436             :       }
    6437             :     }
    6438             : 
    6439             :     // see if the cell to the iEnd side had a rowspan and its bEnd-iStart border
    6440             :     // needs be joined with this one's bEnd
    6441             :     // if  there is a cell to the iEnd and the cell to iEnd side was a rowspan
    6442           0 :     if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
    6443           0 :         (lastBEndBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
    6444           0 :       BCCornerInfo& corner = bEndCorners[info.GetCellEndColIndex() + 1];
    6445           0 :       if (!IsBlock(LogicalSide(corner.ownerSide))) {
    6446             :         // not a block-dir owner
    6447           0 :         BCCellBorder& thisBorder = lastBEndBorder;
    6448           0 :         BCCellBorder& nextBorder = lastBEndBorders[info.mColIndex + 1];
    6449           0 :         if ((thisBorder.color == nextBorder.color) &&
    6450           0 :             (thisBorder.width == nextBorder.width) &&
    6451           0 :             (thisBorder.style == nextBorder.style)) {
    6452             :           // set the flag on the next border indicating it is not the start of a
    6453             :           // new segment
    6454           0 :           if (iter.mCellMap) {
    6455           0 :             tableCellMap->ResetBStartStart(eLogicalSideBEnd, *iter.mCellMap,
    6456           0 :                                            info.GetCellEndRowIndex(),
    6457           0 :                                            info.GetCellEndColIndex() + 1);
    6458             :           }
    6459             :         }
    6460             :       }
    6461             :     }
    6462             :   } // for (iter.First(info); info.mCell; iter.Next(info)) {
    6463             :   // reset the bc flag and damage area
    6464           0 :   SetNeedToCalcBCBorders(false);
    6465           0 :   propData->mDamageArea = TableArea(0, 0, 0, 0);
    6466             : #ifdef DEBUG_TABLE_CELLMAP
    6467             :   mCellMap->Dump();
    6468             : #endif
    6469             : }
    6470             : 
    6471             : class BCPaintBorderIterator;
    6472             : 
    6473           0 : struct BCBorderParameters
    6474             : {
    6475             :   uint8_t mBorderStyle;
    6476             :   nscolor mBorderColor;
    6477             :   nscolor mBGColor;
    6478             :   nsRect mBorderRect;
    6479             :   int32_t mAppUnitsPerDevPixel;
    6480             :   uint8_t mStartBevelSide;
    6481             :   nscoord mStartBevelOffset;
    6482             :   uint8_t mEndBevelSide;
    6483             :   nscoord mEndBevelOffset;
    6484             : };
    6485             : 
    6486             : struct BCBlockDirSeg
    6487             : {
    6488             :   BCBlockDirSeg();
    6489             : 
    6490             :   void Start(BCPaintBorderIterator& aIter,
    6491             :              BCBorderOwner          aBorderOwner,
    6492             :              BCPixelSize            aBlockSegISize,
    6493             :              BCPixelSize            aInlineSegBSize);
    6494             : 
    6495             :   void Initialize(BCPaintBorderIterator& aIter);
    6496             :   void GetBEndCorner(BCPaintBorderIterator& aIter,
    6497             :                      BCPixelSize            aInlineSegBSize);
    6498             : 
    6499             :   Maybe<BCBorderParameters> BuildBorderParameters(BCPaintBorderIterator& aIter,
    6500             :                                                   BCPixelSize aInlineSegBSize);
    6501             :   void Paint(BCPaintBorderIterator& aIter,
    6502             :              DrawTarget&            aDrawTarget,
    6503             :              BCPixelSize            aInlineSegBSize);
    6504             :   void CreateWebRenderCommands(BCPaintBorderIterator& aIter,
    6505             :                                BCPixelSize aInlineSegBSize,
    6506             :                                wr::DisplayListBuilder& aBuilder,
    6507             :                                const layers::StackingContextHelper& aSc,
    6508             :                                nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    6509             :                                const nsPoint& aPt);
    6510             :   void AdvanceOffsetB();
    6511             :   void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
    6512             : 
    6513             : 
    6514             :   union {
    6515             :     nsTableColFrame*  mCol;
    6516             :     int32_t           mColWidth;
    6517             :   };
    6518             :   nscoord               mOffsetI;    // i-offset with respect to the table edge
    6519             :   nscoord               mOffsetB;    // b-offset with respect to the table edge
    6520             :   nscoord               mLength;     // block-dir length including corners
    6521             :   BCPixelSize           mWidth;      // thickness in pixels
    6522             : 
    6523             :   nsTableCellFrame*     mAjaCell;       // previous sibling to the first cell
    6524             :                                         // where the segment starts, it can be
    6525             :                                         // the owner of a segment
    6526             :   nsTableCellFrame*     mFirstCell;     // cell at the start of the segment
    6527             :   nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
    6528             :   nsTableRowFrame*      mFirstRow;      // row at the start of the segment
    6529             :   nsTableCellFrame*     mLastCell;      // cell at the current end of the
    6530             :                                         // segment
    6531             : 
    6532             : 
    6533             :   uint8_t               mOwner;         // owner of the border, defines the
    6534             :                                         // style
    6535             :   LogicalSide           mBStartBevelSide;    // direction to bevel at the bStart
    6536             :   nscoord               mBStartBevelOffset;  // how much to bevel at the bStart
    6537             :   BCPixelSize           mBEndInlineSegBSize; // bSize of the crossing
    6538             :                                              // inline-dir border
    6539             :   nscoord               mBEndOffset;    // how much longer is the segment due
    6540             :                                         // to the inline-dir border, by this
    6541             :                                         // amount the next segment needs to be
    6542             :                                         // shifted.
    6543             :   bool                  mIsBEndBevel;   // should we bevel at the bEnd
    6544             : };
    6545             : 
    6546             : struct BCInlineDirSeg
    6547             : {
    6548             :   BCInlineDirSeg();
    6549             : 
    6550             :   void Start(BCPaintBorderIterator& aIter,
    6551             :              BCBorderOwner          aBorderOwner,
    6552             :              BCPixelSize            aBEndBlockSegISize,
    6553             :              BCPixelSize            aInlineSegBSize);
    6554             :   void GetIEndCorner(BCPaintBorderIterator& aIter,
    6555             :                      BCPixelSize            aIStartSegISize);
    6556             :   void AdvanceOffsetI();
    6557             :   void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
    6558             :   Maybe<BCBorderParameters> BuildBorderParameters(BCPaintBorderIterator& aIter);
    6559             :   void Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget);
    6560             :   void CreateWebRenderCommands(BCPaintBorderIterator& aIter,
    6561             :                                wr::DisplayListBuilder& aBuilder,
    6562             :                                const layers::StackingContextHelper& aSc,
    6563             :                                nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    6564             :                                const nsPoint& aPt);
    6565             : 
    6566             :   nscoord            mOffsetI;       // i-offset with respect to the table edge
    6567             :   nscoord            mOffsetB;       // b-offset with respect to the table edge
    6568             :   nscoord            mLength;        // inline-dir length including corners
    6569             :   BCPixelSize        mWidth;         // border thickness in pixels
    6570             :   nscoord            mIStartBevelOffset; // how much to bevel at the iStart
    6571             :   LogicalSide        mIStartBevelSide;   // direction to bevel at the iStart
    6572             :   bool               mIsIEndBevel;       // should we bevel at the iEnd end
    6573             :   nscoord            mIEndBevelOffset;   // how much to bevel at the iEnd
    6574             :   LogicalSide        mIEndBevelSide;     // direction to bevel at the iEnd
    6575             :   nscoord            mEndOffset;         // how much longer is the segment due
    6576             :                                          // to the block-dir border, by this
    6577             :                                          // amount the next segment needs to be
    6578             :                                          // shifted.
    6579             :   uint8_t            mOwner;             // owner of the border, defines the
    6580             :                                          // style
    6581             :   nsTableCellFrame*  mFirstCell;         // cell at the start of the segment
    6582             :   nsTableCellFrame*  mAjaCell;           // neighboring cell to the first cell
    6583             :                                          // where the segment starts, it can be
    6584             :                                          // the owner of a segment
    6585             : };
    6586             : 
    6587             : struct BCPaintData
    6588             : {
    6589           0 :   explicit BCPaintData(DrawTarget& aDrawTarget)
    6590           0 :     : mDrawTarget(aDrawTarget)
    6591             :   {
    6592           0 :   }
    6593             : 
    6594             :   DrawTarget& mDrawTarget;
    6595             : };
    6596             : 
    6597             : struct BCCreateWebRenderCommandsData
    6598             : {
    6599           0 :   BCCreateWebRenderCommandsData(wr::DisplayListBuilder& aBuilder,
    6600             :                                 const layers::StackingContextHelper& aSc,
    6601             :                                 nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    6602             :                                 const nsPoint& aOffsetToReferenceFrame)
    6603           0 :     : mBuilder(aBuilder)
    6604             :     , mSc(aSc)
    6605             :     , mParentCommands(aParentCommands)
    6606           0 :     , mOffsetToReferenceFrame(aOffsetToReferenceFrame)
    6607             :   {
    6608           0 :   }
    6609             : 
    6610             :   wr::DisplayListBuilder& mBuilder;
    6611             :   const layers::StackingContextHelper& mSc;
    6612             :   nsTArray<layers::WebRenderParentCommand>& mParentCommands;
    6613             :   const nsPoint& mOffsetToReferenceFrame;
    6614             : };
    6615             : 
    6616             : struct BCPaintBorderAction
    6617             : {
    6618           0 :   explicit BCPaintBorderAction(DrawTarget& aDrawTarget)
    6619           0 :     : mMode(Mode::PAINT)
    6620           0 :     , mPaintData(aDrawTarget)
    6621             :   {
    6622           0 :   }
    6623             : 
    6624           0 :   BCPaintBorderAction(wr::DisplayListBuilder& aBuilder,
    6625             :                       const layers::StackingContextHelper& aSc,
    6626             :                       nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    6627             :                       const nsPoint& aOffsetToReferenceFrame)
    6628           0 :     : mMode(Mode::CREATE_WEBRENDER_COMMANDS)
    6629           0 :     , mCreateWebRenderCommandsData(aBuilder, aSc, aParentCommands, aOffsetToReferenceFrame)
    6630             :   {
    6631           0 :     mMode = Mode::CREATE_WEBRENDER_COMMANDS;
    6632           0 :   }
    6633             : 
    6634             :   enum class Mode {
    6635             :     PAINT,
    6636             :     CREATE_WEBRENDER_COMMANDS,
    6637             :   };
    6638             : 
    6639             :   Mode mMode;
    6640             : 
    6641             :   union {
    6642             :     BCPaintData mPaintData;
    6643             :     BCCreateWebRenderCommandsData mCreateWebRenderCommandsData;
    6644             :   };
    6645             : };
    6646             : 
    6647             : // Iterates over borders (iStart border, corner, bStart border) in the cell map within a damage area
    6648             : // from iStart to iEnd, bStart to bEnd. All members are in terms of the 1st in flow frames, except
    6649             : // where suffixed by InFlow.
    6650             : class BCPaintBorderIterator
    6651             : {
    6652             : public:
    6653             :   explicit BCPaintBorderIterator(nsTableFrame* aTable);
    6654           0 :   ~BCPaintBorderIterator() { if (mBlockDirInfo) {
    6655           0 :                               delete [] mBlockDirInfo;
    6656           0 :                            }}
    6657             :   void Reset();
    6658             : 
    6659             :   /**
    6660             :    * Determine the damage area in terms of rows and columns and finalize
    6661             :    * mInitialOffsetI and mInitialOffsetB.
    6662             :    * @param aDirtyRect - dirty rect in table coordinates
    6663             :    * @return - true if we need to paint something given dirty rect
    6664             :    */
    6665             :   bool SetDamageArea(const nsRect& aDamageRect);
    6666             :   void First();
    6667             :   void Next();
    6668             :   void AccumulateOrDoActionInlineDirSegment(BCPaintBorderAction& aAction);
    6669             :   void AccumulateOrDoActionBlockDirSegment(BCPaintBorderAction& aAction);
    6670             :   void ResetVerInfo();
    6671             :   void StoreColumnWidth(int32_t aIndex);
    6672             :   bool BlockDirSegmentOwnsCorner();
    6673             : 
    6674             :   nsTableFrame*         mTable;
    6675             :   nsTableFrame*         mTableFirstInFlow;
    6676             :   nsTableCellMap*       mTableCellMap;
    6677             :   nsCellMap*            mCellMap;
    6678             :   WritingMode           mTableWM;
    6679             :   nscolor               mTableBgColor;
    6680             :   nsTableFrame::RowGroupArray mRowGroups;
    6681             : 
    6682             :   nsTableRowGroupFrame* mPrevRg;
    6683             :   nsTableRowGroupFrame* mRg;
    6684             :   bool                  mIsRepeatedHeader;
    6685             :   bool                  mIsRepeatedFooter;
    6686             :   nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
    6687             :   int32_t               mRgIndex; // current row group index in the
    6688             :                                   // mRowgroups array
    6689             :   int32_t               mFifRgFirstRowIndex; // start row index of the first in
    6690             :                                              // flow of the row group
    6691             :   int32_t               mRgFirstRowIndex; // row index of the first row in the
    6692             :                                           // row group
    6693             :   int32_t               mRgLastRowIndex; // row index of the last row in the row
    6694             :                                          // group
    6695             :   int32_t               mNumTableRows;   // number of rows in the table and all
    6696             :                                          // continuations
    6697             :   int32_t               mNumTableCols;   // number of columns in the table
    6698             :   int32_t               mColIndex;       // with respect to the table
    6699             :   int32_t               mRowIndex;       // with respect to the table
    6700             :   int32_t               mRepeatedHeaderRowIndex; // row index in a repeated
    6701             :                                             //header, it's equivalent to
    6702             :                                             // mRowIndex when we're in a repeated
    6703             :                                             // header, and set to the last row
    6704             :                                             // index of a repeated header when
    6705             :                                             // we're not
    6706             :   bool                  mIsNewRow;
    6707             :   bool                  mAtEnd;             // the iterator cycled over all
    6708             :                                             // borders
    6709             :   nsTableRowFrame*      mPrevRow;
    6710             :   nsTableRowFrame*      mRow;
    6711             :   nsTableRowFrame*      mStartRow;    //first row in a inside the damagearea
    6712             : 
    6713             : 
    6714             :   // cell properties
    6715             :   nsTableCellFrame*     mPrevCell;
    6716             :   nsTableCellFrame*     mCell;
    6717             :   BCCellData*           mPrevCellData;
    6718             :   BCCellData*           mCellData;
    6719             :   BCData*               mBCData;
    6720             : 
    6721           0 :   bool                  IsTableBStartMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
    6722           0 :   bool                  IsTableIEndMost()   {return (mColIndex >= mNumTableCols);}
    6723           0 :   bool                  IsTableBEndMost()   {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
    6724           0 :   bool                  IsTableIStartMost() {return (mColIndex == 0);}
    6725           0 :   bool IsDamageAreaBStartMost() const
    6726           0 :     { return mRowIndex == mDamageArea.StartRow(); }
    6727           0 :   bool IsDamageAreaIEndMost() const
    6728           0 :     { return mColIndex >= mDamageArea.EndCol(); }
    6729           0 :   bool IsDamageAreaBEndMost() const
    6730           0 :     { return mRowIndex >= mDamageArea.EndRow(); }
    6731           0 :   bool IsDamageAreaIStartMost() const
    6732           0 :     { return mColIndex == mDamageArea.StartCol(); }
    6733           0 :   int32_t GetRelativeColIndex() const
    6734           0 :     { return mColIndex - mDamageArea.StartCol(); }
    6735             : 
    6736             :   TableArea             mDamageArea;        // damageArea in cellmap coordinates
    6737           0 :   bool IsAfterRepeatedHeader()
    6738           0 :     { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1)); }
    6739           0 :   bool StartRepeatedFooter() const
    6740             :   {
    6741           0 :     return mIsRepeatedFooter && mRowIndex == mRgFirstRowIndex &&
    6742           0 :       mRowIndex != mDamageArea.StartRow();
    6743             :   }
    6744             : 
    6745             :   nscoord               mInitialOffsetI;    // offsetI of the first border with
    6746             :                                             // respect to the table
    6747             :   nscoord               mInitialOffsetB;    // offsetB of the first border with
    6748             :                                             // respect to the table
    6749             :   nscoord               mNextOffsetB;       // offsetB of the next segment
    6750             :   BCBlockDirSeg*        mBlockDirInfo; // this array is used differently when
    6751             :                                   // inline-dir and block-dir borders are drawn
    6752             :                                   // When inline-dir border are drawn we cache
    6753             :                                   // the column widths and the width of the
    6754             :                                   // block-dir borders that arrive from bStart
    6755             :                                   // When we draw block-dir borders we store
    6756             :                                   // lengths and width for block-dir borders
    6757             :                                   // before they are drawn while we  move over
    6758             :                                   // the columns in the damage area
    6759             :                                   // It has one more elements than columns are
    6760             :                                   // in the table.
    6761             :   BCInlineDirSeg        mInlineSeg;         // the inline-dir segment while we
    6762             :                                             // move over the colums
    6763             :   BCPixelSize           mPrevInlineSegBSize; // the bSize of the previous
    6764             :                                              // inline-dir border
    6765             : 
    6766             : private:
    6767             : 
    6768             :   bool SetNewRow(nsTableRowFrame* aRow = nullptr);
    6769             :   bool SetNewRowGroup();
    6770             :   void   SetNewData(int32_t aRowIndex, int32_t aColIndex);
    6771             : 
    6772             : };
    6773             : 
    6774             : 
    6775             : 
    6776           0 : BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
    6777             :   : mTable(aTable)
    6778           0 :   , mTableFirstInFlow(static_cast<nsTableFrame*>(aTable->FirstInFlow()))
    6779           0 :   , mTableCellMap(aTable->GetCellMap())
    6780           0 :   , mTableWM(aTable->StyleContext())
    6781             : {
    6782           0 :   mBlockDirInfo    = nullptr;
    6783           0 :   LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
    6784             :   // y position of first row in damage area
    6785           0 :   mInitialOffsetB =
    6786           0 :     mTable->GetPrevInFlow() ? 0 : childAreaOffset.BStart(mTableWM);
    6787           0 :   mNumTableRows  = mTable->GetRowCount();
    6788           0 :   mNumTableCols  = mTable->GetColCount();
    6789             : 
    6790             :   // Get the ordered row groups
    6791           0 :   mTable->OrderRowGroups(mRowGroups);
    6792             :   // initialize to a non existing index
    6793           0 :   mRepeatedHeaderRowIndex = -99;
    6794             : 
    6795             :   nsIFrame* bgFrame =
    6796           0 :     nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
    6797           0 :   mTableBgColor = bgFrame->StyleBackground()->BackgroundColor(bgFrame);
    6798           0 : }
    6799             : 
    6800             : bool
    6801           0 : BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
    6802             : {
    6803           0 :   nsSize containerSize = mTable->GetSize();
    6804           0 :   LogicalRect dirtyRect(mTableWM, aDirtyRect, containerSize);
    6805             :   uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex;
    6806           0 :   startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
    6807           0 :   bool done = false;
    6808           0 :   bool haveIntersect = false;
    6809             :   // find startRowIndex, endRowIndex
    6810           0 :   nscoord rowB = mInitialOffsetB;
    6811           0 :   for (uint32_t rgIdx = 0; rgIdx < mRowGroups.Length() && !done; rgIdx++) {
    6812           0 :     nsTableRowGroupFrame* rgFrame = mRowGroups[rgIdx];
    6813           0 :     for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
    6814             :          rowFrame = rowFrame->GetNextRow()) {
    6815             :       // get the row rect relative to the table rather than the row group
    6816           0 :       nscoord rowBSize = rowFrame->BSize(mTableWM);
    6817           0 :       if (haveIntersect) {
    6818             :         // conservatively estimate the half border widths outside the row
    6819           0 :         nscoord borderHalf = mTable->GetPrevInFlow() ? 0 : nsPresContext::
    6820           0 :           CSSPixelsToAppUnits(rowFrame->GetBStartBCBorderWidth() + 1);
    6821           0 :         if (dirtyRect.BEnd(mTableWM) >= rowB - borderHalf) {
    6822             :           nsTableRowFrame* fifRow =
    6823           0 :             static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
    6824           0 :           endRowIndex = fifRow->GetRowIndex();
    6825             :         }
    6826           0 :         else done = true;
    6827             :       }
    6828             :       else {
    6829             :         // conservatively estimate the half border widths outside the row
    6830           0 :         nscoord borderHalf = mTable->GetNextInFlow() ? 0 : nsPresContext::
    6831           0 :           CSSPixelsToAppUnits(rowFrame->GetBEndBCBorderWidth() + 1);
    6832           0 :         if (rowB + rowBSize + borderHalf >= dirtyRect.BStart(mTableWM)) {
    6833           0 :           mStartRg  = rgFrame;
    6834           0 :           mStartRow = rowFrame;
    6835             :           nsTableRowFrame* fifRow =
    6836           0 :             static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
    6837           0 :           startRowIndex = endRowIndex = fifRow->GetRowIndex();
    6838           0 :           haveIntersect = true;
    6839             :         }
    6840             :         else {
    6841           0 :           mInitialOffsetB += rowBSize;
    6842             :         }
    6843             :       }
    6844           0 :       rowB += rowBSize;
    6845             :     }
    6846             :   }
    6847           0 :   mNextOffsetB = mInitialOffsetB;
    6848             : 
    6849             :   // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
    6850             :   // XXX but I don't understand it, so not changing it for now
    6851             :   // table wrapper borders overflow the table, so the table might be
    6852             :   // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
    6853             :   // on the table
    6854           0 :   if (!haveIntersect)
    6855           0 :     return false;
    6856             :   // find startColIndex, endColIndex, startColX
    6857           0 :   haveIntersect = false;
    6858           0 :   if (0 == mNumTableCols)
    6859           0 :     return false;
    6860             : 
    6861           0 :   LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
    6862             : 
    6863             :   // inline position of first col in damage area
    6864           0 :   mInitialOffsetI = childAreaOffset.IStart(mTableWM);
    6865             : 
    6866           0 :   nscoord x = 0;
    6867             :   int32_t colIdx;
    6868           0 :   for (colIdx = 0; colIdx != mNumTableCols; colIdx++) {
    6869           0 :     nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colIdx);
    6870           0 :     if (!colFrame) ABORT1(false);
    6871             :     // get the col rect relative to the table rather than the col group
    6872           0 :     nscoord colISize = colFrame->ISize(mTableWM);
    6873           0 :     if (haveIntersect) {
    6874             :       // conservatively estimate the iStart half border width outside the col
    6875             :       nscoord iStartBorderHalf = nsPresContext::
    6876           0 :         CSSPixelsToAppUnits(colFrame->GetIStartBorderWidth() + 1);
    6877           0 :       if (dirtyRect.IEnd(mTableWM) >= x - iStartBorderHalf) {
    6878           0 :         endColIndex = colIdx;
    6879             :       }
    6880           0 :       else break;
    6881             :     }
    6882             :     else {
    6883             :       // conservatively estimate the iEnd half border width outside the col
    6884             :       nscoord iEndBorderHalf = nsPresContext::
    6885           0 :         CSSPixelsToAppUnits(colFrame->GetIEndBorderWidth() + 1);
    6886           0 :       if (x + colISize + iEndBorderHalf >= dirtyRect.IStart(mTableWM)) {
    6887           0 :         startColIndex = endColIndex = colIdx;
    6888           0 :         haveIntersect = true;
    6889             :       }
    6890             :       else {
    6891           0 :         mInitialOffsetI += colISize;
    6892             :       }
    6893             :     }
    6894           0 :     x += colISize;
    6895             :   }
    6896           0 :   if (!haveIntersect)
    6897           0 :     return false;
    6898           0 :   mDamageArea = TableArea(startColIndex, startRowIndex,
    6899           0 :                           1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex),
    6900           0 :                           1 + endRowIndex - startRowIndex);
    6901             : 
    6902           0 :   Reset();
    6903           0 :   mBlockDirInfo = new BCBlockDirSeg[mDamageArea.ColCount() + 1];
    6904           0 :   if (!mBlockDirInfo)
    6905           0 :     return false;
    6906           0 :   return true;
    6907             : }
    6908             : 
    6909             : void
    6910           0 : BCPaintBorderIterator::Reset()
    6911             : {
    6912           0 :   mAtEnd = true; // gets reset when First() is called
    6913           0 :   mRg = mStartRg;
    6914           0 :   mPrevRow  = nullptr;
    6915           0 :   mRow      = mStartRow;
    6916           0 :   mRowIndex      = 0;
    6917           0 :   mColIndex      = 0;
    6918           0 :   mRgIndex       = -1;
    6919           0 :   mPrevCell      = nullptr;
    6920           0 :   mCell          = nullptr;
    6921           0 :   mPrevCellData  = nullptr;
    6922           0 :   mCellData      = nullptr;
    6923           0 :   mBCData        = nullptr;
    6924           0 :   ResetVerInfo();
    6925           0 : }
    6926             : 
    6927             : /**
    6928             :  * Set the iterator data to a new cellmap coordinate
    6929             :  * @param aRowIndex - the row index
    6930             :  * @param aColIndex - the col index
    6931             :  */
    6932             : void
    6933           0 : BCPaintBorderIterator::SetNewData(int32_t aY,
    6934             :                                   int32_t aX)
    6935             : {
    6936           0 :   if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
    6937             : 
    6938           0 :   mColIndex    = aX;
    6939           0 :   mRowIndex    = aY;
    6940           0 :   mPrevCellData = mCellData;
    6941           0 :   if (IsTableIEndMost() && IsTableBEndMost()) {
    6942           0 :    mCell = nullptr;
    6943           0 :    mBCData = &mTableCellMap->mBCInfo->mBEndIEndCorner;
    6944             :   }
    6945           0 :   else if (IsTableIEndMost()) {
    6946           0 :     mCellData = nullptr;
    6947           0 :     mBCData = &mTableCellMap->mBCInfo->mIEndBorders.ElementAt(aY);
    6948             :   }
    6949           0 :   else if (IsTableBEndMost()) {
    6950           0 :     mCellData = nullptr;
    6951           0 :     mBCData = &mTableCellMap->mBCInfo->mBEndBorders.ElementAt(aX);
    6952             :   }
    6953             :   else {
    6954           0 :     if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
    6955           0 :       mBCData = nullptr;
    6956           0 :       mCellData =
    6957           0 :         (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
    6958           0 :       if (mCellData) {
    6959           0 :         mBCData = &mCellData->mData;
    6960           0 :         if (!mCellData->IsOrig()) {
    6961           0 :           if (mCellData->IsRowSpan()) {
    6962           0 :             aY -= mCellData->GetRowSpanOffset();
    6963             :           }
    6964           0 :           if (mCellData->IsColSpan()) {
    6965           0 :             aX -= mCellData->GetColSpanOffset();
    6966             :           }
    6967           0 :           if ((aX >= 0) && (aY >= 0)) {
    6968           0 :             mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
    6969             :           }
    6970             :         }
    6971           0 :         if (mCellData->IsOrig()) {
    6972           0 :           mPrevCell = mCell;
    6973           0 :           mCell = mCellData->GetCellFrame();
    6974             :         }
    6975             :       }
    6976             :     }
    6977             :   }
    6978             : }
    6979             : 
    6980             : /**
    6981             :  * Set the iterator to a new row
    6982             :  * @param aRow - the new row frame, if null the iterator will advance to the
    6983             :  *               next row
    6984             :  */
    6985             : bool
    6986           0 : BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
    6987             : {
    6988           0 :   mPrevRow = mRow;
    6989           0 :   mRow     = (aRow) ? aRow : mRow->GetNextRow();
    6990           0 :   if (mRow) {
    6991           0 :     mIsNewRow = true;
    6992           0 :     mRowIndex = mRow->GetRowIndex();
    6993           0 :     mColIndex = mDamageArea.StartCol();
    6994           0 :     mPrevInlineSegBSize = 0;
    6995           0 :     if (mIsRepeatedHeader) {
    6996           0 :       mRepeatedHeaderRowIndex = mRowIndex;
    6997             :     }
    6998             :   }
    6999             :   else {
    7000           0 :     mAtEnd = true;
    7001             :   }
    7002           0 :   return !mAtEnd;
    7003             : }
    7004             : 
    7005             : /**
    7006             :  * Advance the iterator to the next row group
    7007             :  */
    7008             : bool
    7009           0 : BCPaintBorderIterator::SetNewRowGroup()
    7010             : {
    7011             : 
    7012           0 :   mRgIndex++;
    7013             : 
    7014           0 :   mIsRepeatedHeader = false;
    7015           0 :   mIsRepeatedFooter = false;
    7016             : 
    7017           0 :   NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
    7018           0 :   if (uint32_t(mRgIndex) < mRowGroups.Length()) {
    7019           0 :     mPrevRg = mRg;
    7020           0 :     mRg = mRowGroups[mRgIndex];
    7021             :     nsTableRowGroupFrame* fifRg =
    7022           0 :       static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow());
    7023           0 :     mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
    7024           0 :     mRgFirstRowIndex    = mRg->GetStartRowIndex();
    7025           0 :     mRgLastRowIndex     = mRgFirstRowIndex + mRg->GetRowCount() - 1;
    7026             : 
    7027           0 :     if (SetNewRow(mRg->GetFirstRow())) {
    7028           0 :       mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr);
    7029           0 :       if (!mCellMap) ABORT1(false);
    7030             :     }
    7031           0 :     if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
    7032             :       // if mRowGroup doesn't have a prev in flow, then it may be a repeated
    7033             :       // header or footer
    7034           0 :       const nsStyleDisplay* display = mRg->StyleDisplay();
    7035           0 :       if (mRowIndex == mDamageArea.StartRow()) {
    7036           0 :         mIsRepeatedHeader = (mozilla::StyleDisplay::TableHeaderGroup == display->mDisplay);
    7037             :       } else {
    7038           0 :         mIsRepeatedFooter = (mozilla::StyleDisplay::TableFooterGroup == display->mDisplay);
    7039             :       }
    7040             :     }
    7041             :   }
    7042             :   else {
    7043           0 :     mAtEnd = true;
    7044             :   }
    7045           0 :   return !mAtEnd;
    7046             : }
    7047             : 
    7048             : /**
    7049             :  *  Move the iterator to the first position in the damageArea
    7050             :  */
    7051             : void
    7052           0 : BCPaintBorderIterator::First()
    7053             : {
    7054           0 :   if (!mTable || mDamageArea.StartCol() >= mNumTableCols ||
    7055           0 :       mDamageArea.StartRow() >= mNumTableRows) ABORT0();
    7056             : 
    7057           0 :   mAtEnd = false;
    7058             : 
    7059           0 :   uint32_t numRowGroups = mRowGroups.Length();
    7060           0 :   for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) {
    7061           0 :     nsTableRowGroupFrame* rowG = mRowGroups[rgY];
    7062           0 :     int32_t start = rowG->GetStartRowIndex();
    7063           0 :     int32_t end   = start + rowG->GetRowCount() - 1;
    7064           0 :     if (mDamageArea.StartRow() >= start && mDamageArea.StartRow() <= end) {
    7065           0 :       mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
    7066           0 :       if (SetNewRowGroup()) {
    7067           0 :         while (mRowIndex < mDamageArea.StartRow() && !mAtEnd) {
    7068           0 :           SetNewRow();
    7069             :         }
    7070           0 :         if (!mAtEnd) {
    7071           0 :           SetNewData(mDamageArea.StartRow(), mDamageArea.StartCol());
    7072             :         }
    7073             :       }
    7074           0 :       return;
    7075             :     }
    7076             :   }
    7077           0 :   mAtEnd = true;
    7078             : }
    7079             : 
    7080             : /**
    7081             :  * Advance the iterator to the next position
    7082             :  */
    7083             : void
    7084           0 : BCPaintBorderIterator::Next()
    7085             : {
    7086           0 :   if (mAtEnd) ABORT0();
    7087           0 :   mIsNewRow = false;
    7088             : 
    7089           0 :   mColIndex++;
    7090           0 :   if (mColIndex > mDamageArea.EndCol()) {
    7091           0 :     mRowIndex++;
    7092           0 :     if (mRowIndex == mDamageArea.EndRow()) {
    7093           0 :       mColIndex = mDamageArea.StartCol();
    7094             :     }
    7095           0 :     else if (mRowIndex < mDamageArea.EndRow()) {
    7096           0 :       if (mRowIndex <= mRgLastRowIndex) {
    7097           0 :         SetNewRow();
    7098             :       }
    7099             :       else {
    7100           0 :         SetNewRowGroup();
    7101             :       }
    7102             :     }
    7103             :     else {
    7104           0 :       mAtEnd = true;
    7105             :     }
    7106             :   }
    7107           0 :   if (!mAtEnd) {
    7108           0 :     SetNewData(mRowIndex, mColIndex);
    7109             :   }
    7110             : }
    7111             : 
    7112             : // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
    7113             : // them
    7114             : // XXX Update terminology from physical to logical
    7115             : /** Compute the vertical offset of a vertical border segment
    7116             :   * @param aCornerOwnerSide - which side owns the corner
    7117             :   * @param aCornerSubWidth  - how wide is the nonwinning side of the corner
    7118             :   * @param aHorWidth        - how wide is the horizontal edge of the corner
    7119             :   * @param aIsStartOfSeg    - does this corner start a new segment
    7120             :   * @param aIsBevel         - is this corner beveled
    7121             :   * @return                 - offset in twips
    7122             :   */
    7123             : static nscoord
    7124           0 : CalcVerCornerOffset(LogicalSide aCornerOwnerSide,
    7125             :                     BCPixelSize aCornerSubWidth,
    7126             :                     BCPixelSize aHorWidth,
    7127             :                     bool        aIsStartOfSeg,
    7128             :                     bool        aIsBevel)
    7129             : {
    7130           0 :   nscoord offset = 0;
    7131             :   // XXX These should be replaced with appropriate side-specific macros (which?)
    7132             :   BCPixelSize smallHalf, largeHalf;
    7133           0 :   if (IsBlock(aCornerOwnerSide)) {
    7134           0 :     DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
    7135           0 :     if (aIsBevel) {
    7136           0 :       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
    7137             :     }
    7138             :     else {
    7139           0 :       offset = (eLogicalSideBStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
    7140             :     }
    7141             :   }
    7142             :   else {
    7143           0 :     DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
    7144           0 :     if (aIsBevel) {
    7145           0 :       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
    7146             :     }
    7147             :     else {
    7148           0 :       offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
    7149             :     }
    7150             :   }
    7151           0 :   return nsPresContext::CSSPixelsToAppUnits(offset);
    7152             : }
    7153             : 
    7154             : /** Compute the horizontal offset of a horizontal border segment
    7155             :   * @param aCornerOwnerSide - which side owns the corner
    7156             :   * @param aCornerSubWidth  - how wide is the nonwinning side of the corner
    7157             :   * @param aVerWidth        - how wide is the vertical edge of the corner
    7158             :   * @param aIsStartOfSeg    - does this corner start a new segment
    7159             :   * @param aIsBevel         - is this corner beveled
    7160             :   * @return                 - offset in twips
    7161             :   */
    7162             : static nscoord
    7163           0 : CalcHorCornerOffset(LogicalSide aCornerOwnerSide,
    7164             :                     BCPixelSize aCornerSubWidth,
    7165             :                     BCPixelSize aVerWidth,
    7166             :                     bool        aIsStartOfSeg,
    7167             :                     bool        aIsBevel)
    7168             : {
    7169           0 :   nscoord offset = 0;
    7170             :   // XXX These should be replaced with appropriate side-specific macros (which?)
    7171             :   BCPixelSize smallHalf, largeHalf;
    7172           0 :   if (IsInline(aCornerOwnerSide)) {
    7173           0 :     DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
    7174           0 :     if (aIsBevel) {
    7175           0 :       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
    7176             :     }
    7177             :     else {
    7178           0 :       offset = (eLogicalSideIStart == aCornerOwnerSide) ? smallHalf : -largeHalf;
    7179             :     }
    7180             :   }
    7181             :   else {
    7182           0 :     DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
    7183           0 :     if (aIsBevel) {
    7184           0 :       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
    7185             :     }
    7186             :     else {
    7187           0 :       offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
    7188             :     }
    7189             :   }
    7190           0 :   return nsPresContext::CSSPixelsToAppUnits(offset);
    7191             : }
    7192             : 
    7193           0 : BCBlockDirSeg::BCBlockDirSeg()
    7194             : {
    7195           0 :   mCol = nullptr;
    7196           0 :   mFirstCell = mLastCell = mAjaCell = nullptr;
    7197           0 :   mOffsetI = mOffsetB = mLength = mWidth = mBStartBevelOffset = 0;
    7198           0 :   mBStartBevelSide = eLogicalSideBStart;
    7199           0 :   mOwner = eCellOwner;
    7200           0 : }
    7201             : 
    7202             : /**
    7203             :  * Start a new block-direction segment
    7204             :  * @param aIter         - iterator containing the structural information
    7205             :  * @param aBorderOwner  - determines the border style
    7206             :  * @param aBlockSegISize  - the width of segment in pixel
    7207             :  * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
    7208             :  *                        at the start
    7209             :  */
    7210             : void
    7211           0 : BCBlockDirSeg::Start(BCPaintBorderIterator& aIter,
    7212             :                      BCBorderOwner          aBorderOwner,
    7213             :                      BCPixelSize            aBlockSegISize,
    7214             :                      BCPixelSize            aInlineSegBSize)
    7215             : {
    7216           0 :   LogicalSide ownerSide   = eLogicalSideBStart;
    7217           0 :   bool bevel       = false;
    7218             : 
    7219           0 :   nscoord cornerSubWidth  = (aIter.mBCData) ?
    7220           0 :                                aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
    7221             : 
    7222           0 :   bool    bStartBevel     = (aBlockSegISize > 0) ? bevel : false;
    7223           0 :   BCPixelSize maxInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
    7224           0 :   nscoord offset          = CalcVerCornerOffset(ownerSide, cornerSubWidth,
    7225             :                                                 maxInlineSegBSize, true,
    7226           0 :                                                 bStartBevel);
    7227             : 
    7228           0 :   mBStartBevelOffset = bStartBevel ?
    7229           0 :     nsPresContext::CSSPixelsToAppUnits(maxInlineSegBSize): 0;
    7230             :   // XXX this assumes that only corners where 2 segments join can be beveled
    7231           0 :   mBStartBevelSide     = (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
    7232           0 :   mOffsetB      += offset;
    7233           0 :   mLength        = -offset;
    7234           0 :   mWidth         = aBlockSegISize;
    7235           0 :   mOwner         = aBorderOwner;
    7236           0 :   mFirstCell     = aIter.mCell;
    7237           0 :   mFirstRowGroup = aIter.mRg;
    7238           0 :   mFirstRow      = aIter.mRow;
    7239           0 :   if (aIter.GetRelativeColIndex() > 0) {
    7240           0 :     mAjaCell = aIter.mBlockDirInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
    7241             :   }
    7242           0 : }
    7243             : 
    7244             : /**
    7245             :  * Initialize the block-dir segments with information that will persist for any
    7246             :  * block-dir segment in this column
    7247             :  * @param aIter - iterator containing the structural information
    7248             :  */
    7249             : void
    7250           0 : BCBlockDirSeg::Initialize(BCPaintBorderIterator& aIter)
    7251             : {
    7252           0 :   int32_t relColIndex = aIter.GetRelativeColIndex();
    7253           0 :   mCol = aIter.IsTableIEndMost() ? aIter.mBlockDirInfo[relColIndex - 1].mCol :
    7254           0 :            aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
    7255           0 :   if (!mCol) ABORT0();
    7256           0 :   if (0 == relColIndex) {
    7257           0 :     mOffsetI = aIter.mInitialOffsetI;
    7258             :   }
    7259             :   // set mOffsetI for the next column
    7260           0 :   if (!aIter.IsDamageAreaIEndMost()) {
    7261           0 :     aIter.mBlockDirInfo[relColIndex + 1].mOffsetI =
    7262           0 :       mOffsetI + mCol->ISize(aIter.mTableWM);
    7263             :   }
    7264           0 :   mOffsetB = aIter.mInitialOffsetB;
    7265           0 :   mLastCell = aIter.mCell;
    7266             : }
    7267             : 
    7268             : /**
    7269             :  * Compute the offsets for the bEnd corner of a block-dir segment
    7270             :  * @param aIter           - iterator containing the structural information
    7271             :  * @param aInlineSegBSize - the width of the inline-dir segment joining the corner
    7272             :  *                          at the start
    7273             :  */
    7274             : void
    7275           0 : BCBlockDirSeg::GetBEndCorner(BCPaintBorderIterator& aIter,
    7276             :                                BCPixelSize            aInlineSegBSize)
    7277             : {
    7278           0 :    LogicalSide ownerSide = eLogicalSideBStart;
    7279           0 :    nscoord cornerSubWidth = 0;
    7280           0 :    bool bevel = false;
    7281           0 :    if (aIter.mBCData) {
    7282           0 :      cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
    7283             :    }
    7284           0 :    mIsBEndBevel = (mWidth > 0) ? bevel : false;
    7285           0 :    mBEndInlineSegBSize = std::max(aIter.mPrevInlineSegBSize, aInlineSegBSize);
    7286           0 :    mBEndOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
    7287           0 :                                     mBEndInlineSegBSize,
    7288           0 :                                     false, mIsBEndBevel);
    7289           0 :    mLength += mBEndOffset;
    7290           0 : }
    7291             : 
    7292             : Maybe<BCBorderParameters>
    7293           0 : BCBlockDirSeg::BuildBorderParameters(BCPaintBorderIterator& aIter,
    7294             :                                      BCPixelSize aInlineSegBSize)
    7295             : {
    7296           0 :   BCBorderParameters result;
    7297             : 
    7298             :   // get the border style, color and paint the segment
    7299             :   LogicalSide side =
    7300           0 :     aIter.IsDamageAreaIEndMost() ? eLogicalSideIEnd : eLogicalSideIStart;
    7301           0 :   int32_t relColIndex = aIter.GetRelativeColIndex();
    7302           0 :   nsTableColFrame* col           = mCol; if (!col) ABORT1(Nothing());
    7303           0 :   nsTableCellFrame* cell         = mFirstCell; // ???
    7304           0 :   nsIFrame* owner = nullptr;
    7305           0 :   result.mBorderStyle = NS_STYLE_BORDER_STYLE_SOLID;
    7306           0 :   result.mBorderColor = 0xFFFFFFFF;
    7307           0 :   result.mBGColor = aIter.mTableBgColor;
    7308             : 
    7309             :   // All the tables frames have the same presContext, so we just use any one
    7310             :   // that exists here:
    7311           0 :   result.mAppUnitsPerDevPixel = col->PresContext()->AppUnitsPerDevPixel();
    7312             : 
    7313           0 :   switch (mOwner) {
    7314             :     case eTableOwner:
    7315           0 :       owner = aIter.mTable;
    7316           0 :       break;
    7317             :     case eAjaColGroupOwner:
    7318           0 :       side = eLogicalSideIEnd;
    7319           0 :       if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
    7320           0 :         col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
    7321             :       }
    7322             :       MOZ_FALLTHROUGH;
    7323             :     case eColGroupOwner:
    7324           0 :       if (col) {
    7325           0 :         owner = col->GetParent();
    7326             :       }
    7327           0 :       break;
    7328             :     case eAjaColOwner:
    7329           0 :       side = eLogicalSideIEnd;
    7330           0 :       if (!aIter.IsTableIEndMost() && (relColIndex > 0)) {
    7331           0 :         col = aIter.mBlockDirInfo[relColIndex - 1].mCol;
    7332             :       }
    7333             :       MOZ_FALLTHROUGH;
    7334             :     case eColOwner:
    7335           0 :       owner = col;
    7336           0 :       break;
    7337             :     case eAjaRowGroupOwner:
    7338           0 :       NS_ERROR("a neighboring rowgroup can never own a vertical border");
    7339             :       MOZ_FALLTHROUGH;
    7340             :     case eRowGroupOwner:
    7341           0 :       NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
    7342             :                   "row group can own border only at table edge");
    7343           0 :       owner = mFirstRowGroup;
    7344           0 :       break;
    7345             :     case eAjaRowOwner:
    7346           0 :       NS_ERROR("program error");
    7347             :       MOZ_FALLTHROUGH;
    7348             :     case eRowOwner:
    7349           0 :       NS_ASSERTION(aIter.IsTableIStartMost() || aIter.IsTableIEndMost(),
    7350             :                    "row can own border only at table edge");
    7351           0 :       owner = mFirstRow;
    7352           0 :       break;
    7353             :     case eAjaCellOwner:
    7354           0 :       side = eLogicalSideIEnd;
    7355           0 :       cell = mAjaCell;
    7356             :       MOZ_FALLTHROUGH;
    7357             :     case eCellOwner:
    7358           0 :       owner = cell;
    7359           0 :       break;
    7360             :   }
    7361           0 :   if (owner) {
    7362           0 :     ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &result.mBorderStyle, &result.mBorderColor);
    7363             :   }
    7364             :   BCPixelSize smallHalf, largeHalf;
    7365           0 :   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
    7366             :   LogicalRect segRect(aIter.mTableWM,
    7367           0 :                  mOffsetI - nsPresContext::CSSPixelsToAppUnits(largeHalf),
    7368             :                  mOffsetB,
    7369           0 :                  nsPresContext::CSSPixelsToAppUnits(mWidth), mLength);
    7370           0 :   nscoord bEndBevelOffset = (mIsBEndBevel) ?
    7371           0 :                   nsPresContext::CSSPixelsToAppUnits(mBEndInlineSegBSize) : 0;
    7372             :   LogicalSide bEndBevelSide =
    7373           0 :     (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart;
    7374             : 
    7375             :   // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
    7376             : 
    7377           0 :   result.mBorderRect = segRect.GetPhysicalRect(aIter.mTableWM, aIter.mTable->GetSize());
    7378             :   // XXX For reversed vertical writing-modes (with direction:rtl), we need to
    7379             :   // invert physicalRect's y-position here, with respect to the table.
    7380             :   // However, it's not worth fixing the border positions here until the
    7381             :   // ordering of the table columns themselves is also fixed (bug 1180528).
    7382             : 
    7383           0 :   result.mStartBevelSide = aIter.mTableWM.PhysicalSide(mBStartBevelSide);
    7384           0 :   result.mEndBevelSide = aIter.mTableWM.PhysicalSide(bEndBevelSide);
    7385           0 :   result.mStartBevelOffset = mBStartBevelOffset;
    7386           0 :   result.mEndBevelOffset = bEndBevelOffset;
    7387             :   // In vertical-rl mode, the 'start' and 'end' of the block-dir (horizontal)
    7388             :   // border segment need to be swapped because DrawTableBorderSegment will
    7389             :   // apply the 'start' bevel at the left edge, and 'end' at the right.
    7390             :   // (Note: In this case, startBevelSide/endBevelSide will usually both be
    7391             :   // "top" or "bottom". DrawTableBorderSegment works purely with physical
    7392             :   // coordinates, so it expects startBevelOffset to be the indentation-from-
    7393             :   // the-left for the "start" (left) end of the border-segment, and
    7394             :   // endBevelOffset is the indentation-from-the-right for the "end" (right)
    7395             :   // end of the border-segment. We've got them reversed, since our block dir
    7396             :   // is RTL, so we have to swap them here.)
    7397           0 :   if (aIter.mTableWM.IsVerticalRL()) {
    7398           0 :     Swap(result.mStartBevelSide, result.mEndBevelSide);
    7399           0 :     Swap(result.mStartBevelOffset, result.mEndBevelOffset);
    7400             :   }
    7401             : 
    7402           0 :   return Some(result);
    7403             : }
    7404             : 
    7405             : /**
    7406             :  * Paint the block-dir segment
    7407             :  * @param aIter           - iterator containing the structural information
    7408             :  * @param aDrawTarget     - the draw target
    7409             :  * @param aInlineSegBSize - the width of the inline-dir segment joining the
    7410             :  *                          corner at the start
    7411             :  */
    7412             : void
    7413           0 : BCBlockDirSeg::Paint(BCPaintBorderIterator& aIter,
    7414             :                      DrawTarget&            aDrawTarget,
    7415             :                      BCPixelSize            aInlineSegBSize)
    7416             : {
    7417           0 :   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter, aInlineSegBSize);
    7418           0 :   if (param.isNothing()) {
    7419           0 :     return;
    7420             :   }
    7421             : 
    7422           0 :   nsCSSRendering::DrawTableBorderSegment(aDrawTarget, param->mBorderStyle, param->mBorderColor,
    7423           0 :                                          param->mBGColor, param->mBorderRect,
    7424           0 :                                          param->mAppUnitsPerDevPixel,
    7425             :                                          nsPresContext::AppUnitsPerCSSPixel(),
    7426           0 :                                          param->mStartBevelSide, param->mStartBevelOffset,
    7427           0 :                                          param->mEndBevelSide, param->mEndBevelOffset);
    7428             : }
    7429             : 
    7430             : void
    7431           0 : BCBlockDirSeg::CreateWebRenderCommands(BCPaintBorderIterator& aIter,
    7432             :                                        BCPixelSize aInlineSegBSize,
    7433             :                                        wr::DisplayListBuilder& aBuilder,
    7434             :                                        const layers::StackingContextHelper& aSc,
    7435             :                                        nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    7436             :                                        const nsPoint& aOffset)
    7437             : {
    7438           0 :   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter, aInlineSegBSize);
    7439           0 :   if (param.isNothing()) {
    7440           0 :     return;
    7441             :   }
    7442             : 
    7443             :   //TODO: Currently, we don't support border with m{Start,End}Bevel{Side,Offset} attributes.
    7444             : 
    7445           0 :   LayoutDeviceRect borderRect = LayoutDeviceRect::FromUnknownRect(NSRectToRect(param->mBorderRect + aOffset,
    7446           0 :                                                                                param->mAppUnitsPerDevPixel));
    7447           0 :   WrRect transformedRect = aSc.ToRelativeWrRect(borderRect);
    7448             :   WrBorderSide wrSide[4];
    7449           0 :   NS_FOR_CSS_SIDES(i) {
    7450           0 :     wrSide[i] = wr::ToWrBorderSide(ToDeviceColor(param->mBorderColor), NS_STYLE_BORDER_STYLE_NONE);
    7451             :   }
    7452           0 :   wrSide[eSideLeft] = wr::ToWrBorderSide(ToDeviceColor(param->mBorderColor), param->mBorderStyle);
    7453             : 
    7454           0 :   WrBorderRadius borderRadii = wr::ToWrBorderRadius( {0, 0}, {0, 0}, {0, 0}, {0, 0} );
    7455             : 
    7456             :   // All border style is set to none except left side. So setting the widths of
    7457             :   // each side to width of rect is fine.
    7458             :   WrBorderWidths borderWidths = wr::ToWrBorderWidths(transformedRect.width,
    7459             :                                                      transformedRect.width,
    7460             :                                                      transformedRect.width,
    7461           0 :                                                      transformedRect.width);
    7462           0 :   transformedRect.width *= 2.0f;
    7463           0 :   Range<const WrBorderSide> wrsides(wrSide, 4);
    7464             :   aBuilder.PushBorder(transformedRect,
    7465             :                       transformedRect,
    7466             :                       borderWidths,
    7467             :                       wrsides,
    7468           0 :                       borderRadii);
    7469             : }
    7470             : 
    7471             : /**
    7472             :  * Advance the start point of a segment
    7473             :  */
    7474             : void
    7475           0 : BCBlockDirSeg::AdvanceOffsetB()
    7476             : {
    7477           0 :   mOffsetB +=  mLength - mBEndOffset;
    7478           0 : }
    7479             : 
    7480             : /**
    7481             :  * Accumulate the current segment
    7482             :  */
    7483             : void
    7484           0 : BCBlockDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
    7485             : {
    7486           0 :   mLastCell = aIter.mCell;
    7487           0 :   mLength  += aIter.mRow->BSize(aIter.mTableWM);
    7488           0 : }
    7489             : 
    7490           0 : BCInlineDirSeg::BCInlineDirSeg()
    7491             : {
    7492           0 :   mOffsetI = mOffsetB = mLength = mWidth =  mIStartBevelOffset = 0;
    7493           0 :   mIStartBevelSide = eLogicalSideBStart;
    7494           0 :   mFirstCell = mAjaCell = nullptr;
    7495           0 : }
    7496             : 
    7497             : /** Initialize an inline-dir border segment for painting
    7498             :   * @param aIter              - iterator storing the current and adjacent frames
    7499             :   * @param aBorderOwner       - which frame owns the border
    7500             :   * @param aBEndBlockSegISize - block-dir segment width coming from up
    7501             :   * @param aInlineSegBSize    - the thickness of the segment
    7502             :   +  */
    7503             : void
    7504           0 : BCInlineDirSeg::Start(BCPaintBorderIterator& aIter,
    7505             :                       BCBorderOwner          aBorderOwner,
    7506             :                       BCPixelSize            aBEndBlockSegISize,
    7507             :                       BCPixelSize            aInlineSegBSize)
    7508             : {
    7509           0 :   LogicalSide cornerOwnerSide = eLogicalSideBStart;
    7510           0 :   bool bevel     = false;
    7511             : 
    7512           0 :   mOwner = aBorderOwner;
    7513           0 :   nscoord cornerSubWidth  = (aIter.mBCData) ?
    7514           0 :                              aIter.mBCData->GetCorner(cornerOwnerSide,
    7515           0 :                                                        bevel) : 0;
    7516             : 
    7517           0 :   bool    iStartBevel = (aInlineSegBSize > 0) ? bevel : false;
    7518           0 :   int32_t relColIndex = aIter.GetRelativeColIndex();
    7519           0 :   nscoord maxBlockSegISize = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
    7520           0 :                                       aBEndBlockSegISize);
    7521           0 :   nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
    7522           0 :                                        maxBlockSegISize, true, iStartBevel);
    7523           0 :   mIStartBevelOffset = (iStartBevel && (aInlineSegBSize > 0)) ? maxBlockSegISize : 0;
    7524             :   // XXX this assumes that only corners where 2 segments join can be beveled
    7525           0 :   mIStartBevelSide   = (aBEndBlockSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
    7526           0 :   mOffsetI += offset;
    7527           0 :   mLength          = -offset;
    7528           0 :   mWidth           = aInlineSegBSize;
    7529           0 :   mFirstCell       = aIter.mCell;
    7530           0 :   mAjaCell         = (aIter.IsDamageAreaBStartMost()) ? nullptr :
    7531           0 :                      aIter.mBlockDirInfo[relColIndex].mLastCell;
    7532           0 : }
    7533             : 
    7534             : /**
    7535             :  * Compute the offsets for the iEnd corner of an inline-dir segment
    7536             :  * @param aIter         - iterator containing the structural information
    7537             :  * @param aIStartSegISize - the iSize of the block-dir segment joining the corner
    7538             :  *                        at the start
    7539             :  */
    7540             : void
    7541           0 : BCInlineDirSeg::GetIEndCorner(BCPaintBorderIterator& aIter,
    7542             :                               BCPixelSize            aIStartSegISize)
    7543             : {
    7544           0 :   LogicalSide ownerSide = eLogicalSideBStart;
    7545           0 :   nscoord cornerSubWidth = 0;
    7546           0 :   bool bevel = false;
    7547           0 :   if (aIter.mBCData) {
    7548           0 :     cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
    7549             :   }
    7550             : 
    7551           0 :   mIsIEndBevel = (mWidth > 0) ? bevel : 0;
    7552           0 :   int32_t relColIndex = aIter.GetRelativeColIndex();
    7553           0 :   nscoord verWidth = std::max(aIter.mBlockDirInfo[relColIndex].mWidth,
    7554           0 :                               aIStartSegISize);
    7555           0 :   mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
    7556           0 :                                    false, mIsIEndBevel);
    7557           0 :   mLength += mEndOffset;
    7558           0 :   mIEndBevelOffset = (mIsIEndBevel) ?
    7559             :                        nsPresContext::CSSPixelsToAppUnits(verWidth) : 0;
    7560           0 :   mIEndBevelSide = (aIStartSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart;
    7561           0 : }
    7562             : 
    7563             : Maybe<BCBorderParameters>
    7564           0 : BCInlineDirSeg::BuildBorderParameters(BCPaintBorderIterator& aIter)
    7565             : {
    7566           0 :   BCBorderParameters result;
    7567             : 
    7568             :   // get the border style, color and paint the segment
    7569             :   LogicalSide side =
    7570           0 :     aIter.IsDamageAreaBEndMost() ? eLogicalSideBEnd : eLogicalSideBStart;
    7571           0 :   nsIFrame* rg   = aIter.mRg;  if (!rg) ABORT1(Nothing());
    7572           0 :   nsIFrame* row  = aIter.mRow; if (!row) ABORT1(Nothing());
    7573           0 :   nsIFrame* cell = mFirstCell;
    7574             :   nsIFrame* col;
    7575           0 :   nsIFrame* owner = nullptr;
    7576             : 
    7577             :   // All the tables frames have the same presContext, so we just use any one
    7578             :   // that exists here:
    7579           0 :   result.mAppUnitsPerDevPixel = row->PresContext()->AppUnitsPerDevPixel();
    7580             : 
    7581           0 :   result.mBorderStyle = NS_STYLE_BORDER_STYLE_SOLID;
    7582           0 :   result.mBorderColor = 0xFFFFFFFF;
    7583           0 :   result.mBGColor = aIter.mTableBgColor;
    7584             : 
    7585           0 :   switch (mOwner) {
    7586             :     case eTableOwner:
    7587           0 :       owner = aIter.mTable;
    7588           0 :       break;
    7589             :     case eAjaColGroupOwner:
    7590           0 :       NS_ERROR("neighboring colgroups can never own an inline-dir border");
    7591             :       MOZ_FALLTHROUGH;
    7592             :     case eColGroupOwner:
    7593           0 :       NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
    7594             :                    "col group can own border only at the table edge");
    7595           0 :       col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
    7596           0 :       if (!col) ABORT1(Nothing());
    7597           0 :       owner = col->GetParent();
    7598           0 :       break;
    7599             :     case eAjaColOwner:
    7600           0 :       NS_ERROR("neighboring column can never own an inline-dir border");
    7601             :       MOZ_FALLTHROUGH;
    7602             :     case eColOwner:
    7603           0 :       NS_ASSERTION(aIter.IsTableBStartMost() || aIter.IsTableBEndMost(),
    7604             :                    "col can own border only at the table edge");
    7605           0 :       owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
    7606           0 :       break;
    7607             :     case eAjaRowGroupOwner:
    7608           0 :       side = eLogicalSideBEnd;
    7609           0 :       rg = (aIter.IsTableBEndMost()) ? aIter.mRg : aIter.mPrevRg;
    7610             :       MOZ_FALLTHROUGH;
    7611             :     case eRowGroupOwner:
    7612           0 :       owner = rg;
    7613           0 :       break;
    7614             :     case eAjaRowOwner:
    7615           0 :       side = eLogicalSideBEnd;
    7616           0 :       row = (aIter.IsTableBEndMost()) ? aIter.mRow : aIter.mPrevRow;
    7617             :       MOZ_FALLTHROUGH;
    7618             :     case eRowOwner:
    7619           0 :       owner = row;
    7620           0 :       break;
    7621             :     case eAjaCellOwner:
    7622           0 :       side = eLogicalSideBEnd;
    7623             :       // if this is null due to the damage area origin-y > 0, then the border
    7624             :       // won't show up anyway
    7625           0 :       cell = mAjaCell;
    7626             :       MOZ_FALLTHROUGH;
    7627             :     case eCellOwner:
    7628           0 :       owner = cell;
    7629           0 :       break;
    7630             :   }
    7631           0 :   if (owner) {
    7632           0 :     ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &result.mBorderStyle, &result.mBorderColor);
    7633             :   }
    7634             :   BCPixelSize smallHalf, largeHalf;
    7635           0 :   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
    7636             :   LogicalRect segRect(aIter.mTableWM, mOffsetI,
    7637           0 :                       mOffsetB - nsPresContext::CSSPixelsToAppUnits(largeHalf),
    7638             :                       mLength,
    7639           0 :                       nsPresContext::CSSPixelsToAppUnits(mWidth));
    7640             : 
    7641             :   // Convert logical to physical sides/coordinates for DrawTableBorderSegment.
    7642           0 :   result.mBorderRect = segRect.GetPhysicalRect(aIter.mTableWM, aIter.mTable->GetSize());
    7643           0 :   result.mStartBevelSide = aIter.mTableWM.PhysicalSide(mIStartBevelSide);
    7644           0 :   result.mEndBevelSide = aIter.mTableWM.PhysicalSide(mIEndBevelSide);
    7645           0 :   result.mStartBevelOffset =
    7646           0 :     nsPresContext::CSSPixelsToAppUnits(mIStartBevelOffset);
    7647           0 :   result.mEndBevelOffset = mIEndBevelOffset;
    7648             :   // With inline-RTL directionality, the 'start' and 'end' of the inline-dir
    7649             :   // border segment need to be swapped because DrawTableBorderSegment will
    7650             :   // apply the 'start' bevel physically at the left or top edge, and 'end' at
    7651             :   // the right or bottom.
    7652             :   // (Note: startBevelSide/endBevelSide will be "top" or "bottom" in horizontal
    7653             :   // writing mode, or "left" or "right" in vertical mode.
    7654             :   // DrawTableBorderSegment works purely with physical coordinates, so it
    7655             :   // expects startBevelOffset to be the indentation-from-the-left or top end
    7656             :   // of the border-segment, and endBevelOffset is the indentation-from-the-
    7657             :   // right or bottom end. If the writing mode is inline-RTL, our "start" and
    7658             :   // "end" will be reversed from this physical-coord view, so we have to swap
    7659             :   // them here.
    7660           0 :   if (!aIter.mTableWM.IsBidiLTR()) {
    7661           0 :     Swap(result.mStartBevelSide, result.mEndBevelSide);
    7662           0 :     Swap(result.mStartBevelOffset, result.mEndBevelOffset);
    7663             :   }
    7664             : 
    7665           0 :   return Some(result);
    7666             : }
    7667             : 
    7668             : /**
    7669             :  * Paint the inline-dir segment
    7670             :  * @param aIter       - iterator containing the structural information
    7671             :  * @param aDrawTarget - the draw target
    7672             :  */
    7673             : void
    7674           0 : BCInlineDirSeg::Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget)
    7675             : {
    7676           0 :   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter);
    7677           0 :   if (param.isNothing()) {
    7678           0 :     return;
    7679             :   }
    7680             : 
    7681           0 :   nsCSSRendering::DrawTableBorderSegment(aDrawTarget, param->mBorderStyle, param->mBorderColor,
    7682           0 :                                          param->mBGColor, param->mBorderRect,
    7683           0 :                                          param->mAppUnitsPerDevPixel,
    7684             :                                          nsPresContext::AppUnitsPerCSSPixel(),
    7685           0 :                                          param->mStartBevelSide, param->mStartBevelOffset,
    7686           0 :                                          param->mEndBevelSide, param->mEndBevelOffset);
    7687             : }
    7688             : 
    7689             : void
    7690           0 : BCInlineDirSeg::CreateWebRenderCommands(BCPaintBorderIterator& aIter,
    7691             :                                         wr::DisplayListBuilder& aBuilder,
    7692             :                                         const layers::StackingContextHelper& aSc,
    7693             :                                         nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    7694             :                                         const nsPoint& aPt)
    7695             : {
    7696           0 :   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter);
    7697           0 :   if (param.isNothing()) {
    7698           0 :     return;
    7699             :   }
    7700             : 
    7701             :   //TODO: Currently, we don't support border with m{Start,End}Bevel{Side,Offset} attributes.
    7702             : 
    7703           0 :   LayoutDeviceRect borderRect = LayoutDeviceRect::FromUnknownRect(NSRectToRect(param->mBorderRect + aPt,
    7704           0 :                                                                                param->mAppUnitsPerDevPixel));
    7705           0 :   WrRect transformedRect = aSc.ToRelativeWrRect(borderRect);
    7706             :   WrBorderSide wrSide[4];
    7707           0 :   NS_FOR_CSS_SIDES(i) {
    7708           0 :     wrSide[i] = wr::ToWrBorderSide(ToDeviceColor(param->mBorderColor), NS_STYLE_BORDER_STYLE_NONE);
    7709             :   }
    7710           0 :   wrSide[eSideTop] = wr::ToWrBorderSide(ToDeviceColor(param->mBorderColor), param->mBorderStyle);
    7711             : 
    7712           0 :   WrBorderRadius borderRadii = wr::ToWrBorderRadius( {0, 0}, {0, 0}, {0, 0}, {0, 0} );
    7713             : 
    7714             :   // All border style is set to none except top side. So setting the widths of
    7715             :   // each side to height of rect is fine.
    7716             :   WrBorderWidths borderWidths = wr::ToWrBorderWidths(transformedRect.height,
    7717             :                                                      transformedRect.height,
    7718             :                                                      transformedRect.height,
    7719           0 :                                                      transformedRect.height);
    7720           0 :   transformedRect.height *= 2.0f;
    7721           0 :   Range<const WrBorderSide> wrsides(wrSide, 4);
    7722             :   aBuilder.PushBorder(transformedRect,
    7723             :                       transformedRect,
    7724             :                       borderWidths,
    7725             :                       wrsides,
    7726           0 :                       borderRadii);
    7727             : }
    7728             : 
    7729             : /**
    7730             :  * Advance the start point of a segment
    7731             :  */
    7732             : void
    7733           0 : BCInlineDirSeg::AdvanceOffsetI()
    7734             : {
    7735           0 :   mOffsetI += (mLength - mEndOffset);
    7736           0 : }
    7737             : 
    7738             : /**
    7739             :  * Accumulate the current segment
    7740             :  */
    7741             : void
    7742           0 : BCInlineDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
    7743             : {
    7744           0 :   mLength += aIter.mBlockDirInfo[aIter.GetRelativeColIndex()].mColWidth;
    7745           0 : }
    7746             : 
    7747             : /**
    7748             :  * store the column width information while painting inline-dir segment
    7749             :  */
    7750             : void
    7751           0 : BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex)
    7752             : {
    7753           0 :   if (IsTableIEndMost()) {
    7754           0 :     mBlockDirInfo[aIndex].mColWidth = mBlockDirInfo[aIndex - 1].mColWidth;
    7755             :   }
    7756             :   else {
    7757           0 :     nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
    7758           0 :     if (!col) ABORT0();
    7759           0 :     mBlockDirInfo[aIndex].mColWidth = col->ISize(mTableWM);
    7760             :   }
    7761             : }
    7762             : /**
    7763             :  * Determine if a block-dir segment owns the corner
    7764             :  */
    7765             : bool
    7766           0 : BCPaintBorderIterator::BlockDirSegmentOwnsCorner()
    7767             : {
    7768           0 :   LogicalSide cornerOwnerSide = eLogicalSideBStart;
    7769           0 :   bool bevel = false;
    7770           0 :   if (mBCData) {
    7771           0 :     mBCData->GetCorner(cornerOwnerSide, bevel);
    7772             :   }
    7773             :   // unitialized ownerside, bevel
    7774           0 :   return  (eLogicalSideBStart == cornerOwnerSide) ||
    7775           0 :           (eLogicalSideBEnd == cornerOwnerSide);
    7776             : }
    7777             : 
    7778             : /**
    7779             :  * Paint if necessary an inline-dir segment, otherwise accumulate it
    7780             :  * @param aDrawTarget - the draw target
    7781             :  */
    7782             : void
    7783           0 : BCPaintBorderIterator::AccumulateOrDoActionInlineDirSegment(BCPaintBorderAction& aAction)
    7784             : {
    7785             : 
    7786           0 :   int32_t relColIndex = GetRelativeColIndex();
    7787             :   // store the current col width if it hasn't been already
    7788           0 :   if (mBlockDirInfo[relColIndex].mColWidth < 0) {
    7789           0 :     StoreColumnWidth(relColIndex);
    7790             :   }
    7791             : 
    7792           0 :   BCBorderOwner borderOwner = eCellOwner;
    7793             :   BCBorderOwner ignoreBorderOwner;
    7794           0 :   bool isSegStart = true;
    7795             :   bool ignoreSegStart;
    7796             : 
    7797             :   nscoord iStartSegISize =
    7798           0 :     mBCData ? mBCData->GetIStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
    7799             :   nscoord bStartSegBSize =
    7800           0 :     mBCData ? mBCData->GetBStartEdge(borderOwner, isSegStart) : 0;
    7801             : 
    7802           0 :   if (mIsNewRow || (IsDamageAreaIStartMost() && IsDamageAreaBEndMost())) {
    7803             :     // reset for every new row and on the bottom of the last row
    7804           0 :     mInlineSeg.mOffsetB = mNextOffsetB;
    7805           0 :     mNextOffsetB     = mNextOffsetB + mRow->BSize(mTableWM);
    7806           0 :     mInlineSeg.mOffsetI = mInitialOffsetI;
    7807           0 :     mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
    7808             :   }
    7809             : 
    7810           0 :   if (!IsDamageAreaIStartMost() && (isSegStart || IsDamageAreaIEndMost() ||
    7811           0 :                                     BlockDirSegmentOwnsCorner())) {
    7812             :     // paint the previous seg or the current one if IsDamageAreaIEndMost()
    7813           0 :     if (mInlineSeg.mLength > 0) {
    7814           0 :       mInlineSeg.GetIEndCorner(*this, iStartSegISize);
    7815           0 :       if (mInlineSeg.mWidth > 0) {
    7816           0 :         if (aAction.mMode == BCPaintBorderAction::Mode::PAINT) {
    7817           0 :           mInlineSeg.Paint(*this, aAction.mPaintData.mDrawTarget);
    7818             :         } else {
    7819           0 :           MOZ_ASSERT(aAction.mMode == BCPaintBorderAction::Mode::CREATE_WEBRENDER_COMMANDS);
    7820           0 :           mInlineSeg.CreateWebRenderCommands(*this,
    7821             :                                              aAction.mCreateWebRenderCommandsData.mBuilder,
    7822             :                                              aAction.mCreateWebRenderCommandsData.mSc,
    7823             :                                              aAction.mCreateWebRenderCommandsData.mParentCommands,
    7824           0 :                                              aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame);
    7825             :         }
    7826             :       }
    7827           0 :       mInlineSeg.AdvanceOffsetI();
    7828             :     }
    7829           0 :     mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
    7830             :   }
    7831           0 :   mInlineSeg.IncludeCurrentBorder(*this);
    7832           0 :   mBlockDirInfo[relColIndex].mWidth = iStartSegISize;
    7833           0 :   mBlockDirInfo[relColIndex].mLastCell = mCell;
    7834           0 : }
    7835             : 
    7836             : /**
    7837             :  * Paint if necessary a block-dir segment, otherwise accumulate it
    7838             :  * @param aDrawTarget - the draw target
    7839             :  */
    7840             : void
    7841           0 : BCPaintBorderIterator::AccumulateOrDoActionBlockDirSegment(BCPaintBorderAction& aAction)
    7842             : {
    7843           0 :   BCBorderOwner borderOwner = eCellOwner;
    7844             :   BCBorderOwner ignoreBorderOwner;
    7845           0 :   bool isSegStart = true;
    7846             :   bool ignoreSegStart;
    7847             : 
    7848             :   nscoord blockSegISize  =
    7849           0 :     mBCData ? mBCData->GetIStartEdge(borderOwner, isSegStart) : 0;
    7850             :   nscoord inlineSegBSize =
    7851           0 :     mBCData ? mBCData->GetBStartEdge(ignoreBorderOwner, ignoreSegStart) : 0;
    7852             : 
    7853           0 :   int32_t relColIndex = GetRelativeColIndex();
    7854           0 :   BCBlockDirSeg& blockDirSeg = mBlockDirInfo[relColIndex];
    7855           0 :   if (!blockDirSeg.mCol) { // on the first damaged row and the first segment in the
    7856             :                            // col
    7857           0 :     blockDirSeg.Initialize(*this);
    7858           0 :     blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
    7859             :   }
    7860             : 
    7861           0 :   if (!IsDamageAreaBStartMost() && (isSegStart || IsDamageAreaBEndMost() ||
    7862           0 :                                     IsAfterRepeatedHeader() ||
    7863           0 :                                     StartRepeatedFooter())) {
    7864             :     // paint the previous seg or the current one if IsDamageAreaBEndMost()
    7865           0 :     if (blockDirSeg.mLength > 0) {
    7866           0 :       blockDirSeg.GetBEndCorner(*this, inlineSegBSize);
    7867           0 :       if (blockDirSeg.mWidth > 0) {
    7868           0 :         if (aAction.mMode == BCPaintBorderAction::Mode::PAINT) {
    7869           0 :           blockDirSeg.Paint(*this, aAction.mPaintData.mDrawTarget, inlineSegBSize);
    7870             :         } else {
    7871           0 :           MOZ_ASSERT(aAction.mMode == BCPaintBorderAction::Mode::CREATE_WEBRENDER_COMMANDS);
    7872           0 :           blockDirSeg.CreateWebRenderCommands(*this,
    7873             :                                               inlineSegBSize,
    7874             :                                               aAction.mCreateWebRenderCommandsData.mBuilder,
    7875             :                                               aAction.mCreateWebRenderCommandsData.mSc,
    7876             :                                               aAction.mCreateWebRenderCommandsData.mParentCommands,
    7877           0 :                                               aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame);
    7878             :         }
    7879             :       }
    7880           0 :       blockDirSeg.AdvanceOffsetB();
    7881             :     }
    7882           0 :     blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
    7883             :   }
    7884           0 :   blockDirSeg.IncludeCurrentBorder(*this);
    7885           0 :   mPrevInlineSegBSize = inlineSegBSize;
    7886           0 : }
    7887             : 
    7888             : /**
    7889             :  * Reset the block-dir information cache
    7890             :  */
    7891             : void
    7892           0 : BCPaintBorderIterator::ResetVerInfo()
    7893             : {
    7894           0 :   if (mBlockDirInfo) {
    7895           0 :     memset(mBlockDirInfo, 0, mDamageArea.ColCount() * sizeof(BCBlockDirSeg));
    7896             :     // XXX reinitialize properly
    7897           0 :     for (auto xIndex : IntegerRange(mDamageArea.ColCount())) {
    7898           0 :       mBlockDirInfo[xIndex].mColWidth = -1;
    7899             :     }
    7900             :   }
    7901           0 : }
    7902             : 
    7903             : void
    7904           0 : nsTableFrame::IterateBCBorders(BCPaintBorderAction& aAction, const nsRect& aDirtyRect)
    7905             : {
    7906             :   // We first transfer the aDirtyRect into cellmap coordinates to compute which
    7907             :   // cell borders need to be painted
    7908           0 :   BCPaintBorderIterator iter(this);
    7909           0 :   if (!iter.SetDamageArea(aDirtyRect))
    7910           0 :     return;
    7911             : 
    7912             :   // XXX comment still has physical terminology
    7913             :   // First, paint all of the vertical borders from top to bottom and left to
    7914             :   // right as they become complete. They are painted first, since they are less
    7915             :   // efficient to paint than horizontal segments. They were stored with as few
    7916             :   // segments as possible (since horizontal borders are painted last and
    7917             :   // possibly over them). For every cell in a row that fails in the damage are
    7918             :   // we look up if the current border would start a new segment, if so we paint
    7919             :   // the previously stored vertical segment and start a new segment. After
    7920             :   // this we  the now active segment with the current border. These
    7921             :   // segments are stored in mBlockDirInfo to be used on the next row
    7922           0 :   for (iter.First(); !iter.mAtEnd; iter.Next()) {
    7923           0 :     iter.AccumulateOrDoActionBlockDirSegment(aAction);
    7924             :   }
    7925             : 
    7926             :   // Next, paint all of the inline-dir border segments from bStart to bEnd reuse
    7927             :   // the mBlockDirInfo array to keep track of col widths and block-dir segments for
    7928             :   // corner calculations
    7929           0 :   iter.Reset();
    7930           0 :   for (iter.First(); !iter.mAtEnd; iter.Next()) {
    7931           0 :     iter.AccumulateOrDoActionInlineDirSegment(aAction);
    7932             :   }
    7933             : }
    7934             : 
    7935             : /**
    7936             :  * Method to paint BCBorders, this does not use currently display lists although
    7937             :  * it will do this in future
    7938             :  * @param aDrawTarget - the rendering context
    7939             :  * @param aDirtyRect  - inside this rectangle the BC Borders will redrawn
    7940             :  */
    7941             : void
    7942           0 : nsTableFrame::PaintBCBorders(DrawTarget& aDrawTarget, const nsRect& aDirtyRect)
    7943             : {
    7944           0 :   BCPaintBorderAction action(aDrawTarget);
    7945           0 :   IterateBCBorders(action, aDirtyRect);
    7946           0 : }
    7947             : 
    7948             : void
    7949           0 : nsTableFrame::CreateWebRenderCommandsForBCBorders(wr::DisplayListBuilder& aBuilder,
    7950             :                                                   const mozilla::layers::StackingContextHelper& aSc,
    7951             :                                                   nsTArray<layers::WebRenderParentCommand>& aParentCommands,
    7952             :                                                   const nsPoint& aOffsetToReferenceFrame)
    7953             : {
    7954           0 :   BCPaintBorderAction action(aBuilder, aSc, aParentCommands, aOffsetToReferenceFrame);
    7955             :   // We always draw whole table border for webrender. Passing the table rect as
    7956             :   // dirty rect.
    7957           0 :   IterateBCBorders(action, GetRect());
    7958           0 : }
    7959             : 
    7960             : bool
    7961           0 : nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
    7962             : {
    7963           0 :   bool result = false;
    7964           0 :   nsTableCellMap* cellMap = GetCellMap();
    7965           0 :   NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
    7966           0 :   if (cellMap) {
    7967           0 :     result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
    7968             :   }
    7969           0 :   return result;
    7970             : }
    7971             : 
    7972             : bool
    7973           0 : nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols)
    7974             : {
    7975           0 :   bool result = false;
    7976           0 :   nsTableCellMap* cellMap = GetCellMap();
    7977           0 :   NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
    7978           0 :   if (cellMap) {
    7979           0 :     result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
    7980             :   }
    7981           0 :   return result;
    7982             : }
    7983             : 
    7984             : /* static */
    7985             : void
    7986           0 : nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame,
    7987             :                                    const nsRect& aOrigRect,
    7988             :                                    const nsRect& aOrigVisualOverflow,
    7989             :                                    bool aIsFirstReflow)
    7990             : {
    7991           0 :   nsIFrame* parent = aFrame->GetParent();
    7992           0 :   NS_ASSERTION(parent, "What happened here?");
    7993             : 
    7994           0 :   if (parent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    7995             :     // Don't bother; we'll invalidate the parent's overflow rect when
    7996             :     // we finish reflowing it.
    7997           0 :     return;
    7998             :   }
    7999             : 
    8000             :   // The part that looks at both the rect and the overflow rect is a
    8001             :   // bit of a hack.  See nsBlockFrame::ReflowLine for an eloquent
    8002             :   // description of its hackishness.
    8003             :   //
    8004             :   // This doesn't really make sense now that we have DLBI.
    8005             :   // This code can probably be simplified a fair bit.
    8006           0 :   nsRect visualOverflow = aFrame->GetVisualOverflowRect();
    8007           0 :   if (aIsFirstReflow ||
    8008           0 :       aOrigRect.TopLeft() != aFrame->GetPosition() ||
    8009           0 :       aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
    8010             :     // Invalidate the old and new overflow rects.  Note that if the
    8011             :     // frame moved, we can't just use aOrigVisualOverflow, since it's in
    8012             :     // coordinates relative to the old position.  So invalidate via
    8013             :     // aFrame's parent, and reposition that overflow rect to the right
    8014             :     // place.
    8015             :     // XXXbz this doesn't handle outlines, does it?
    8016           0 :     aFrame->InvalidateFrame();
    8017           0 :     parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft());
    8018           0 :   } else if (aOrigRect.Size() != aFrame->GetSize() ||
    8019           0 :              aOrigVisualOverflow.Size() != visualOverflow.Size()){
    8020           0 :     aFrame->InvalidateFrameWithRect(aOrigVisualOverflow);
    8021           0 :     aFrame->InvalidateFrame();
    8022           0 :     parent->InvalidateFrameWithRect(aOrigRect);
    8023           0 :     parent->InvalidateFrame();
    8024             :   }
    8025             : }
    8026             : 
    8027             : void
    8028           0 : nsTableFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
    8029             : {
    8030           0 :   nsIFrame* wrapper = GetParent();
    8031           0 :   MOZ_ASSERT(wrapper->StyleContext()->GetPseudo() ==
    8032             :                nsCSSAnonBoxes::tableWrapper,
    8033             :              "What happened to our parent?");
    8034           0 :   aResult.AppendElement(
    8035           0 :     OwnedAnonBox(wrapper, &UpdateStyleOfOwnedAnonBoxesForTableWrapper));
    8036           0 : }
    8037             : 
    8038             : /* static */ void
    8039           0 : nsTableFrame::UpdateStyleOfOwnedAnonBoxesForTableWrapper(
    8040             :   nsIFrame* aOwningFrame,
    8041             :   nsIFrame* aWrapperFrame,
    8042             :   ServoRestyleState& aRestyleState)
    8043             : {
    8044           0 :   MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() ==
    8045             :                nsCSSAnonBoxes::tableWrapper,
    8046             :              "What happened to our parent?");
    8047             : 
    8048             :   RefPtr<nsStyleContext> newContext =
    8049           0 :     aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
    8050           0 :       nsCSSAnonBoxes::tableWrapper, aOwningFrame->StyleContext());
    8051             : 
    8052             :   // Figure out whether we have an actual change.  It's important that we do
    8053             :   // this, even though all the wrapper's changes are due to properties it
    8054             :   // inherits from us, because it's possible that no one ever asked us for those
    8055             :   // style structs and hence changes to them aren't reflected in
    8056             :   // the handled changes at all.
    8057             :   //
    8058             :   // Also note that extensions can add/remove stylesheets that change the styles
    8059             :   // of anonymous boxes directly, so we need to handle that potential change
    8060             :   // here.
    8061             :   //
    8062             :   // NOTE(emilio): We can't use the ChangesHandledFor optimization (and we
    8063             :   // assert against that), because the table wrapper is up in the frame tree
    8064             :   // compared to the owner frame.
    8065             :   uint32_t equalStructs, samePointerStructs; // Not used, actually.
    8066           0 :   nsChangeHint wrapperHint = aWrapperFrame->StyleContext()->CalcStyleDifference(
    8067             :     newContext,
    8068             :     &equalStructs,
    8069           0 :     &samePointerStructs);
    8070           0 :   if (wrapperHint) {
    8071           0 :     aRestyleState.ChangeList().AppendChange(
    8072           0 :       aWrapperFrame, aWrapperFrame->GetContent(), wrapperHint);
    8073             :   }
    8074             : 
    8075           0 :   for (nsIFrame* cur = aWrapperFrame; cur; cur = cur->GetNextContinuation()) {
    8076           0 :     cur->SetStyleContext(newContext);
    8077             :   }
    8078             : 
    8079           0 :   MOZ_ASSERT(!(aWrapperFrame->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES),
    8080             :              "Wrapper frame doesn't have any anon boxes of its own!");
    8081           0 : }

Generated by: LCOV version 1.13