layout/tables/nsTableFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 #include "mozilla/Likely.h"
     8 #include "mozilla/MathAlgorithms.h"
    10 #include "nsCOMPtr.h"
    11 #include "nsTableFrame.h"
    12 #include "nsRenderingContext.h"
    13 #include "nsStyleContext.h"
    14 #include "nsStyleConsts.h"
    15 #include "nsIContent.h"
    16 #include "nsCellMap.h"
    17 #include "nsTableCellFrame.h"
    18 #include "nsHTMLParts.h"
    19 #include "nsTableColFrame.h"
    20 #include "nsTableColGroupFrame.h"
    21 #include "nsTableRowFrame.h"
    22 #include "nsTableRowGroupFrame.h"
    23 #include "nsTableOuterFrame.h"
    24 #include "nsTablePainter.h"
    26 #include "BasicTableLayoutStrategy.h"
    27 #include "FixedTableLayoutStrategy.h"
    29 #include "nsPresContext.h"
    30 #include "nsContentUtils.h"
    31 #include "nsCSSRendering.h"
    32 #include "nsGkAtoms.h"
    33 #include "nsCSSAnonBoxes.h"
    34 #include "nsIPresShell.h"
    35 #include "nsIDOMElement.h"
    36 #include "nsIDOMHTMLElement.h"
    37 #include "nsIScriptError.h"
    38 #include "nsFrameManager.h"
    39 #include "nsError.h"
    40 #include "nsAutoPtr.h"
    41 #include "nsCSSFrameConstructor.h"
    42 #include "nsStyleSet.h"
    43 #include "nsDisplayList.h"
    44 #include "nsIScrollableFrame.h"
    45 #include "nsCSSProps.h"
    46 #include "RestyleTracker.h"
    47 #include <algorithm>
    49 using namespace mozilla;
    50 using namespace mozilla::layout;
    52 /********************************************************************************
    53  ** nsTableReflowState                                                         **
    54  ********************************************************************************/
    56 struct nsTableReflowState {
    58   // the real reflow state
    59   const nsHTMLReflowState& reflowState;
    61   // The table's available size
    62   nsSize availSize;
    64   // Stationary x-offset
    65   nscoord x;
    67   // Running y-offset
    68   nscoord y;
    70   nsTableReflowState(nsPresContext&           aPresContext,
    71                      const nsHTMLReflowState& aReflowState,
    72                      nsTableFrame&            aTableFrame,
    73                      nscoord                  aAvailWidth,
    74                      nscoord                  aAvailHeight)
    75     : reflowState(aReflowState)
    76   {
    77     Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight);
    78   }
    80   void Init(nsPresContext&  aPresContext,
    81             nsTableFrame&   aTableFrame,
    82             nscoord         aAvailWidth,
    83             nscoord         aAvailHeight)
    84   {
    85     nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
    86     nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
    87     nscoord cellSpacingX = table->GetCellSpacingX();
    89     x = borderPadding.left + cellSpacingX;
    90     y = borderPadding.top; //cellspacing added during reflow
    92     availSize.width  = aAvailWidth;
    93     if (NS_UNCONSTRAINEDSIZE != availSize.width) {
    94       availSize.width -= borderPadding.left + borderPadding.right
    95                          + (2 * cellSpacingX);
    96       availSize.width = std::max(0, availSize.width);
    97     }
    99     availSize.height = aAvailHeight;
   100     if (NS_UNCONSTRAINEDSIZE != availSize.height) {
   101       availSize.height -= borderPadding.top + borderPadding.bottom
   102                           + (2 * table->GetCellSpacingY());
   103       availSize.height = std::max(0, availSize.height);
   104     }
   105   }
   107   nsTableReflowState(nsPresContext&           aPresContext,
   108                      const nsHTMLReflowState& aReflowState,
   109                      nsTableFrame&            aTableFrame)
   110     : reflowState(aReflowState)
   111   {
   112     Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
   113   }
   115 };
   117 /********************************************************************************
   118  ** nsTableFrame                                                               **
   119  ********************************************************************************/
   121 struct BCPropertyData
   122 {
   123   BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0),
   124                      mBottomBorderWidth(0), mLeftBorderWidth(0),
   125                      mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {}
   126   nsIntRect mDamageArea;
   127   BCPixelSize mTopBorderWidth;
   128   BCPixelSize mRightBorderWidth;
   129   BCPixelSize mBottomBorderWidth;
   130   BCPixelSize mLeftBorderWidth;
   131   BCPixelSize mLeftCellBorderWidth;
   132   BCPixelSize mRightCellBorderWidth;
   133 };
   135 nsIFrame*
   136 nsTableFrame::GetParentStyleContextFrame() const
   137 {
   138   // Since our parent, the table outer frame, returned this frame, we
   139   // must return whatever our parent would normally have returned.
   141   NS_PRECONDITION(mParent, "table constructed without outer table");
   142   if (!mContent->GetParent() && !StyleContext()->GetPseudo()) {
   143     // We're the root.  We have no style context parent.
   144     return nullptr;
   145   }
   147   return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame();
   148 }
   151 nsIAtom*
   152 nsTableFrame::GetType() const
   153 {
   154   return nsGkAtoms::tableFrame;
   155 }
   158 nsTableFrame::nsTableFrame(nsStyleContext* aContext)
   159   : nsContainerFrame(aContext),
   160     mCellMap(nullptr),
   161     mTableLayoutStrategy(nullptr)
   162 {
   163   memset(&mBits, 0, sizeof(mBits));
   164 }
   166 void
   167 nsTableFrame::Init(nsIContent*      aContent,
   168                    nsIFrame*        aParent,
   169                    nsIFrame*        aPrevInFlow)
   170 {
   171   NS_PRECONDITION(!mCellMap, "Init called twice");
   172   NS_PRECONDITION(!aPrevInFlow ||
   173                   aPrevInFlow->GetType() == nsGkAtoms::tableFrame,
   174                   "prev-in-flow must be of same type");
   176   // Let the base class do its processing
   177   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
   179   // see if border collapse is on, if so set it
   180   const nsStyleTableBorder* tableStyle = StyleTableBorder();
   181   bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
   182   SetBorderCollapse(borderCollapse);
   184   // Create the cell map if this frame is the first-in-flow.
   185   if (!aPrevInFlow) {
   186     mCellMap = new nsTableCellMap(*this, borderCollapse);
   187   }
   189   if (aPrevInFlow) {
   190     // set my width, because all frames in a table flow are the same width and
   191     // code in nsTableOuterFrame depends on this being set
   192     mRect.width = aPrevInFlow->GetSize().width;
   193   }
   194   else {
   195     NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called");
   196     // create the strategy
   197     if (IsAutoLayout())
   198       mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
   199     else
   200       mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
   201   }
   202 }
   204 nsTableFrame::~nsTableFrame()
   205 {
   206   delete mCellMap;
   207   delete mTableLayoutStrategy;
   208 }
   210 void
   211 nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
   212 {
   213   mColGroups.DestroyFramesFrom(aDestructRoot);
   214   nsContainerFrame::DestroyFrom(aDestructRoot);
   215 }
   217 // Make sure any views are positioned properly
   218 void
   219 nsTableFrame::RePositionViews(nsIFrame* aFrame)
   220 {
   221   nsContainerFrame::PositionFrameView(aFrame);
   222   nsContainerFrame::PositionChildViews(aFrame);
   223 }
   225 static bool
   226 IsRepeatedFrame(nsIFrame* kidFrame)
   227 {
   228   return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
   229           kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
   230          (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
   231 }
   233 bool
   234 nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
   235                              nsIFrame* aNextFrame)
   236 {
   237   const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
   238   nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
   239   // don't allow a page break after a repeated element ...
   240   if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
   241       !IsRepeatedFrame(aSourceFrame)) {
   242     return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
   243   }
   245   if (aNextFrame) {
   246     display = aNextFrame->StyleDisplay();
   247     // don't allow a page break before a repeated element ...
   248      nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
   249     if ((display->mBreakBefore ||
   250         (nextRg && nextRg->HasInternalBreakBefore())) &&
   251         !IsRepeatedFrame(aNextFrame)) {
   252       return !IsRepeatedFrame(aSourceFrame); // or after
   253     }
   254   }
   255   return false;
   256 }
   258 typedef nsTArray<nsIFrame*> FrameTArray;
   260 /* static */ void
   261 nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue)
   262 {
   263   auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue);
   264   delete positionedObjs;
   265 }
   267 /* static */ void
   268 nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame)
   269 {
   270   // Supporting relative positioning for table parts other than table cells has
   271   // the potential to break sites that apply 'position: relative' to those
   272   // parts, expecting nothing to happen. We warn at the console to make tracking
   273   // down the issue easy.
   274   if (nsGkAtoms::tableCellFrame != aFrame->GetType()) {
   275     nsIContent* content = aFrame->GetContent();
   276     nsPresContext* presContext = aFrame->PresContext();
   277     if (content && !presContext->HasWarnedAboutPositionedTableParts()) {
   278       presContext->SetHasWarnedAboutPositionedTableParts();
   279       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   280                                       NS_LITERAL_CSTRING("Layout: Tables"),
   281                                       content->OwnerDoc(),
   282                                       nsContentUtils::eLAYOUT_PROPERTIES,
   283                                       "TablePartRelPosWarning");
   284     }
   285   }
   287   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame);
   288   MOZ_ASSERT(tableFrame, "Should have a table frame here");
   289   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
   291   // Retrieve the positioned parts array for this table.
   292   FrameProperties props = tableFrame->Properties();
   293   auto positionedParts =
   294     static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
   296   // Lazily create the array if it doesn't exist yet.
   297   if (!positionedParts) {
   298     positionedParts = new FrameTArray;
   299     props.Set(PositionedTablePartArray(), positionedParts);
   300   }
   302   // Add this frame to the list.
   303   positionedParts->AppendElement(aFrame);
   304 }
   306 /* static */ void
   307 nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame,
   308                                             nsIFrame* aDestructRoot)
   309 {
   310   // Retrieve the table frame, and ensure that we hit aDestructRoot on the way.
   311   // If we don't, that means that the table frame will be destroyed, so we don't
   312   // need to bother with unregistering this frame.
   313   nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame);
   314   if (!tableFrame) {
   315     return;
   316   }
   317   tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
   319   // Retrieve the positioned parts array for this table.
   320   FrameProperties props = tableFrame->Properties();
   321   auto positionedParts =
   322     static_cast<FrameTArray*>(props.Get(PositionedTablePartArray()));
   324   // Remove the frame.
   325   MOZ_ASSERT(positionedParts &&
   326              positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex,
   327              "Asked to unregister a positioned table part that wasn't registered");
   328   if (positionedParts) {
   329     positionedParts->RemoveElement(aFrame);
   330   }
   331 }
   333 // XXX this needs to be cleaned up so that the frame constructor breaks out col group
   334 // frames into a separate child list, bug 343048.
   335 nsresult
   336 nsTableFrame::SetInitialChildList(ChildListID     aListID,
   337                                   nsFrameList&    aChildList)
   338 {
   340   if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) {
   341     // We already have child frames which means we've already been
   342     // initialized
   343     NS_NOTREACHED("unexpected second call to SetInitialChildList");
   344     return NS_ERROR_UNEXPECTED;
   345   }
   346   if (aListID != kPrincipalList) {
   347     // All we know about is the principal child list.
   348     NS_NOTREACHED("unknown frame list");
   349     return NS_ERROR_INVALID_ARG;
   350   }
   352   // XXXbz the below code is an icky cesspit that's only needed in its current
   353   // form for two reasons:
   354   // 1) Both rowgroups and column groups come in on the principal child list.
   355   while (aChildList.NotEmpty()) {
   356     nsIFrame* childFrame = aChildList.FirstChild();
   357     aChildList.RemoveFirstChild();
   358     const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
   360     if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) {
   361       NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
   362                    "This is not a colgroup");
   363       mColGroups.AppendFrame(nullptr, childFrame);
   364     }
   365     else { // row groups and unknown frames go on the main list for now
   366       mFrames.AppendFrame(nullptr, childFrame);
   367     }
   368   }
   370   // If we have a prev-in-flow, then we're a table that has been split and
   371   // so don't treat this like an append
   372   if (!GetPrevInFlow()) {
   373     // process col groups first so that real cols get constructed before
   374     // anonymous ones due to cells in rows.
   375     InsertColGroups(0, mColGroups);
   376     InsertRowGroups(mFrames);
   377     // calc collapsing borders
   378     if (IsBorderCollapse()) {
   379       SetFullBCDamageArea();
   380     }
   381   }
   383   return NS_OK;
   384 }
   386 void nsTableFrame::AttributeChangedFor(nsIFrame*       aFrame,
   387                                        nsIContent*     aContent,
   388                                        nsIAtom*        aAttribute)
   389 {
   390   nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
   391   if (cellFrame) {
   392     if ((nsGkAtoms::rowspan == aAttribute) ||
   393         (nsGkAtoms::colspan == aAttribute)) {
   394       nsTableCellMap* cellMap = GetCellMap();
   395       if (cellMap) {
   396         // for now just remove the cell from the map and reinsert it
   397         int32_t rowIndex, colIndex;
   398         cellFrame->GetRowIndex(rowIndex);
   399         cellFrame->GetColIndex(colIndex);
   400         RemoveCell(cellFrame, rowIndex);
   401         nsAutoTArray<nsTableCellFrame*, 1> cells;
   402         cells.AppendElement(cellFrame);
   403         InsertCells(cells, rowIndex, colIndex - 1);
   405         // XXX Should this use eStyleChange?  It currently doesn't need
   406         // to, but it might given more optimization.
   407         PresContext()->PresShell()->
   408           FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
   409       }
   410     }
   411   }
   412 }
   415 /* ****** CellMap methods ******* */
   417 /* return the effective col count */
   418 int32_t nsTableFrame::GetEffectiveColCount() const
   419 {
   420   int32_t colCount = GetColCount();
   421   if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
   422     nsTableCellMap* cellMap = GetCellMap();
   423     if (!cellMap) {
   424       return 0;
   425     }
   426     // don't count cols at the end that don't have originating cells
   427     for (int32_t colX = colCount - 1; colX >= 0; colX--) {
   428       if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) {
   429         break;
   430       }
   431       colCount--;
   432     }
   433   }
   434   return colCount;
   435 }
   437 int32_t nsTableFrame::GetIndexOfLastRealCol()
   438 {
   439   int32_t numCols = mColFrames.Length();
   440   if (numCols > 0) {
   441     for (int32_t colX = numCols - 1; colX >= 0; colX--) {
   442       nsTableColFrame* colFrame = GetColFrame(colX);
   443       if (colFrame) {
   444         if (eColAnonymousCell != colFrame->GetColType()) {
   445           return colX;
   446         }
   447       }
   448     }
   449   }
   450   return -1;
   451 }
   453 nsTableColFrame*
   454 nsTableFrame::GetColFrame(int32_t aColIndex) const
   455 {
   456   NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
   457   int32_t numCols = mColFrames.Length();
   458   if ((aColIndex >= 0) && (aColIndex < numCols)) {
   459     return mColFrames.ElementAt(aColIndex);
   460   }
   461   else {
   462     NS_ERROR("invalid col index");
   463     return nullptr;
   464   }
   465 }
   467 int32_t nsTableFrame::GetEffectiveRowSpan(int32_t                 aRowIndex,
   468                                           const nsTableCellFrame& aCell) const
   469 {
   470   nsTableCellMap* cellMap = GetCellMap();
   471   NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated.");
   473   int32_t colIndex;
   474   aCell.GetColIndex(colIndex);
   475   return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
   476 }
   478 int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
   479                                           nsCellMap*              aCellMap)
   480 {
   481   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
   483   int32_t colIndex, rowIndex;
   484   aCell.GetColIndex(colIndex);
   485   aCell.GetRowIndex(rowIndex);
   487   if (aCellMap)
   488     return aCellMap->GetRowSpan(rowIndex, colIndex, true);
   489   else
   490     return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
   491 }
   493 int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
   494                                           nsCellMap*              aCellMap) const
   495 {
   496   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
   498   int32_t colIndex, rowIndex;
   499   aCell.GetColIndex(colIndex);
   500   aCell.GetRowIndex(rowIndex);
   501   bool ignore;
   503   if (aCellMap)
   504     return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore);
   505   else
   506     return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
   507 }
   509 bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const
   510 {
   511   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
   512   return tableCellMap->HasMoreThanOneCell(aRowIndex);
   513 }
   515 void nsTableFrame::AdjustRowIndices(int32_t         aRowIndex,
   516                                     int32_t         aAdjustment)
   517 {
   518   // Iterate over the row groups and adjust the row indices of all rows
   519   // whose index is >= aRowIndex.
   520   RowGroupArray rowGroups;
   521   OrderRowGroups(rowGroups);
   523   for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
   524     rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment);
   525   }
   526 }
   529 void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
   530 {
   531   // Iterate over the row groups and adjust the row indices of all rows
   532   // omit the rowgroups that will be inserted later
   533   RowGroupArray rowGroups;
   534   OrderRowGroups(rowGroups);
   536   int32_t rowIndex = 0;
   537   nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
   538   nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
   539   while (!excludeRowGroupsEnumerator.AtEnd()) {
   540     excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
   541     excludeRowGroupsEnumerator.Next();
   542   }
   544   for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
   545     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
   546     if (!excludeRowGroups.GetEntry(rgFrame)) {
   547       const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
   548       for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
   549         if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->StyleDisplay()->mDisplay) {
   550           ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
   551           rowIndex++;
   552         }
   553       }
   554     }
   555   }
   556 }
   557 void nsTableFrame::InsertColGroups(int32_t                   aStartColIndex,
   558                                    const nsFrameList::Slice& aColGroups)
   559 {
   560   int32_t colIndex = aStartColIndex;
   561   nsFrameList::Enumerator colGroups(aColGroups);
   562   for (; !colGroups.AtEnd(); colGroups.Next()) {
   563     MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame);
   564     nsTableColGroupFrame* cgFrame =
   565       static_cast<nsTableColGroupFrame*>(colGroups.get());
   566     cgFrame->SetStartColumnIndex(colIndex);
   567     // XXXbz this sucks.  AddColsToTable will actually remove colgroups from
   568     // the list we're traversing!  Need to fix things here.  :( I guess this is
   569     // why the old code used pointer-to-last-frame as opposed to
   570     // pointer-to-frame-after-last....
   572     // How about dealing with this by storing a const reference to the
   573     // mNextSibling of the framelist's last frame, instead of storing a pointer
   574     // to the first-after-next frame?  Will involve making nsFrameList friend
   575     // of nsIFrame, but it's time for that anyway.
   576     cgFrame->AddColsToTable(colIndex, false,
   577                               colGroups.get()->PrincipalChildList());
   578     int32_t numCols = cgFrame->GetColCount();
   579     colIndex += numCols;
   580   }
   582   nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
   583   if (!remainingColgroups.AtEnd()) {
   584     nsTableColGroupFrame::ResetColIndices(
   585       static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
   586   }
   587 }
   589 void nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
   590                              int32_t          aColIndex)
   591 {
   592   mColFrames.InsertElementAt(aColIndex, &aColFrame);
   593   nsTableColType insertedColType = aColFrame.GetColType();
   594   int32_t numCacheCols = mColFrames.Length();
   595   nsTableCellMap* cellMap = GetCellMap();
   596   if (cellMap) {
   597     int32_t numMapCols = cellMap->GetColCount();
   598     if (numCacheCols > numMapCols) {
   599       bool removedFromCache = false;
   600       if (eColAnonymousCell != insertedColType) {
   601         nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
   602         if (lastCol) {
   603           nsTableColType lastColType = lastCol->GetColType();
   604           if (eColAnonymousCell == lastColType) {
   605             // remove the col from the cache
   606             mColFrames.RemoveElementAt(numCacheCols - 1);
   607             // remove the col from the eColGroupAnonymousCell col group
   608             nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
   609             if (lastColGroup) {
   610               lastColGroup->RemoveChild(*lastCol, false);
   612               // remove the col group if it is empty
   613               if (lastColGroup->GetColCount() <= 0) {
   614                 mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
   615               }
   616             }
   617             removedFromCache = true;
   618           }
   619         }
   620       }
   621       if (!removedFromCache) {
   622         cellMap->AddColsAtEnd(1);
   623       }
   624     }
   625   }
   626   // for now, just bail and recalc all of the collapsing borders
   627   if (IsBorderCollapse()) {
   628     nsIntRect damageArea(aColIndex, 0, 1, GetRowCount());
   629     AddBCDamageArea(damageArea);
   630   }
   631 }
   633 void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
   634                              int32_t               aColIndex,
   635                              bool                  aRemoveFromCache,
   636                              bool                  aRemoveFromCellMap)
   637 {
   638   if (aRemoveFromCache) {
   639     mColFrames.RemoveElementAt(aColIndex);
   640   }
   641   if (aRemoveFromCellMap) {
   642     nsTableCellMap* cellMap = GetCellMap();
   643     if (cellMap) {
   644       AppendAnonymousColFrames(1);
   645     }
   646   }
   647   // for now, just bail and recalc all of the collapsing borders
   648   if (IsBorderCollapse()) {
   649     nsIntRect damageArea(0, 0, GetColCount(), GetRowCount());
   650     AddBCDamageArea(damageArea);
   651   }
   652 }
   654 /** Get the cell map for this table frame.  It is not always mCellMap.
   655   * Only the first-in-flow has a legit cell map.
   656   */
   657 nsTableCellMap*
   658 nsTableFrame::GetCellMap() const
   659 {
   660   return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap;
   661 }
   663 // XXX this needs to be moved to nsCSSFrameConstructor
   664 nsTableColGroupFrame*
   665 nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
   666 {
   667   nsIContent* colGroupContent = GetContent();
   668   nsPresContext* presContext = PresContext();
   669   nsIPresShell *shell = presContext->PresShell();
   671   nsRefPtr<nsStyleContext> colGroupStyle;
   672   colGroupStyle = shell->StyleSet()->
   673     ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext);
   674   // Create a col group frame
   675   nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
   676   ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
   677   newFrame->Init(colGroupContent, this, nullptr);
   678   return (nsTableColGroupFrame *)newFrame;
   679 }
   681 void
   682 nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd)
   683 {
   684   // get the last col group frame
   685   nsTableColGroupFrame* colGroupFrame =
   686     static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
   688   if (!colGroupFrame ||
   689       (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
   690     int32_t colIndex = (colGroupFrame) ?
   691                         colGroupFrame->GetStartColumnIndex() +
   692                         colGroupFrame->GetColCount() : 0;
   693     colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
   694     if (!colGroupFrame) {
   695       return;
   696     }
   697     // add the new frame to the child list
   698     mColGroups.AppendFrame(this, colGroupFrame);
   699     colGroupFrame->SetStartColumnIndex(colIndex);
   700   }
   701   AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
   702                            true);
   704 }
   706 // XXX this needs to be moved to nsCSSFrameConstructor
   707 // Right now it only creates the col frames at the end
   708 void
   709 nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
   710                                        int32_t               aNumColsToAdd,
   711                                        nsTableColType        aColType,
   712                                        bool                  aAddToTable)
   713 {
   714   NS_PRECONDITION(aColGroupFrame, "null frame");
   715   NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
   717   nsIPresShell *shell = PresContext()->PresShell();
   719   // Get the last col frame
   720   nsFrameList newColFrames;
   722   int32_t startIndex = mColFrames.Length();
   723   int32_t lastIndex  = startIndex + aNumColsToAdd - 1;
   725   for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
   726     nsIContent* iContent;
   727     nsRefPtr<nsStyleContext> styleContext;
   728     nsStyleContext* parentStyleContext;
   730     // all anonymous cols that we create here use a pseudo style context of the
   731     // col group
   732     iContent = aColGroupFrame->GetContent();
   733     parentStyleContext = aColGroupFrame->StyleContext();
   734     styleContext = shell->StyleSet()->
   735       ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext);
   736     // ASSERTION to check for bug 54454 sneaking back in...
   737     NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
   739     // create the new col frame
   740     nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
   741     ((nsTableColFrame *) colFrame)->SetColType(aColType);
   742     colFrame->Init(iContent, aColGroupFrame, nullptr);
   744     newColFrames.AppendFrame(nullptr, colFrame);
   745   }
   746   nsFrameList& cols = aColGroupFrame->GetWritableChildList();
   747   nsIFrame* oldLastCol = cols.LastChild();
   748   const nsFrameList::Slice& newCols =
   749     cols.InsertFrames(nullptr, oldLastCol, newColFrames);
   750   if (aAddToTable) {
   751     // get the starting col index in the cache
   752     int32_t startColIndex;
   753     if (oldLastCol) {
   754       startColIndex =
   755         static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
   756     } else {
   757       startColIndex = aColGroupFrame->GetStartColumnIndex();
   758     }
   760     aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
   761   }
   762 }
   764 void
   765 nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
   766 {
   767   int32_t numColsInMap   = GetColCount();
   768   int32_t numColsInCache = mColFrames.Length();
   769   int32_t numColsToAdd = numColsInMap - numColsInCache;
   770   if (numColsToAdd > 0) {
   771     // this sets the child list, updates the col cache and cell map
   772     AppendAnonymousColFrames(numColsToAdd);
   773   }
   774   if (numColsToAdd < 0) {
   775     int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
   776     // if the cell map has fewer cols than the cache, correct it
   777     if (numColsNotRemoved > 0) {
   778       aCellMap->AddColsAtEnd(numColsNotRemoved);
   779     }
   780   }
   781   if (numColsToAdd && HasZeroColSpans()) {
   782     SetNeedColSpanExpansion(true);
   783   }
   784   if (NeedColSpanExpansion()) {
   785     // This flag can be set in two ways -- either by changing
   786     // the number of columns (that happens in the block above),
   787     // or by adding a cell with colspan="0" to the cellmap.  To
   788     // handle the latter case we need to explicitly check the
   789     // flag here -- it may be set even if the number of columns
   790     // did not change.
   791     //
   792     // @see nsCellMap::AppendCell
   794     aCellMap->ExpandZeroColSpans();
   795   }
   796 }
   798 void
   799 nsTableFrame::DidResizeColumns()
   800 {
   801   NS_PRECONDITION(!GetPrevInFlow(),
   802                   "should only be called on first-in-flow");
   803   if (mBits.mResizedColumns)
   804     return; // already marked
   806   for (nsTableFrame *f = this; f;
   807        f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
   808     f->mBits.mResizedColumns = true;
   809 }
   811 void
   812 nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
   813                          int32_t           aRowIndex)
   814 {
   815   nsTableCellMap* cellMap = GetCellMap();
   816   if (cellMap) {
   817     nsIntRect damageArea(0,0,0,0);
   818     cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
   819     MatchCellMapToColCache(cellMap);
   820     if (IsBorderCollapse()) {
   821       AddBCDamageArea(damageArea);
   822     }
   823   }
   824 }
   826 void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
   827                                int32_t                      aRowIndex,
   828                                int32_t                      aColIndexBefore)
   829 {
   830   nsTableCellMap* cellMap = GetCellMap();
   831   if (cellMap) {
   832     nsIntRect damageArea(0,0,0,0);
   833     cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
   834     MatchCellMapToColCache(cellMap);
   835     if (IsBorderCollapse()) {
   836       AddBCDamageArea(damageArea);
   837     }
   838   }
   839 }
   841 // this removes the frames from the col group and table, but not the cell map
   842 int32_t
   843 nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames)
   844 {
   845   // only remove cols that are of type eTypeAnonymous cell (they are at the end)
   846   int32_t endIndex   = mColFrames.Length() - 1;
   847   int32_t startIndex = (endIndex - aNumFrames) + 1;
   848   int32_t numColsRemoved = 0;
   849   for (int32_t colX = endIndex; colX >= startIndex; colX--) {
   850     nsTableColFrame* colFrame = GetColFrame(colX);
   851     if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
   852       nsTableColGroupFrame* cgFrame =
   853         static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
   854       // remove the frame from the colgroup
   855       cgFrame->RemoveChild(*colFrame, false);
   856       // remove the frame from the cache, but not the cell map
   857       RemoveCol(nullptr, colX, true, false);
   858       numColsRemoved++;
   859     }
   860     else {
   861       break;
   862     }
   863   }
   864   return (aNumFrames - numColsRemoved);
   865 }
   867 void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
   868                               int32_t           aRowIndex)
   869 {
   870   nsTableCellMap* cellMap = GetCellMap();
   871   if (cellMap) {
   872     nsIntRect damageArea(0,0,0,0);
   873     cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
   874     MatchCellMapToColCache(cellMap);
   875     if (IsBorderCollapse()) {
   876       AddBCDamageArea(damageArea);
   877     }
   878   }
   879 }
   881 int32_t
   882 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
   883 {
   884   RowGroupArray orderedRowGroups;
   885   OrderRowGroups(orderedRowGroups);
   887   int32_t rowIndex = 0;
   888   for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
   889     nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
   890     if (rgFrame == aRowGroupFrame) {
   891       break;
   892     }
   893     int32_t numRows = rgFrame->GetRowCount();
   894     rowIndex += numRows;
   895   }
   896   return rowIndex;
   897 }
   899 // this cannot extend beyond a single row group
   900 void nsTableFrame::AppendRows(nsTableRowGroupFrame*       aRowGroupFrame,
   901                               int32_t                     aRowIndex,
   902                               nsTArray<nsTableRowFrame*>& aRowFrames)
   903 {
   904   nsTableCellMap* cellMap = GetCellMap();
   905   if (cellMap) {
   906     int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
   907     InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
   908   }
   909 }
   911 // this cannot extend beyond a single row group
   912 int32_t
   913 nsTableFrame::InsertRows(nsTableRowGroupFrame*       aRowGroupFrame,
   914                          nsTArray<nsTableRowFrame*>& aRowFrames,
   915                          int32_t                     aRowIndex,
   916                          bool                        aConsiderSpans)
   917 {
   918 #ifdef DEBUG_TABLE_CELLMAP
   919   printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
   920   Dump(true, false, true);
   921 #endif
   923   int32_t numColsToAdd = 0;
   924   nsTableCellMap* cellMap = GetCellMap();
   925   if (cellMap) {
   926     nsIntRect damageArea(0,0,0,0);
   927     int32_t origNumRows = cellMap->GetRowCount();
   928     int32_t numNewRows = aRowFrames.Length();
   929     cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
   930     MatchCellMapToColCache(cellMap);
   931     if (aRowIndex < origNumRows) {
   932       AdjustRowIndices(aRowIndex, numNewRows);
   933     }
   934     // assign the correct row indices to the new rows. If they were adjusted above
   935     // it may not have been done correctly because each row is constructed with index 0
   936     for (int32_t rowY = 0; rowY < numNewRows; rowY++) {
   937       nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY);
   938       rowFrame->SetRowIndex(aRowIndex + rowY);
   939     }
   940     if (IsBorderCollapse()) {
   941       AddBCDamageArea(damageArea);
   942     }
   943   }
   944 #ifdef DEBUG_TABLE_CELLMAP
   945   printf("=== insertRowsAfter \n");
   946   Dump(true, false, true);
   947 #endif
   949   return numColsToAdd;
   950 }
   952 // this cannot extend beyond a single row group
   953 void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
   954                               int32_t          aNumRowsToRemove,
   955                               bool             aConsiderSpans)
   956 {
   957 #ifdef TBD_OPTIMIZATION
   958   // decide if we need to rebalance. we have to do this here because the row group
   959   // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
   960   bool stopTelling = false;
   961   for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
   962        kidFrame = kidFrame->GetNextSibling()) {
   963     nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
   964     if (cellFrame) {
   965       stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
   966                                                  cellFrame->GetMaximumWidth(), true);
   967     }
   968   }
   969   // XXX need to consider what happens if there are cells that have rowspans
   970   // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
   971 #endif
   973   int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
   974 #ifdef DEBUG_TABLE_CELLMAP
   975   printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
   976   Dump(true, false, true);
   977 #endif
   978   nsTableCellMap* cellMap = GetCellMap();
   979   if (cellMap) {
   980     nsIntRect damageArea(0,0,0,0);
   981     cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
   982     MatchCellMapToColCache(cellMap);
   983     if (IsBorderCollapse()) {
   984       AddBCDamageArea(damageArea);
   985     }
   986   }
   987   AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
   988 #ifdef DEBUG_TABLE_CELLMAP
   989   printf("=== removeRowsAfter\n");
   990   Dump(true, true, true);
   991 #endif
   992 }
   994 // collect the rows ancestors of aFrame
   995 int32_t
   996 nsTableFrame::CollectRows(nsIFrame*                   aFrame,
   997                           nsTArray<nsTableRowFrame*>& aCollection)
   998 {
   999   NS_PRECONDITION(aFrame, "null frame");
  1000   int32_t numRows = 0;
  1001   nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
  1002   while (childFrame) {
  1003     aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
  1004     numRows++;
  1005     childFrame = childFrame->GetNextSibling();
  1007   return numRows;
  1010 void
  1011 nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
  1013 #ifdef DEBUG_TABLE_CELLMAP
  1014   printf("=== insertRowGroupsBefore\n");
  1015   Dump(true, false, true);
  1016 #endif
  1017   nsTableCellMap* cellMap = GetCellMap();
  1018   if (cellMap) {
  1019     RowGroupArray orderedRowGroups;
  1020     OrderRowGroups(orderedRowGroups);
  1022     nsAutoTArray<nsTableRowFrame*, 8> rows;
  1023     // Loop over the rowgroups and check if some of them are new, if they are
  1024     // insert cellmaps in the order that is predefined by OrderRowGroups,
  1025     // XXXbz this code is O(N*M) where N is number of new rowgroups
  1026     // and M is number of rowgroups we have!
  1027     uint32_t rgIndex;
  1028     for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  1029       for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
  1030            rowgroups.Next()) {
  1031         if (orderedRowGroups[rgIndex] == rowgroups.get()) {
  1032           nsTableRowGroupFrame* priorRG =
  1033             (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
  1034           // create and add the cell map for the row group
  1035           cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
  1037           break;
  1041     cellMap->Synchronize(this);
  1042     ResetRowIndices(aRowGroups);
  1044     //now that the cellmaps are reordered too insert the rows
  1045     for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  1046       for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
  1047            rowgroups.Next()) {
  1048         if (orderedRowGroups[rgIndex] == rowgroups.get()) {
  1049           nsTableRowGroupFrame* priorRG =
  1050             (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
  1051           // collect the new row frames in an array and add them to the table
  1052           int32_t numRows = CollectRows(rowgroups.get(), rows);
  1053           if (numRows > 0) {
  1054             int32_t rowIndex = 0;
  1055             if (priorRG) {
  1056               int32_t priorNumRows = priorRG->GetRowCount();
  1057               rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
  1059             InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
  1060             rows.Clear();
  1062           break;
  1068 #ifdef DEBUG_TABLE_CELLMAP
  1069   printf("=== insertRowGroupsAfter\n");
  1070   Dump(true, true, true);
  1071 #endif
  1075 /////////////////////////////////////////////////////////////////////////////
  1076 // Child frame enumeration
  1078 const nsFrameList&
  1079 nsTableFrame::GetChildList(ChildListID aListID) const
  1081   if (aListID == kColGroupList) {
  1082     return mColGroups;
  1084   return nsContainerFrame::GetChildList(aListID);
  1087 void
  1088 nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
  1090   nsContainerFrame::GetChildLists(aLists);
  1091   mColGroups.AppendIfNonempty(aLists, kColGroupList);
  1094 nsRect
  1095 nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
  1096   *aSnap = false;
  1097   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  1100 bool
  1101 nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
  1102                                                    nsIFrame* aFrame)
  1104   if (!mPartHasFixedBackground)
  1105     return false;
  1107   // If aFrame is mFrame or an ancestor in this document, and aFrame is
  1108   // not the viewport frame, then moving aFrame will move mFrame
  1109   // relative to the viewport, so our fixed-pos background will change.
  1110   return mFrame == aFrame ||
  1111     nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame);
  1114 /* static */ void
  1115 nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
  1117   nsStyleContext *bgSC;
  1118   if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
  1119     return;
  1120   if (!bgSC->StyleBackground()->HasFixedBackground())
  1121     return;
  1123   mPartHasFixedBackground = true;
  1126 class nsDisplayTableBorderBackground : public nsDisplayTableItem {
  1127 public:
  1128   nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder,
  1129                                  nsTableFrame* aFrame) :
  1130     nsDisplayTableItem(aBuilder, aFrame) {
  1131     MOZ_COUNT_CTOR(nsDisplayTableBorderBackground);
  1133 #ifdef NS_BUILD_REFCNT_LOGGING
  1134   virtual ~nsDisplayTableBorderBackground() {
  1135     MOZ_COUNT_DTOR(nsDisplayTableBorderBackground);
  1137 #endif
  1139   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  1140                                          const nsDisplayItemGeometry* aGeometry,
  1141                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
  1142   virtual void Paint(nsDisplayListBuilder* aBuilder,
  1143                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
  1144   NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND)
  1145 };
  1147 #ifdef DEBUG
  1148 static bool
  1149 IsFrameAllowedInTable(nsIAtom* aType)
  1151   return IS_TABLE_CELL(aType) ||
  1152          nsGkAtoms::tableRowFrame == aType ||
  1153          nsGkAtoms::tableRowGroupFrame == aType ||
  1154          nsGkAtoms::scrollFrame == aType ||
  1155          nsGkAtoms::tableFrame == aType ||
  1156          nsGkAtoms::tableColFrame == aType ||
  1157          nsGkAtoms::tableColGroupFrame == aType;
  1159 #endif
  1161 /* static */ bool
  1162 nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart,
  1163                                                       nsIFrame* aEnd)
  1165   for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
  1166     NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
  1168     if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f))
  1169       return true;
  1171     nsTableCellFrame *cellFrame = do_QueryFrame(f);
  1172     if (cellFrame)
  1173       continue;
  1175     if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr))
  1176       return true;
  1179   return false;
  1182 void
  1183 nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  1184                                                           const nsDisplayItemGeometry* aGeometry,
  1185                                                           nsRegion *aInvalidRegion)
  1187   if (aBuilder->ShouldSyncDecodeImages()) {
  1188     if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) ||
  1189         nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(
  1190           mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) {
  1191       bool snap;
  1192       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  1196   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  1199 void
  1200 nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
  1201                                       nsRenderingContext* aCtx)
  1203   static_cast<nsTableFrame*>(mFrame)->
  1204     PaintTableBorderBackground(*aCtx, mVisibleRect,
  1205                                ToReferenceFrame(),
  1206                                aBuilder->GetBackgroundPaintFlags());
  1209 static int32_t GetTablePartRank(nsDisplayItem* aItem)
  1211   nsIAtom* type = aItem->Frame()->GetType();
  1212   if (type == nsGkAtoms::tableFrame)
  1213     return 0;
  1214   if (type == nsGkAtoms::tableRowGroupFrame)
  1215     return 1;
  1216   if (type == nsGkAtoms::tableRowFrame)
  1217     return 2;
  1218   return 3;
  1221 static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
  1222                                      void* aClosure)
  1224   return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2);
  1227 /* static */ void
  1228 nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
  1229                                const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
  1231   // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
  1232   // does, except that we allow the children's background and borders to go
  1233   // in our BorderBackground list. This doesn't really affect background
  1234   // painting --- the children won't actually draw their own backgrounds
  1235   // because the nsTableFrame already drew them, unless a child has its own
  1236   // stacking context, in which case the child won't use its passed-in
  1237   // BorderBackground list anyway. It does affect cell borders though; this
  1238   // lets us get cell borders into the nsTableFrame's BorderBackground list.
  1239   nsIFrame* kid = aFrame->GetFirstPrincipalChild();
  1240   while (kid) {
  1241     aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
  1242     kid = kid->GetNextSibling();
  1246 /* static */ void
  1247 nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
  1248                                       nsFrame* aFrame,
  1249                                       const nsRect& aDirtyRect,
  1250                                       const nsDisplayListSet& aLists,
  1251                                       nsDisplayTableItem* aDisplayItem,
  1252                                       DisplayGenericTablePartTraversal aTraversal)
  1254   nsDisplayList eventsBorderBackground;
  1255   // If we need to sort the event backgrounds, then we'll put descendants'
  1256   // display items into their own set of lists.
  1257   bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery();
  1258   nsDisplayListCollection separatedCollection;
  1259   const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists;
  1261   nsAutoPushCurrentTableItem pushTableItem;
  1262   if (aDisplayItem) {
  1263     pushTableItem.Push(aBuilder, aDisplayItem);
  1266   if (aFrame->IsVisibleForPainting(aBuilder)) {
  1267     nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
  1268     // currentItem may be null, when none of the table parts have a
  1269     // background or border
  1270     if (currentItem) {
  1271       currentItem->UpdateForFrameBackground(aFrame);
  1274     // Paint the outset box-shadows for the table frames
  1275     bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr;
  1276     if (hasBoxShadow) {
  1277       lists->BorderBackground()->AppendNewToTop(
  1278         new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame));
  1281     // Create dedicated background display items per-frame when we're
  1282     // handling events.
  1283     // XXX how to handle collapsed borders?
  1284     if (aBuilder->IsForEventDelivery()) {
  1285       nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame,
  1286                                                            lists->BorderBackground());
  1289     // Paint the inset box-shadows for the table frames
  1290     if (hasBoxShadow) {
  1291       lists->BorderBackground()->AppendNewToTop(
  1292         new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame));
  1296   aTraversal(aBuilder, aFrame, aDirtyRect, *lists);
  1298   if (sortEventBackgrounds) {
  1299     // Ensure that the table frame event background goes before the
  1300     // table rowgroups event backgrounds, before the table row event backgrounds,
  1301     // before everything else (cells and their blocks)
  1302     separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nullptr);
  1303     separatedCollection.MoveTo(aLists);
  1306   aFrame->DisplayOutline(aBuilder, aLists);
  1309 static bool
  1310 AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd)
  1312   for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
  1313     NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
  1315     if (FrameHasBorderOrBackground(f))
  1316       return true;
  1318     nsTableCellFrame *cellFrame = do_QueryFrame(f);
  1319     if (cellFrame)
  1320       continue;
  1322     if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr))
  1323       return true;
  1326   return false;
  1329 // table paint code is concerned primarily with borders and bg color
  1330 // SEC: TODO: adjust the rect for captions
  1331 void
  1332 nsTableFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1333                                const nsRect&           aDirtyRect,
  1334                                const nsDisplayListSet& aLists)
  1336   DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
  1338   nsDisplayTableItem* item = nullptr;
  1339   if (IsVisibleInSelection(aBuilder)) {
  1340     if (StyleVisibility()->IsVisible()) {
  1341       nsMargin deflate = GetDeflationForBackground(PresContext());
  1342       // If 'deflate' is (0,0,0,0) then we can paint the table background
  1343       // in its own display item, so do that to take advantage of
  1344       // opacity and visibility optimizations
  1345       if (deflate == nsMargin(0, 0, 0, 0)) {
  1346         DisplayBackgroundUnconditional(aBuilder, aLists, false);
  1350     // This background is created if any of the table parts are visible,
  1351     // or if we're doing event handling (since DisplayGenericTablePart
  1352     // needs the item for the |sortEventBackgrounds|-dependent code).
  1353     // Specific visibility decisions are delegated to the table background
  1354     // painter, which handles borders and backgrounds for the table.
  1355     if (aBuilder->IsForEventDelivery() ||
  1356         AnyTablePartHasBorderOrBackground(this, GetNextSibling()) ||
  1357         AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) {
  1358       item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this);
  1359       aLists.BorderBackground()->AppendNewToTop(item);
  1362   DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
  1365 nsMargin
  1366 nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
  1368   if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
  1369       !IsBorderCollapse())
  1370     return nsMargin(0,0,0,0);
  1372   return GetOuterBCBorder();
  1375 // XXX We don't put the borders and backgrounds in tree order like we should.
  1376 // That requires some major surgery which we aren't going to do right now.
  1377 void
  1378 nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
  1379                                          const nsRect& aDirtyRect,
  1380                                          nsPoint aPt, uint32_t aBGPaintFlags)
  1382   nsPresContext* presContext = PresContext();
  1384   TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
  1385                                  presContext, aRenderingContext,
  1386                                  aDirtyRect, aPt, aBGPaintFlags);
  1387   nsMargin deflate = GetDeflationForBackground(presContext);
  1388   // If 'deflate' is (0,0,0,0) then we'll paint the table background
  1389   // in a separate display item, so don't do it here.
  1390   nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
  1391   if (NS_FAILED(rv)) return;
  1393   if (StyleVisibility()->IsVisible()) {
  1394     if (!IsBorderCollapse()) {
  1395       int skipSides = GetSkipSides();
  1396       nsRect rect(aPt, mRect.Size());
  1397       nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  1398                                   aDirtyRect, rect, mStyleContext, skipSides);
  1400     else {
  1401       // XXX we should probably get rid of this translation at some stage
  1402       // But that would mean modifying PaintBCBorders, ugh
  1403       nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt);
  1404       PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
  1409 int
  1410 nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
  1412   int skip = 0;
  1413   // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
  1414   // account for pagination
  1415   if (nullptr != GetPrevInFlow()) {
  1416     skip |= LOGICAL_SIDE_B_START;
  1418   if (nullptr != GetNextInFlow()) {
  1419     skip |= LOGICAL_SIDE_B_END;
  1421   return skip;
  1424 void
  1425 nsTableFrame::SetColumnDimensions(nscoord         aHeight,
  1426                                   const nsMargin& aBorderPadding)
  1428   nscoord cellSpacingX = GetCellSpacingX();
  1429   nscoord cellSpacingY = GetCellSpacingY();
  1430   nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
  1431                                  2* cellSpacingY;
  1433   nsTableIterator iter(mColGroups);
  1434   nsIFrame* colGroupFrame = iter.First();
  1435   bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
  1436   int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1);
  1437   int32_t tableColIncr = tableIsLTR ? 1 : -1;
  1438   nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
  1439                          aBorderPadding.top + cellSpacingY);
  1440   while (colGroupFrame) {
  1441     MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
  1442     nscoord colGroupWidth = 0;
  1443     nsTableIterator iterCol(*colGroupFrame);
  1444     nsIFrame* colFrame = iterCol.First();
  1445     nsPoint colOrigin(0,0);
  1446     while (colFrame) {
  1447       if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
  1448           colFrame->StyleDisplay()->mDisplay) {
  1449         NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
  1450         nscoord colWidth = GetColumnWidth(colX);
  1451         nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
  1452         colFrame->SetRect(colRect);
  1453         colOrigin.x += colWidth + cellSpacingX;
  1454         colGroupWidth += colWidth + cellSpacingX;
  1455         colX += tableColIncr;
  1457       colFrame = iterCol.Next();
  1459     if (colGroupWidth) {
  1460       colGroupWidth -= cellSpacingX;
  1463     nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight);
  1464     colGroupFrame->SetRect(colGroupRect);
  1465     colGroupFrame = iter.Next();
  1466     colGroupOrigin.x += colGroupWidth + cellSpacingX;
  1470 // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
  1472 // XXX this could be made more general to handle row modifications that change the
  1473 // table height, but first we need to scrutinize every Invalidate
  1474 void
  1475 nsTableFrame::ProcessRowInserted(nscoord aNewHeight)
  1477   SetRowInserted(false); // reset the bit that got us here
  1478   nsTableFrame::RowGroupArray rowGroups;
  1479   OrderRowGroups(rowGroups);
  1480   // find the row group containing the inserted row
  1481   for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
  1482     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  1483     NS_ASSERTION(rgFrame, "Must have rgFrame here");
  1484     nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild();
  1485     // find the row that was inserted first
  1486     while (childFrame) {
  1487       nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
  1488       if (rowFrame) {
  1489         if (rowFrame->IsFirstInserted()) {
  1490           rowFrame->SetFirstInserted(false);
  1491           // damage the table from the 1st row inserted to the end of the table
  1492           nsIFrame::InvalidateFrame();
  1493           // XXXbz didn't we do this up front?  Why do we need to do it again?
  1494           SetRowInserted(false);
  1495           return; // found it, so leave
  1498       childFrame = childFrame->GetNextSibling();
  1503 /* virtual */ void
  1504 nsTableFrame::MarkIntrinsicWidthsDirty()
  1506   nsITableLayoutStrategy* tls = LayoutStrategy();
  1507   if (MOZ_UNLIKELY(!tls)) {
  1508     // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
  1509     // walking up the ancestor chain in a table next-in-flow.  In this case
  1510     // our original first-in-flow (which owns the TableLayoutStrategy) has
  1511     // already been destroyed and unhooked from the flow chain and thusly
  1512     // LayoutStrategy() returns null.  All the frames in the flow will be
  1513     // destroyed so no need to mark anything dirty here.  See bug 595758.
  1514     return;
  1516   tls->MarkIntrinsicWidthsDirty();
  1518   // XXXldb Call SetBCDamageArea?
  1520   nsContainerFrame::MarkIntrinsicWidthsDirty();
  1523 /* virtual */ nscoord
  1524 nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
  1526   if (NeedToCalcBCBorders())
  1527     CalcBCBorders();
  1529   ReflowColGroups(aRenderingContext);
  1531   return LayoutStrategy()->GetMinWidth(aRenderingContext);
  1534 /* virtual */ nscoord
  1535 nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
  1537   if (NeedToCalcBCBorders())
  1538     CalcBCBorders();
  1540   ReflowColGroups(aRenderingContext);
  1542   return LayoutStrategy()->GetPrefWidth(aRenderingContext, false);
  1545 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
  1546 nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext)
  1548   IntrinsicWidthOffsetData result =
  1549     nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
  1551   result.hMargin = 0;
  1552   result.hPctMargin = 0;
  1554   if (IsBorderCollapse()) {
  1555     result.hPadding = 0;
  1556     result.hPctPadding = 0;
  1558     nsMargin outerBC = GetIncludedOuterBCBorder();
  1559     result.hBorder = outerBC.LeftRight();
  1562   return result;
  1565 /* virtual */ nsSize
  1566 nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext,
  1567                           nsSize aCBSize, nscoord aAvailableWidth,
  1568                           nsSize aMargin, nsSize aBorder, nsSize aPadding,
  1569                           uint32_t aFlags)
  1571   nsSize result =
  1572     nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
  1573                                   aMargin, aBorder, aPadding, aFlags);
  1575   // If we're a container for font size inflation, then shrink
  1576   // wrapping inside of us should not apply font size inflation.
  1577   AutoMaybeDisableFontInflation an(this);
  1579   // Tables never shrink below their min width.
  1580   nscoord minWidth = GetMinWidth(aRenderingContext);
  1581   if (minWidth > result.width)
  1582     result.width = minWidth;
  1584   return result;
  1587 nscoord
  1588 nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext,
  1589                                     nscoord aWidthInCB)
  1591   // If we're a container for font size inflation, then shrink
  1592   // wrapping inside of us should not apply font size inflation.
  1593   AutoMaybeDisableFontInflation an(this);
  1595   nscoord result;
  1596   nscoord minWidth = GetMinWidth(aRenderingContext);
  1597   if (minWidth > aWidthInCB) {
  1598     result = minWidth;
  1599   } else {
  1600     // Tables shrink width to fit with a slightly different algorithm
  1601     // from the one they use for their intrinsic widths (the difference
  1602     // relates to handling of percentage widths on columns).  So this
  1603     // function differs from nsFrame::ShrinkWidthToFit by only the
  1604     // following line.
  1605     // Since we've already called GetMinWidth, we don't need to do any
  1606     // of the other stuff GetPrefWidth does.
  1607     nscoord prefWidth =
  1608       LayoutStrategy()->GetPrefWidth(aRenderingContext, true);
  1609     if (prefWidth > aWidthInCB) {
  1610       result = aWidthInCB;
  1611     } else {
  1612       result = prefWidth;
  1615   return result;
  1618 /* virtual */ nsSize
  1619 nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
  1620                               nsSize aCBSize, nscoord aAvailableWidth,
  1621                               nsSize aMargin, nsSize aBorder, nsSize aPadding,
  1622                               bool aShrinkWrap)
  1624   // Tables always shrink-wrap.
  1625   nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width -
  1626                     aPadding.width;
  1627   return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased),
  1628                 NS_UNCONSTRAINEDSIZE);
  1631 // Return true if aParentReflowState.frame or any of its ancestors within
  1632 // the containing table have non-auto height. (e.g. pct or fixed height)
  1633 bool
  1634 nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState)
  1636   for (const nsHTMLReflowState* rs = &aParentReflowState;
  1637        rs && rs->frame; rs = rs->parentReflowState) {
  1638     nsIAtom* frameType = rs->frame->GetType();
  1639     if (IS_TABLE_CELL(frameType)                     ||
  1640         (nsGkAtoms::tableRowFrame      == frameType) ||
  1641         (nsGkAtoms::tableRowGroupFrame == frameType)) {
  1642       const nsStyleCoord &height = rs->mStylePosition->mHeight;
  1643       // calc() with percentages treated like 'auto' on internal table elements
  1644       if (height.GetUnit() != eStyleUnit_Auto &&
  1645           (!height.IsCalcUnit() || !height.HasPercent())) {
  1646         return true;
  1649     else if (nsGkAtoms::tableFrame == frameType) {
  1650       // we reached the containing table, so always return
  1651       return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto;
  1654   return false;
  1657 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
  1658 void
  1659 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
  1661   NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) ||
  1662                aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame ||
  1663                aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame ||
  1664                aReflowState.frame->GetType() == nsGkAtoms::tableFrame,
  1665                "unexpected frame type");
  1666   if (!aReflowState.frame->GetPrevInFlow() &&  // 1st in flow
  1667       (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() ||  // no computed height
  1668        0                    == aReflowState.ComputedHeight()) &&
  1669       eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height
  1670       nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) {
  1671     nsTableFrame::RequestSpecialHeightReflow(aReflowState);
  1675 // Notify the frame and its ancestors (up to the containing table) that a special
  1676 // height reflow will occur. During a special height reflow, a table, row group,
  1677 // row, or cell returns the last size it was reflowed at. However, the table may
  1678 // change the height of row groups, rows, cells in DistributeHeightToRows after.
  1679 // And the row group can change the height of rows, cells in CalculateRowHeights.
  1680 void
  1681 nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
  1683   // notify the frame and its ancestors of the special reflow, stopping at the containing table
  1684   for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) {
  1685     nsIAtom* frameType = rs->frame->GetType();
  1686     NS_ASSERTION(IS_TABLE_CELL(frameType) ||
  1687                  nsGkAtoms::tableRowFrame == frameType ||
  1688                  nsGkAtoms::tableRowGroupFrame == frameType ||
  1689                  nsGkAtoms::tableFrame == frameType,
  1690                  "unexpected frame type");
  1692     rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
  1693     if (nsGkAtoms::tableFrame == frameType) {
  1694       NS_ASSERTION(rs != &aReflowState,
  1695                    "should not request special height reflow for table");
  1696       // always stop when we reach a table
  1697       break;
  1702 /******************************************************************************************
  1703  * Before reflow, intrinsic width calculation is done using GetMinWidth
  1704  * and GetPrefWidth.  This used to be known as pass 1 reflow.
  1706  * After the intrinsic width calculation, the table determines the
  1707  * column widths using BalanceColumnWidths() and
  1708  * then reflows each child again with a constrained avail width. This reflow is referred to
  1709  * as the pass 2 reflow.
  1711  * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow
  1712  * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed
  1713  * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables
  1714  * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is
  1715  * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
  1716  * the special height reflow (variety a) are as follows:
  1718  * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
  1719  *    to indicate that it should get the reflow. It does this when it has a percent height but
  1720  *    no computed height by calling CheckRequestSpecialHeightReflow(). This method calls
  1721  *    RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until
  1722  *    it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
  1723  *    percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight()
  1724  *    is called (the cell is the reflow state's mPercentHeightObserver in this case).
  1725  *    NotifyPercentHeight() calls RequestSpecialHeightReflow().
  1727  * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
  1728  *    will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow
  1729  *    to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
  1730  *    returns true because in that case another special height reflow will be coming along with the
  1731  *    containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
  1732  *    the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
  1733  *    appropriate heights will not be known.
  1735  * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2
  1736  *    reflow, they return their last desired sizes during the special height reflow. The reflow only
  1737  *    permits percent height frames inside the cells to resize based on the cells height and that height
  1738  *    was determined during the pass 2 reflow.
  1740  * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
  1741  * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
  1742  * until the current initiator is its containing table. Since these reflows are only received by
  1743  * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
  1745  * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
  1746  * that all frames will receive the reflow even if they don't really need them.
  1748  * Open issues with the special height reflow:
  1750  * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are
  1751  *    really quite different. This would avoid unnecessary reflows during printing.
  1752  * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245).
  1753  *    However, this can also occur if a cell has a fixed height and there is no special height reflow.
  1755  * XXXldb Special height reflow should really be its own method, not
  1756  * part of nsIFrame::Reflow.  It should then call nsIFrame::Reflow on
  1757  * the contents of the cells to do the necessary vertical resizing.
  1759  ******************************************************************************************/
  1761 /* Layout the entire inner table. */
  1762 nsresult nsTableFrame::Reflow(nsPresContext*           aPresContext,
  1763                                nsHTMLReflowMetrics&     aDesiredSize,
  1764                                const nsHTMLReflowState& aReflowState,
  1765                                nsReflowStatus&          aStatus)
  1767   DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
  1768   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
  1769   bool isPaginated = aPresContext->IsPaginated();
  1771   aStatus = NS_FRAME_COMPLETE;
  1772   if (!GetPrevInFlow() && !mTableLayoutStrategy) {
  1773     NS_ASSERTION(false, "strategy should have been created in Init");
  1774     return NS_ERROR_NULL_POINTER;
  1776   nsresult rv = NS_OK;
  1778   // see if collapsing borders need to be calculated
  1779   if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
  1780     CalcBCBorders();
  1783   aDesiredSize.Width() = aReflowState.AvailableWidth();
  1785   // Check for an overflow list, and append any row group frames being pushed
  1786   MoveOverflowToChildList();
  1788   bool haveDesiredHeight = false;
  1789   SetHaveReflowedColGroups(false);
  1791   // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
  1792   // constrained initial reflow and other reflows which require either a strategy init or balance.
  1793   // This isn't done during an unconstrained reflow, because it will occur later when the parent
  1794   // reflows with a constrained width.
  1795   if (NS_SUBTREE_DIRTY(this) ||
  1796       aReflowState.ShouldReflowAllKids() ||
  1797       IsGeometryDirty() ||
  1798       aReflowState.mFlags.mVResize) {
  1800     if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ||
  1801         // Also check mVResize, to handle the first Reflow preceding a
  1802         // special height Reflow, when we've already had a special height
  1803         // Reflow (where mComputedHeight would not be
  1804         // NS_UNCONSTRAINEDSIZE, but without a style change in between).
  1805         aReflowState.mFlags.mVResize) {
  1806       // XXX Eventually, we should modify DistributeHeightToRows to use
  1807       // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height.
  1808       // That way, it will make its calculations based on internal table
  1809       // frame heights as they are before they ever had any extra height
  1810       // distributed to them.  In the meantime, this reflows all the
  1811       // internal table frames, which restores them to their state before
  1812       // DistributeHeightToRows was called.
  1813       SetGeometryDirty();
  1816     bool needToInitiateSpecialReflow =
  1817       !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
  1818     // see if an extra reflow will be necessary in pagination mode when there is a specified table height
  1819     if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) {
  1820       nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
  1821       if ((tableSpecifiedHeight > 0) &&
  1822           (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) {
  1823         needToInitiateSpecialReflow = true;
  1826     nsIFrame* lastChildReflowed = nullptr;
  1828     NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow,
  1829                  "Shouldn't be in special height reflow here!");
  1831     // do the pass 2 reflow unless this is a special height reflow and we will be
  1832     // initiating a special height reflow
  1833     // XXXldb I changed this.  Should I change it back?
  1835     // if we need to initiate a special height reflow, then don't constrain the
  1836     // height of the reflow before that
  1837     nscoord availHeight = needToInitiateSpecialReflow
  1838                           ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight();
  1840     ReflowTable(aDesiredSize, aReflowState, availHeight,
  1841                 lastChildReflowed, aStatus);
  1843     // reevaluate special height reflow conditions
  1844     if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
  1845       needToInitiateSpecialReflow = true;
  1847     // XXXldb Are all these conditions correct?
  1848     if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
  1849       // XXXldb Do we need to set the mVResize flag on any reflow states?
  1851       nsHTMLReflowState &mutable_rs =
  1852         const_cast<nsHTMLReflowState&>(aReflowState);
  1854       // distribute extra vertical space to rows
  1855       CalcDesiredHeight(aReflowState, aDesiredSize);
  1856       mutable_rs.mFlags.mSpecialHeightReflow = true;
  1858       ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(),
  1859                   lastChildReflowed, aStatus);
  1861       if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
  1862         // if there is an incomplete child, then set the desired height to include it but not the next one
  1863         nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
  1864         aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() +
  1865                               lastChildReflowed->GetRect().YMost();
  1867       haveDesiredHeight = true;
  1869       mutable_rs.mFlags.mSpecialHeightReflow = false;
  1872   else {
  1873     // Calculate the overflow area contribution from our children.
  1874     for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
  1875       ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
  1879   aDesiredSize.Width() = aReflowState.ComputedWidth() +
  1880                        aReflowState.ComputedPhysicalBorderPadding().LeftRight();
  1881   if (!haveDesiredHeight) {
  1882     CalcDesiredHeight(aReflowState, aDesiredSize);
  1884   if (IsRowInserted()) {
  1885     ProcessRowInserted(aDesiredSize.Height());
  1888   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
  1889   SetColumnDimensions(aDesiredSize.Height(), borderPadding);
  1890   if (NeedToCollapse() &&
  1891       (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) {
  1892     AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
  1895   // If there are any relatively-positioned table parts, we need to reflow their
  1896   // absolutely-positioned descendants now that their dimensions are final.
  1897   FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState);
  1899   // make sure the table overflow area does include the table rect.
  1900   nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
  1902   if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
  1903     // collapsed border may leak out
  1904     nsMargin bcMargin = GetExcludedOuterBCBorder();
  1905     tableRect.Inflate(bcMargin);
  1907   aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
  1909   if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
  1910       nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
  1911       nsIFrame::InvalidateFrame();
  1914   FinishAndStoreOverflow(&aDesiredSize);
  1915   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
  1916   return rv;
  1919 void
  1920 nsTableFrame::FixupPositionedTableParts(nsPresContext*           aPresContext,
  1921                                         nsHTMLReflowMetrics&     aDesiredSize,
  1922                                         const nsHTMLReflowState& aReflowState)
  1924   auto positionedParts =
  1925     static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray()));
  1926   if (!positionedParts) {
  1927     return;
  1930   OverflowChangedTracker overflowTracker;
  1931   overflowTracker.SetSubtreeRoot(this);
  1933   for (size_t i = 0; i < positionedParts->Length(); ++i) {
  1934     nsIFrame* positionedPart = positionedParts->ElementAt(i);
  1936     // As we've already finished reflow, positionedParts's size and overflow
  1937     // areas have already been assigned, so we just pull them back out.
  1938     nsSize size(positionedPart->GetSize());
  1939     nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode());
  1940     desiredSize.Width() = size.width;
  1941     desiredSize.Height() = size.height;
  1942     desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf();
  1944     // Construct a dummy reflow state and reflow status.
  1945     // XXX(seth): Note that the dummy reflow state doesn't have a correct
  1946     // chain of parent reflow states. It also doesn't necessarily have a
  1947     // correct containing block.
  1948     nsHTMLReflowState reflowState(aPresContext, positionedPart,
  1949                                   aReflowState.rendContext,
  1950                                   nsSize(size.width, NS_UNCONSTRAINEDSIZE),
  1951                                   nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE);
  1952     nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
  1954     // Reflow absolutely-positioned descendants of the positioned part.
  1955     // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and
  1956     // ignoring any change to the reflow status aren't correct. We'll never
  1957     // paginate absolutely positioned frames.
  1958     overflowTracker.AddFrame(positionedPart,
  1959       OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED);
  1960     nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart);
  1961     positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(),
  1962                                                     desiredSize,
  1963                                                     reflowState,
  1964                                                     reflowStatus,
  1965                                                     true);
  1968   // Propagate updated overflow areas up the tree.
  1969   overflowTracker.Flush();
  1971   // Update our own overflow areas. (OverflowChangedTracker doesn't update the
  1972   // subtree root itself.)
  1973   aDesiredSize.SetOverflowAreasToDesiredBounds();
  1974   nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
  1977 bool
  1978 nsTableFrame::UpdateOverflow()
  1980   nsRect bounds(nsPoint(0, 0), GetSize());
  1982   // As above in Reflow, make sure the table overflow area includes the table
  1983   // rect, and check for collapsed borders leaking out.
  1984   if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
  1985     nsMargin bcMargin = GetExcludedOuterBCBorder();
  1986     bounds.Inflate(bcMargin);
  1989   nsOverflowAreas overflowAreas(bounds, bounds);
  1990   nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
  1992   return FinishAndStoreOverflow(overflowAreas, GetSize());
  1995 nsresult
  1996 nsTableFrame::ReflowTable(nsHTMLReflowMetrics&     aDesiredSize,
  1997                           const nsHTMLReflowState& aReflowState,
  1998                           nscoord                  aAvailHeight,
  1999                           nsIFrame*&               aLastChildReflowed,
  2000                           nsReflowStatus&          aStatus)
  2002   nsresult rv = NS_OK;
  2003   aLastChildReflowed = nullptr;
  2005   if (!GetPrevInFlow()) {
  2006     mTableLayoutStrategy->ComputeColumnWidths(aReflowState);
  2008   // Constrain our reflow width to the computed table width (of the 1st in flow).
  2009   // and our reflow height to our avail height minus border, padding, cellspacing
  2010   aDesiredSize.Width() = aReflowState.ComputedWidth() +
  2011                        aReflowState.ComputedPhysicalBorderPadding().LeftRight();
  2012   nsTableReflowState reflowState(*PresContext(), aReflowState, *this,
  2013                                  aDesiredSize.Width(), aAvailHeight);
  2014   ReflowChildren(reflowState, aStatus, aLastChildReflowed,
  2015                  aDesiredSize.mOverflowAreas);
  2017   ReflowColGroups(aReflowState.rendContext);
  2018   return rv;
  2021 nsIFrame*
  2022 nsTableFrame::GetFirstBodyRowGroupFrame()
  2024   nsIFrame* headerFrame = nullptr;
  2025   nsIFrame* footerFrame = nullptr;
  2027   for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) {
  2028     const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay();
  2030     // We expect the header and footer row group frames to be first, and we only
  2031     // allow one header and one footer
  2032     if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) {
  2033       if (headerFrame) {
  2034         // We already have a header frame and so this header frame is treated
  2035         // like an ordinary body row group frame
  2036         return kidFrame;
  2038       headerFrame = kidFrame;
  2040     } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) {
  2041       if (footerFrame) {
  2042         // We already have a footer frame and so this footer frame is treated
  2043         // like an ordinary body row group frame
  2044         return kidFrame;
  2046       footerFrame = kidFrame;
  2048     } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) {
  2049       return kidFrame;
  2052     // Get the next child
  2053     kidFrame = kidFrame->GetNextSibling();
  2056   return nullptr;
  2059 // Table specific version that takes into account repeated header and footer
  2060 // frames when continuing table frames
  2061 void
  2062 nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
  2063                            int32_t aPushFrom)
  2065   NS_PRECONDITION(aPushFrom > 0, "pushing first child");
  2067   // extract the frames from the array into a sibling list
  2068   nsFrameList frames;
  2069   uint32_t childX;
  2070   for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
  2071     nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
  2072     if (!rgFrame->IsRepeatable()) {
  2073       mFrames.RemoveFrame(rgFrame);
  2074       frames.AppendFrame(nullptr, rgFrame);
  2078   if (frames.IsEmpty()) {
  2079     return;
  2082   nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
  2083   if (nextInFlow) {
  2084     // Insert the frames after any repeated header and footer frames.
  2085     nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
  2086     nsIFrame* prevSibling = nullptr;
  2087     if (firstBodyFrame) {
  2088       prevSibling = firstBodyFrame->GetPrevSibling();
  2090     // When pushing and pulling frames we need to check for whether any
  2091     // views need to be reparented.
  2092     ReparentFrameViewList(frames, this, nextInFlow);
  2093     nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
  2094                                      frames);
  2096   else {
  2097     // Add the frames to our overflow list.
  2098     SetOverflowFrames(frames);
  2102 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
  2103 // reflow so that it has no effect on the calculations of reflow.
  2104 void
  2105 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
  2106                                           nsMargin             aBorderPadding)
  2108   nscoord yTotalOffset = 0; // total offset among all rows in all row groups
  2110   // reset the bit, it will be set again if row/rowgroup or col/colgroup are
  2111   // collapsed
  2112   SetNeedToCollapse(false);
  2114   // collapse the rows and/or row groups as necessary
  2115   // Get the ordered children
  2116   RowGroupArray rowGroups;
  2117   OrderRowGroups(rowGroups);
  2119   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
  2120   nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding);
  2121   nscoord rgWidth = width - 2 * GetCellSpacingX();
  2122   nsOverflowAreas overflow;
  2123   // Walk the list of children
  2124   for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
  2125     nsTableRowGroupFrame* rgFrame = rowGroups[childX];
  2126     NS_ASSERTION(rgFrame, "Must have row group frame here");
  2127     yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
  2128     ConsiderChildOverflow(overflow, rgFrame);
  2131   aDesiredSize.Height() -= yTotalOffset;
  2132   aDesiredSize.Width() = width;
  2133   overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
  2134   FinishAndStoreOverflow(overflow,
  2135                          nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
  2139 nscoord
  2140 nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
  2142   NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow");
  2143   nscoord cellSpacingX = GetCellSpacingX();
  2144   nscoord width = cellSpacingX;
  2145   width += aBorderPadding.left + aBorderPadding.right;
  2146   for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame;
  2147          groupFrame = groupFrame->GetNextSibling()) {
  2148     const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
  2149     bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
  2150     nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
  2151     for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
  2152          colFrame = colFrame->GetNextCol()) {
  2153       const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
  2154       int32_t colX = colFrame->GetColIndex();
  2155       if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
  2156         const nsStyleVisibility* colVis = colFrame->StyleVisibility();
  2157         bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
  2158         int32_t colWidth = GetColumnWidth(colX);
  2159         if (!collapseGroup && !collapseCol) {
  2160           width += colWidth;
  2161           if (ColumnHasCellSpacingBefore(colX))
  2162             width += cellSpacingX;
  2164         else {
  2165           SetNeedToCollapse(true);
  2170   return width;
  2173 /* virtual */ void
  2174 nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
  2176   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
  2178   if (!aOldStyleContext) //avoid this on init
  2179     return;
  2181   if (IsBorderCollapse() &&
  2182       BCRecalcNeeded(aOldStyleContext, StyleContext())) {
  2183     SetFullBCDamageArea();
  2186   //avoid this on init or nextinflow
  2187   if (!mTableLayoutStrategy || GetPrevInFlow())
  2188     return;
  2190   bool isAuto = IsAutoLayout();
  2191   if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
  2192     nsITableLayoutStrategy* temp;
  2193     if (isAuto)
  2194       temp = new BasicTableLayoutStrategy(this);
  2195     else
  2196       temp = new FixedTableLayoutStrategy(this);
  2198     if (temp) {
  2199       delete mTableLayoutStrategy;
  2200       mTableLayoutStrategy = temp;
  2207 nsresult
  2208 nsTableFrame::AppendFrames(ChildListID     aListID,
  2209                            nsFrameList&    aFrameList)
  2211   NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
  2212                "unexpected child list");
  2214   // Because we actually have two child lists, one for col group frames and one
  2215   // for everything else, we need to look at each frame individually
  2216   // XXX The frame construction code should be separating out child frames
  2217   // based on the type, bug 343048.
  2218   while (!aFrameList.IsEmpty()) {
  2219     nsIFrame* f = aFrameList.FirstChild();
  2220     aFrameList.RemoveFrame(f);
  2222     // See what kind of frame we have
  2223     const nsStyleDisplay* display = f->StyleDisplay();
  2225     if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
  2226       nsTableColGroupFrame* lastColGroup =
  2227         nsTableColGroupFrame::GetLastRealColGroup(this);
  2228       int32_t startColIndex = (lastColGroup)
  2229         ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
  2230       mColGroups.InsertFrame(nullptr, lastColGroup, f);
  2231       // Insert the colgroup and its cols into the table
  2232       InsertColGroups(startColIndex,
  2233                       nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
  2234     } else if (IsRowGroup(display->mDisplay)) {
  2235       // Append the new row group frame to the sibling chain
  2236       mFrames.AppendFrame(nullptr, f);
  2238       // insert the row group and its rows into the table
  2239       InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr));
  2240     } else {
  2241       // Nothing special to do, just add the frame to our child list
  2242       NS_NOTREACHED("How did we get here?  Frame construction screwed up");
  2243       mFrames.AppendFrame(nullptr, f);
  2247 #ifdef DEBUG_TABLE_CELLMAP
  2248   printf("=== TableFrame::AppendFrames\n");
  2249   Dump(true, true, true);
  2250 #endif
  2251   PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  2252                                                NS_FRAME_HAS_DIRTY_CHILDREN);
  2253   SetGeometryDirty();
  2255   return NS_OK;
  2258 // Needs to be at file scope or ArrayLength fails to compile.
  2259 struct ChildListInsertions {
  2260   nsIFrame::ChildListID mID;
  2261   nsFrameList mList;
  2262 };
  2264 nsresult
  2265 nsTableFrame::InsertFrames(ChildListID     aListID,
  2266                            nsIFrame*       aPrevFrame,
  2267                            nsFrameList&    aFrameList)
  2269   // The frames in aFrameList can be a mix of row group frames and col group
  2270   // frames. The problem is that they should go in separate child lists so
  2271   // we need to deal with that here...
  2272   // XXX The frame construction code should be separating out child frames
  2273   // based on the type, bug 343048.
  2275   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
  2276                "inserting after sibling frame with different parent");
  2278   if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
  2279       (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
  2280     // Treat this like an append; still a workaround for bug 343048.
  2281     return AppendFrames(aListID, aFrameList);
  2284   // Collect ColGroupFrames into a separate list and insert those separately
  2285   // from the other frames (bug 759249).
  2286   ChildListInsertions insertions[2]; // ColGroup, other
  2287   const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
  2288   nsFrameList::FrameLinkEnumerator e(aFrameList);
  2289   for (; !aFrameList.IsEmpty(); e.Next()) {
  2290     nsIFrame* next = e.NextFrame();
  2291     if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) {
  2292       nsFrameList head = aFrameList.ExtractHead(e);
  2293       if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) {
  2294         insertions[0].mID = kColGroupList;
  2295         insertions[0].mList.AppendFrames(nullptr, head);
  2296       } else {
  2297         insertions[1].mID = kPrincipalList;
  2298         insertions[1].mList.AppendFrames(nullptr, head);
  2300       if (!next) {
  2301         break;
  2303       display = next->StyleDisplay();
  2306   for (uint32_t i = 0; i < ArrayLength(insertions); ++i) {
  2307     // We pass aPrevFrame for both ColGroup and other frames since
  2308     // HomogenousInsertFrames will only use it if it's a suitable
  2309     // prev-sibling for the frames in the frame list.
  2310     if (!insertions[i].mList.IsEmpty()) {
  2311       HomogenousInsertFrames(insertions[i].mID, aPrevFrame,
  2312                              insertions[i].mList);
  2315   return NS_OK;
  2318 void
  2319 nsTableFrame::HomogenousInsertFrames(ChildListID     aListID,
  2320                                      nsIFrame*       aPrevFrame,
  2321                                      nsFrameList&    aFrameList)
  2323   // See what kind of frame we have
  2324   const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
  2325 #ifdef DEBUG
  2326   // Verify that either all siblings have display:table-column-group, or they
  2327   // all have display values different from table-column-group.
  2328   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
  2329     const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay();
  2330     MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) ==
  2331                (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP),
  2332                "heterogenous childlist");
  2334 #endif
  2335   if (aPrevFrame) {
  2336     const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
  2337     // Make sure they belong on the same frame list
  2338     if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) !=
  2339         (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) {
  2340       // the previous frame is not valid, see comment at ::AppendFrames
  2341       // XXXbz Using content indices here means XBL will get screwed
  2342       // over...  Oh, well.
  2343       nsIFrame* pseudoFrame = aFrameList.FirstChild();
  2344       nsIContent* parentContent = GetContent();
  2345       nsIContent* content;
  2346       aPrevFrame = nullptr;
  2347       while (pseudoFrame  && (parentContent ==
  2348                               (content = pseudoFrame->GetContent()))) {
  2349         pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
  2351       nsCOMPtr<nsIContent> container = content->GetParent();
  2352       if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823.
  2353         int32_t newIndex = container->IndexOf(content);
  2354         nsIFrame* kidFrame;
  2355         bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ==
  2356                              display->mDisplay);
  2357         nsTableColGroupFrame* lastColGroup;
  2358         if (isColGroup) {
  2359           kidFrame = mColGroups.FirstChild();
  2360           lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
  2362         else {
  2363           kidFrame = mFrames.FirstChild();
  2365         // Important: need to start at a value smaller than all valid indices
  2366         int32_t lastIndex = -1;
  2367         while (kidFrame) {
  2368           if (isColGroup) {
  2369             if (kidFrame == lastColGroup) {
  2370               aPrevFrame = kidFrame; // there is no real colgroup after this one
  2371               break;
  2374           pseudoFrame = kidFrame;
  2375           while (pseudoFrame  && (parentContent ==
  2376                                   (content = pseudoFrame->GetContent()))) {
  2377             pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
  2379           int32_t index = container->IndexOf(content);
  2380           if (index > lastIndex && index < newIndex) {
  2381             lastIndex = index;
  2382             aPrevFrame = kidFrame;
  2384           kidFrame = kidFrame->GetNextSibling();
  2389   if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
  2390     NS_ASSERTION(aListID == kColGroupList, "unexpected child list");
  2391     // Insert the column group frames
  2392     const nsFrameList::Slice& newColgroups =
  2393       mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList);
  2394     // find the starting col index for the first new col group
  2395     int32_t startColIndex = 0;
  2396     if (aPrevFrame) {
  2397       nsTableColGroupFrame* prevColGroup =
  2398         (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
  2399                                                   nsGkAtoms::tableColGroupFrame);
  2400       if (prevColGroup) {
  2401         startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
  2404     InsertColGroups(startColIndex, newColgroups);
  2405   } else if (IsRowGroup(display->mDisplay)) {
  2406     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2407     // Insert the frames in the sibling chain
  2408     const nsFrameList::Slice& newRowGroups =
  2409       mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  2411     InsertRowGroups(newRowGroups);
  2412   } else {
  2413     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2414     NS_NOTREACHED("How did we even get here?");
  2415     // Just insert the frame and don't worry about reflowing it
  2416     mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  2417     return;
  2420   PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  2421                                                NS_FRAME_HAS_DIRTY_CHILDREN);
  2422   SetGeometryDirty();
  2423 #ifdef DEBUG_TABLE_CELLMAP
  2424   printf("=== TableFrame::InsertFrames\n");
  2425   Dump(true, true, true);
  2426 #endif
  2427   return;
  2430 void
  2431 nsTableFrame::DoRemoveFrame(ChildListID     aListID,
  2432                             nsIFrame*       aOldFrame)
  2434   if (aListID == kColGroupList) {
  2435     nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
  2436     nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
  2437     int32_t firstColIndex = colGroup->GetStartColumnIndex();
  2438     int32_t lastColIndex  = firstColIndex + colGroup->GetColCount() - 1;
  2439     mColGroups.DestroyFrame(aOldFrame);
  2440     nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
  2441     // remove the cols from the table
  2442     int32_t colX;
  2443     for (colX = lastColIndex; colX >= firstColIndex; colX--) {
  2444       nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX);
  2445       if (colFrame) {
  2446         RemoveCol(colGroup, colX, true, false);
  2450     int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
  2451     if (numAnonymousColsToAdd > 0) {
  2452       // this sets the child list, updates the col cache and cell map
  2453       AppendAnonymousColFrames(numAnonymousColsToAdd);
  2456   } else {
  2457     NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
  2458     nsTableRowGroupFrame* rgFrame =
  2459       static_cast<nsTableRowGroupFrame*>(aOldFrame);
  2460     // remove the row group from the cell map
  2461     nsTableCellMap* cellMap = GetCellMap();
  2462     if (cellMap) {
  2463       cellMap->RemoveGroupCellMap(rgFrame);
  2466     // remove the row group frame from the sibling chain
  2467     mFrames.DestroyFrame(aOldFrame);
  2469     // the removal of a row group changes the cellmap, the columns might change
  2470     if (cellMap) {
  2471       cellMap->Synchronize(this);
  2472       // Create an empty slice
  2473       ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr));
  2474       nsIntRect damageArea;
  2475       cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea);
  2477       static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap);
  2482 nsresult
  2483 nsTableFrame::RemoveFrame(ChildListID     aListID,
  2484                           nsIFrame*       aOldFrame)
  2486   NS_ASSERTION(aListID == kColGroupList ||
  2487                NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP !=
  2488                  aOldFrame->StyleDisplay()->mDisplay,
  2489                "Wrong list name; use kColGroupList iff colgroup");
  2490   nsIPresShell* shell = PresContext()->PresShell();
  2491   nsTableFrame* lastParent = nullptr;
  2492   while (aOldFrame) {
  2493     nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
  2494     nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
  2495     if (parent != lastParent) {
  2496       parent->DrainSelfOverflowList();
  2498     parent->DoRemoveFrame(aListID, aOldFrame);
  2499     aOldFrame = oldFrameNextContinuation;
  2500     if (parent != lastParent) {
  2501       // for now, just bail and recalc all of the collapsing borders
  2502       // as the cellmap changes we need to recalc
  2503       if (parent->IsBorderCollapse()) {
  2504         parent->SetFullBCDamageArea();
  2506       parent->SetGeometryDirty();
  2507       shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
  2508                               NS_FRAME_HAS_DIRTY_CHILDREN);
  2509       lastParent = parent;
  2512 #ifdef DEBUG_TABLE_CELLMAP
  2513   printf("=== TableFrame::RemoveFrame\n");
  2514   Dump(true, true, true);
  2515 #endif
  2516   return NS_OK;
  2519 /* virtual */ nsMargin
  2520 nsTableFrame::GetUsedBorder() const
  2522   if (!IsBorderCollapse())
  2523     return nsContainerFrame::GetUsedBorder();
  2525   return GetIncludedOuterBCBorder();
  2528 /* virtual */ nsMargin
  2529 nsTableFrame::GetUsedPadding() const
  2531   if (!IsBorderCollapse())
  2532     return nsContainerFrame::GetUsedPadding();
  2534   return nsMargin(0,0,0,0);
  2537 /* virtual */ nsMargin
  2538 nsTableFrame::GetUsedMargin() const
  2540   // The margin is inherited to the outer table frame via
  2541   // the ::-moz-table-outer rule in ua.css.
  2542   return nsMargin(0, 0, 0, 0);
  2545 // Destructor function for BCPropertyData properties
  2546 static void
  2547 DestroyBCProperty(void* aPropertyValue)
  2549   delete static_cast<BCPropertyData*>(aPropertyValue);
  2552 NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty)
  2554 BCPropertyData*
  2555 nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const
  2557   FrameProperties props = Properties();
  2558   BCPropertyData* value = static_cast<BCPropertyData*>
  2559                           (props.Get(TableBCProperty()));
  2560   if (!value && aCreateIfNecessary) {
  2561     value = new BCPropertyData();
  2562     props.Set(TableBCProperty(), value);
  2565   return value;
  2568 static void
  2569 DivideBCBorderSize(BCPixelSize  aPixelSize,
  2570                    BCPixelSize& aSmallHalf,
  2571                    BCPixelSize& aLargeHalf)
  2573   aSmallHalf = aPixelSize / 2;
  2574   aLargeHalf = aPixelSize - aSmallHalf;
  2577 nsMargin
  2578 nsTableFrame::GetOuterBCBorder() const
  2580   if (NeedToCalcBCBorders())
  2581     const_cast<nsTableFrame*>(this)->CalcBCBorders();
  2583   nsMargin border(0, 0, 0, 0);
  2584   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
  2585   BCPropertyData* propData = GetBCProperty();
  2586   if (propData) {
  2587     border.top    = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
  2588     border.right  = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
  2589     border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
  2590     border.left   = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
  2592   return border;
  2595 nsMargin
  2596 nsTableFrame::GetIncludedOuterBCBorder() const
  2598   if (NeedToCalcBCBorders())
  2599     const_cast<nsTableFrame*>(this)->CalcBCBorders();
  2601   nsMargin border(0, 0, 0, 0);
  2602   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
  2603   BCPropertyData* propData = GetBCProperty();
  2604   if (propData) {
  2605     border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
  2606     border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth);
  2607     border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
  2608     border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth);
  2610   return border;
  2613 nsMargin
  2614 nsTableFrame::GetExcludedOuterBCBorder() const
  2616   return GetOuterBCBorder() - GetIncludedOuterBCBorder();
  2619 static
  2620 void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
  2621                                    nsStyleContext&          aStyleContext,
  2622                                    nsMargin&                aBorderPadding)
  2624   // XXXbz Either we _do_ have a reflow state and then we can use its
  2625   // mComputedBorderPadding or we don't and then we get the padding
  2626   // wrong!
  2627   const nsStyleBorder* border = aStyleContext.StyleBorder();
  2628   aBorderPadding = border->GetComputedBorder();
  2629   if (aReflowState) {
  2630     aBorderPadding += aReflowState->ComputedPhysicalPadding();
  2634 nsMargin
  2635 nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
  2637   nsMargin offset(0,0,0,0);
  2638   if (IsBorderCollapse()) {
  2639     offset = GetIncludedOuterBCBorder();
  2641   else {
  2642     GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
  2644   return offset;
  2647 void
  2648 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
  2650   nsMargin collapseBorder;
  2651   nsMargin padding(0,0,0,0);
  2652   nsMargin* pCollapseBorder = nullptr;
  2653   nsPresContext* presContext = PresContext();
  2654   if (IsBorderCollapse()) {
  2655     nsTableRowGroupFrame* rgFrame =
  2656        static_cast<nsTableRowGroupFrame*>(aReflowState.frame);
  2657     pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder);
  2659   aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding);
  2661   NS_ASSERTION(!mBits.mResizedColumns ||
  2662                !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow,
  2663                "should not resize columns on special height reflow");
  2664   if (mBits.mResizedColumns) {
  2665     aReflowState.mFlags.mHResize = true;
  2669 // Position and size aKidFrame and update our reflow state. The origin of
  2670 // aKidRect is relative to the upper-left origin of our frame
  2671 void nsTableFrame::PlaceChild(nsTableReflowState&  aReflowState,
  2672                               nsIFrame*            aKidFrame,
  2673                               nsHTMLReflowMetrics& aKidDesiredSize,
  2674                               const nsRect&        aOriginalKidRect,
  2675                               const nsRect&        aOriginalKidVisualOverflow)
  2677   bool isFirstReflow =
  2678     (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
  2680   // Place and size the child
  2681   FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
  2682                     aReflowState.x, aReflowState.y, 0);
  2684   InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
  2685                        isFirstReflow);
  2687   // Adjust the running y-offset
  2688   aReflowState.y += aKidDesiredSize.Height();
  2690   // If our height is constrained, then update the available height
  2691   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
  2692     aReflowState.availSize.height -= aKidDesiredSize.Height();
  2696 void
  2697 nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
  2698                              nsTableRowGroupFrame** aHead,
  2699                              nsTableRowGroupFrame** aFoot) const
  2701   aChildren.Clear();
  2702   nsTableRowGroupFrame* head = nullptr;
  2703   nsTableRowGroupFrame* foot = nullptr;
  2705   nsIFrame* kidFrame = mFrames.FirstChild();
  2706   while (kidFrame) {
  2707     const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
  2708     nsTableRowGroupFrame* rowGroup =
  2709       static_cast<nsTableRowGroupFrame*>(kidFrame);
  2711     switch (kidDisplay->mDisplay) {
  2712     case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
  2713       if (head) { // treat additional thead like tbody
  2714         aChildren.AppendElement(rowGroup);
  2716       else {
  2717         head = rowGroup;
  2719       break;
  2720     case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
  2721       if (foot) { // treat additional tfoot like tbody
  2722         aChildren.AppendElement(rowGroup);
  2724       else {
  2725         foot = rowGroup;
  2727       break;
  2728     case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
  2729       aChildren.AppendElement(rowGroup);
  2730       break;
  2731     default:
  2732       NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
  2733       // Just ignore it
  2734       break;
  2736     // Get the next sibling but skip it if it's also the next-in-flow, since
  2737     // a next-in-flow will not be part of the current table.
  2738     while (kidFrame) {
  2739       nsIFrame* nif = kidFrame->GetNextInFlow();
  2740       kidFrame = kidFrame->GetNextSibling();
  2741       if (kidFrame != nif)
  2742         break;
  2746   // put the thead first
  2747   if (head) {
  2748     aChildren.InsertElementAt(0, head);
  2750   if (aHead)
  2751     *aHead = head;
  2752   // put the tfoot after the last tbody
  2753   if (foot) {
  2754     aChildren.AppendElement(foot);
  2756   if (aFoot)
  2757     *aFoot = foot;
  2760 nsTableRowGroupFrame*
  2761 nsTableFrame::GetTHead() const
  2763   nsIFrame* kidFrame = mFrames.FirstChild();
  2764   while (kidFrame) {
  2765     if (kidFrame->StyleDisplay()->mDisplay ==
  2766           NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) {
  2767       return static_cast<nsTableRowGroupFrame*>(kidFrame);
  2770     // Get the next sibling but skip it if it's also the next-in-flow, since
  2771     // a next-in-flow will not be part of the current table.
  2772     while (kidFrame) {
  2773       nsIFrame* nif = kidFrame->GetNextInFlow();
  2774       kidFrame = kidFrame->GetNextSibling();
  2775       if (kidFrame != nif)
  2776         break;
  2780   return nullptr;
  2783 nsTableRowGroupFrame*
  2784 nsTableFrame::GetTFoot() const
  2786   nsIFrame* kidFrame = mFrames.FirstChild();
  2787   while (kidFrame) {
  2788     if (kidFrame->StyleDisplay()->mDisplay ==
  2789           NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) {
  2790       return static_cast<nsTableRowGroupFrame*>(kidFrame);
  2793     // Get the next sibling but skip it if it's also the next-in-flow, since
  2794     // a next-in-flow will not be part of the current table.
  2795     while (kidFrame) {
  2796       nsIFrame* nif = kidFrame->GetNextInFlow();
  2797       kidFrame = kidFrame->GetNextSibling();
  2798       if (kidFrame != nif)
  2799         break;
  2803   return nullptr;
  2806 static bool
  2807 IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
  2809   return aFrameHeight < (aPageHeight / 4);
  2812 nsresult
  2813 nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState,
  2814                                      nsTableRowGroupFrame* aFrame,
  2815                                      nscoord* aDesiredHeight)
  2817   nsPresContext* presContext = PresContext();
  2818   nscoord pageHeight = presContext->GetPageSize().height;
  2820   // Reflow the child with unconstrainted height
  2821   nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
  2822                                    aFrame,
  2823                                    nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE),
  2824                                    -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
  2825   InitChildReflowState(kidReflowState);
  2826   kidReflowState.mFlags.mIsTopOfPage = true;
  2827   nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
  2828   desiredSize.Width() = desiredSize.Height() = 0;
  2829   nsReflowStatus status;
  2830   nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState,
  2831                             aReflowState.x, aReflowState.y, 0, status);
  2832   NS_ENSURE_SUCCESS(rv, rv);
  2833   // The child will be reflowed again "for real" so no need to place it now
  2835   aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight));
  2836   *aDesiredHeight = desiredSize.Height();
  2837   return NS_OK;
  2840 void 
  2841 nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState,
  2842                                   nsTableRowGroupFrame *aTfoot,
  2843                                   nscoord aFooterHeight)
  2845   nsPresContext* presContext = PresContext();
  2846   nsSize kidAvailSize(aReflowState.availSize);
  2847   kidAvailSize.height = aFooterHeight;
  2848   nsHTMLReflowState footerReflowState(presContext,
  2849                                       aReflowState.reflowState,
  2850                                       aTfoot, kidAvailSize,
  2851                                       -1, -1,
  2852                                       nsHTMLReflowState::CALLER_WILL_INIT);
  2853   InitChildReflowState(footerReflowState);
  2854   aReflowState.y += GetCellSpacingY();
  2856   nsRect origTfootRect = aTfoot->GetRect();
  2857   nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
  2859   nsReflowStatus footerStatus;
  2860   nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
  2861   desiredSize.Width() = desiredSize.Height() = 0;
  2862   ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
  2863               aReflowState.x, aReflowState.y, 0, footerStatus);
  2864   PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
  2865              origTfootVisualOverflow);
  2868 // Reflow the children based on the avail size and reason in aReflowState
  2869 // update aReflowMetrics a aStatus
  2870 nsresult
  2871 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
  2872                              nsReflowStatus&     aStatus,
  2873                              nsIFrame*&          aLastChildReflowed,
  2874                              nsOverflowAreas&    aOverflowAreas)
  2876   aStatus = NS_FRAME_COMPLETE;
  2877   aLastChildReflowed = nullptr;
  2879   nsIFrame* prevKidFrame = nullptr;
  2880   nsresult  rv = NS_OK;
  2881   nscoord   cellSpacingY = GetCellSpacingY();
  2883   nsPresContext* presContext = PresContext();
  2884   // XXXldb Should we be checking constrained height instead?
  2885   // tables are not able to pull back children from its next inflow, so even
  2886   // under paginated contexts tables are should not paginate if they are inside
  2887   // column set
  2888   bool isPaginated = presContext->IsPaginated() &&
  2889                        NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height &&
  2890                        aReflowState.reflowState.mFlags.mTableIsSplittable;
  2892   aOverflowAreas.Clear();
  2894   bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
  2895                          mBits.mResizedColumns ||
  2896                          IsGeometryDirty();
  2898   RowGroupArray rowGroups;
  2899   nsTableRowGroupFrame *thead, *tfoot;
  2900   OrderRowGroups(rowGroups, &thead, &tfoot);
  2901   bool pageBreak = false;
  2902   nscoord footerHeight = 0;
  2904   // Determine the repeatablility of headers and footers, and also the desired
  2905   // height of any repeatable footer.
  2906   // The repeatability of headers on continued tables is handled
  2907   // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
  2908   // We handle the repeatability of footers again here because we need to
  2909   // determine the footer's height anyway. We could perhaps optimize by
  2910   // using the footer's prev-in-flow's height instead of reflowing it again,
  2911   // but there's no real need.
  2912   if (isPaginated) {
  2913     if (thead && !GetPrevInFlow()) {
  2914       nscoord desiredHeight;
  2915       rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight);
  2916       if (NS_FAILED(rv))
  2917         return rv;
  2919     if (tfoot) {
  2920       rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight);
  2921       if (NS_FAILED(rv))
  2922         return rv;
  2925    // if the child is a tbody in paginated mode reduce the height by a repeated footer
  2926   bool allowRepeatedFooter = false;
  2927   for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
  2928     nsIFrame* kidFrame = rowGroups[childX];
  2929     // Get the frame state bits
  2930     // See if we should only reflow the dirty child frames
  2931     if (reflowAllKids ||
  2932         NS_SUBTREE_DIRTY(kidFrame) ||
  2933         (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
  2934          (isPaginated || (kidFrame->GetStateBits() &
  2935                           NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
  2936       if (pageBreak) {
  2937         if (allowRepeatedFooter) {
  2938           PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
  2940         else if (tfoot && tfoot->IsRepeatable()) {
  2941           tfoot->SetRepeatable(false);
  2943         PushChildren(rowGroups, childX);
  2944         aStatus = NS_FRAME_NOT_COMPLETE;
  2945         break;
  2948       nsSize kidAvailSize(aReflowState.availSize);
  2949       allowRepeatedFooter = false;
  2950       if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
  2951         nsTableRowGroupFrame* kidRG =
  2952           static_cast<nsTableRowGroupFrame*>(kidFrame);
  2953         if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
  2954           // the child is a tbody and there is a repeatable footer
  2955           NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
  2956           if (footerHeight + cellSpacingY < kidAvailSize.height) {
  2957             allowRepeatedFooter = true;
  2958             kidAvailSize.height -= footerHeight + cellSpacingY;
  2963       nsRect oldKidRect = kidFrame->GetRect();
  2964       nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
  2966       nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
  2967       desiredSize.Width() = desiredSize.Height() = 0;
  2969       // Reflow the child into the available space
  2970       nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
  2971                                        kidFrame, kidAvailSize,
  2972                                        -1, -1,
  2973                                        nsHTMLReflowState::CALLER_WILL_INIT);
  2974       InitChildReflowState(kidReflowState);
  2976       // If this isn't the first row group, and the previous row group has a
  2977       // nonzero YMost, then we can't be at the top of the page.
  2978       // We ignore a repeated head row group in this check to avoid causing
  2979       // infinite loops in some circumstances - see bug 344883.
  2980       if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
  2981           (rowGroups[childX - 1]->GetRect().YMost() > 0)) {
  2982         kidReflowState.mFlags.mIsTopOfPage = false;
  2984       aReflowState.y += cellSpacingY;
  2985       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
  2986         aReflowState.availSize.height -= cellSpacingY;
  2988       // record the presence of a next in flow, it might get destroyed so we
  2989       // need to reorder the row group array
  2990       bool reorder = false;
  2991       if (kidFrame->GetNextInFlow())
  2992         reorder = true;
  2994       rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
  2995                        aReflowState.x, aReflowState.y, 0, aStatus);
  2997       if (reorder) {
  2998         // reorder row groups the reflow may have changed the nextinflows
  2999         OrderRowGroups(rowGroups, &thead, &tfoot);
  3000         childX = rowGroups.IndexOf(kidFrame);
  3001         if (childX == RowGroupArray::NoIndex) {
  3002           // XXXbz can this happen?
  3003           childX = rowGroups.Length();
  3006       if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
  3007           ShouldAvoidBreakInside(aReflowState.reflowState)) {
  3008         aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  3009         break;
  3011       // see if the rowgroup did not fit on this page might be pushed on
  3012       // the next page
  3013       if (isPaginated &&
  3014           (NS_INLINE_IS_BREAK_BEFORE(aStatus) ||
  3015            (NS_FRAME_IS_COMPLETE(aStatus) &&
  3016             (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight()) &&
  3017             kidReflowState.AvailableHeight() < desiredSize.Height()))) {
  3018         if (ShouldAvoidBreakInside(aReflowState.reflowState)) {
  3019           aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  3020           break;
  3022         // if we are on top of the page place with dataloss
  3023         if (kidReflowState.mFlags.mIsTopOfPage) {
  3024           if (childX+1 < rowGroups.Length()) {
  3025             nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
  3026             if (nextRowGroupFrame) {
  3027               PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
  3028                          oldKidVisualOverflow);
  3029               if (allowRepeatedFooter) {
  3030                 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
  3032               else if (tfoot && tfoot->IsRepeatable()) {
  3033                 tfoot->SetRepeatable(false);
  3035               aStatus = NS_FRAME_NOT_COMPLETE;
  3036               PushChildren(rowGroups, childX + 1);
  3037               aLastChildReflowed = kidFrame;
  3038               break;
  3042         else { // we are not on top, push this rowgroup onto the next page
  3043           if (prevKidFrame) { // we had a rowgroup before so push this
  3044             if (allowRepeatedFooter) {
  3045               PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
  3047             else if (tfoot && tfoot->IsRepeatable()) {
  3048               tfoot->SetRepeatable(false);
  3050             aStatus = NS_FRAME_NOT_COMPLETE;
  3051             PushChildren(rowGroups, childX);
  3052             aLastChildReflowed = prevKidFrame;
  3053             break;
  3055           else { // we can't push so lets make clear how much space we need
  3056             PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
  3057                                      oldKidVisualOverflow);
  3058             aLastChildReflowed = kidFrame;
  3059             if (allowRepeatedFooter) {
  3060               PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
  3061               aLastChildReflowed = tfoot;
  3063             break;
  3068       aLastChildReflowed   = kidFrame;
  3070       pageBreak = false;
  3071       // see if there is a page break after this row group or before the next one
  3072       if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
  3073           (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) {
  3074         nsIFrame* nextKid =
  3075           (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
  3076         pageBreak = PageBreakAfter(kidFrame, nextKid);
  3079       // Place the child
  3080       PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
  3081                  oldKidVisualOverflow);
  3083       // Remember where we just were in case we end up pushing children
  3084       prevKidFrame = kidFrame;
  3086       // Special handling for incomplete children
  3087       if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
  3088         nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
  3089         if (!kidNextInFlow) {
  3090           // The child doesn't have a next-in-flow so create a continuing
  3091           // frame. This hooks the child into the flow
  3092           kidNextInFlow = presContext->PresShell()->FrameConstructor()->
  3093             CreateContinuingFrame(presContext, kidFrame, this);
  3095           // Insert the kid's new next-in-flow into our sibling list...
  3096           mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
  3097           // and in rowGroups after childX so that it will get pushed below.
  3098           rowGroups.InsertElementAt(childX + 1,
  3099                       static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
  3100         } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
  3101           // OrderRowGroups excludes NIFs in the child list from 'rowGroups'
  3102           // so we deal with that here to make sure they get pushed.
  3103           MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow),
  3104                      "OrderRowGroups must not put our NIF in 'rowGroups'");
  3105           rowGroups.InsertElementAt(childX + 1,
  3106                       static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
  3109         // We've used up all of our available space so push the remaining
  3110         // children.
  3111         if (allowRepeatedFooter) {
  3112           PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
  3114         else if (tfoot && tfoot->IsRepeatable()) {
  3115           tfoot->SetRepeatable(false);
  3118         nsIFrame* nextSibling = kidFrame->GetNextSibling();
  3119         if (nextSibling) {
  3120           PushChildren(rowGroups, childX + 1);
  3122         break;
  3125     else { // it isn't being reflowed
  3126       aReflowState.y += cellSpacingY;
  3127       nsRect kidRect = kidFrame->GetRect();
  3128       if (kidRect.y != aReflowState.y) {
  3129         // invalidate the old position
  3130         kidFrame->InvalidateFrameSubtree();
  3131         kidRect.y = aReflowState.y;
  3132         kidFrame->SetRect(kidRect);        // move to the new position
  3133         RePositionViews(kidFrame);
  3134         // invalidate the new position
  3135         kidFrame->InvalidateFrameSubtree();
  3137       aReflowState.y += kidRect.height;
  3139       // If our height is constrained then update the available height.
  3140       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
  3141         aReflowState.availSize.height -= cellSpacingY + kidRect.height;
  3144     ConsiderChildOverflow(aOverflowAreas, kidFrame);
  3147   // We've now propagated the column resizes and geometry changes to all
  3148   // the children.
  3149   mBits.mResizedColumns = false;
  3150   ClearGeometryDirty();
  3152   return rv;
  3155 void
  3156 nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext)
  3158   if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
  3159     nsHTMLReflowMetrics kidMet(GetWritingMode());
  3160     nsPresContext *presContext = PresContext();
  3161     for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame;
  3162          kidFrame = kidFrame->GetNextSibling()) {
  3163       if (NS_SUBTREE_DIRTY(kidFrame)) {
  3164         // The column groups don't care about dimensions or reflow states.
  3165         nsHTMLReflowState kidReflowState(presContext, kidFrame,
  3166                                        aRenderingContext, nsSize(0,0));
  3167         nsReflowStatus cgStatus;
  3168         ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0,
  3169                     cgStatus);
  3170         FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0);
  3173     SetHaveReflowedColGroups(true);
  3177 void
  3178 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize)
  3180   nsTableCellMap* cellMap = GetCellMap();
  3181   if (!cellMap) {
  3182     NS_ASSERTION(false, "never ever call me until the cell map is built!");
  3183     aDesiredSize.Height() = 0;
  3184     return;
  3186   nscoord  cellSpacingY = GetCellSpacingY();
  3187   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
  3189   // get the natural height based on the last child's (row group) rect
  3190   RowGroupArray rowGroups;
  3191   OrderRowGroups(rowGroups);
  3192   if (rowGroups.IsEmpty()) {
  3193     // tables can be used as rectangular items without content
  3194     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
  3195     if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
  3196         (tableSpecifiedHeight > 0) &&
  3197         eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
  3198           // empty tables should not have a size in quirks mode
  3199       aDesiredSize.Height() = tableSpecifiedHeight;
  3201     else
  3202       aDesiredSize.Height() = 0;
  3203     return;
  3205   int32_t rowCount = cellMap->GetRowCount();
  3206   int32_t colCount = cellMap->GetColCount();
  3207   nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
  3208   if (rowCount > 0 && colCount > 0) {
  3209     desiredHeight += cellSpacingY;
  3210     for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
  3211       desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
  3215   // see if a specified table height requires dividing additional space to rows
  3216   if (!GetPrevInFlow()) {
  3217     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
  3218     if ((tableSpecifiedHeight > 0) &&
  3219         (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
  3220         (tableSpecifiedHeight > desiredHeight)) {
  3221       // proportionately distribute the excess height to unconstrained rows in each
  3222       // unconstrained row group.
  3223       DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight);
  3224       // this might have changed the overflow area incorporate the childframe overflow area.
  3225       for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
  3226         ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
  3228       desiredHeight = tableSpecifiedHeight;
  3231   aDesiredSize.Height() = desiredHeight;
  3234 static
  3235 void ResizeCells(nsTableFrame& aTableFrame)
  3237   nsTableFrame::RowGroupArray rowGroups;
  3238   aTableFrame.OrderRowGroups(rowGroups);
  3239   nsHTMLReflowMetrics tableDesiredSize(aTableFrame.GetWritingMode()); // ???
  3240   nsRect tableRect = aTableFrame.GetRect();
  3241   tableDesiredSize.Width() = tableRect.width;
  3242   tableDesiredSize.Height() = tableRect.height;
  3243   tableDesiredSize.SetOverflowAreasToDesiredBounds();
  3245   for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
  3246     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  3248     nsRect rowGroupRect = rgFrame->GetRect();
  3249     nsHTMLReflowMetrics groupDesiredSize(tableDesiredSize.GetWritingMode());
  3250     groupDesiredSize.Width() = rowGroupRect.width;
  3251     groupDesiredSize.Height() = rowGroupRect.height;
  3252     groupDesiredSize.SetOverflowAreasToDesiredBounds();
  3254     nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3255     while (rowFrame) {
  3256       rowFrame->DidResize();
  3257       rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
  3258       rowFrame = rowFrame->GetNextRow();
  3260     rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
  3261     tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
  3262                                               rgFrame->GetPosition());
  3264   aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
  3267 void
  3268 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
  3269                                      nscoord                  aAmount)
  3271   nscoord cellSpacingY = GetCellSpacingY();
  3273   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
  3275   RowGroupArray rowGroups;
  3276   OrderRowGroups(rowGroups);
  3278   nscoord amountUsed = 0;
  3279   // distribute space to each pct height row whose row group doesn't have a computed
  3280   // height, and base the pct on the table height. If the row group had a computed
  3281   // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
  3282   nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1));
  3283   nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
  3284   nscoord yEndRG = yOriginRG;
  3285   uint32_t rgX;
  3286   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
  3287     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  3288     nscoord amountUsedByRG = 0;
  3289     nscoord yOriginRow = 0;
  3290     nsRect rgRect = rgFrame->GetRect();
  3291     if (!rgFrame->HasStyleHeight()) {
  3292       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3293       while (rowFrame) {
  3294         nsRect rowRect = rowFrame->GetRect();
  3295         if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
  3296           nscoord pctHeight = rowFrame->GetHeight(pctBasis);
  3297           nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height);
  3298           if (amountForRow > 0) {
  3299             nsRect oldRowRect = rowRect;
  3300             rowRect.height += amountForRow;
  3301             // XXXbz we don't need to change rowRect.y to be yOriginRow?
  3302             rowFrame->SetRect(rowRect);
  3303             yOriginRow += rowRect.height + cellSpacingY;
  3304             yEndRG += rowRect.height + cellSpacingY;
  3305             amountUsed += amountForRow;
  3306             amountUsedByRG += amountForRow;
  3307             //rowFrame->DidResize();
  3308             nsTableFrame::RePositionViews(rowFrame);
  3310             rgFrame->InvalidateFrameWithRect(oldRowRect);
  3311             rgFrame->InvalidateFrame();
  3314         else {
  3315           if (amountUsed > 0 && yOriginRow != rowRect.y &&
  3316               !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
  3317             rowFrame->InvalidateFrameSubtree();
  3318             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
  3319             nsTableFrame::RePositionViews(rowFrame);
  3320             rowFrame->InvalidateFrameSubtree();
  3322           yOriginRow += rowRect.height + cellSpacingY;
  3323           yEndRG += rowRect.height + cellSpacingY;
  3325         rowFrame = rowFrame->GetNextRow();
  3327       if (amountUsed > 0) {
  3328         if (rgRect.y != yOriginRG) {
  3329           rgFrame->InvalidateFrameSubtree();
  3332         nsRect origRgRect = rgRect;
  3333         nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
  3335         rgRect.y = yOriginRG;
  3336         rgRect.height += amountUsedByRG;
  3338         rgFrame->SetRect(rgRect);
  3340         nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect,
  3341                                            origRgVisualOverflow, false);
  3344     else if (amountUsed > 0 && yOriginRG != rgRect.y) {
  3345       rgFrame->InvalidateFrameSubtree();
  3346       rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
  3347       // Make sure child views are properly positioned
  3348       nsTableFrame::RePositionViews(rgFrame);
  3349       rgFrame->InvalidateFrameSubtree();
  3351     yOriginRG = yEndRG;
  3354   if (amountUsed >= aAmount) {
  3355     ResizeCells(*this);
  3356     return;
  3359   // get the first row without a style height where its row group has an
  3360   // unconstrained height
  3361   nsTableRowGroupFrame* firstUnStyledRG  = nullptr;
  3362   nsTableRowFrame*      firstUnStyledRow = nullptr;
  3363   for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) {
  3364     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  3365     if (!rgFrame->HasStyleHeight()) {
  3366       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3367       while (rowFrame) {
  3368         if (!rowFrame->HasStyleHeight()) {
  3369           firstUnStyledRG = rgFrame;
  3370           firstUnStyledRow = rowFrame;
  3371           break;
  3373         rowFrame = rowFrame->GetNextRow();
  3378   nsTableRowFrame* lastEligibleRow = nullptr;
  3379   // Accumulate the correct divisor. This will be the total total height of all
  3380   // unstyled rows inside unstyled row groups, unless there are none, in which
  3381   // case, it will be number of all rows. If the unstyled rows don't have a
  3382   // height, divide the space equally among them.
  3383   nscoord divisor = 0;
  3384   int32_t eligibleRows = 0;
  3385   bool expandEmptyRows = false;
  3387   if (!firstUnStyledRow) {
  3388     // there is no unstyled row
  3389     divisor = GetRowCount();
  3391   else {
  3392     for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
  3393       nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  3394       if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
  3395         nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3396         while (rowFrame) {
  3397           if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
  3398             NS_ASSERTION(rowFrame->GetSize().height >= 0,
  3399                          "negative row frame height");
  3400             divisor += rowFrame->GetSize().height;
  3401             eligibleRows++;
  3402             lastEligibleRow = rowFrame;
  3404           rowFrame = rowFrame->GetNextRow();
  3408     if (divisor <= 0) {
  3409       if (eligibleRows > 0) {
  3410         expandEmptyRows = true;
  3412       else {
  3413         NS_ERROR("invalid divisor");
  3414         return;
  3418   // allocate the extra height to the unstyled row groups and rows
  3419   nscoord heightToDistribute = aAmount - amountUsed;
  3420   yOriginRG = borderPadding.top + cellSpacingY;
  3421   yEndRG = yOriginRG;
  3422   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
  3423     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  3424     nscoord amountUsedByRG = 0;
  3425     nscoord yOriginRow = 0;
  3426     nsRect rgRect = rgFrame->GetRect();
  3427     nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
  3428     // see if there is an eligible row group or we distribute to all rows
  3429     if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
  3430       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
  3431       while (rowFrame) {
  3432         nsRect rowRect = rowFrame->GetRect();
  3433         nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
  3434         // see if there is an eligible row or we distribute to all rows
  3435         if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
  3436           float ratio;
  3437           if (eligibleRows) {
  3438             if (!expandEmptyRows) {
  3439               // The amount of additional space each row gets is proportional to
  3440               // its height
  3441               ratio = float(rowRect.height) / float(divisor);
  3442             } else {
  3443               // empty rows get all the same additional space
  3444               ratio = 1.0f / float(eligibleRows);
  3447           else {
  3448             // all rows get the same additional space
  3449             ratio = 1.0f / float(divisor);
  3451           // give rows their additional space, except for the last row which
  3452           // gets the remainder
  3453           nscoord amountForRow = (rowFrame == lastEligibleRow)
  3454                                  ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
  3455           amountForRow = std::min(amountForRow, aAmount - amountUsed);
  3457           if (yOriginRow != rowRect.y) {
  3458             rowFrame->InvalidateFrameSubtree();
  3461           // update the row height
  3462           nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
  3463                             rowRect.height + amountForRow);
  3464           rowFrame->SetRect(newRowRect);
  3466           yOriginRow += newRowRect.height + cellSpacingY;
  3467           yEndRG += newRowRect.height + cellSpacingY;
  3469           amountUsed += amountForRow;
  3470           amountUsedByRG += amountForRow;
  3471           NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
  3472           //rowFrame->DidResize();
  3473           nsTableFrame::RePositionViews(rowFrame);
  3475           nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow,
  3476                                              false);
  3478         else {
  3479           if (amountUsed > 0 && yOriginRow != rowRect.y) {
  3480             rowFrame->InvalidateFrameSubtree();
  3481             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
  3482             nsTableFrame::RePositionViews(rowFrame);
  3483             rowFrame->InvalidateFrameSubtree();
  3485           yOriginRow += rowRect.height + cellSpacingY;
  3486           yEndRG += rowRect.height + cellSpacingY;
  3488         rowFrame = rowFrame->GetNextRow();
  3490       if (amountUsed > 0) {
  3491         if (rgRect.y != yOriginRG) {
  3492           rgFrame->InvalidateFrameSubtree();
  3495         rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
  3496                                 rgRect.height + amountUsedByRG));
  3498         nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow,
  3499                                            false);
  3501       // Make sure child views are properly positioned
  3503     else if (amountUsed > 0 && yOriginRG != rgRect.y) {
  3504       rgFrame->InvalidateFrameSubtree();
  3505       rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
  3506       // Make sure child views are properly positioned
  3507       nsTableFrame::RePositionViews(rgFrame);
  3508       rgFrame->InvalidateFrameSubtree();
  3510     yOriginRG = yEndRG;
  3513   ResizeCells(*this);
  3516 int32_t nsTableFrame::GetColumnWidth(int32_t aColIndex)
  3518   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
  3519   if (this == firstInFlow) {
  3520     nsTableColFrame* colFrame = GetColFrame(aColIndex);
  3521     return colFrame ? colFrame->GetFinalWidth() : 0;
  3523   return firstInFlow->GetColumnWidth(aColIndex);
  3526 // XXX: could cache this.  But be sure to check style changes if you do!
  3527 nscoord nsTableFrame::GetCellSpacingX()
  3529   if (IsBorderCollapse())
  3530     return 0;
  3532   return StyleTableBorder()->mBorderSpacingX;
  3535 // XXX: could cache this. But be sure to check style changes if you do!
  3536 nscoord nsTableFrame::GetCellSpacingY()
  3538   if (IsBorderCollapse())
  3539     return 0;
  3541   return StyleTableBorder()->mBorderSpacingY;
  3545 /* virtual */ nscoord
  3546 nsTableFrame::GetBaseline() const
  3548   nscoord ascent = 0;
  3549   RowGroupArray orderedRowGroups;
  3550   OrderRowGroups(orderedRowGroups);
  3551   nsTableRowFrame* firstRow = nullptr;
  3552   for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
  3553     nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
  3554     if (rgFrame->GetRowCount()) {
  3555       firstRow = rgFrame->GetFirstRow();
  3556       ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline();
  3557       break;
  3560   if (!firstRow)
  3561     ascent = GetRect().height;
  3562   return ascent;
  3564 /* ----- global methods ----- */
  3566 nsIFrame*
  3567 NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  3569   return new (aPresShell) nsTableFrame(aContext);
  3572 NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
  3574 nsTableFrame*
  3575 nsTableFrame::GetTableFrame(nsIFrame* aFrame)
  3577   for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
  3578        ancestor = ancestor->GetParent()) {
  3579     if (nsGkAtoms::tableFrame == ancestor->GetType()) {
  3580       return static_cast<nsTableFrame*>(ancestor);
  3583   NS_RUNTIMEABORT("unable to find table parent");
  3584   return nullptr;
  3587 nsTableFrame*
  3588 nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
  3589                                           nsIFrame* aFrame)
  3591   MOZ_ASSERT(aMustPassThrough == aFrame ||
  3592              nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame),
  3593              "aMustPassThrough should be an ancestor");
  3595   // Retrieve the table frame, and ensure that we hit aMustPassThrough on the
  3596   // way.  If we don't, just return null.
  3597   bool hitPassThroughFrame = false;
  3598   nsTableFrame* tableFrame = nullptr;
  3599   for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) {
  3600     if (ancestor == aMustPassThrough) {
  3601       hitPassThroughFrame = true;
  3603     if (nsGkAtoms::tableFrame == ancestor->GetType()) {
  3604       tableFrame = static_cast<nsTableFrame*>(ancestor);
  3605       break;
  3609   MOZ_ASSERT(tableFrame, "Should have a table frame here");
  3610   return hitPassThroughFrame ? tableFrame : nullptr;
  3613 bool
  3614 nsTableFrame::IsAutoHeight()
  3616   const nsStyleCoord &height = StylePosition()->mHeight;
  3617   // Don't consider calc() here like this quirk for percent.
  3618   return height.GetUnit() == eStyleUnit_Auto ||
  3619          (height.GetUnit() == eStyleUnit_Percent &&
  3620           height.GetPercentValue() <= 0.0f);
  3623 nscoord
  3624 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
  3626   nscoord height = aState.ComputedHeight();
  3627   if (NS_AUTOHEIGHT != height) {
  3628     nsMargin borderPadding = GetChildAreaOffset(&aState);
  3629     height += borderPadding.top + borderPadding.bottom;
  3631   height = std::max(0, height);
  3633   return height;
  3636 bool
  3637 nsTableFrame::IsAutoLayout()
  3639   if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
  3640     return true;
  3641   // a fixed-layout inline-table must have a width
  3642   // and tables with 'width: -moz-max-content' must be auto-layout
  3643   // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns
  3644   // nscoord_MAX)
  3645   const nsStyleCoord &width = StylePosition()->mWidth;
  3646   return (width.GetUnit() == eStyleUnit_Auto) ||
  3647          (width.GetUnit() == eStyleUnit_Enumerated &&
  3648           width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
  3651 #ifdef DEBUG_FRAME_DUMP
  3652 nsresult
  3653 nsTableFrame::GetFrameName(nsAString& aResult) const
  3655   return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
  3657 #endif
  3659 // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
  3660 // is of type aChildType
  3661 nsIFrame*
  3662 nsTableFrame::GetFrameAtOrBefore(nsIFrame*       aParentFrame,
  3663                                  nsIFrame*       aPriorChildFrame,
  3664                                  nsIAtom*        aChildType)
  3666   nsIFrame* result = nullptr;
  3667   if (!aPriorChildFrame) {
  3668     return result;
  3670   if (aChildType == aPriorChildFrame->GetType()) {
  3671     return aPriorChildFrame;
  3674   // aPriorChildFrame is not of type aChildType, so we need start from
  3675   // the beginnng and find the closest one
  3676   nsIFrame* lastMatchingFrame = nullptr;
  3677   nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
  3678   while (childFrame && (childFrame != aPriorChildFrame)) {
  3679     if (aChildType == childFrame->GetType()) {
  3680       lastMatchingFrame = childFrame;
  3682     childFrame = childFrame->GetNextSibling();
  3684   return lastMatchingFrame;
  3687 #ifdef DEBUG
  3688 void
  3689 nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
  3691   if (!aKidFrame)
  3692     return;
  3694   nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild();
  3695   while (cFrame) {
  3696     nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
  3697     if (rowFrame) {
  3698       printf("row(%d)=%p ", rowFrame->GetRowIndex(),
  3699              static_cast<void*>(rowFrame));
  3700       nsIFrame* childFrame = cFrame->GetFirstPrincipalChild();
  3701       while (childFrame) {
  3702         nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
  3703         if (cellFrame) {
  3704           int32_t colIndex;
  3705           cellFrame->GetColIndex(colIndex);
  3706           printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame));
  3708         childFrame = childFrame->GetNextSibling();
  3710       printf("\n");
  3712     else {
  3713       DumpRowGroup(rowFrame);
  3715     cFrame = cFrame->GetNextSibling();
  3719 void
  3720 nsTableFrame::Dump(bool            aDumpRows,
  3721                    bool            aDumpCols,
  3722                    bool            aDumpCellMap)
  3724   printf("***START TABLE DUMP*** \n");
  3725   // dump the columns widths array
  3726   printf("mColWidths=");
  3727   int32_t numCols = GetColCount();
  3728   int32_t colX;
  3729   for (colX = 0; colX < numCols; colX++) {
  3730     printf("%d ", GetColumnWidth(colX));
  3732   printf("\n");
  3734   if (aDumpRows) {
  3735     nsIFrame* kidFrame = mFrames.FirstChild();
  3736     while (kidFrame) {
  3737       DumpRowGroup(kidFrame);
  3738       kidFrame = kidFrame->GetNextSibling();
  3742   if (aDumpCols) {
  3743 	  // output col frame cache
  3744     printf("\n col frame cache ->");
  3745 	   for (colX = 0; colX < numCols; colX++) {
  3746       nsTableColFrame* colFrame = mColFrames.ElementAt(colX);
  3747       if (0 == (colX % 8)) {
  3748         printf("\n");
  3750       printf ("%d=%p ", colX, static_cast<void*>(colFrame));
  3751       nsTableColType colType = colFrame->GetColType();
  3752       switch (colType) {
  3753       case eColContent:
  3754         printf(" content ");
  3755         break;
  3756       case eColAnonymousCol:
  3757         printf(" anonymous-column ");
  3758         break;
  3759       case eColAnonymousColGroup:
  3760         printf(" anonymous-colgroup ");
  3761         break;
  3762       case eColAnonymousCell:
  3763         printf(" anonymous-cell ");
  3764         break;
  3767     printf("\n colgroups->");
  3768     for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame;
  3769          childFrame = childFrame->GetNextSibling()) {
  3770       if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
  3771         nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
  3772         colGroupFrame->Dump(1);
  3775     for (colX = 0; colX < numCols; colX++) {
  3776       printf("\n");
  3777       nsTableColFrame* colFrame = GetColFrame(colX);
  3778       colFrame->Dump(1);
  3781   if (aDumpCellMap) {
  3782     nsTableCellMap* cellMap = GetCellMap();
  3783     cellMap->Dump();
  3785   printf(" ***END TABLE DUMP*** \n");
  3787 #endif
  3789 // nsTableIterator
  3790 nsTableIterator::nsTableIterator(nsIFrame& aSource)
  3792   nsIFrame* firstChild = aSource.GetFirstPrincipalChild();
  3793   Init(firstChild);
  3796 nsTableIterator::nsTableIterator(nsFrameList& aSource)
  3798   nsIFrame* firstChild = aSource.FirstChild();
  3799   Init(firstChild);
  3802 void nsTableIterator::Init(nsIFrame* aFirstChild)
  3804   mFirstListChild = aFirstChild;
  3805   mFirstChild     = aFirstChild;
  3806   mCurrentChild   = nullptr;
  3807   mLeftToRight    = true;
  3808   mCount          = -1;
  3810   if (!mFirstChild) {
  3811     return;
  3814   nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild);
  3815   mLeftToRight = (NS_STYLE_DIRECTION_LTR ==
  3816                   table->StyleVisibility()->mDirection);
  3818   if (!mLeftToRight) {
  3819     mCount = 0;
  3820     nsIFrame* nextChild = mFirstChild->GetNextSibling();
  3821     while (nullptr != nextChild) {
  3822       mCount++;
  3823       mFirstChild = nextChild;
  3824       nextChild = nextChild->GetNextSibling();
  3829 nsIFrame* nsTableIterator::First()
  3831   mCurrentChild = mFirstChild;
  3832   return mCurrentChild;
  3835 nsIFrame* nsTableIterator::Next()
  3837   if (!mCurrentChild) {
  3838     return nullptr;
  3841   if (mLeftToRight) {
  3842     mCurrentChild = mCurrentChild->GetNextSibling();
  3843     return mCurrentChild;
  3845   else {
  3846     nsIFrame* targetChild = mCurrentChild;
  3847     mCurrentChild = nullptr;
  3848     nsIFrame* child = mFirstListChild;
  3849     while (child && (child != targetChild)) {
  3850       mCurrentChild = child;
  3851       child = child->GetNextSibling();
  3853     return mCurrentChild;
  3857 bool nsTableIterator::IsLeftToRight()
  3859   return mLeftToRight;
  3862 int32_t nsTableIterator::Count()
  3864   if (-1 == mCount) {
  3865     mCount = 0;
  3866     nsIFrame* child = mFirstListChild;
  3867     while (nullptr != child) {
  3868       mCount++;
  3869       child = child->GetNextSibling();
  3872   return mCount;
  3875 bool
  3876 nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const
  3878   // Since fixed-layout tables should not have their column sizes change
  3879   // as they load, we assume that all columns are significant.
  3880   if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
  3881     return true;
  3882   // the first column is always significant
  3883   if (aColIndex == 0)
  3884     return true;
  3885   nsTableCellMap* cellMap = GetCellMap();
  3886   if (!cellMap)
  3887     return false;
  3888   return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
  3891 /********************************************************************************
  3892  * Collapsing Borders
  3894  *  The CSS spec says to resolve border conflicts in this order:
  3895  *  1) any border with the style HIDDEN wins
  3896  *  2) the widest border with a style that is not NONE wins
  3897  *  3) the border styles are ranked in this order, highest to lowest precedence:
  3898  *     double, solid, dashed, dotted, ridge, outset, groove, inset
  3899  *  4) borders that are of equal width and style (differ only in color) have this precedence:
  3900  *     cell, row, rowgroup, col, colgroup, table
  3901  *  5) if all border styles are NONE, then that's the computed border style.
  3902  *******************************************************************************/
  3904 #ifdef DEBUG
  3905 #define VerifyNonNegativeDamageRect(r)                                  \
  3906   NS_ASSERTION((r).x >= 0, "negative col index");                       \
  3907   NS_ASSERTION((r).y >= 0, "negative row index");                       \
  3908   NS_ASSERTION((r).width >= 0, "negative horizontal damage");           \
  3909   NS_ASSERTION((r).height >= 0, "negative vertical damage");
  3910 #define VerifyDamageRect(r)                                             \
  3911   VerifyNonNegativeDamageRect(r);                                       \
  3912   NS_ASSERTION((r).XMost() <= GetColCount(),                            \
  3913                "horizontal damage extends outside table");              \
  3914   NS_ASSERTION((r).YMost() <= GetRowCount(),                            \
  3915                "vertical damage extends outside table");
  3916 #endif
  3918 void
  3919 nsTableFrame::AddBCDamageArea(const nsIntRect& aValue)
  3921   NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
  3922 #ifdef DEBUG
  3923   VerifyDamageRect(aValue);
  3924 #endif
  3926   SetNeedToCalcBCBorders(true);
  3927   // Get the property
  3928   BCPropertyData* value = GetBCProperty(true);
  3929   if (value) {
  3930 #ifdef DEBUG
  3931     VerifyNonNegativeDamageRect(value->mDamageArea);
  3932 #endif
  3933     // Clamp the old damage area to the current table area in case it shrunk.
  3934     int32_t cols = GetColCount();
  3935     if (value->mDamageArea.XMost() > cols) {
  3936       if (value->mDamageArea.x > cols) {
  3937         value->mDamageArea.x = cols;
  3938         value->mDamageArea.width = 0;
  3940       else {
  3941         value->mDamageArea.width = cols - value->mDamageArea.x;
  3944     int32_t rows = GetRowCount();
  3945     if (value->mDamageArea.YMost() > rows) {
  3946       if (value->mDamageArea.y > rows) {
  3947         value->mDamageArea.y = rows;
  3948         value->mDamageArea.height = 0;
  3950       else {
  3951         value->mDamageArea.height = rows - value->mDamageArea.y;
  3955     // Construct a union of the new and old damage areas.
  3956     value->mDamageArea.UnionRect(value->mDamageArea, aValue);
  3961 void
  3962 nsTableFrame::SetFullBCDamageArea()
  3964   NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
  3966   SetNeedToCalcBCBorders(true);
  3968   BCPropertyData* value = GetBCProperty(true);
  3969   if (value) {
  3970     value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount());
  3975 /* BCCellBorder represents a border segment which can be either a horizontal
  3976  * or a vertical segment. For each segment we need to know the color, width,
  3977  * style, who owns it and how long it is in cellmap coordinates.
  3978  * Ownership of these segments is important to calculate which corners should
  3979  * be bevelled. This structure has dual use, its used first to compute the
  3980  * dominant border for horizontal and vertical segments and to store the
  3981  * preliminary computed border results in the BCCellBorders structure.
  3982  * This temporary storage is not symmetric with respect to horizontal and
  3983  * vertical border segments, its always column oriented. For each column in
  3984  * the cellmap there is a temporary stored vertical and horizontal segment.
  3985  * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
  3986  */
  3987 struct BCCellBorder
  3989   BCCellBorder() { Reset(0, 1); }
  3990   void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
  3991   nscolor       color;    // border segment color
  3992   BCPixelSize   width;    // border segment width in pixel coordinates !!
  3993   uint8_t       style;    // border segment style, possible values are defined
  3994                           // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
  3995   BCBorderOwner owner;    // border segment owner, possible values are defined
  3996                           // in celldata.h. In the cellmap for each border
  3997                           // segment we store the owner and later when
  3998                           // painting we know the owner and can retrieve the
  3999                           // style info from the corresponding frame
  4000   int32_t       rowIndex; // rowIndex of temporary stored horizontal border
  4001                           // segments relative to the table
  4002   int32_t       rowSpan;  // row span of temporary stored horizontal border
  4003                           // segments
  4004 };
  4006 void
  4007 BCCellBorder::Reset(uint32_t aRowIndex,
  4008                     uint32_t aRowSpan)
  4010   style = NS_STYLE_BORDER_STYLE_NONE;
  4011   color = 0;
  4012   width = 0;
  4013   owner = eTableOwner;
  4014   rowIndex = aRowIndex;
  4015   rowSpan  = aRowSpan;
  4018 class BCMapCellIterator;
  4020 /*****************************************************************
  4021  *  BCMapCellInfo
  4022  * This structure stores information about the cellmap and all involved
  4023  * table related frames that are used during the computation of winning borders
  4024  * in CalcBCBorders so that they do need to be looked up again and again when
  4025  * iterating over the cells.
  4026  ****************************************************************/
  4027 struct BCMapCellInfo
  4029   BCMapCellInfo(nsTableFrame* aTableFrame);
  4030   void ResetCellInfo();
  4031   void SetInfo(nsTableRowFrame*   aNewRow,
  4032                int32_t            aColIndex,
  4033                BCCellData*        aCellData,
  4034                BCMapCellIterator* aIter,
  4035                nsCellMap*         aCellMap = nullptr);
  4036   // The BCMapCellInfo has functions to set the continous
  4037   // border widths (see nsTablePainter.cpp for a description of the continous
  4038   // borders concept). The widths are computed inside these functions based on
  4039   // the current position inside the table and the cached frames that correspond
  4040   // to this position. The widths are stored in member variables of the internal
  4041   // table frames.
  4042   void SetTableTopLeftContBCBorder();
  4043   void SetRowGroupLeftContBCBorder();
  4044   void SetRowGroupRightContBCBorder();
  4045   void SetRowGroupBottomContBCBorder();
  4046   void SetRowLeftContBCBorder();
  4047   void SetRowRightContBCBorder();
  4048   void SetColumnTopRightContBCBorder();
  4049   void SetColumnBottomContBCBorder();
  4050   void SetColGroupBottomContBCBorder();
  4051   void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
  4052                                           nsTableRowFrame* aNextRow);
  4054   // functions to set the border widths on the table related frames, where the
  4055   // knowledge about the current position in the table is used.
  4056   void SetTableTopBorderWidth(BCPixelSize aWidth);
  4057   void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth);
  4058   void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth);
  4059   void SetTableBottomBorderWidth(BCPixelSize aWidth);
  4060   void SetLeftBorderWidths(BCPixelSize aWidth);
  4061   void SetRightBorderWidths(BCPixelSize aWidth);
  4062   void SetTopBorderWidths(BCPixelSize aWidth);
  4063   void SetBottomBorderWidths(BCPixelSize aWidth);
  4065   // functions to compute the borders; they depend on the
  4066   // knowledge about the current position in the table. The edge functions
  4067   // should be called if a table edge is involved, otherwise the internal
  4068   // functions should be called.
  4069   BCCellBorder GetTopEdgeBorder();
  4070   BCCellBorder GetBottomEdgeBorder();
  4071   BCCellBorder GetLeftEdgeBorder();
  4072   BCCellBorder GetRightEdgeBorder();
  4073   BCCellBorder GetRightInternalBorder();
  4074   BCCellBorder GetLeftInternalBorder();
  4075   BCCellBorder GetTopInternalBorder();
  4076   BCCellBorder GetBottomInternalBorder();
  4078   // functions to set the interal position information
  4079   void SetColumn(int32_t aColX);
  4080   // Increment the row as we loop over the rows of a rowspan
  4081   void IncrementRow(bool aResetToTopRowOfCell = false);
  4083   // Helper functions to get extent of the cell
  4084   int32_t GetCellEndRowIndex() const;
  4085   int32_t GetCellEndColIndex() const;
  4087   // storage of table information
  4088   nsTableFrame*         mTableFrame;
  4089   int32_t               mNumTableRows;
  4090   int32_t               mNumTableCols;
  4091   BCPropertyData*       mTableBCData;
  4093   // storage of table ltr information, the border collapse code swaps the sides
  4094   // to account for rtl tables, this is done through mStartSide and mEndSide
  4095   bool                  mTableIsLTR;
  4096   mozilla::css::Side    mStartSide;
  4097   mozilla::css::Side    mEndSide;
  4099   // a cell can only belong to one rowgroup
  4100   nsTableRowGroupFrame* mRowGroup;
  4102   // a cell with a rowspan has a top and a bottom row, and rows in between
  4103   nsTableRowFrame*      mTopRow;
  4104   nsTableRowFrame*      mBottomRow;
  4105   nsTableRowFrame*      mCurrentRowFrame;
  4107   // a cell with a colspan has a left and right column and columns in between
  4108   // they can belong to different colgroups
  4109   nsTableColGroupFrame* mColGroup;
  4110   nsTableColGroupFrame* mCurrentColGroupFrame;
  4112   nsTableColFrame*      mLeftCol;
  4113   nsTableColFrame*      mRightCol;
  4114   nsTableColFrame*      mCurrentColFrame;
  4116   // cell information
  4117   BCCellData*           mCellData;
  4118   nsBCTableCellFrame*   mCell;
  4120   int32_t               mRowIndex;
  4121   int32_t               mRowSpan;
  4122   int32_t               mColIndex;
  4123   int32_t               mColSpan;
  4125   // flags to describe the position of the cell with respect to the row- and
  4126   // colgroups, for instance mRgAtTop documents that the top cell border hits
  4127   // a rowgroup border
  4128   bool                  mRgAtTop;
  4129   bool                  mRgAtBottom;
  4130   bool                  mCgAtLeft;
  4131   bool                  mCgAtRight;
  4133 };
  4136 BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
  4138   mTableFrame = aTableFrame;
  4139   mTableIsLTR =
  4140     aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
  4141   if (mTableIsLTR) {
  4142     mStartSide = NS_SIDE_LEFT;
  4143     mEndSide = NS_SIDE_RIGHT;
  4145   else {
  4146     mStartSide = NS_SIDE_RIGHT;
  4147     mEndSide = NS_SIDE_LEFT;
  4149   mNumTableRows = mTableFrame->GetRowCount();
  4150   mNumTableCols = mTableFrame->GetColCount();
  4151   mTableBCData = static_cast<BCPropertyData*>
  4152     (mTableFrame->Properties().Get(TableBCProperty()));
  4154   ResetCellInfo();
  4157 void BCMapCellInfo::ResetCellInfo()
  4159   mCellData  = nullptr;
  4160   mRowGroup  = nullptr;
  4161   mTopRow    = nullptr;
  4162   mBottomRow = nullptr;
  4163   mColGroup  = nullptr;
  4164   mLeftCol   = nullptr;
  4165   mRightCol  = nullptr;
  4166   mCell      = nullptr;
  4167   mRowIndex  = mRowSpan = mColIndex = mColSpan = 0;
  4168   mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false;
  4171 inline int32_t BCMapCellInfo::GetCellEndRowIndex() const
  4173   return mRowIndex + mRowSpan - 1;
  4176 inline int32_t BCMapCellInfo::GetCellEndColIndex() const
  4178   return mColIndex + mColSpan - 1;
  4182 class BCMapCellIterator
  4184 public:
  4185   BCMapCellIterator(nsTableFrame* aTableFrame,
  4186                     const nsIntRect& aDamageArea);
  4188   void First(BCMapCellInfo& aMapCellInfo);
  4190   void Next(BCMapCellInfo& aMapCellInfo);
  4192   void PeekRight(BCMapCellInfo& aRefInfo,
  4193                  uint32_t     aRowIndex,
  4194                  BCMapCellInfo& aAjaInfo);
  4196   void PeekBottom(BCMapCellInfo& aRefInfo,
  4197                   uint32_t     aColIndex,
  4198                   BCMapCellInfo& aAjaInfo);
  4200   bool IsNewRow() { return mIsNewRow; }
  4202   nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
  4203   nsTableRowFrame* GetCurrentRow() const { return mRow; }
  4204   nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;}
  4206   int32_t    mRowGroupStart;
  4207   int32_t    mRowGroupEnd;
  4208   bool       mAtEnd;
  4209   nsCellMap* mCellMap;
  4211 private:
  4212   bool SetNewRow(nsTableRowFrame* row = nullptr);
  4213   bool SetNewRowGroup(bool aFindFirstDamagedRow);
  4215   nsTableFrame*         mTableFrame;
  4216   nsTableCellMap*       mTableCellMap;
  4217   nsTableFrame::RowGroupArray mRowGroups;
  4218   nsTableRowGroupFrame* mRowGroup;
  4219   int32_t               mRowGroupIndex;
  4220   uint32_t              mNumTableRows;
  4221   nsTableRowFrame*      mRow;
  4222   nsTableRowFrame*      mPrevRow;
  4223   bool                  mIsNewRow;
  4224   int32_t               mRowIndex;
  4225   uint32_t              mNumTableCols;
  4226   int32_t               mColIndex;
  4227   nsPoint               mAreaStart;
  4228   nsPoint               mAreaEnd;
  4229 };
  4231 BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
  4232                                      const nsIntRect& aDamageArea)
  4233 :mTableFrame(aTableFrame)
  4235   mTableCellMap  = aTableFrame->GetCellMap();
  4237   mAreaStart.x   = aDamageArea.x;
  4238   mAreaStart.y   = aDamageArea.y;
  4239   mAreaEnd.y     = aDamageArea.y + aDamageArea.height - 1;
  4240   mAreaEnd.x     = aDamageArea.x + aDamageArea.width - 1;
  4242   mNumTableRows  = mTableFrame->GetRowCount();
  4243   mRow           = nullptr;
  4244   mRowIndex      = 0;
  4245   mNumTableCols  = mTableFrame->GetColCount();
  4246   mColIndex      = 0;
  4247   mRowGroupIndex = -1;
  4249   // Get the ordered row groups
  4250   aTableFrame->OrderRowGroups(mRowGroups);
  4252   mAtEnd = true; // gets reset when First() is called
  4255 // fill fields that we need for border collapse computation on a given cell
  4256 void
  4257 BCMapCellInfo::SetInfo(nsTableRowFrame*   aNewRow,
  4258                        int32_t            aColIndex,
  4259                        BCCellData*        aCellData,
  4260                        BCMapCellIterator* aIter,
  4261                        nsCellMap*         aCellMap)
  4263   // fill the cell information
  4264   mCellData = aCellData;
  4265   mColIndex = aColIndex;
  4267   // initialize the row information if it was not previously set for cells in
  4268   // this row
  4269   mRowIndex = 0;
  4270   if (aNewRow) {
  4271     mTopRow = aNewRow;
  4272     mRowIndex = aNewRow->GetRowIndex();
  4275   // fill cell frame info and row information
  4276   mCell      = nullptr;
  4277   mRowSpan   = 1;
  4278   mColSpan   = 1;
  4279   if (aCellData) {
  4280     mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
  4281     if (mCell) {
  4282       if (!mTopRow) {
  4283         mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent());
  4284         if (!mTopRow) ABORT0();
  4285         mRowIndex = mTopRow->GetRowIndex();
  4287       mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
  4288       mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
  4292   if (!mTopRow) {
  4293     mTopRow = aIter->GetCurrentRow();
  4295   if (1 == mRowSpan) {
  4296     mBottomRow = mTopRow;
  4298   else {
  4299     mBottomRow = mTopRow->GetNextRow();
  4300     if (mBottomRow) {
  4301       for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) {
  4302         mBottomRow = mBottomRow->GetNextRow();
  4304       NS_ASSERTION(mBottomRow, "spanned row not found");
  4306     else {
  4307       NS_ASSERTION(false, "error in cell map");
  4308       mRowSpan = 1;
  4309       mBottomRow = mTopRow;
  4312   // row group frame info
  4313   // try to reuse the rgStart and rgEnd from the iterator as calls to
  4314   // GetRowCount() are computationally expensive and should be avoided if
  4315   // possible
  4316   uint32_t rgStart  = aIter->mRowGroupStart;
  4317   uint32_t rgEnd    = aIter->mRowGroupEnd;
  4318   mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent());
  4319   if (mRowGroup != aIter->GetCurrentRowGroup()) {
  4320     rgStart = mRowGroup->GetStartRowIndex();
  4321     rgEnd   = rgStart + mRowGroup->GetRowCount() - 1;
  4323   uint32_t rowIndex = mTopRow->GetRowIndex();
  4324   mRgAtTop    = (rgStart == rowIndex);
  4325   mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1);
  4327    // col frame info
  4328   mLeftCol = mTableFrame->GetColFrame(aColIndex);
  4329   if (!mLeftCol) ABORT0();
  4331   mRightCol = mLeftCol;
  4332   if (mColSpan > 1) {
  4333     nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
  4334                                                          mColSpan -1);
  4335     if (!colFrame) ABORT0();
  4336     mRightCol = colFrame;
  4339   // col group frame info
  4340   mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent());
  4341   int32_t cgStart = mColGroup->GetStartColumnIndex();
  4342   int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
  4343   mCgAtLeft  = (cgStart == aColIndex);
  4344   mCgAtRight = (cgEnd == aColIndex + mColSpan - 1);
  4347 bool
  4348 BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
  4350   mAtEnd   = true;
  4351   mPrevRow = mRow;
  4352   if (aRow) {
  4353     mRow = aRow;
  4355   else if (mRow) {
  4356     mRow = mRow->GetNextRow();
  4358   if (mRow) {
  4359     mRowIndex = mRow->GetRowIndex();
  4360     // get to the first entry with an originating cell
  4361     int32_t rgRowIndex = mRowIndex - mRowGroupStart;
  4362     if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length())
  4363       ABORT1(false);
  4364     const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
  4366     for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
  4367       CellData* cellData = row.SafeElementAt(mColIndex);
  4368       if (!cellData) { // add a dead cell data
  4369         nsIntRect damageArea;
  4370         cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
  4371                                         false, 0, damageArea);
  4372         if (!cellData) ABORT1(false);
  4374       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  4375         break;
  4378     mIsNewRow = true;
  4379     mAtEnd    = false;
  4381   else ABORT1(false);
  4383   return !mAtEnd;
  4386 bool
  4387 BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
  4389    mAtEnd = true;
  4390   int32_t numRowGroups = mRowGroups.Length();
  4391   mCellMap = nullptr;
  4392   for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
  4393     mRowGroup = mRowGroups[mRowGroupIndex];
  4394     int32_t rowCount = mRowGroup->GetRowCount();
  4395     mRowGroupStart = mRowGroup->GetStartRowIndex();
  4396     mRowGroupEnd   = mRowGroupStart + rowCount - 1;
  4397     if (rowCount > 0) {
  4398       mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
  4399       if (!mCellMap) ABORT1(false);
  4400       nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
  4401       if (aFindFirstDamagedRow) {
  4402         if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
  4403           // the damage area starts in the row group
  4404           if (aFindFirstDamagedRow) {
  4405             // find the correct first damaged row
  4406             int32_t numRows = mAreaStart.y - mRowGroupStart;
  4407             for (int32_t i = 0; i < numRows; i++) {
  4408               firstRow = firstRow->GetNextRow();
  4409               if (!firstRow) ABORT1(false);
  4413         else {
  4414           continue;
  4417       if (SetNewRow(firstRow)) { // sets mAtEnd
  4418         break;
  4423   return !mAtEnd;
  4426 void
  4427 BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
  4429   aMapInfo.ResetCellInfo();
  4431   SetNewRowGroup(true); // sets mAtEnd
  4432   while (!mAtEnd) {
  4433     if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
  4434       BCCellData* cellData =
  4435         static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
  4436                                                       mRowGroupStart,
  4437                                                       mAreaStart.x));
  4438       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  4439         aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
  4440         return;
  4442       else {
  4443         NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
  4444                      "damage area expanded incorrectly");
  4447     SetNewRowGroup(true); // sets mAtEnd
  4451 void
  4452 BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
  4454   if (mAtEnd) ABORT0();
  4455   aMapInfo.ResetCellInfo();
  4457   mIsNewRow = false;
  4458   mColIndex++;
  4459   while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
  4460     for (; mColIndex <= mAreaEnd.x; mColIndex++) {
  4461       int32_t rgRowIndex = mRowIndex - mRowGroupStart;
  4462       BCCellData* cellData =
  4463          static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
  4464       if (!cellData) { // add a dead cell data
  4465         nsIntRect damageArea;
  4466         cellData =
  4467           static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
  4468                                                          rgRowIndex, false, 0,
  4469                                                          damageArea));
  4470         if (!cellData) ABORT0();
  4472       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
  4473         aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
  4474         return;
  4477     if (mRowIndex >= mRowGroupEnd) {
  4478       SetNewRowGroup(false); // could set mAtEnd
  4480     else {
  4481       SetNewRow(); // could set mAtEnd
  4484   mAtEnd = true;
  4487 void
  4488 BCMapCellIterator::PeekRight(BCMapCellInfo&   aRefInfo,
  4489                              uint32_t         aRowIndex,
  4490                              BCMapCellInfo&   aAjaInfo)
  4492   aAjaInfo.ResetCellInfo();
  4493   int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
  4494   uint32_t rgRowIndex = aRowIndex - mRowGroupStart;
  4496   BCCellData* cellData =
  4497     static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
  4498   if (!cellData) { // add a dead cell data
  4499     NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
  4500     nsIntRect damageArea;
  4501     cellData =
  4502       static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr,
  4503                                                      rgRowIndex, false, 0,
  4504                                                      damageArea));
  4505     if (!cellData) ABORT0();
  4507   nsTableRowFrame* row = nullptr;
  4508   if (cellData->IsRowSpan()) {
  4509     rgRowIndex -= cellData->GetRowSpanOffset();
  4510     cellData =
  4511       static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
  4512     if (!cellData)
  4513       ABORT0();
  4515   else {
  4516     row = mRow;
  4518   aAjaInfo.SetInfo(row, colIndex, cellData, this);
  4521 void
  4522 BCMapCellIterator::PeekBottom(BCMapCellInfo&   aRefInfo,
  4523                               uint32_t         aColIndex,
  4524                               BCMapCellInfo&   aAjaInfo)
  4526   aAjaInfo.ResetCellInfo();
  4527   int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
  4528   int32_t rgRowIndex = rowIndex - mRowGroupStart;
  4529   nsTableRowGroupFrame* rg = mRowGroup;
  4530   nsCellMap* cellMap = mCellMap;
  4531   nsTableRowFrame* nextRow = nullptr;
  4532   if (rowIndex > mRowGroupEnd) {
  4533     int32_t nextRgIndex = mRowGroupIndex;
  4534     do {
  4535       nextRgIndex++;
  4536       rg = mRowGroups.SafeElementAt(nextRgIndex);
  4537       if (rg) {
  4538         cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
  4539         rgRowIndex = 0;
  4540         nextRow = rg->GetFirstRow();
  4543     while (rg && !nextRow);
  4544     if(!rg) return;
  4546   else {
  4547     // get the row within the same row group
  4548     nextRow = mRow;
  4549     for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
  4550       nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
  4554   BCCellData* cellData =
  4555     static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
  4556   if (!cellData) { // add a dead cell data
  4557     NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
  4558     nsIntRect damageArea;
  4559     cellData =
  4560       static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr,
  4561                                                     rgRowIndex, false, 0,
  4562                                                     damageArea));
  4563     if (!cellData) ABORT0();
  4565   if (cellData->IsColSpan()) {
  4566     aColIndex -= cellData->GetColSpanOffset();
  4567     cellData =
  4568       static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
  4570   aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
  4573 // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
  4574 // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
  4575 static uint8_t styleToPriority[13] = { 0,  // NS_STYLE_BORDER_STYLE_NONE
  4576                                        2,  // NS_STYLE_BORDER_STYLE_GROOVE
  4577                                        4,  // NS_STYLE_BORDER_STYLE_RIDGE
  4578                                        5,  // NS_STYLE_BORDER_STYLE_DOTTED
  4579                                        6,  // NS_STYLE_BORDER_STYLE_DASHED
  4580                                        7,  // NS_STYLE_BORDER_STYLE_SOLID
  4581                                        8,  // NS_STYLE_BORDER_STYLE_DOUBLE
  4582                                        1,  // NS_STYLE_BORDER_STYLE_INSET
  4583                                        3,  // NS_STYLE_BORDER_STYLE_OUTSET
  4584                                        9 };// NS_STYLE_BORDER_STYLE_HIDDEN
  4585 // priority rules follow CSS 2.1 spec
  4586 // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
  4587 // and the lowest: 'inset'. none is even weaker
  4588 #define CELL_CORNER true
  4590 /** return the border style, border color for a given frame and side
  4591   * @param aFrame           - query the info for this frame
  4592   * @param aSide            - the side of the frame
  4593   * @param aStyle           - the border style
  4594   * @param aColor           - the border color
  4595   * @param aTableIsLTR      - table direction is LTR
  4596   */
  4597 static void
  4598 GetColorAndStyle(const nsIFrame*  aFrame,
  4599                  mozilla::css::Side aSide,
  4600                  uint8_t&         aStyle,
  4601                  nscolor&         aColor,
  4602                  bool             aTableIsLTR)
  4604   NS_PRECONDITION(aFrame, "null frame");
  4605   // initialize out arg
  4606   aColor = 0;
  4607   const nsStyleBorder* styleData = aFrame->StyleBorder();
  4608   if(!aTableIsLTR) { // revert the directions
  4609     if (NS_SIDE_RIGHT == aSide) {
  4610       aSide = NS_SIDE_LEFT;
  4612     else if (NS_SIDE_LEFT == aSide) {
  4613       aSide = NS_SIDE_RIGHT;
  4616   aStyle = styleData->GetBorderStyle(aSide);
  4618   if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
  4619       (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
  4620     return;
  4622   aColor = aFrame->StyleContext()->GetVisitedDependentColor(
  4623              nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]);
  4626 /** coerce the paint style as required by CSS2.1
  4627   * @param aFrame           - query the info for this frame
  4628   * @param aSide            - the side of the frame
  4629   * @param aStyle           - the border style
  4630   * @param aColor           - the border color
  4631   * @param aTableIsLTR      - table direction is LTR
  4632   */
  4633 static void
  4634 GetPaintStyleInfo(const nsIFrame*  aFrame,
  4635                   mozilla::css::Side aSide,
  4636                   uint8_t&         aStyle,
  4637                   nscolor&         aColor,
  4638                   bool             aTableIsLTR)
  4640   GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
  4641   if (NS_STYLE_BORDER_STYLE_INSET    == aStyle) {
  4642     aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
  4644   else if (NS_STYLE_BORDER_STYLE_OUTSET    == aStyle) {
  4645     aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
  4649 /** return the border style, border color and the width in pixel for a given
  4650   * frame and side
  4651   * @param aFrame           - query the info for this frame
  4652   * @param aSide            - the side of the frame
  4653   * @param aStyle           - the border style
  4654   * @param aColor           - the border color
  4655   * @param aTableIsLTR      - table direction is LTR
  4656   * @param aWidth           - the border width in px.
  4657   * @param aTwipsToPixels   - conversion factor from twips to pixel
  4658   */
  4659 static void
  4660 GetColorAndStyle(const nsIFrame*  aFrame,
  4661                  mozilla::css::Side aSide,
  4662                  uint8_t&         aStyle,
  4663                  nscolor&         aColor,
  4664                  bool             aTableIsLTR,
  4665                  BCPixelSize&     aWidth)
  4667   GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
  4668   if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
  4669       (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
  4670     aWidth = 0;
  4671     return;
  4673   const nsStyleBorder* styleData = aFrame->StyleBorder();
  4674   nscoord width;
  4675   if(!aTableIsLTR) { // revert the directions
  4676     if (NS_SIDE_RIGHT == aSide) {
  4677       aSide = NS_SIDE_LEFT;
  4679     else if (NS_SIDE_LEFT == aSide) {
  4680       aSide = NS_SIDE_RIGHT;
  4683   width = styleData->GetComputedBorderWidth(aSide);
  4684   aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
  4687 class nsDelayedCalcBCBorders : public nsRunnable {
  4688 public:
  4689   nsDelayedCalcBCBorders(nsIFrame* aFrame) :
  4690     mFrame(aFrame) {}
  4692   NS_IMETHOD Run() MOZ_OVERRIDE {
  4693     if (mFrame) {
  4694       nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
  4695       if (tableFrame->NeedToCalcBCBorders()) {
  4696         tableFrame->CalcBCBorders();
  4699     return NS_OK;
  4701 private:
  4702   nsWeakFrame mFrame;
  4703 };
  4705 bool
  4706 nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
  4707                              nsStyleContext* aNewStyleContext)
  4709   // Attention: the old style context is the one we're forgetting,
  4710   // and hence possibly completely bogus for GetStyle* purposes.
  4711   // We use PeekStyleData instead.
  4713   const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
  4714   if (!oldStyleData)
  4715     return false;
  4717   const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder();
  4718   nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
  4719   if (!change)
  4720     return false;
  4721   if (change & nsChangeHint_NeedReflow)
  4722     return true; // the caller only needs to mark the bc damage area
  4723   if (change & nsChangeHint_RepaintFrame) {
  4724     // we need to recompute the borders and the caller needs to mark
  4725     // the bc damage area
  4726     // XXX In principle this should only be necessary for border style changes
  4727     // However the bc painting code tries to maximize the drawn border segments
  4728     // so it stores in the cellmap where a new border segment starts and this
  4729     // introduces a unwanted cellmap data dependence on color
  4730     nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
  4731     NS_DispatchToCurrentThread(evt);
  4732     return true;
  4734   return false;
  4738 // Compare two border segments, this comparison depends whether the two
  4739 // segments meet at a corner and whether the second segment is horizontal.
  4740 // The return value is whichever of aBorder1 or aBorder2 dominates.
  4741 static const BCCellBorder&
  4742 CompareBorders(bool                aIsCorner, // Pass true for corner calculations
  4743                const BCCellBorder& aBorder1,
  4744                const BCCellBorder& aBorder2,
  4745                bool                aSecondIsHorizontal,
  4746                bool*             aFirstDominates = nullptr)
  4748   bool firstDominates = true;
  4750   if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
  4751     firstDominates = (aIsCorner) ? false : true;
  4753   else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
  4754     firstDominates = (aIsCorner) ? true : false;
  4756   else if (aBorder1.width < aBorder2.width) {
  4757     firstDominates = false;
  4759   else if (aBorder1.width == aBorder2.width) {
  4760     if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
  4761       firstDominates = false;
  4763     else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
  4764       if (aBorder1.owner == aBorder2.owner) {
  4765         firstDominates = !aSecondIsHorizontal;
  4767       else if (aBorder1.owner < aBorder2.owner) {
  4768         firstDominates = false;
  4773   if (aFirstDominates)
  4774     *aFirstDominates = firstDominates;
  4776   if (firstDominates)
  4777     return aBorder1;
  4778   return aBorder2;
  4781 /** calc the dominant border by considering the table, row/col group, row/col,
  4782   * cell.
  4783   * Depending on whether the side is vertical or horizontal and whether
  4784   * adjacent frames are taken into account the ownership of a single border
  4785   * segment is defined. The return value is the dominating border
  4786   * The cellmap stores only top and left borders for each cellmap position.
  4787   * If the cell border is owned by the cell that is left of the border
  4788   * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
  4789   * scenarios with a adjacent owner.
  4790   * @param xxxFrame         - the frame for style information, might be zero if
  4791   *                           it should not be considered
  4792   * @param aSide            - side of the frames that should be considered
  4793   * @param aAja             - the border comparison takes place from the point of
  4794   *                           a frame that is adjacent to the cellmap entry, for
  4795   *                           when a cell owns its lower border it will be the
  4796   *                           adjacent owner as in the cellmap only top and left
  4797   *                           borders are stored.
  4798   * @param aTwipsToPixels   - conversion factor as borders need to be drawn pixel
  4799   *                           aligned.
  4800   */
  4801 static BCCellBorder
  4802 CompareBorders(const nsIFrame*  aTableFrame,
  4803                const nsIFrame*  aColGroupFrame,
  4804                const nsIFrame*  aColFrame,
  4805                const nsIFrame*  aRowGroupFrame,
  4806                const nsIFrame*  aRowFrame,
  4807                const nsIFrame*  aCellFrame,
  4808                bool             aTableIsLTR,
  4809                mozilla::css::Side aSide,
  4810                bool             aAja)
  4812   BCCellBorder border, tempBorder;
  4813   bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide);
  4815   // start with the table as dominant if present
  4816   if (aTableFrame) {
  4817     GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width);
  4818     border.owner = eTableOwner;
  4819     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4820       return border;
  4823   // see if the colgroup is dominant
  4824   if (aColGroupFrame) {
  4825     GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
  4826     tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner;
  4827     // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations.
  4828     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4829     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4830       return border;
  4833   // see if the col is dominant
  4834   if (aColFrame) {
  4835     GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
  4836     tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner;
  4837     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4838     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4839       return border;
  4842   // see if the rowgroup is dominant
  4843   if (aRowGroupFrame) {
  4844     GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
  4845     tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner;
  4846     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4847     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4848       return border;
  4851   // see if the row is dominant
  4852   if (aRowFrame) {
  4853     GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
  4854     tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner;
  4855     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4856     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
  4857       return border;
  4860   // see if the cell is dominant
  4861   if (aCellFrame) {
  4862     GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
  4863     tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner;
  4864     border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
  4866   return border;
  4869 static bool
  4870 Perpendicular(mozilla::css::Side aSide1,
  4871               mozilla::css::Side aSide2)
  4873   switch (aSide1) {
  4874   case NS_SIDE_TOP:
  4875     return (NS_SIDE_BOTTOM != aSide2);
  4876   case NS_SIDE_RIGHT:
  4877     return (NS_SIDE_LEFT != aSide2);
  4878   case NS_SIDE_BOTTOM:
  4879     return (NS_SIDE_TOP != aSide2);
  4880   default: // NS_SIDE_LEFT
  4881     return (NS_SIDE_RIGHT != aSide2);
  4885 // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
  4886 struct BCCornerInfo
  4888   BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
  4889                    subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP;
  4890                    ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID;  }
  4891   void Set(mozilla::css::Side aSide,
  4892            BCCellBorder  border);
  4894   void Update(mozilla::css::Side aSide,
  4895               BCCellBorder  border);
  4897   nscolor   ownerColor;     // color of borderOwner
  4898   uint16_t  ownerWidth;     // pixel width of borderOwner
  4899   uint16_t  subWidth;       // pixel width of the largest border intersecting the border perpendicular
  4900                             // to ownerSide
  4901   uint32_t  ownerSide:2;    // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border
  4902                             // owning the corner relative to the corner
  4903   uint32_t  ownerElem:3;    // elem type (e.g. eTable, eGroup, etc) owning the corner
  4904   uint32_t  ownerStyle:8;   // border style of ownerElem
  4905   uint32_t  subSide:2;      // side of border with subWidth relative to the corner
  4906   uint32_t  subElem:3;      // elem type (e.g. eTable, eGroup, etc) of sub owner
  4907   uint32_t  subStyle:8;     // border style of subElem
  4908   uint32_t  hasDashDot:1;   // does a dashed, dotted segment enter the corner, they cannot be beveled
  4909   uint32_t  numSegs:3;      // number of segments entering corner
  4910   uint32_t  bevel:1;        // is the corner beveled (uses the above two fields together with subWidth)
  4911   uint32_t  unused:1;
  4912 };
  4914 void
  4915 BCCornerInfo::Set(mozilla::css::Side aSide,
  4916                   BCCellBorder  aBorder)
  4918   ownerElem  = aBorder.owner;
  4919   ownerStyle = aBorder.style;
  4920   ownerWidth = aBorder.width;
  4921   ownerColor = aBorder.color;
  4922   ownerSide  = aSide;
  4923   hasDashDot = 0;
  4924   numSegs    = 0;
  4925   if (aBorder.width > 0) {
  4926     numSegs++;
  4927     hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
  4928                  (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
  4930   bevel      = 0;
  4931   subWidth   = 0;
  4932   // the following will get set later
  4933   subSide    = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT;
  4934   subElem    = eTableOwner;
  4935   subStyle   = NS_STYLE_BORDER_STYLE_SOLID;
  4938 void
  4939 BCCornerInfo::Update(mozilla::css::Side aSide,
  4940                      BCCellBorder  aBorder)
  4942   bool existingWins = false;
  4943   if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
  4944     Set(aSide, aBorder);
  4946   else {
  4947     bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner
  4948     BCCellBorder oldBorder, tempBorder;
  4949     oldBorder.owner  = (BCBorderOwner) ownerElem;
  4950     oldBorder.style =  ownerStyle;
  4951     oldBorder.width =  ownerWidth;
  4952     oldBorder.color =  ownerColor;
  4954     mozilla::css::Side oldSide  = mozilla::css::Side(ownerSide);
  4956     tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins);
  4958     ownerElem  = tempBorder.owner;
  4959     ownerStyle = tempBorder.style;
  4960     ownerWidth = tempBorder.width;
  4961     ownerColor = tempBorder.color;
  4962     if (existingWins) { // existing corner is dominant
  4963       if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) {
  4964         // see if the new sub info replaces the old
  4965         BCCellBorder subBorder;
  4966         subBorder.owner = (BCBorderOwner) subElem;
  4967         subBorder.style =  subStyle;
  4968         subBorder.width =  subWidth;
  4969         subBorder.color = 0; // we are not interested in subBorder color
  4970         bool firstWins;
  4972         tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins);
  4974         subElem  = tempBorder.owner;
  4975         subStyle = tempBorder.style;
  4976         subWidth = tempBorder.width;
  4977         if (!firstWins) {
  4978           subSide = aSide;
  4982     else { // input args are dominant
  4983       ownerSide = aSide;
  4984       if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) {
  4985         subElem  = oldBorder.owner;
  4986         subStyle = oldBorder.style;
  4987         subWidth = oldBorder.width;
  4988         subSide  = oldSide;
  4991     if (aBorder.width > 0) {
  4992       numSegs++;
  4993       if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
  4994                           (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
  4995         hasDashDot = 1;
  4999     // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
  5000     bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
  5004 struct BCCorners
  5006   BCCorners(int32_t aNumCorners,
  5007             int32_t aStartIndex);
  5009   ~BCCorners() { delete [] corners; }
  5011   BCCornerInfo& operator [](int32_t i) const
  5012   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
  5013     return corners[clamped(i, startIndex, endIndex) - startIndex]; }
  5015   int32_t       startIndex;
  5016   int32_t       endIndex;
  5017   BCCornerInfo* corners;
  5018 };
  5020 BCCorners::BCCorners(int32_t aNumCorners,
  5021                      int32_t aStartIndex)
  5023   NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
  5024   startIndex = aStartIndex;
  5025   endIndex   = aStartIndex + aNumCorners - 1;
  5026   corners    = new BCCornerInfo[aNumCorners];
  5030 struct BCCellBorders
  5032   BCCellBorders(int32_t aNumBorders,
  5033                 int32_t aStartIndex);
  5035   ~BCCellBorders() { delete [] borders; }
  5037   BCCellBorder& operator [](int32_t i) const
  5038   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
  5039     return borders[clamped(i, startIndex, endIndex) - startIndex]; }
  5041   int32_t       startIndex;
  5042   int32_t       endIndex;
  5043   BCCellBorder* borders;
  5044 };
  5046 BCCellBorders::BCCellBorders(int32_t aNumBorders,
  5047                              int32_t aStartIndex)
  5049   NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
  5050   startIndex = aStartIndex;
  5051   endIndex   = aStartIndex + aNumBorders - 1;
  5052   borders    = new BCCellBorder[aNumBorders];
  5055 // this function sets the new border properties and returns true if the border
  5056 // segment will start a new segment and not be accumulated into the previous
  5057 // segment.
  5058 static bool
  5059 SetBorder(const BCCellBorder&   aNewBorder,
  5060           BCCellBorder&         aBorder)
  5062   bool changed = (aNewBorder.style != aBorder.style) ||
  5063                    (aNewBorder.width != aBorder.width) ||
  5064                    (aNewBorder.color != aBorder.color);
  5065   aBorder.color        = aNewBorder.color;
  5066   aBorder.width        = aNewBorder.width;
  5067   aBorder.style        = aNewBorder.style;
  5068   aBorder.owner        = aNewBorder.owner;
  5070   return changed;
  5073 // this function will set the horizontal border. It will return true if the
  5074 // existing segment will not be continued. Having a vertical owner of a corner
  5075 // should also start a new segment.
  5076 static bool
  5077 SetHorBorder(const BCCellBorder& aNewBorder,
  5078              const BCCornerInfo& aCorner,
  5079              BCCellBorder&       aBorder)
  5081   bool startSeg = ::SetBorder(aNewBorder, aBorder);
  5082   if (!startSeg) {
  5083     startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide));
  5085   return startSeg;
  5088 // Make the damage area larger on the top and bottom by at least one row and on the left and right
  5089 // at least one column. This is done so that adjacent elements are part of the border calculations.
  5090 // The extra segments and borders outside the actual damage area will not be updated in the cell map,
  5091 // because they in turn would need info from adjacent segments outside the damage area to be accurate.
  5092 void
  5093 nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const
  5095   int32_t numRows = GetRowCount();
  5096   int32_t numCols = GetColCount();
  5098   int32_t dStartX = aRect.x;
  5099   int32_t dEndX   = aRect.XMost() - 1;
  5100   int32_t dStartY = aRect.y;
  5101   int32_t dEndY   = aRect.YMost() - 1;
  5103   // expand the damage area in each direction
  5104   if (dStartX > 0) {
  5105     dStartX--;
  5107   if (dEndX < (numCols - 1)) {
  5108     dEndX++;
  5110   if (dStartY > 0) {
  5111     dStartY--;
  5113   if (dEndY < (numRows - 1)) {
  5114     dEndY++;
  5116   // Check the damage area so that there are no cells spanning in or out. If there are any then
  5117   // make the damage area as big as the table, similarly to the way the cell map decides whether
  5118   // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
  5119   // no spanners, but it may not be worth the effort in general, and it would need to be done in the
  5120   // cell map as well.
  5121   bool haveSpanner = false;
  5122   if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
  5123     nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
  5124     // Get the ordered row groups
  5125     RowGroupArray rowGroups;
  5126     OrderRowGroups(rowGroups);
  5128     // Scope outside loop to be used as hint.
  5129     nsCellMap* cellMap = nullptr;
  5130     for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
  5131       nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
  5132       int32_t rgStartY = rgFrame->GetStartRowIndex();
  5133       int32_t rgEndY   = rgStartY + rgFrame->GetRowCount() - 1;
  5134       if (dEndY < rgStartY)
  5135         break;
  5136       cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
  5137       if (!cellMap) ABORT0();
  5138       // check for spanners from above and below
  5139       if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
  5140         if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length())
  5141           ABORT0();
  5142         const nsCellMap::CellDataArray& row =
  5143           cellMap->mRows[dStartY - rgStartY];
  5144         for (int32_t x = dStartX; x <= dEndX; x++) {
  5145           CellData* cellData = row.SafeElementAt(x);
  5146           if (cellData && (cellData->IsRowSpan())) {
  5147              haveSpanner = true;
  5148              break;
  5151         if (dEndY < rgEndY) {
  5152           if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
  5153             ABORT0();
  5154           const nsCellMap::CellDataArray& row2 =
  5155             cellMap->mRows[dEndY + 1 - rgStartY];
  5156           for (int32_t x = dStartX; x <= dEndX; x++) {
  5157             CellData* cellData = row2.SafeElementAt(x);
  5158             if (cellData && (cellData->IsRowSpan())) {
  5159               haveSpanner = true;
  5160               break;
  5165       // check for spanners on the left and right
  5166       int32_t iterStartY = -1;
  5167       int32_t iterEndY   = -1;
  5168       if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
  5169         // the damage area starts in the row group
  5170         iterStartY = dStartY;
  5171         iterEndY   = std::min(dEndY, rgEndY);
  5173       else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
  5174         // the damage area ends in the row group
  5175         iterStartY = rgStartY;
  5176         iterEndY   = dEndY;
  5178       else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
  5179         // the damage area contains the row group
  5180         iterStartY = rgStartY;
  5181         iterEndY   = rgEndY;
  5183       if ((iterStartY >= 0) && (iterEndY >= 0)) {
  5184         for (int32_t y = iterStartY; y <= iterEndY; y++) {
  5185           if (uint32_t(y - rgStartY) >= cellMap->mRows.Length())
  5186             ABORT0();
  5187           const nsCellMap::CellDataArray& row =
  5188             cellMap->mRows[y - rgStartY];
  5189           CellData* cellData = row.SafeElementAt(dStartX);
  5190           if (cellData && (cellData->IsColSpan())) {
  5191             haveSpanner = true;
  5192             break;
  5194           if (dEndX < (numCols - 1)) {
  5195             cellData = row.SafeElementAt(dEndX + 1);
  5196             if (cellData && (cellData->IsColSpan())) {
  5197               haveSpanner = true;
  5198               break;
  5205   if (haveSpanner) {
  5206     // make the damage area the whole table
  5207     aRect.x      = 0;
  5208     aRect.y      = 0;
  5209     aRect.width  = numCols;
  5210     aRect.height = numRows;
  5212   else {
  5213     aRect.x      = dStartX;
  5214     aRect.y      = dStartY;
  5215     aRect.width  = 1 + dEndX - dStartX;
  5216     aRect.height = 1 + dEndY - dStartY;
  5221 #define ADJACENT    true
  5222 #define HORIZONTAL  true
  5224 void
  5225 BCMapCellInfo::SetTableTopLeftContBCBorder()
  5227   BCCellBorder currentBorder;
  5228   //calculate continuous top first row & rowgroup border: special case
  5229   //because it must include the table in the collapse
  5230   if (mTopRow) {
  5231     currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
  5232                                    mTopRow, nullptr, mTableIsLTR,
  5233                                    NS_SIDE_TOP, !ADJACENT);
  5234     mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
  5236   if (mCgAtRight && mColGroup) {
  5237     //calculate continuous top colgroup border once per colgroup
  5238     currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
  5239                                    mTopRow, nullptr, mTableIsLTR,
  5240                                    NS_SIDE_TOP, !ADJACENT);
  5241     mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
  5243   if (0 == mColIndex) {
  5244     currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr,
  5245                                    nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT,
  5246                                    !ADJACENT);
  5247     mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width);
  5251 void
  5252 BCMapCellInfo::SetRowGroupLeftContBCBorder()
  5254   BCCellBorder currentBorder;
  5255   //get row group continuous borders
  5256   if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom
  5257      currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
  5258                                     nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT,
  5259                                     !ADJACENT);
  5260      mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width);
  5264 void
  5265 BCMapCellInfo::SetRowGroupRightContBCBorder()
  5267   BCCellBorder currentBorder;
  5268   //get row group continuous borders
  5269   if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom
  5270     currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
  5271                                    nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT,
  5272                                    ADJACENT);
  5273     mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width);
  5277 void
  5278 BCMapCellInfo::SetColumnTopRightContBCBorder()
  5280   BCCellBorder currentBorder;
  5281   //calculate column continuous borders
  5282   //we only need to do this once, so we'll do it only on the first row
  5283   currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  5284                                  mCurrentColFrame, mRowGroup, mTopRow, nullptr,
  5285                                  mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
  5286   ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP,
  5287                                                            currentBorder.width);
  5288   if (mNumTableCols == GetCellEndColIndex() + 1) {
  5289     currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  5290                                    mCurrentColFrame, nullptr, nullptr, nullptr,
  5291                                    mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
  5293   else {
  5294     currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame,
  5295                                    mCurrentColFrame, nullptr,nullptr, nullptr,
  5296                                    mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
  5298   mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT,
  5299                                                currentBorder.width);
  5302 void
  5303 BCMapCellInfo::SetColumnBottomContBCBorder()
  5305   BCCellBorder currentBorder;
  5306   //get col continuous border
  5307   currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
  5308                                  mCurrentColFrame, mRowGroup, mBottomRow,
  5309                                  nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
  5310   mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM,
  5311                                                currentBorder.width);
  5314 void
  5315 BCMapCellInfo::SetColGroupBottomContBCBorder()
  5317   BCCellBorder currentBorder;
  5318   if (mColGroup) {
  5319     currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup,
  5320                                    mBottomRow, nullptr, mTableIsLTR,
  5321                                    NS_SIDE_BOTTOM, ADJACENT);
  5322     mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
  5326 void
  5327 BCMapCellInfo::SetRowGroupBottomContBCBorder()
  5329   BCCellBorder currentBorder;
  5330   if (mRowGroup) {
  5331     currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup,
  5332                                    mBottomRow, nullptr, mTableIsLTR,
  5333                                    NS_SIDE_BOTTOM, ADJACENT);
  5334     mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
  5338 void
  5339 BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
  5340                                                   nsTableRowFrame* aNextRow)
  5342   BCCellBorder currentBorder, adjacentBorder;
  5344   const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr;
  5345   currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow,
  5346                                  nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
  5348   adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup,
  5349                                   aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP,
  5350                                   !ADJACENT);
  5351   currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
  5352                                  HORIZONTAL);
  5353   if (aNextRow) {
  5354     aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
  5356   if (mRgAtBottom && mRowGroup) {
  5357     mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
  5361 void
  5362 BCMapCellInfo::SetRowLeftContBCBorder()
  5364   //get row continuous borders
  5365   if (mCurrentRowFrame) {
  5366     BCCellBorder currentBorder;
  5367     currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
  5368                                    mCurrentRowFrame, nullptr, mTableIsLTR,
  5369                                    NS_SIDE_LEFT, !ADJACENT);
  5370     mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide,
  5371                                                  currentBorder.width);
  5375 void
  5376 BCMapCellInfo::SetRowRightContBCBorder()
  5378   if (mCurrentRowFrame) {
  5379     BCCellBorder currentBorder;
  5380     currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
  5381                                    mCurrentRowFrame, nullptr, mTableIsLTR,
  5382                                    NS_SIDE_RIGHT, ADJACENT);
  5383     mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide,
  5384                                                  currentBorder.width);
  5387 void
  5388 BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth)
  5390   mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth);
  5393 void
  5394 BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth)
  5396   // update the left/right first cell border
  5397   if (aRowY == 0) {
  5398     if (mTableIsLTR) {
  5399       mTableBCData->mLeftCellBorderWidth = aWidth;
  5401     else {
  5402       mTableBCData->mRightCellBorderWidth = aWidth;
  5405   mTableBCData->mLeftBorderWidth = std::max(mTableBCData->mLeftBorderWidth,
  5406                                           aWidth);
  5409 void
  5410 BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth)
  5412   // update the left/right first cell border
  5413   if (aRowY == 0) {
  5414     if (mTableIsLTR) {
  5415       mTableBCData->mRightCellBorderWidth = aWidth;
  5417     else {
  5418       mTableBCData->mLeftCellBorderWidth = aWidth;
  5421   mTableBCData->mRightBorderWidth = std::max(mTableBCData->mRightBorderWidth,
  5422                                            aWidth);
  5425 void
  5426 BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth)
  5428    // update the borders of the cells and cols affected
  5429   if (mCell) {
  5430     mCell->SetBorderWidth(mEndSide, std::max(aWidth,
  5431                           mCell->GetBorderWidth(mEndSide)));
  5433   if (mRightCol) {
  5434     BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth);
  5435     mRightCol->SetRightBorderWidth(std::max(nscoord(half),
  5436                                    mRightCol->GetRightBorderWidth()));
  5440 void
  5441 BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth)
  5443   // update the borders of the affected cells and rows
  5444   if (mCell) {
  5445     mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth,
  5446                           mCell->GetBorderWidth(NS_SIDE_BOTTOM)));
  5448   if (mBottomRow) {
  5449     BCPixelSize half = BC_BORDER_TOP_HALF(aWidth);
  5450     mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half),
  5451                                        mBottomRow->GetBottomBCBorderWidth()));
  5454 void
  5455 BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth)
  5457  if (mCell) {
  5458      mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth,
  5459                            mCell->GetBorderWidth(NS_SIDE_TOP)));
  5461   if (mTopRow) {
  5462     BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth);
  5463     mTopRow->SetTopBCBorderWidth(std::max(nscoord(half),
  5464                                         mTopRow->GetTopBCBorderWidth()));
  5467 void
  5468 BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth)
  5470   if (mCell) {
  5471     mCell->SetBorderWidth(mStartSide, std::max(aWidth,
  5472                           mCell->GetBorderWidth(mStartSide)));
  5474   if (mLeftCol) {
  5475     BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth);
  5476     mLeftCol->SetLeftBorderWidth(std::max(nscoord(half),
  5477                                         mLeftCol->GetLeftBorderWidth()));
  5481 void
  5482 BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth)
  5484   mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth,
  5485                                             aWidth);
  5488 void
  5489 BCMapCellInfo::SetColumn(int32_t aColX)
  5491   mCurrentColFrame = mTableFrame->GetColFrame(aColX);
  5492   if (!mCurrentColFrame) {
  5493     NS_ERROR("null mCurrentColFrame");
  5495   mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
  5496                             (mCurrentColFrame->GetParent());
  5497   if (!mCurrentColGroupFrame) {
  5498     NS_ERROR("null mCurrentColGroupFrame");
  5502 void
  5503 BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell)
  5505   mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow :
  5506                                                 mCurrentRowFrame->GetNextRow();
  5509 BCCellBorder
  5510 BCMapCellInfo::GetTopEdgeBorder()
  5512   return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
  5513                         mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP,
  5514                         !ADJACENT);
  5517 BCCellBorder
  5518 BCMapCellInfo::GetBottomEdgeBorder()
  5520   return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
  5521                         mRowGroup, mBottomRow, mCell, mTableIsLTR,
  5522                         NS_SIDE_BOTTOM, ADJACENT);
  5524 BCCellBorder
  5525 BCMapCellInfo::GetLeftEdgeBorder()
  5527   return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
  5528                         mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT,
  5529                         !ADJACENT);
  5531 BCCellBorder
  5532 BCMapCellInfo::GetRightEdgeBorder()
  5534   return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
  5535                         mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT,
  5536                         ADJACENT);
  5538 BCCellBorder
  5539 BCMapCellInfo::GetRightInternalBorder()
  5541   const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr;
  5542   return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell,
  5543                         mTableIsLTR, NS_SIDE_RIGHT, ADJACENT);
  5546 BCCellBorder
  5547 BCMapCellInfo::GetLeftInternalBorder()
  5549   const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr;
  5550   return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell,
  5551                         mTableIsLTR, NS_SIDE_LEFT, !ADJACENT);
  5554 BCCellBorder
  5555 BCMapCellInfo::GetBottomInternalBorder()
  5557   const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr;
  5558   return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell,
  5559                         mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
  5562 BCCellBorder
  5563 BCMapCellInfo::GetTopInternalBorder()
  5565   const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr;
  5566   return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell,
  5567                         mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
  5570 /* Here is the order for storing border edges in the cell map as a cell is processed. There are
  5571    n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
  5573    1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
  5574       a bottom edge from a cell above will take care of it.
  5575    2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
  5576       a right edge from a cell to the left will take care of it.
  5577    3) Store the right edge (or edges if a row span)
  5578    4) Store the bottom edge (or edges if a col span)
  5580    Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
  5581    calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
  5582    would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
  5583    borders already have about an extra 8 byte per cell map entry overhead (this could be
  5584    reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
  5585    rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
  5586    left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
  5587    edges per cell and n=rowspan left and right border edges per cell.
  5589    1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
  5590       Never store the top-right corner, since it will get stored as a right-top corner.
  5591    2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
  5592       since it will get stored as a bottom-left corner.
  5593    3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
  5594       the top edge of the table. Never store the right-bottom corner since it will get stored as a
  5595       bottom-right corner.
  5596    4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
  5597       otherwise, since it will get stored as either a right-top corner by a cell below or
  5598       a bottom-left corner from a cell to the right.
  5599    5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
  5600       the top side of a colspan in its interior. Never store the corner otherwise, since it will
  5601       get stored as a right-top corner by a cell from below.
  5603    XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
  5604    In order to draw borders in rtl conditions somehow correct, the existing structure which relies
  5605    heavily on the assumption that the next cell sibling will be on the right side, has been modified.
  5606    We flip the border during painting and during style lookup. Look for tableIsLTR for places where
  5607    the flipping is done.
  5608  */
  5612 // Calc the dominant border at every cell edge and corner within the current damage area
  5613 void
  5614 nsTableFrame::CalcBCBorders()
  5616   NS_ASSERTION(IsBorderCollapse(),
  5617                "calling CalcBCBorders on separated-border table");
  5618   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
  5619   int32_t numRows = GetRowCount();
  5620   int32_t numCols = GetColCount();
  5621   if (!numRows || !numCols)
  5622     return; // nothing to do
  5624   // Get the property holding the table damage area and border widths
  5625   BCPropertyData* propData = GetBCProperty();
  5626   if (!propData) ABORT0();
  5630   // calculate an expanded damage area
  5631   nsIntRect damageArea(propData->mDamageArea);
  5632   ExpandBCDamageArea(damageArea);
  5634   // segments that are on the table border edges need
  5635   // to be initialized only once
  5636   bool tableBorderReset[4];
  5637   for (uint32_t sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) {
  5638     tableBorderReset[sideX] = false;
  5641   // vertical borders indexed in x-direction (cols)
  5642   BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x);
  5643   if (!lastVerBorders.borders) ABORT0();
  5644   BCCellBorder  lastTopBorder, lastBottomBorder;
  5645   // horizontal borders indexed in x-direction (cols)
  5646   BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x);
  5647   if (!lastBottomBorders.borders) ABORT0();
  5648   bool startSeg;
  5649   bool gotRowBorder = false;
  5651   BCMapCellInfo  info(this), ajaInfo(this);
  5653   BCCellBorder currentBorder, adjacentBorder;
  5654   BCCorners topCorners(damageArea.width + 1, damageArea.x);
  5655   if (!topCorners.corners) ABORT0();
  5656   BCCorners bottomCorners(damageArea.width + 1, damageArea.x);
  5657   if (!bottomCorners.corners) ABORT0();
  5659   BCMapCellIterator iter(this, damageArea);
  5660   for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
  5661     // see if lastTopBorder, lastBottomBorder need to be reset
  5662     if (iter.IsNewRow()) {
  5663       gotRowBorder = false;
  5664       lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
  5665       lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
  5667     else if (info.mColIndex > damageArea.x) {
  5668       lastBottomBorder = lastBottomBorders[info.mColIndex - 1];
  5669       if (info.mRowIndex >
  5670           (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) {
  5671         // the top border's left edge butts against the middle of a rowspan
  5672         lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
  5674       if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
  5675         // the bottom border's left edge butts against the middle of a rowspan
  5676         lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
  5680     // find the dominant border considering the cell's top border and the table,
  5681     // row group, row if the border is at the top of the table, otherwise it was
  5682     // processed in a previous row
  5683     if (0 == info.mRowIndex) {
  5684       if (!tableBorderReset[NS_SIDE_TOP]) {
  5685         propData->mTopBorderWidth = 0;
  5686         tableBorderReset[NS_SIDE_TOP] = true;
  5688       for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
  5689            colX++) {
  5690         info.SetColumn(colX);
  5691         currentBorder = info.GetTopEdgeBorder();
  5692         // update/store the top left & top right corners of the seg
  5693         BCCornerInfo& tlCorner = topCorners[colX]; // top left
  5694         if (0 == colX) {
  5695           // we are on right hand side of the corner
  5696           tlCorner.Set(NS_SIDE_RIGHT, currentBorder);
  5698         else {
  5699           tlCorner.Update(NS_SIDE_RIGHT, currentBorder);
  5700           tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX,
  5701                                           mozilla::css::Side(tlCorner.ownerSide),
  5702                                           tlCorner.subWidth,
  5703                                           tlCorner.bevel);
  5705         topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right
  5706         // update lastTopBorder and see if a new segment starts
  5707         startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder);
  5708         // store the border segment in the cell map
  5709         tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX,
  5710                                       1, currentBorder.owner,
  5711                                       currentBorder.width, startSeg);
  5713         info.SetTableTopBorderWidth(currentBorder.width);
  5714         info.SetTopBorderWidths(currentBorder.width);
  5715         info.SetColumnTopRightContBCBorder();
  5717       info.SetTableTopLeftContBCBorder();
  5719     else {
  5720       // see if the top border needs to be the start of a segment due to a
  5721       // vertical border owning the corner
  5722       if (info.mColIndex > 0) {
  5723         BCData& data = info.mCellData->mData;
  5724         if (!data.IsTopStart()) {
  5725           mozilla::css::Side cornerSide;
  5726           bool bevel;
  5727           data.GetCorner(cornerSide, bevel);
  5728           if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) {
  5729             data.SetTopStart(true);
  5735     // find the dominant border considering the cell's left border and the
  5736     // table, col group, col if the border is at the left of the table,
  5737     // otherwise it was processed in a previous col
  5738     if (0 == info.mColIndex) {
  5739       if (!tableBorderReset[NS_SIDE_LEFT]) {
  5740         propData->mLeftBorderWidth = 0;
  5741         tableBorderReset[NS_SIDE_LEFT] = true;
  5743       info.mCurrentRowFrame = nullptr;
  5744       for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
  5745            rowY++) {
  5746         info.IncrementRow(rowY == info.mRowIndex);
  5747         currentBorder = info.GetLeftEdgeBorder();
  5748         BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0];
  5749         tlCorner.Update(NS_SIDE_BOTTOM, currentBorder);
  5750         tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap,
  5751                                         iter.mRowGroupStart, rowY, 0,
  5752                                         mozilla::css::Side(tlCorner.ownerSide),
  5753                                         tlCorner.subWidth,
  5754                                         tlCorner.bevel);
  5755         bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left
  5757         // update lastVerBordersBorder and see if a new segment starts
  5758         startSeg = SetBorder(currentBorder, lastVerBorders[0]);
  5759         // store the border segment in the cell map
  5760         tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap,
  5761                                       iter.mRowGroupStart, rowY, info.mColIndex,
  5762                                       1, currentBorder.owner,
  5763                                       currentBorder.width, startSeg);
  5764         info.SetTableLeftBorderWidth(rowY , currentBorder.width);
  5765         info.SetLeftBorderWidths(currentBorder.width);
  5766         info.SetRowLeftContBCBorder();
  5768       info.SetRowGroupLeftContBCBorder();
  5771     // find the dominant border considering the cell's right border, adjacent
  5772     // cells and the table, row group, row
  5773     if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
  5774       // touches right edge of table
  5775       if (!tableBorderReset[NS_SIDE_RIGHT]) {
  5776         propData->mRightBorderWidth = 0;
  5777         tableBorderReset[NS_SIDE_RIGHT] = true;
  5779       info.mCurrentRowFrame = nullptr;
  5780       for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
  5781            rowY++) {
  5782         info.IncrementRow(rowY == info.mRowIndex);
  5783         currentBorder = info.GetRightEdgeBorder();
  5784         // update/store the top right & bottom right corners
  5785         BCCornerInfo& trCorner = (0 == rowY) ?
  5786                                  topCorners[info.GetCellEndColIndex() + 1] :
  5787                                  bottomCorners[info.GetCellEndColIndex() + 1];
  5788         trCorner.Update(NS_SIDE_BOTTOM, currentBorder);   // top right
  5789         tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
  5790                                         iter.mRowGroupStart, rowY,
  5791                                         info.GetCellEndColIndex(),
  5792                                         mozilla::css::Side(trCorner.ownerSide),
  5793                                         trCorner.subWidth,
  5794                                         trCorner.bevel);
  5795         BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1];
  5796         brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right
  5797         tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
  5798                                         iter.mRowGroupStart, rowY,
  5799                                         info.GetCellEndColIndex(),
  5800                                         mozilla::css::Side(brCorner.ownerSide),
  5801                                         brCorner.subWidth,
  5802                                         brCorner.bevel);
  5803         // update lastVerBorders and see if a new segment starts
  5804         startSeg = SetBorder(currentBorder,
  5805                              lastVerBorders[info.GetCellEndColIndex() + 1]);
  5806         // store the border segment in the cell map and update cellBorders
  5807         tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
  5808                                       iter.mRowGroupStart, rowY,
  5809                                       info.GetCellEndColIndex(), 1,
  5810                                       currentBorder.owner, currentBorder.width,
  5811                                       startSeg);
  5812         info.SetTableRightBorderWidth(rowY, currentBorder.width);
  5813         info.SetRightBorderWidths(currentBorder.width);
  5814         info.SetRowRightContBCBorder();
  5816       info.SetRowGroupRightContBCBorder();
  5818     else {
  5819       int32_t segLength = 0;
  5820       BCMapCellInfo priorAjaInfo(this);
  5821       for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
  5822            rowY += segLength) {
  5823         iter.PeekRight(info, rowY, ajaInfo);
  5824         currentBorder  = info.GetRightInternalBorder();
  5825         adjacentBorder = ajaInfo.GetLeftInternalBorder();
  5826         currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5827                                         adjacentBorder, !HORIZONTAL);
  5829         segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY);
  5830         segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowY);
  5832         // update lastVerBorders and see if a new segment starts
  5833         startSeg = SetBorder(currentBorder,
  5834                              lastVerBorders[info.GetCellEndColIndex() + 1]);
  5835         // store the border segment in the cell map and update cellBorders
  5836         if (info.GetCellEndColIndex() < damageArea.XMost() &&
  5837             rowY >= damageArea.y && rowY < damageArea.YMost()) {
  5838           tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
  5839                                         iter.mRowGroupStart, rowY,
  5840                                         info.GetCellEndColIndex(), segLength,
  5841                                         currentBorder.owner,
  5842                                         currentBorder.width, startSeg);
  5843           info.SetRightBorderWidths(currentBorder.width);
  5844           ajaInfo.SetLeftBorderWidths(currentBorder.width);
  5846         // update the top right corner
  5847         bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) &&
  5848                                   (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
  5849         BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ?
  5850                                  &topCorners[info.GetCellEndColIndex() + 1] :
  5851                                  &bottomCorners[info.GetCellEndColIndex() + 1];
  5852         trCorner->Update(NS_SIDE_BOTTOM, currentBorder);
  5853         // if this is not the first time through,
  5854         // consider the segment to the right
  5855         if (rowY != info.mRowIndex) {
  5856           currentBorder  = priorAjaInfo.GetBottomInternalBorder();
  5857           adjacentBorder = ajaInfo.GetTopInternalBorder();
  5858           currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5859                                           adjacentBorder, HORIZONTAL);
  5860           trCorner->Update(NS_SIDE_RIGHT, currentBorder);
  5862         // store the top right corner in the cell map
  5863         if (info.GetCellEndColIndex() < damageArea.XMost() &&
  5864             rowY >= damageArea.y) {
  5865           if (0 != rowY) {
  5866             tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
  5867                                             iter.mRowGroupStart, rowY,
  5868                                             info.GetCellEndColIndex(),
  5869                                             mozilla::css::Side(trCorner->ownerSide),
  5870                                             trCorner->subWidth,
  5871                                             trCorner->bevel);
  5873           // store any corners this cell spans together with the aja cell
  5874           for (int32_t rX = rowY + 1; rX < rowY + segLength; rX++) {
  5875             tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
  5876                                             iter.mRowGroupStart, rX,
  5877                                             info.GetCellEndColIndex(),
  5878                                             mozilla::css::Side(trCorner->ownerSide),
  5879                                             trCorner->subWidth, false);
  5882         // update bottom right corner, topCorners, bottomCorners
  5883         hitsSpanOnRight = (rowY + segLength <
  5884                            ajaInfo.mRowIndex + ajaInfo.mRowSpan);
  5885         BCCornerInfo& brCorner = (hitsSpanOnRight) ?
  5886                                  topCorners[info.GetCellEndColIndex() + 1] :
  5887                                  bottomCorners[info.GetCellEndColIndex() + 1];
  5888         brCorner.Set(NS_SIDE_TOP, currentBorder);
  5889         priorAjaInfo = ajaInfo;
  5892     for (int32_t colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex();
  5893          colX++) {
  5894       lastVerBorders[colX].Reset(0,1);
  5897     // find the dominant border considering the cell's bottom border, adjacent
  5898     // cells and the table, row group, row
  5899     if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
  5900       // touches bottom edge of table
  5901       if (!tableBorderReset[NS_SIDE_BOTTOM]) {
  5902         propData->mBottomBorderWidth = 0;
  5903         tableBorderReset[NS_SIDE_BOTTOM] = true;
  5905       for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
  5906            colX++) {
  5907         info.SetColumn(colX);
  5908         currentBorder = info.GetBottomEdgeBorder();
  5909         // update/store the bottom left & bottom right corners
  5910         BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
  5911         blCorner.Update(NS_SIDE_RIGHT, currentBorder);
  5912         tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
  5913                                         iter.mRowGroupStart,
  5914                                         info.GetCellEndRowIndex(),
  5915                                         colX,
  5916                                         mozilla::css::Side(blCorner.ownerSide),
  5917                                         blCorner.subWidth, blCorner.bevel);
  5918         BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right
  5919         brCorner.Update(NS_SIDE_LEFT, currentBorder);
  5920         if (info.mNumTableCols == colX + 1) { // lower right corner of the table
  5921           tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
  5922                                           iter.mRowGroupStart,
  5923                                           info.GetCellEndRowIndex(),colX,
  5924                                           mozilla::css::Side(brCorner.ownerSide),
  5925                                           brCorner.subWidth,
  5926                                           brCorner.bevel, true);
  5928         // update lastBottomBorder and see if a new segment starts
  5929         startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
  5930         if (!startSeg) {
  5931            // make sure that we did not compare apples to oranges i.e. the
  5932            // current border should be a continuation of the lastBottomBorder,
  5933            // as it is a bottom border
  5934            // add 1 to the info.GetCellEndRowIndex()
  5935            startSeg = (lastBottomBorder.rowIndex !=
  5936                        (info.GetCellEndRowIndex() + 1));
  5938         // store the border segment in the cell map and update cellBorders
  5939         tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
  5940                                       iter.mRowGroupStart,
  5941                                       info.GetCellEndRowIndex(),
  5942                                       colX, 1, currentBorder.owner,
  5943                                       currentBorder.width, startSeg);
  5944         // update lastBottomBorders
  5945         lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
  5946         lastBottomBorder.rowSpan = info.mRowSpan;
  5947         lastBottomBorders[colX] = lastBottomBorder;
  5949         info.SetBottomBorderWidths(currentBorder.width);
  5950         info.SetTableBottomBorderWidth(currentBorder.width);
  5951         info.SetColumnBottomContBCBorder();
  5953       info.SetRowGroupBottomContBCBorder();
  5954       info.SetColGroupBottomContBCBorder();
  5956     else {
  5957       int32_t segLength = 0;
  5958       for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex();
  5959            colX += segLength) {
  5960         iter.PeekBottom(info, colX, ajaInfo);
  5961         currentBorder  = info.GetBottomInternalBorder();
  5962         adjacentBorder = ajaInfo.GetTopInternalBorder();
  5963         currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
  5964                                         adjacentBorder, HORIZONTAL);
  5965         segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX);
  5966         segLength = std::min(segLength, info.mColIndex + info.mColSpan - colX);
  5968         // update, store the bottom left corner
  5969         BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
  5970         bool hitsSpanBelow = (colX > ajaInfo.mColIndex) &&
  5971                                (colX < ajaInfo.mColIndex + ajaInfo.mColSpan);
  5972         bool update = true;
  5973         if ((colX == info.mColIndex) && (colX > damageArea.x)) {
  5974           int32_t prevRowIndex = lastBottomBorders[colX - 1].rowIndex;
  5975           if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
  5976             // hits a rowspan on the right
  5977             update = false;
  5978             // the corner was taken care of during the cell on the left
  5980           else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
  5981             // spans below the cell to the left
  5982             topCorners[colX] = blCorner;
  5983             blCorner.Set(NS_SIDE_RIGHT, currentBorder);
  5984             update = false;
  5987         if (update) {
  5988           blCorner.Update(NS_SIDE_RIGHT, currentBorder);
  5990         if (info.GetCellEndRowIndex() < damageArea.YMost() &&
  5991             (colX >= damageArea.x)) {
  5992           if (hitsSpanBelow) {
  5993             tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
  5994                                             iter.mRowGroupStart,
  5995                                             info.GetCellEndRowIndex(), colX,
  5996                                             mozilla::css::Side(blCorner.ownerSide),
  5997                                             blCorner.subWidth, blCorner.bevel);
  5999           // store any corners this cell spans together with the aja cell
  6000           for (int32_t cX = colX + 1; cX < colX + segLength; cX++) {
  6001             BCCornerInfo& corner = bottomCorners[cX];
  6002             corner.Set(NS_SIDE_RIGHT, currentBorder);
  6003             tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
  6004                                             iter.mRowGroupStart,
  6005                                             info.GetCellEndRowIndex(), cX,
  6006                                             mozilla::css::Side(corner.ownerSide),
  6007                                             corner.subWidth,
  6008                                             false);
  6011         // update lastBottomBorders and see if a new segment starts
  6012         startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
  6013         if (!startSeg) {
  6014            // make sure that we did not compare apples to oranges i.e. the
  6015            // current border should be a continuation of the lastBottomBorder,
  6016            // as it is a bottom border
  6017            // add 1 to the info.GetCellEndRowIndex()
  6018            startSeg = (lastBottomBorder.rowIndex !=
  6019                        info.GetCellEndRowIndex() + 1);
  6021         lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
  6022         lastBottomBorder.rowSpan = info.mRowSpan;
  6023         for (int32_t cX = colX; cX < colX + segLength; cX++) {
  6024           lastBottomBorders[cX] = lastBottomBorder;
  6027         // store the border segment the cell map and update cellBorders
  6028         if (info.GetCellEndRowIndex() < damageArea.YMost() &&
  6029             (colX >= damageArea.x) &&
  6030             (colX < damageArea.XMost())) {
  6031           tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
  6032                                         iter.mRowGroupStart,
  6033                                         info.GetCellEndRowIndex(),
  6034                                         colX, segLength, currentBorder.owner,
  6035                                         currentBorder.width, startSeg);
  6036           info.SetBottomBorderWidths(currentBorder.width);
  6037           ajaInfo.SetTopBorderWidths(currentBorder.width);
  6039         // update bottom right corner
  6040         BCCornerInfo& brCorner = bottomCorners[colX + segLength];
  6041         brCorner.Update(NS_SIDE_LEFT, currentBorder);
  6043       if (!gotRowBorder && 1 == info.mRowSpan &&
  6044           (ajaInfo.mTopRow || info.mRgAtBottom)) {
  6045         //get continuous row/row group border
  6046         //we need to check the row group's bottom border if this is
  6047         //the last row in the row group, but only a cell with rowspan=1
  6048         //will know whether *this* row is at the bottom
  6049         const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup :
  6050                                                              nullptr;
  6051         info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow);
  6052         gotRowBorder = true;
  6056     // see if the cell to the right had a rowspan and its lower left border
  6057     // needs be joined with this one's bottom
  6058     // if  there is a cell to the right and the cell to right was a rowspan
  6059     if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
  6060         (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
  6061       BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1];
  6062       if ((NS_SIDE_TOP != corner.ownerSide) &&
  6063           (NS_SIDE_BOTTOM != corner.ownerSide)) {
  6064         // not a vertical owner
  6065         BCCellBorder& thisBorder = lastBottomBorder;
  6066         BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1];
  6067         if ((thisBorder.color == nextBorder.color) &&
  6068             (thisBorder.width == nextBorder.width) &&
  6069             (thisBorder.style == nextBorder.style)) {
  6070           // set the flag on the next border indicating it is not the start of a
  6071           // new segment
  6072           if (iter.mCellMap) {
  6073             tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap,
  6074                                         info.GetCellEndRowIndex(),
  6075                                         info.GetCellEndColIndex() + 1);
  6080   } // for (iter.First(info); info.mCell; iter.Next(info)) {
  6081   // reset the bc flag and damage area
  6082   SetNeedToCalcBCBorders(false);
  6083   propData->mDamageArea = nsIntRect(0,0,0,0);
  6084 #ifdef DEBUG_TABLE_CELLMAP
  6085   mCellMap->Dump();
  6086 #endif
  6089 class BCPaintBorderIterator;
  6091 struct BCVerticalSeg
  6093   BCVerticalSeg();
  6095   void Start(BCPaintBorderIterator& aIter,
  6096              BCBorderOwner          aBorderOwner,
  6097              BCPixelSize            aVerSegWidth,
  6098              BCPixelSize            aHorSegHeight);
  6100   void Initialize(BCPaintBorderIterator& aIter);
  6101   void GetBottomCorner(BCPaintBorderIterator& aIter,
  6102                        BCPixelSize            aHorSegHeight);
  6105    void Paint(BCPaintBorderIterator& aIter,
  6106               nsRenderingContext&   aRenderingContext,
  6107               BCPixelSize            aHorSegHeight);
  6108   void AdvanceOffsetY();
  6109   void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
  6112   union {
  6113     nsTableColFrame*  mCol;
  6114     int32_t           mColWidth;
  6115   };
  6116   nscoord               mOffsetX;    // x-offset with respect to the table edge
  6117   nscoord               mOffsetY;    // y-offset with respect to the table edge
  6118   nscoord               mLength;     // vertical length including corners
  6119   BCPixelSize           mWidth;      // width in pixels
  6121   nsTableCellFrame*     mAjaCell;       // previous sibling to the first cell
  6122                                         // where the segment starts, it can be
  6123                                         // the owner of a segment
  6124   nsTableCellFrame*     mFirstCell;     // cell at the start of the segment
  6125   nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
  6126   nsTableRowFrame*      mFirstRow;      // row at the start of the segment
  6127   nsTableCellFrame*     mLastCell;      // cell at the current end of the
  6128                                         // segment
  6131   uint8_t               mOwner;         // owner of the border, defines the
  6132                                         // style
  6133   mozilla::css::Side    mTopBevelSide;  // direction to bevel at the top
  6134   nscoord               mTopBevelOffset; // how much to bevel at the top
  6135   BCPixelSize           mBottomHorSegHeight; // height of the crossing
  6136                                         //horizontal border
  6137   nscoord               mBottomOffset;  // how much longer is the segment due
  6138                                         // to the horizontal border, by this
  6139                                         // amount the next segment needs to be
  6140                                         // shifted.
  6141   bool                  mIsBottomBevel; // should we bevel at the bottom
  6142 };
  6144 struct BCHorizontalSeg
  6146   BCHorizontalSeg();
  6148   void Start(BCPaintBorderIterator& aIter,
  6149              BCBorderOwner          aBorderOwner,
  6150              BCPixelSize            aBottomVerSegWidth,
  6151              BCPixelSize            aHorSegHeight);
  6152    void GetRightCorner(BCPaintBorderIterator& aIter,
  6153                        BCPixelSize            aLeftSegWidth);
  6154    void AdvanceOffsetX(int32_t aIncrement);
  6155    void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
  6156    void Paint(BCPaintBorderIterator& aIter,
  6157               nsRenderingContext&   aRenderingContext);
  6159   nscoord            mOffsetX;       // x-offset with respect to the table edge
  6160   nscoord            mOffsetY;       // y-offset with respect to the table edge
  6161   nscoord            mLength;        // horizontal length including corners
  6162   BCPixelSize        mWidth;         // border width in pixels
  6163   nscoord            mLeftBevelOffset;   // how much to bevel at the left
  6164   mozilla::css::Side mLeftBevelSide;     // direction to bevel at the left
  6165   bool               mIsRightBevel;      // should we bevel at the right end
  6166   nscoord            mRightBevelOffset;  // how much to bevel at the right
  6167   mozilla::css::Side mRightBevelSide;    // direction to bevel at the right
  6168   nscoord            mEndOffset;         // how much longer is the segment due
  6169                                          // to the vertical border, by this
  6170                                          // amount the next segment needs to be
  6171                                          // shifted.
  6172   uint8_t            mOwner;             // owner of the border, defines the
  6173                                          // style
  6174   nsTableCellFrame*  mFirstCell;         // cell at the start of the segment
  6175   nsTableCellFrame*  mAjaCell;           // neighboring cell to the first cell
  6176                                          // where the segment starts, it can be
  6177                                          // the owner of a segment
  6178 };
  6180 // Iterates over borders (left border, corner, top border) in the cell map within a damage area
  6181 // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except
  6182 // where suffixed by InFlow.
  6183 class BCPaintBorderIterator
  6185 public:
  6188   BCPaintBorderIterator(nsTableFrame* aTable);
  6189   ~BCPaintBorderIterator() { if (mVerInfo) {
  6190                               delete [] mVerInfo;
  6191                            }}
  6192   void Reset();
  6194   /**
  6195    * Determine the damage area in terms of rows and columns and finalize
  6196    * mInitialOffsetX and mInitialOffsetY.
  6197    * @param aDirtyRect - dirty rect in table coordinates
  6198    * @return - true if we need to paint something given dirty rect
  6199    */
  6200   bool SetDamageArea(const nsRect& aDamageRect);
  6201   void First();
  6202   void Next();
  6203   void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext);
  6204   void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext);
  6205   void ResetVerInfo();
  6206   void StoreColumnWidth(int32_t aIndex);
  6207   bool VerticalSegmentOwnsCorner();
  6209   nsTableFrame*         mTable;
  6210   nsTableFrame*         mTableFirstInFlow;
  6211   nsTableCellMap*       mTableCellMap;
  6212   nsCellMap*            mCellMap;
  6213   bool                  mTableIsLTR;
  6214   int32_t               mColInc;            // +1 for ltr -1 for rtl
  6215   const nsStyleBackground* mTableBgColor;
  6216   nsTableFrame::RowGroupArray mRowGroups;
  6218   nsTableRowGroupFrame* mPrevRg;
  6219   nsTableRowGroupFrame* mRg;
  6220   bool                  mIsRepeatedHeader;
  6221   bool                  mIsRepeatedFooter;
  6222   nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
  6223   int32_t               mRgIndex; // current row group index in the
  6224                                         // mRowgroups array
  6225   int32_t               mFifRgFirstRowIndex; // start row index of the first in
  6226                                            // flow of the row group
  6227   int32_t               mRgFirstRowIndex; // row index of the first row in the
  6228                                           // row group
  6229   int32_t               mRgLastRowIndex; // row index of the last row in the row
  6230                                          // group
  6231   int32_t               mNumTableRows;   // number of rows in the table and all
  6232                                          // continuations
  6233   int32_t               mNumTableCols;   // number of columns in the table
  6234   int32_t               mColIndex;       // with respect to the table
  6235   int32_t               mRowIndex;       // with respect to the table
  6236   int32_t               mRepeatedHeaderRowIndex; // row index in a repeated
  6237                                             //header, it's equivalent to
  6238                                             // mRowIndex when we're in a repeated
  6239                                             // header, and set to the last row
  6240                                             // index of a repeated header when
  6241                                             // we're not
  6242   bool                  mIsNewRow;
  6243   bool                  mAtEnd;             // the iterator cycled over all
  6244                                              // borders
  6245   nsTableRowFrame*      mPrevRow;
  6246   nsTableRowFrame*      mRow;
  6247   nsTableRowFrame*      mStartRow;    //first row in a inside the damagearea
  6250   // cell properties
  6251   nsTableCellFrame*     mPrevCell;
  6252   nsTableCellFrame*     mCell;
  6253   BCCellData*           mPrevCellData;
  6254   BCCellData*           mCellData;
  6255   BCData*               mBCData;
  6257   bool                  IsTableTopMost()    {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
  6258   bool                  IsTableRightMost()  {return (mColIndex >= mNumTableCols);}
  6259   bool                  IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
  6260   bool                  IsTableLeftMost()   {return (mColIndex == 0);}
  6261   bool                  IsDamageAreaTopMost()    {return (mRowIndex == mDamageArea.y);}
  6262   bool                  IsDamageAreaRightMost()  {return (mColIndex >= mDamageArea.XMost());}
  6263   bool                  IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());}
  6264   bool                  IsDamageAreaLeftMost()   {return (mColIndex == mDamageArea.x);}
  6265   int32_t               GetRelativeColIndex() {return (mColIndex - mDamageArea.x);}
  6267   nsIntRect             mDamageArea;        // damageArea in cellmap coordinates
  6268   bool                  IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));}
  6269   bool                  StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);}
  6270   nscoord               mInitialOffsetX;  // offsetX of the first border with
  6271                                             // respect to the table
  6272   nscoord               mInitialOffsetY;    // offsetY of the first border with
  6273                                             // respect to the table
  6274   nscoord               mNextOffsetY;       // offsetY of the next segment
  6275   BCVerticalSeg*        mVerInfo; // this array is used differently when
  6276                                   // horizontal and vertical borders are drawn
  6277                                   // When horizontal border are drawn we cache
  6278                                   // the column widths and the width of the
  6279                                   // vertical borders that arrive from top
  6280                                   // When we draw vertical borders we store
  6281                                   // lengths and width for vertical borders
  6282                                   // before they are drawn while we  move over
  6283                                   // the columns in the damage area
  6284                                   // It has one more elements than columns are
  6285                                   //in the table.
  6286   BCHorizontalSeg       mHorSeg;            // the horizontal segment while we
  6287                                             // move over the colums
  6288   BCPixelSize           mPrevHorSegHeight;  // the height of the previous
  6289                                             // horizontal border
  6291 private:
  6293   bool SetNewRow(nsTableRowFrame* aRow = nullptr);
  6294   bool SetNewRowGroup();
  6295   void   SetNewData(int32_t aRowIndex, int32_t aColIndex);
  6297 };
  6301 BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
  6303   mTable      = aTable;
  6304   mVerInfo    = nullptr;
  6305   nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
  6306   mTableFirstInFlow    = static_cast<nsTableFrame*>(mTable->FirstInFlow());
  6307   mTableCellMap        = mTable->GetCellMap();
  6308   // y position of first row in damage area
  6309   mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top;
  6310   mNumTableRows  = mTable->GetRowCount();
  6311   mNumTableCols  = mTable->GetColCount();
  6313   // Get the ordered row groups
  6314   mTable->OrderRowGroups(mRowGroups);
  6315   // initialize to a non existing index
  6316   mRepeatedHeaderRowIndex = -99;
  6318   mTableIsLTR = mTable->StyleVisibility()->mDirection ==
  6319                    NS_STYLE_DIRECTION_LTR;
  6320   mColInc = (mTableIsLTR) ? 1 : -1;
  6322   nsIFrame* bgFrame =
  6323     nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
  6324   mTableBgColor = bgFrame->StyleBackground();
  6327 bool
  6328 BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
  6331   uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex;
  6332   startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
  6333   bool done = false;
  6334   bool haveIntersect = false;
  6335   // find startRowIndex, endRowIndex
  6336   nscoord rowY = mInitialOffsetY;
  6337   for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) {
  6338     nsTableRowGroupFrame* rgFrame = mRowGroups[rgX];
  6339     for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
  6340          rowFrame = rowFrame->GetNextRow()) {
  6341       // conservatively estimate the half border widths outside the row
  6342       nscoord topBorderHalf    = (mTable->GetPrevInFlow()) ? 0 :
  6343        nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1);
  6344       nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 :
  6345         nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1);
  6346       // get the row rect relative to the table rather than the row group
  6347       nsSize rowSize = rowFrame->GetSize();
  6348       if (haveIntersect) {
  6349         if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) {
  6350           nsTableRowFrame* fifRow =
  6351             static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
  6352           endRowIndex = fifRow->GetRowIndex();
  6354         else done = true;
  6356       else {
  6357         if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) {
  6358           mStartRg  = rgFrame;
  6359           mStartRow = rowFrame;
  6360           nsTableRowFrame* fifRow =
  6361             static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow());
  6362           startRowIndex = endRowIndex = fifRow->GetRowIndex();
  6363           haveIntersect = true;
  6365         else {
  6366           mInitialOffsetY += rowSize.height;
  6369       rowY += rowSize.height;
  6372   mNextOffsetY = mInitialOffsetY;
  6374   // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
  6375   // XXX but I don't understand it, so not changing it for now
  6376   // outer table borders overflow the table, so the table might be
  6377   // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
  6378   // on the table
  6379   if (!haveIntersect)
  6380     return false;
  6381   // find startColIndex, endColIndex, startColX
  6382   haveIntersect = false;
  6383   if (0 == mNumTableCols)
  6384     return false;
  6385   int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol)
  6387   nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
  6388   if (mTableIsLTR) {
  6389     mInitialOffsetX = childAreaOffset.left; // x position of first col in
  6390                                             // damage area
  6391     leftCol = 0;
  6392     rightCol = mNumTableCols;
  6393   } else {
  6394     // x position of first col in damage area
  6395     mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
  6396     leftCol = mNumTableCols-1;
  6397     rightCol = -1;
  6399   nscoord x = 0;
  6400   int32_t colX;
  6401   for (colX = leftCol; colX != rightCol; colX += mColInc) {
  6402     nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX);
  6403     if (!colFrame) ABORT1(false);
  6404     // get the col rect relative to the table rather than the col group
  6405     nsSize size = colFrame->GetSize();
  6406     if (haveIntersect) {
  6407       // conservatively estimate the left half border width outside the col
  6408       nscoord leftBorderHalf =
  6409         nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1);
  6410       if (aDirtyRect.XMost() >= (x - leftBorderHalf)) {
  6411         endColIndex = colX;
  6413       else break;
  6415     else {
  6416       // conservatively estimate the right half border width outside the col
  6417       nscoord rightBorderHalf =
  6418         nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1);
  6419       if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) {
  6420         startColIndex = endColIndex = colX;
  6421         haveIntersect = true;
  6423       else {
  6424         mInitialOffsetX += mColInc * size.width;
  6427     x += size.width;
  6429   if (!mTableIsLTR) {
  6430     uint32_t temp;
  6431     mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
  6432     temp = startColIndex; startColIndex = endColIndex; endColIndex = temp;
  6433     for (uint32_t column = 0; column < startColIndex; column++) {
  6434       nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column);
  6435       if (!colFrame) ABORT1(false);
  6436       nsSize size = colFrame->GetSize();
  6437       mInitialOffsetX += mColInc * size.width;
  6440   if (!haveIntersect)
  6441     return false;
  6442   mDamageArea = nsIntRect(startColIndex, startRowIndex,
  6443                           1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex),
  6444                           1 + endRowIndex - startRowIndex);
  6446   Reset();
  6447   mVerInfo = new BCVerticalSeg[mDamageArea.width + 1];
  6448   if (!mVerInfo)
  6449     return false;
  6450   return true;
  6453 void
  6454 BCPaintBorderIterator::Reset()
  6456   mAtEnd = true; // gets reset when First() is called
  6457   mRg = mStartRg;
  6458   mPrevRow  = nullptr;
  6459   mRow      = mStartRow;
  6460   mRowIndex      = 0;
  6461   mColIndex      = 0;
  6462   mRgIndex       = -1;
  6463   mPrevCell      = nullptr;
  6464   mCell          = nullptr;
  6465   mPrevCellData  = nullptr;
  6466   mCellData      = nullptr;
  6467   mBCData        = nullptr;
  6468   ResetVerInfo();
  6471 /**
  6472  * Set the iterator data to a new cellmap coordinate
  6473  * @param aRowIndex - the row index
  6474  * @param aColIndex - the col index
  6475  */
  6476 void
  6477 BCPaintBorderIterator::SetNewData(int32_t aY,
  6478                                 int32_t aX)
  6480   if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
  6482   mColIndex    = aX;
  6483   mRowIndex    = aY;
  6484   mPrevCellData = mCellData;
  6485   if (IsTableRightMost() && IsTableBottomMost()) {
  6486    mCell = nullptr;
  6487    mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner;
  6489   else if (IsTableRightMost()) {
  6490     mCellData = nullptr;
  6491     mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY);
  6493   else if (IsTableBottomMost()) {
  6494     mCellData = nullptr;
  6495     mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX);
  6497   else {
  6498     if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
  6499       mBCData = nullptr;
  6500       mCellData =
  6501         (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
  6502       if (mCellData) {
  6503         mBCData = &mCellData->mData;
  6504         if (!mCellData->IsOrig()) {
  6505           if (mCellData->IsRowSpan()) {
  6506             aY -= mCellData->GetRowSpanOffset();
  6508           if (mCellData->IsColSpan()) {
  6509             aX -= mCellData->GetColSpanOffset();
  6511           if ((aX >= 0) && (aY >= 0)) {
  6512             mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
  6515         if (mCellData->IsOrig()) {
  6516           mPrevCell = mCell;
  6517           mCell = mCellData->GetCellFrame();
  6524 /**
  6525  * Set the iterator to a new row
  6526  * @param aRow - the new row frame, if null the iterator will advance to the
  6527  *               next row
  6528  */
  6529 bool
  6530 BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
  6532   mPrevRow = mRow;
  6533   mRow     = (aRow) ? aRow : mRow->GetNextRow();
  6534   if (mRow) {
  6535     mIsNewRow = true;
  6536     mRowIndex = mRow->GetRowIndex();
  6537     mColIndex = mDamageArea.x;
  6538     mPrevHorSegHeight = 0;
  6539     if (mIsRepeatedHeader) {
  6540       mRepeatedHeaderRowIndex = mRowIndex;
  6543   else {
  6544     mAtEnd = true;
  6546   return !mAtEnd;
  6549 /**
  6550  * Advance the iterator to the next row group
  6551  */
  6552 bool
  6553 BCPaintBorderIterator::SetNewRowGroup()
  6556   mRgIndex++;
  6558   mIsRepeatedHeader = false;
  6559   mIsRepeatedFooter = false;
  6561   NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
  6562   if (uint32_t(mRgIndex) < mRowGroups.Length()) {
  6563     mPrevRg = mRg;
  6564     mRg = mRowGroups[mRgIndex];
  6565     nsTableRowGroupFrame* fifRg =
  6566       static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow());
  6567     mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
  6568     mRgFirstRowIndex    = mRg->GetStartRowIndex();
  6569     mRgLastRowIndex     = mRgFirstRowIndex + mRg->GetRowCount() - 1;
  6571     if (SetNewRow(mRg->GetFirstRow())) {
  6572       mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr);
  6573       if (!mCellMap) ABORT1(false);
  6575     if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
  6576       // if mRowGroup doesn't have a prev in flow, then it may be a repeated
  6577       // header or footer
  6578       const nsStyleDisplay* display = mRg->StyleDisplay();
  6579       if (mRowIndex == mDamageArea.y) {
  6580         mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay);
  6582       else {
  6583         mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay);
  6587   else {
  6588     mAtEnd = true;
  6590   return !mAtEnd;
  6593 /**
  6594  *  Move the iterator to the first position in the damageArea
  6595  */
  6596 void
  6597 BCPaintBorderIterator::First()
  6599   if (!mTable || (mDamageArea.x >= mNumTableCols) ||
  6600       (mDamageArea.y >= mNumTableRows)) ABORT0();
  6602   mAtEnd = false;
  6604   uint32_t numRowGroups = mRowGroups.Length();
  6605   for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) {
  6606     nsTableRowGroupFrame* rowG = mRowGroups[rgY];
  6607     int32_t start = rowG->GetStartRowIndex();
  6608     int32_t end   = start + rowG->GetRowCount() - 1;
  6609     if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) {
  6610       mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
  6611       if (SetNewRowGroup()) {
  6612         while ((mRowIndex < mDamageArea.y) && !mAtEnd) {
  6613           SetNewRow();
  6615         if (!mAtEnd) {
  6616           SetNewData(mDamageArea.y, mDamageArea.x);
  6619       return;
  6622   mAtEnd = true;
  6625 /**
  6626  * Advance the iterator to the next position
  6627  */
  6628 void
  6629 BCPaintBorderIterator::Next()
  6631   if (mAtEnd) ABORT0();
  6632   mIsNewRow = false;
  6634   mColIndex++;
  6635   if (mColIndex > mDamageArea.XMost()) {
  6636     mRowIndex++;
  6637     if (mRowIndex == mDamageArea.YMost()) {
  6638       mColIndex = mDamageArea.x;
  6640     else if (mRowIndex < mDamageArea.YMost()) {
  6641       if (mRowIndex <= mRgLastRowIndex) {
  6642         SetNewRow();
  6644       else {
  6645         SetNewRowGroup();
  6648     else {
  6649       mAtEnd = true;
  6652   if (!mAtEnd) {
  6653     SetNewData(mRowIndex, mColIndex);
  6657 // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
  6658 // them
  6659 /** Compute the vertical offset of a vertical border segment
  6660   * @param aCornerOwnerSide - which side owns the corner
  6661   * @param aCornerSubWidth  - how wide is the nonwinning side of the corner
  6662   * @param aHorWidth        - how wide is the horizontal edge of the corner
  6663   * @param aIsStartOfSeg    - does this corner start a new segment
  6664   * @param aIsBevel         - is this corner beveled
  6665   * @return                 - offset in twips
  6666   */
  6667 static nscoord
  6668 CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide,
  6669                     BCPixelSize aCornerSubWidth,
  6670                     BCPixelSize aHorWidth,
  6671                     bool        aIsStartOfSeg,
  6672                     bool        aIsBevel)
  6674   nscoord offset = 0;
  6675   // XXX These should be replaced with appropriate side-specific macros (which?)
  6676   BCPixelSize smallHalf, largeHalf;
  6677   if ((NS_SIDE_TOP == aCornerOwnerSide) ||
  6678       (NS_SIDE_BOTTOM == aCornerOwnerSide)) {
  6679     DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
  6680     if (aIsBevel) {
  6681       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6683     else {
  6684       offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf;
  6687   else {
  6688     DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
  6689     if (aIsBevel) {
  6690       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6692     else {
  6693       offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
  6696   return nsPresContext::CSSPixelsToAppUnits(offset);
  6699 /** Compute the horizontal offset of a horizontal border segment
  6700   * @param aCornerOwnerSide - which side owns the corner
  6701   * @param aCornerSubWidth  - how wide is the nonwinning side of the corner
  6702   * @param aVerWidth        - how wide is the vertical edge of the corner
  6703   * @param aIsStartOfSeg    - does this corner start a new segment
  6704   * @param aIsBevel         - is this corner beveled
  6705   * @param aTableIsLTR      - direction, the computation depends on ltr or rtl
  6706   * @return                 - offset in twips
  6707   */
  6708 static nscoord
  6709 CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide,
  6710                     BCPixelSize aCornerSubWidth,
  6711                     BCPixelSize aVerWidth,
  6712                     bool        aIsStartOfSeg,
  6713                     bool        aIsBevel,
  6714                     bool        aTableIsLTR)
  6716   nscoord offset = 0;
  6717   // XXX These should be replaced with appropriate side-specific macros (which?)
  6718   BCPixelSize smallHalf, largeHalf;
  6719   if ((NS_SIDE_LEFT == aCornerOwnerSide) ||
  6720       (NS_SIDE_RIGHT == aCornerOwnerSide)) {
  6721     if (aTableIsLTR) {
  6722       DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
  6724     else {
  6725       DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf);
  6727     if (aIsBevel) {
  6728       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6730     else {
  6731       offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf;
  6734   else {
  6735     if (aTableIsLTR) {
  6736       DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
  6738     else {
  6739       DivideBCBorderSize(aVerWidth, largeHalf, smallHalf);
  6741     if (aIsBevel) {
  6742       offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
  6744     else {
  6745       offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
  6748   return nsPresContext::CSSPixelsToAppUnits(offset);
  6751 BCVerticalSeg::BCVerticalSeg()
  6753   mCol = nullptr;
  6754   mFirstCell = mLastCell = mAjaCell = nullptr;
  6755   mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0;
  6756   mTopBevelSide = NS_SIDE_TOP;
  6757   mOwner = eCellOwner;
  6760 /**
  6761  * Start a new vertical segment
  6762  * @param aIter         - iterator containing the structural information
  6763  * @param aBorderOwner  - determines the border style
  6764  * @param aVerSegWidth  - the width of segment in pixel
  6765  * @param aHorSegHeight - the width of the horizontal segment joining the corner
  6766  *                        at the start
  6767  */
  6768 void
  6769 BCVerticalSeg::Start(BCPaintBorderIterator& aIter,
  6770                      BCBorderOwner          aBorderOwner,
  6771                      BCPixelSize            aVerSegWidth,
  6772                      BCPixelSize            aHorSegHeight)
  6774   mozilla::css::Side ownerSide   = NS_SIDE_TOP;
  6775   bool bevel       = false;
  6778   nscoord cornerSubWidth  = (aIter.mBCData) ?
  6779                                aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
  6781   bool    topBevel        = (aVerSegWidth > 0) ? bevel : false;
  6782   BCPixelSize maxHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight);
  6783   nscoord offset          = CalcVerCornerOffset(ownerSide, cornerSubWidth,
  6784                                                 maxHorSegHeight, true,
  6785                                                 topBevel);
  6787   mTopBevelOffset = topBevel ?
  6788     nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0;
  6789   // XXX this assumes that only corners where 2 segments join can be beveled
  6790   mTopBevelSide     = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
  6791   mOffsetY      += offset;
  6792   mLength        = -offset;
  6793   mWidth         = aVerSegWidth;
  6794   mOwner         = aBorderOwner;
  6795   mFirstCell     = aIter.mCell;
  6796   mFirstRowGroup = aIter.mRg;
  6797   mFirstRow      = aIter.mRow;
  6798   if (aIter.GetRelativeColIndex() > 0) {
  6799     mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
  6803 /**
  6804  * Initialize the vertical segments with information that will persist for any
  6805  * vertical segment in this column
  6806  * @param aIter - iterator containing the structural information
  6807  */
  6808 void
  6809 BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter)
  6811   int32_t relColIndex = aIter.GetRelativeColIndex();
  6812   mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol :
  6813            aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
  6814   if (!mCol) ABORT0();
  6815   if (0 == relColIndex) {
  6816     mOffsetX = aIter.mInitialOffsetX;
  6818   // set colX for the next column
  6819   if (!aIter.IsDamageAreaRightMost()) {
  6820     aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX +
  6821                                          aIter.mColInc * mCol->GetSize().width;
  6823   mOffsetY = aIter.mInitialOffsetY;
  6824   mLastCell = aIter.mCell;
  6827 /**
  6828  * Compute the offsets for the bottom corner of a vertical segment
  6829  * @param aIter         - iterator containing the structural information
  6830  * @param aHorSegHeight - the width of the horizontal segment joining the corner
  6831  *                        at the start
  6832  */
  6833 void
  6834 BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter,
  6835                                BCPixelSize            aHorSegHeight)
  6837    mozilla::css::Side ownerSide = NS_SIDE_TOP;
  6838    nscoord cornerSubWidth = 0;
  6839    bool bevel = false;
  6840    if (aIter.mBCData) {
  6841      cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
  6843    mIsBottomBevel = (mWidth > 0) ? bevel : false;
  6844    mBottomHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight);
  6845    mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
  6846                                     mBottomHorSegHeight,
  6847                                     false, mIsBottomBevel);
  6848    mLength += mBottomOffset;
  6851 /**
  6852  * Paint the vertical segment
  6853  * @param aIter         - iterator containing the structural information
  6854  * @param aRenderingContext - the rendering context
  6855  * @param aHorSegHeight - the width of the horizontal segment joining the corner
  6856  *                        at the start
  6857  */
  6858 void
  6859 BCVerticalSeg::Paint(BCPaintBorderIterator& aIter,
  6860                      nsRenderingContext&   aRenderingContext,
  6861                      BCPixelSize            aHorSegHeight)
  6863   // get the border style, color and paint the segment
  6864   mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT :
  6865                                                     NS_SIDE_LEFT;
  6866   int32_t relColIndex = aIter.GetRelativeColIndex();
  6867   nsTableColFrame* col           = mCol; if (!col) ABORT0();
  6868   nsTableCellFrame* cell         = mFirstCell; // ???
  6869   nsIFrame* owner = nullptr;
  6870   uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
  6871   nscolor color = 0xFFFFFFFF;
  6873   switch (mOwner) {
  6874     case eTableOwner:
  6875       owner = aIter.mTable;
  6876       break;
  6877     case eAjaColGroupOwner:
  6878       side = NS_SIDE_RIGHT;
  6879       if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
  6880         col = aIter.mVerInfo[relColIndex - 1].mCol;
  6881       } // and fall through
  6882     case eColGroupOwner:
  6883       if (col) {
  6884         owner = col->GetParent();
  6886       break;
  6887     case eAjaColOwner:
  6888       side = NS_SIDE_RIGHT;
  6889       if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
  6890         col = aIter.mVerInfo[relColIndex - 1].mCol;
  6891       } // and fall through
  6892     case eColOwner:
  6893       owner = col;
  6894       break;
  6895     case eAjaRowGroupOwner:
  6896       NS_ERROR("a neighboring rowgroup can never own a vertical border");
  6897       // and fall through
  6898     case eRowGroupOwner:
  6899       NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
  6900                   "row group can own border only at table edge");
  6901       owner = mFirstRowGroup;
  6902       break;
  6903     case eAjaRowOwner:
  6904       NS_ASSERTION(false, "program error"); // and fall through
  6905     case eRowOwner:
  6906       NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
  6907                    "row can own border only at table edge");
  6908       owner = mFirstRow;
  6909       break;
  6910     case eAjaCellOwner:
  6911       side = NS_SIDE_RIGHT;
  6912       cell = mAjaCell; // and fall through
  6913     case eCellOwner:
  6914       owner = cell;
  6915       break;
  6917   if (owner) {
  6918     ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
  6920   BCPixelSize smallHalf, largeHalf;
  6921   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
  6922   nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf),
  6923                  mOffsetY,
  6924                  nsPresContext::CSSPixelsToAppUnits(mWidth), mLength);
  6925   nscoord bottomBevelOffset = (mIsBottomBevel) ?
  6926                   nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0;
  6927   mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ?
  6928                             NS_SIDE_RIGHT : NS_SIDE_LEFT;
  6929   mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)?
  6930                          NS_SIDE_RIGHT : NS_SIDE_LEFT;
  6931   nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
  6932                                          aIter.mTableBgColor, segRect,
  6933                                          nsPresContext::AppUnitsPerCSSPixel(),
  6934                                          topBevelSide, mTopBevelOffset,
  6935                                          bottomBevelSide, bottomBevelOffset);
  6938 /**
  6939  * Advance the start point of a segment
  6940  */
  6941 void
  6942 BCVerticalSeg::AdvanceOffsetY()
  6944   mOffsetY +=  mLength - mBottomOffset;
  6947 /**
  6948  * Accumulate the current segment
  6949  */
  6950 void
  6951 BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
  6953   mLastCell = aIter.mCell;
  6954   mLength  += aIter.mRow->GetRect().height;
  6957 BCHorizontalSeg::BCHorizontalSeg()
  6959   mOffsetX = mOffsetY = mLength = mWidth =  mLeftBevelOffset = 0;
  6960   mLeftBevelSide = NS_SIDE_TOP;
  6961   mFirstCell = mAjaCell = nullptr;
  6964 /** Initialize a horizontal border segment for painting
  6965   * @param aIter              - iterator storing the current and adjacent frames
  6966   * @param aBorderOwner       - which frame owns the border
  6967   * @param aBottomVerSegWidth - vertical segment width coming from up
  6968   * @param aHorSegHeight      - the height of the segment
  6969   +  */
  6970 void
  6971 BCHorizontalSeg::Start(BCPaintBorderIterator& aIter,
  6972                        BCBorderOwner        aBorderOwner,
  6973                        BCPixelSize          aBottomVerSegWidth,
  6974                        BCPixelSize          aHorSegHeight)
  6976   mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
  6977   bool bevel     = false;
  6979   mOwner = aBorderOwner;
  6980   nscoord cornerSubWidth  = (aIter.mBCData) ?
  6981                              aIter.mBCData->GetCorner(cornerOwnerSide,
  6982                                                        bevel) : 0;
  6984   bool    leftBevel = (aHorSegHeight > 0) ? bevel : false;
  6985   int32_t relColIndex = aIter.GetRelativeColIndex();
  6986   nscoord maxVerSegWidth = std::max(aIter.mVerInfo[relColIndex].mWidth,
  6987                                   aBottomVerSegWidth);
  6988   nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
  6989                                        maxVerSegWidth, true, leftBevel,
  6990                                        aIter.mTableIsLTR);
  6991   mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0;
  6992   // XXX this assumes that only corners where 2 segments join can be beveled
  6993   mLeftBevelSide   = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
  6994   if (aIter.mTableIsLTR) {
  6995     mOffsetX += offset;
  6997   else {
  6998     mOffsetX -= offset;
  7000   mLength          = -offset;
  7001   mWidth           = aHorSegHeight;
  7002   mFirstCell       = aIter.mCell;
  7003   mAjaCell         = (aIter.IsDamageAreaTopMost()) ? nullptr :
  7004                      aIter.mVerInfo[relColIndex].mLastCell;
  7007 /**
  7008  * Compute the offsets for the right corner of a horizontal segment
  7009  * @param aIter         - iterator containing the structural information
  7010  * @param aLeftSegWidth - the width of the vertical segment joining the corner
  7011  *                        at the start
  7012  */
  7013 void
  7014 BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter,
  7015                                 BCPixelSize            aLeftSegWidth)
  7017   mozilla::css::Side ownerSide = NS_SIDE_TOP;
  7018   nscoord cornerSubWidth = 0;
  7019   bool bevel = false;
  7020   if (aIter.mBCData) {
  7021     cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
  7024   mIsRightBevel = (mWidth > 0) ? bevel : 0;
  7025   int32_t relColIndex = aIter.GetRelativeColIndex();
  7026   nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth);
  7027   mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
  7028                                    false, mIsRightBevel, aIter.mTableIsLTR);
  7029   mLength += mEndOffset;
  7030   mRightBevelOffset = (mIsRightBevel) ?
  7031                        nsPresContext::CSSPixelsToAppUnits(verWidth) : 0;
  7032   mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
  7035 /**
  7036  * Paint the horizontal segment
  7037  * @param aIter         - iterator containing the structural information
  7038  * @param aRenderingContext - the rendering context
  7039  */
  7040 void
  7041 BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter,
  7042                        nsRenderingContext&   aRenderingContext)
  7044   // get the border style, color and paint the segment
  7045   mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM :
  7046                                                      NS_SIDE_TOP;
  7047   nsIFrame* rg   = aIter.mRg;  if (!rg) ABORT0();
  7048   nsIFrame* row  = aIter.mRow; if (!row) ABORT0();
  7049   nsIFrame* cell = mFirstCell;
  7050   nsIFrame* col;
  7051   nsIFrame* owner = nullptr;
  7053   uint8_t style = NS_STYLE_BORDER_STYLE_SOLID;
  7054   nscolor color = 0xFFFFFFFF;
  7057   switch (mOwner) {
  7058     case eTableOwner:
  7059       owner = aIter.mTable;
  7060       break;
  7061     case eAjaColGroupOwner:
  7062       NS_ERROR("neighboring colgroups can never own a horizontal border");
  7063       // and fall through
  7064     case eColGroupOwner:
  7065       NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
  7066                    "col group can own border only at the table edge");
  7067       col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
  7068       if (!col) ABORT0();
  7069       owner = col->GetParent();
  7070       break;
  7071     case eAjaColOwner:
  7072       NS_ERROR("neighboring column can never own a horizontal border");
  7073       // and fall through
  7074     case eColOwner:
  7075       NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
  7076                    "col can own border only at the table edge");
  7077       owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
  7078       break;
  7079     case eAjaRowGroupOwner:
  7080       side = NS_SIDE_BOTTOM;
  7081       rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg;
  7082       // and fall through
  7083     case eRowGroupOwner:
  7084       owner = rg;
  7085       break;
  7086     case eAjaRowOwner:
  7087       side = NS_SIDE_BOTTOM;
  7088       row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow;
  7089       // and fall through
  7090       case eRowOwner:
  7091       owner = row;
  7092       break;
  7093     case eAjaCellOwner:
  7094       side = NS_SIDE_BOTTOM;
  7095       // if this is null due to the damage area origin-y > 0, then the border
  7096       // won't show up anyway
  7097       cell = mAjaCell;
  7098       // and fall through
  7099     case eCellOwner:
  7100       owner = cell;
  7101       break;
  7103   if (owner) {
  7104     ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
  7106   BCPixelSize smallHalf, largeHalf;
  7107   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
  7108   nsRect segRect(mOffsetX,
  7109                  mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf),
  7110                  mLength,
  7111                  nsPresContext::CSSPixelsToAppUnits(mWidth));
  7112   if (aIter.mTableIsLTR) {
  7113     nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
  7114                                            aIter.mTableBgColor, segRect,
  7115                                            nsPresContext::AppUnitsPerCSSPixel(),
  7116                                            mLeftBevelSide,
  7117                                            nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset),
  7118                                            mRightBevelSide, mRightBevelOffset);
  7120   else {
  7121     segRect.x -= segRect.width;
  7122     nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
  7123                                            aIter.mTableBgColor, segRect,
  7124                                            nsPresContext::AppUnitsPerCSSPixel(),
  7125                                            mRightBevelSide, mRightBevelOffset,
  7126                                            mLeftBevelSide,
  7127                                            nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset));
  7131 /**
  7132  * Advance the start point of a segment
  7133  */
  7134 void
  7135 BCHorizontalSeg::AdvanceOffsetX(int32_t aIncrement)
  7137   mOffsetX += aIncrement * (mLength - mEndOffset);
  7140 /**
  7141  * Accumulate the current segment
  7142  */
  7143 void
  7144 BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
  7146   mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth;
  7149 /**
  7150  * store the column width information while painting horizontal segment
  7151  */
  7152 void
  7153 BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex)
  7155   if (IsTableRightMost()) {
  7156       mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth;
  7158   else {
  7159     nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
  7160     if (!col) ABORT0();
  7161     mVerInfo[aIndex].mColWidth = col->GetSize().width;
  7164 /**
  7165  * Determine if a vertical segment owns the corder
  7166  */
  7167 bool
  7168 BCPaintBorderIterator::VerticalSegmentOwnsCorner()
  7170   mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
  7171   bool bevel = false;
  7172   if (mBCData) {
  7173     mBCData->GetCorner(cornerOwnerSide, bevel);
  7175   // unitialized ownerside, bevel
  7176   return  (NS_SIDE_TOP == cornerOwnerSide) ||
  7177           (NS_SIDE_BOTTOM == cornerOwnerSide);
  7180 /**
  7181  * Paint if necessary a horizontal segment, otherwise accumulate it
  7182  * @param aRenderingContext - the rendering context
  7183  */
  7184 void
  7185 BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext)
  7188   int32_t relColIndex = GetRelativeColIndex();
  7189   // store the current col width if it hasn't been already
  7190   if (mVerInfo[relColIndex].mColWidth < 0) {
  7191     StoreColumnWidth(relColIndex);
  7194   BCBorderOwner borderOwner = eCellOwner;
  7195   BCBorderOwner ignoreBorderOwner;
  7196   bool isSegStart = true;
  7197   bool ignoreSegStart;
  7199   nscoord leftSegWidth =
  7200     mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0;
  7201   nscoord topSegHeight =
  7202     mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0;
  7204   if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) {
  7205     // reset for every new row and on the bottom of the last row
  7206     mHorSeg.mOffsetY = mNextOffsetY;
  7207     mNextOffsetY     = mNextOffsetY + mRow->GetSize().height;
  7208     mHorSeg.mOffsetX = mInitialOffsetX;
  7209     mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
  7212   if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() ||
  7213                                   VerticalSegmentOwnsCorner())) {
  7214     // paint the previous seg or the current one if IsDamageAreaRightMost()
  7215     if (mHorSeg.mLength > 0) {
  7216       mHorSeg.GetRightCorner(*this, leftSegWidth);
  7217       if (mHorSeg.mWidth > 0) {
  7218         mHorSeg.Paint(*this, aRenderingContext);
  7220       mHorSeg.AdvanceOffsetX(mColInc);
  7222     mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
  7224   mHorSeg.IncludeCurrentBorder(*this);
  7225   mVerInfo[relColIndex].mWidth = leftSegWidth;
  7226   mVerInfo[relColIndex].mLastCell = mCell;
  7228 /**
  7229  * Paint if necessary a vertical segment, otherwise  it
  7230  * @param aRenderingContext - the rendering context
  7231  */
  7232 void
  7233 BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext)
  7235   BCBorderOwner borderOwner = eCellOwner;
  7236   BCBorderOwner ignoreBorderOwner;
  7237   bool isSegStart = true;
  7238   bool ignoreSegStart;
  7240   nscoord verSegWidth  =
  7241     mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0;
  7242   nscoord horSegHeight =
  7243     mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0;
  7245   int32_t relColIndex = GetRelativeColIndex();
  7246   BCVerticalSeg& verSeg = mVerInfo[relColIndex];
  7247   if (!verSeg.mCol) { // on the first damaged row and the first segment in the
  7248                       // col
  7249     verSeg.Initialize(*this);
  7250     verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
  7253   if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() ||
  7254                                  IsAfterRepeatedHeader() ||
  7255                                  StartRepeatedFooter())) {
  7256     // paint the previous seg or the current one if IsDamageAreaBottomMost()
  7257     if (verSeg.mLength > 0) {
  7258       verSeg.GetBottomCorner(*this, horSegHeight);
  7259       if (verSeg.mWidth > 0) {
  7260         verSeg.Paint(*this, aRenderingContext, horSegHeight);
  7262       verSeg.AdvanceOffsetY();
  7264     verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
  7266   verSeg.IncludeCurrentBorder(*this);
  7267   mPrevHorSegHeight = horSegHeight;
  7270 /**
  7271  * Reset the vertical information cache
  7272  */
  7273 void
  7274 BCPaintBorderIterator::ResetVerInfo()
  7276   if (mVerInfo) {
  7277     memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg));
  7278     // XXX reinitialize properly
  7279     for (int32_t xIndex = 0; xIndex < mDamageArea.width; xIndex++) {
  7280       mVerInfo[xIndex].mColWidth = -1;
  7285 /**
  7286  * Method to paint BCBorders, this does not use currently display lists although
  7287  * it will do this in future
  7288  * @param aRenderingContext - the rendering context
  7289  * @param aDirtyRect        - inside this rectangle the BC Borders will redrawn
  7290  */
  7291 void
  7292 nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext,
  7293                              const nsRect&        aDirtyRect)
  7295   // We first transfer the aDirtyRect into cellmap coordinates to compute which
  7296   // cell borders need to be painted
  7297   BCPaintBorderIterator iter(this);
  7298   if (!iter.SetDamageArea(aDirtyRect))
  7299     return;
  7301   // First, paint all of the vertical borders from top to bottom and left to
  7302   // right as they become complete. They are painted first, since they are less
  7303   // efficient to paint than horizontal segments. They were stored with as few
  7304   // segments as possible (since horizontal borders are painted last and
  7305   // possibly over them). For every cell in a row that fails in the damage are
  7306   // we look up if the current border would start a new segment, if so we paint
  7307   // the previously stored vertical segment and start a new segment. After
  7308   // this we  the now active segment with the current border. These
  7309   // segments are stored in mVerInfo to be used on the next row
  7310   for (iter.First(); !iter.mAtEnd; iter.Next()) {
  7311     iter.AccumulateOrPaintVerticalSegment(aRenderingContext);
  7314   // Next, paint all of the horizontal border segments from top to bottom reuse
  7315   // the mVerInfo array to keep track of col widths and vertical segments for
  7316   // corner calculations
  7317   iter.Reset();
  7318   for (iter.First(); !iter.mAtEnd; iter.Next()) {
  7319     iter.AccumulateOrPaintHorizontalSegment(aRenderingContext);
  7323 bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
  7325   bool result = false;
  7326   nsTableCellMap* cellMap = GetCellMap();
  7327   NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
  7328   if (cellMap) {
  7329     result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
  7331   return result;
  7334 bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols)
  7336   bool result = false;
  7337   nsTableCellMap* cellMap = GetCellMap();
  7338   NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
  7339   if (cellMap) {
  7340     result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
  7342   return result;
  7345 /* static */
  7346 void
  7347 nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame,
  7348                                    const nsRect& aOrigRect,
  7349                                    const nsRect& aOrigVisualOverflow,
  7350                                    bool aIsFirstReflow)
  7352   nsIFrame* parent = aFrame->GetParent();
  7353   NS_ASSERTION(parent, "What happened here?");
  7355   if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
  7356     // Don't bother; we'll invalidate the parent's overflow rect when
  7357     // we finish reflowing it.
  7358     return;
  7361   // The part that looks at both the rect and the overflow rect is a
  7362   // bit of a hack.  See nsBlockFrame::ReflowLine for an eloquent
  7363   // description of its hackishness.
  7364   //
  7365   // This doesn't really make sense now that we have DLBI.
  7366   // This code can probably be simplified a fair bit.
  7367   nsRect visualOverflow = aFrame->GetVisualOverflowRect();
  7368   if (aIsFirstReflow ||
  7369       aOrigRect.TopLeft() != aFrame->GetPosition() ||
  7370       aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
  7371     // Invalidate the old and new overflow rects.  Note that if the
  7372     // frame moved, we can't just use aOrigVisualOverflow, since it's in
  7373     // coordinates relative to the old position.  So invalidate via
  7374     // aFrame's parent, and reposition that overflow rect to the right
  7375     // place.
  7376     // XXXbz this doesn't handle outlines, does it?
  7377     aFrame->InvalidateFrame();
  7378     parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft());
  7379   } else if (aOrigRect.Size() != aFrame->GetSize() ||
  7380              aOrigVisualOverflow.Size() != visualOverflow.Size()){
  7381     aFrame->InvalidateFrameWithRect(aOrigVisualOverflow);
  7382     aFrame->InvalidateFrame();
  7383     parent->InvalidateFrameWithRect(aOrigRect);;
  7384     parent->InvalidateFrame();

mercurial