Bug 330964 - Make nsTableFrame::GetCellSpacing[XY] take an argument. r=roc
☠☠ backed out by ba330fb87400 ☠ ☠
authorJames Kitchener <jkitch.bug@gmail.com>
Sun, 15 Jun 2014 00:53:00 +0200
changeset 209671 248103817c8b042c2dc9ecd6321ac13298b2d1c8
parent 209670 1f21c62b41eb63962a639193373b52783ade0cff
child 209672 d70b7473bff7fbaf976b7a027b1a167d3ef863d9
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs330964
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 330964 - Make nsTableFrame::GetCellSpacing[XY] take an argument. r=roc
layout/tables/BasicTableLayoutStrategy.cpp
layout/tables/FixedTableLayoutStrategy.cpp
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowGroupFrame.cpp
--- a/layout/tables/BasicTableLayoutStrategy.cpp
+++ b/layout/tables/BasicTableLayoutStrategy.cpp
@@ -430,28 +430,28 @@ void
 BasicTableLayoutStrategy::ComputeIntrinsicWidths(nsRenderingContext* aRenderingContext)
 {
     ComputeColumnIntrinsicWidths(aRenderingContext);
 
     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
     nscoord min = 0, pref = 0, max_small_pct_pref = 0, nonpct_pref_total = 0;
     float pct_total = 0.0f; // always from 0.0f - 1.0f
     int32_t colCount = cellMap->GetColCount();
-    nscoord spacing = mTableFrame->GetCellSpacingX();
-    nscoord add = spacing; // add (colcount + 1) * spacing for columns 
-                           // where a cell originates
+    // add a total of (colcount + 1) lots of cellSpacingX for columns where a
+    // cell originates
+    nscoord add = mTableFrame->GetCellSpacingX(colCount);
 
     for (int32_t col = 0; col < colCount; ++col) {
         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
         if (!colFrame) {
             NS_ERROR("column frames out of sync with cell map");
             continue;
         }
         if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
-            add += spacing;
+            add += mTableFrame->GetCellSpacingX(col - 1);
         }
         min += colFrame->GetMinCoord();
         pref = NSCoordSaturatingAdd(pref, colFrame->GetPrefCoord());
 
         // Percentages are of the table, so we have to reverse them for
         // intrinsic widths.
         float p = colFrame->GetPrefPercent();
         if (p > 0.0f) {
@@ -649,32 +649,33 @@ BasicTableLayoutStrategy::DistributeWidt
                                                    BtlsWidthType aWidthType,
                                                    bool aSpanHasSpecifiedWidth)
 {
     NS_ASSERTION(aWidthType != BTLS_FINAL_WIDTH || 
                  (aFirstCol == 0 && 
                   aColCount == mTableFrame->GetCellMap()->GetColCount()),
             "Computing final column widths, but didn't get full column range");
 
-    // border-spacing isn't part of the basis for percentages.
-    nscoord spacing = mTableFrame->GetCellSpacingX();
-    nscoord subtract = 0;    
+
+    nscoord subtract = 0;
     // aWidth initially includes border-spacing for the boundaries in between
     // each of the columns. We start at aFirstCol + 1 because the first
     // in-between boundary would be at the left edge of column aFirstCol + 1
     for (int32_t col = aFirstCol + 1; col < aFirstCol + aColCount; ++col) {
         if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
-            subtract += spacing;
+            // border-spacing isn't part of the basis for percentages.
+            subtract += mTableFrame->GetCellSpacingX(col - 1);
         }
     }
     if (aWidthType == BTLS_FINAL_WIDTH) {
         // If we're computing final col-width, then aWidth initially includes
         // border spacing on the table's far left + far right edge, too.  Need
         // to subtract those out, too.
-        subtract += spacing * 2;
+        subtract += (mTableFrame->GetCellSpacingX(-1) +
+                     mTableFrame->GetCellSpacingX(aColCount));
     }
     aWidth = NSCoordSaturatingSubtract(aWidth, subtract, nscoord_MAX);
 
     /*
      * The goal of this function is to distribute |aWidth| between the
      * columns by making an appropriate AddSpanCoords or SetFinalWidth
      * call for each column.  (We call AddSpanCoords if we're 
      * distributing a column-spanning cell's minimum or preferred width
--- a/layout/tables/FixedTableLayoutStrategy.cpp
+++ b/layout/tables/FixedTableLayoutStrategy.cpp
@@ -46,30 +46,30 @@ FixedTableLayoutStrategy::GetMinWidth(ns
     //   'width' property for the table element and the sum of the
     //   column widths (plus cell spacing or borders).
 
     // XXX Should we really ignore 'min-width' and 'max-width'?
     // XXX Should we really ignore widths on column groups?
 
     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
     int32_t colCount = cellMap->GetColCount();
-    nscoord spacing = mTableFrame->GetCellSpacingX();
 
     nscoord result = 0;
 
     if (colCount > 0) {
-        result += spacing * (colCount + 1);
+        result += mTableFrame->GetCellSpacingX(-1, colCount);
     }
 
     for (int32_t col = 0; col < colCount; ++col) {
         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
         if (!colFrame) {
             NS_ERROR("column frames out of sync with cell map");
             continue;
         }
+        nscoord spacing = mTableFrame->GetCellSpacingX(col);
         const nsStyleCoord *styleWidth =
             &colFrame->StylePosition()->mWidth;
         if (styleWidth->ConvertsToLength()) {
             result += nsLayoutUtils::ComputeWidthValue(aRenderingContext,
                         colFrame, 0, 0, 0, *styleWidth);
         } else if (styleWidth->GetUnit() == eStyleUnit_Percent) {
             // do nothing
         } else {
@@ -156,26 +156,25 @@ FixedTableLayoutStrategy::ComputeColumnW
     nscoord tableWidth = aReflowState.ComputedWidth();
 
     if (mLastCalcWidth == tableWidth)
         return;
     mLastCalcWidth = tableWidth;
 
     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
     int32_t colCount = cellMap->GetColCount();
-    nscoord spacing = mTableFrame->GetCellSpacingX();
 
     if (colCount == 0) {
         // No Columns - nothing to compute
         return;
     }
 
     // border-spacing isn't part of the basis for percentages.
-    tableWidth -= spacing * (colCount + 1);
-    
+    tableWidth -= mTableFrame->GetCellSpacingX(-1, colCount);
+
     // store the old column widths. We might call multiple times SetFinalWidth
     // on the columns, due to this we can't compare at the last call that the
     // width has changed with the respect to the last call to
     // ComputeColumnWidths. In order to overcome this we store the old values
     // in this array. A single call to SetFinalWidth would make it possible to
     // call GetFinalWidth before and to compare when setting the final width.
     nsTArray<nscoord> oldColWidths;
 
@@ -276,16 +275,17 @@ FixedTableLayoutStrategy::ComputeColumnW
                     colWidth = unassignedMarker;
                 }
                 if (colWidth != unassignedMarker) {
                     if (colSpan > 1) {
                         // If a column-spanning cell is in the first
                         // row, split up the space evenly.  (XXX This
                         // isn't quite right if some of the columns it's
                         // in have specified widths.  Should we care?)
+                        nscoord spacing = mTableFrame->GetCellSpacingX(col);
                         colWidth = ((colWidth + spacing) / colSpan) - spacing;
                         if (colWidth < 0)
                             colWidth = 0;
                     }
                     if (styleWidth->GetUnit() != eStyleUnit_Percent) {
                         specTotal += colWidth;
                     }
                 }
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -823,19 +823,20 @@ CalcUnpaginagedHeight(nsPresContext*    
   nsTableRowFrame* row =
     static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
   nsTableRowGroupFrame* firstRGInFlow =
     static_cast<nsTableRowGroupFrame*>(row->GetParent());
 
   int32_t rowIndex;
   firstCellInFlow->GetRowIndex(rowIndex);
   int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
-  nscoord cellSpacing = firstTableInFlow->GetCellSpacingY();
 
-  nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding;
+  nscoord computedHeight = firstTableInFlow->GetCellSpacingY(rowIndex,
+                                                             rowIndex + rowSpan - 1);
+  computedHeight -= aVerticalBorderPadding;
   int32_t rowX;
   for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) {
     if (rowX > rowIndex + rowSpan - 1) {
       break;
     }
     else if (rowX >= rowIndex) {
       computedHeight += row->GetUnpaginatedHeight(aPresContext);
     }
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -79,32 +79,34 @@ struct nsTableReflowState {
 
   void Init(nsPresContext&  aPresContext,
             nsTableFrame&   aTableFrame,
             nscoord         aAvailWidth,
             nscoord         aAvailHeight)
   {
     nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
     nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
-    nscoord cellSpacingX = table->GetCellSpacingX();
-
-    x = borderPadding.left + cellSpacingX;
+
+    x = borderPadding.left + table->GetCellSpacingX(-1);
     y = borderPadding.top; //cellspacing added during reflow
 
     availSize.width  = aAvailWidth;
     if (NS_UNCONSTRAINEDSIZE != availSize.width) {
+      int32_t colCount = table->GetColCount();
       availSize.width -= borderPadding.left + borderPadding.right
-                         + (2 * cellSpacingX);
+                         + table->GetCellSpacingX(-1)
+                         + table->GetCellSpacingX(colCount);
       availSize.width = std::max(0, availSize.width);
     }
 
     availSize.height = aAvailHeight;
     if (NS_UNCONSTRAINEDSIZE != availSize.height) {
       availSize.height -= borderPadding.top + borderPadding.bottom
-                          + (2 * table->GetCellSpacingY());
+                          + table->GetCellSpacingY(-1)
+                          + table->GetCellSpacingY(table->GetRowCount());
       availSize.height = std::max(0, availSize.height);
     }
   }
 
   nsTableReflowState(nsPresContext&           aPresContext,
                      const nsHTMLReflowState& aReflowState,
                      nsTableFrame&            aTableFrame)
     : reflowState(aReflowState)
@@ -1414,41 +1416,42 @@ nsTableFrame::GetLogicalSkipSides(const 
   }
   return skip;
 }
 
 void
 nsTableFrame::SetColumnDimensions(nscoord         aHeight,
                                   const nsMargin& aBorderPadding)
 {
-  nscoord cellSpacingX = GetCellSpacingX();
-  nscoord cellSpacingY = GetCellSpacingY();
   nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
-                                 2* cellSpacingY;
+                                 GetCellSpacingY(-1) +
+                                 GetCellSpacingY(GetRowCount());
 
   nsTableIterator iter(mColGroups);
   nsIFrame* colGroupFrame = iter.First();
   bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
   int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1);
+  nscoord cellSpacingX = GetCellSpacingX(colX);
   int32_t tableColIncr = tableIsLTR ? 1 : -1;
-  nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
-                         aBorderPadding.top + cellSpacingY);
+  nsPoint colGroupOrigin(aBorderPadding.left + GetCellSpacingX(-1),
+                         aBorderPadding.top + GetCellSpacingY(-1));
   while (colGroupFrame) {
     MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
     nscoord colGroupWidth = 0;
     nsTableIterator iterCol(*colGroupFrame);
     nsIFrame* colFrame = iterCol.First();
     nsPoint colOrigin(0,0);
     while (colFrame) {
       if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
           colFrame->StyleDisplay()->mDisplay) {
         NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
         nscoord colWidth = GetColumnWidth(colX);
         nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
         colFrame->SetRect(colRect);
+        cellSpacingX = GetCellSpacingX(colX);
         colOrigin.x += colWidth + cellSpacingX;
         colGroupWidth += colWidth + cellSpacingX;
         colX += tableColIncr;
       }
       colFrame = iterCol.Next();
     }
     if (colGroupWidth) {
       colGroupWidth -= cellSpacingX;
@@ -1850,17 +1853,17 @@ nsTableFrame::Reflow(nsPresContext*     
       mutable_rs.mFlags.mSpecialHeightReflow = true;
 
       ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(),
                   lastChildReflowed, aStatus);
 
       if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
         // if there is an incomplete child, then set the desired height to include it but not the next one
         nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
-        aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() +
+        aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY(GetRowCount()) +
                               lastChildReflowed->GetRect().YMost();
       }
       haveDesiredHeight = true;
 
       mutable_rs.mFlags.mSpecialHeightReflow = false;
     }
   }
   else {
@@ -2104,17 +2107,18 @@ nsTableFrame::AdjustForCollapsingRowsCol
 
   // collapse the rows and/or row groups as necessary
   // Get the ordered children
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
 
   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
   nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding);
-  nscoord rgWidth = width - 2 * GetCellSpacingX();
+  nscoord rgWidth = width - GetCellSpacingX(-1) -
+                    GetCellSpacingX(GetColCount());
   nsOverflowAreas overflow;
   // Walk the list of children
   for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[childX];
     NS_ASSERTION(rgFrame, "Must have row group frame here");
     yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
     ConsiderChildOverflow(overflow, rgFrame);
   }
@@ -2126,18 +2130,17 @@ nsTableFrame::AdjustForCollapsingRowsCol
                          nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
 }
 
 
 nscoord
 nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
 {
   NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow");
-  nscoord cellSpacingX = GetCellSpacingX();
-  nscoord width = cellSpacingX;
+  nscoord width = GetCellSpacingX(GetColCount());
   width += aBorderPadding.left + aBorderPadding.right;
   for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame;
          groupFrame = groupFrame->GetNextSibling()) {
     const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
     bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
     nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
     for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
          colFrame = colFrame->GetNextCol()) {
@@ -2145,17 +2148,17 @@ nsTableFrame::GetCollapsedWidth(nsMargin
       int32_t colX = colFrame->GetColIndex();
       if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
         const nsStyleVisibility* colVis = colFrame->StyleVisibility();
         bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
         int32_t colWidth = GetColumnWidth(colX);
         if (!collapseGroup && !collapseCol) {
           width += colWidth;
           if (ColumnHasCellSpacingBefore(colX))
-            width += cellSpacingX;
+            width += GetCellSpacingX(colX-1);
         }
         else {
           SetNeedToCollapse(true);
         }
       }
     }
   }
   return width;
@@ -2833,17 +2836,17 @@ nsTableFrame::PlaceRepeatedFooter(nsTabl
   nsSize kidAvailSize(aReflowState.availSize);
   kidAvailSize.height = aFooterHeight;
   nsHTMLReflowState footerReflowState(presContext,
                                       aReflowState.reflowState,
                                       aTfoot, kidAvailSize,
                                       -1, -1,
                                       nsHTMLReflowState::CALLER_WILL_INIT);
   InitChildReflowState(footerReflowState);
-  aReflowState.y += GetCellSpacingY();
+  aReflowState.y += GetCellSpacingY(GetRowCount());
 
   nsRect origTfootRect = aTfoot->GetRect();
   nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
           
   nsReflowStatus footerStatus;
   nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
   desiredSize.Width() = desiredSize.Height() = 0;
   ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
@@ -2859,17 +2862,16 @@ nsTableFrame::ReflowChildren(nsTableRefl
                              nsReflowStatus&     aStatus,
                              nsIFrame*&          aLastChildReflowed,
                              nsOverflowAreas&    aOverflowAreas)
 {
   aStatus = NS_FRAME_COMPLETE;
   aLastChildReflowed = nullptr;
 
   nsIFrame* prevKidFrame = nullptr;
-  nscoord   cellSpacingY = GetCellSpacingY();
 
   nsPresContext* presContext = PresContext();
   // XXXldb Should we be checking constrained height instead?
   // tables are not able to pull back children from its next inflow, so even
   // under paginated contexts tables are should not paginate if they are inside
   // column set
   bool isPaginated = presContext->IsPaginated() &&
                        NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height &&
@@ -2907,16 +2909,19 @@ nsTableFrame::ReflowChildren(nsTableRefl
       if (NS_FAILED(rv))
         return;
     }
   }
    // if the child is a tbody in paginated mode reduce the height by a repeated footer
   bool allowRepeatedFooter = false;
   for (size_t childX = 0; childX < rowGroups.Length(); childX++) {
     nsIFrame* kidFrame = rowGroups[childX];
+    nsTableRowGroupFrame* rowGroupFrame = rowGroups[childX];
+    nscoord cellSpacingY = GetCellSpacingY(rowGroupFrame->GetStartRowIndex()+
+                                           rowGroupFrame->GetRowCount());
     // Get the frame state bits
     // See if we should only reflow the dirty child frames
     if (reflowAllKids ||
         NS_SUBTREE_DIRTY(kidFrame) ||
         (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
          (isPaginated || (kidFrame->GetStateBits() &
                           NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
       if (pageBreak) {
@@ -3162,17 +3167,16 @@ void
 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize)
 {
   nsTableCellMap* cellMap = GetCellMap();
   if (!cellMap) {
     NS_ASSERTION(false, "never ever call me until the cell map is built!");
     aDesiredSize.Height() = 0;
     return;
   }
-  nscoord  cellSpacingY = GetCellSpacingY();
   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
 
   // get the natural height based on the last child's (row group) rect
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
   if (rowGroups.IsEmpty()) {
     // tables can be used as rectangular items without content
     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
@@ -3185,19 +3189,21 @@ nsTableFrame::CalcDesiredHeight(const ns
     else
       aDesiredSize.Height() = 0;
     return;
   }
   int32_t rowCount = cellMap->GetRowCount();
   int32_t colCount = cellMap->GetColCount();
   nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
   if (rowCount > 0 && colCount > 0) {
-    desiredHeight += cellSpacingY;
+    desiredHeight += GetCellSpacingY(-1);
     for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
-      desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
+      desiredHeight += rowGroups[rgX]->GetSize().height +
+                       GetCellSpacingY(rowGroups[rgX]->GetRowCount() +
+                                       rowGroups[rgX]->GetStartRowIndex());
     }
   }
 
   // see if a specified table height requires dividing additional space to rows
   if (!GetPrevInFlow()) {
     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
     if ((tableSpecifiedHeight > 0) &&
         (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
@@ -3247,40 +3253,39 @@ void ResizeCells(nsTableFrame& aTableFra
   }
   aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
 }
 
 void
 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
                                      nscoord                  aAmount)
 {
-  nscoord cellSpacingY = GetCellSpacingY();
-
   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
 
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
 
   nscoord amountUsed = 0;
   // distribute space to each pct height row whose row group doesn't have a computed
   // height, and base the pct on the table height. If the row group had a computed
   // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
-  nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1));
-  nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
+  nscoord pctBasis = aReflowState.ComputedHeight() - GetCellSpacingY(-1, GetRowCount());
+  nscoord yOriginRG = borderPadding.top + GetCellSpacingY(0);
   nscoord yEndRG = yOriginRG;
   uint32_t rgX;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgRect = rgFrame->GetRect();
     if (!rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         nsRect rowRect = rowFrame->GetRect();
+        nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
         if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
           nscoord pctHeight = rowFrame->GetHeight(pctBasis);
           nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height);
           if (amountForRow > 0) {
             nsRect oldRowRect = rowRect;
             rowRect.height += amountForRow;
             // XXXbz we don't need to change rowRect.y to be yOriginRow?
             rowFrame->SetRect(rowRect);
@@ -3396,28 +3401,29 @@ nsTableFrame::DistributeHeightToRows(con
       else {
         NS_ERROR("invalid divisor");
         return;
       }
     }
   }
   // allocate the extra height to the unstyled row groups and rows
   nscoord heightToDistribute = aAmount - amountUsed;
-  yOriginRG = borderPadding.top + cellSpacingY;
+  yOriginRG = borderPadding.top + GetCellSpacingY(-1);
   yEndRG = yOriginRG;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgRect = rgFrame->GetRect();
     nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
     // see if there is an eligible row group or we distribute to all rows
     if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
+        nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
         nsRect rowRect = rowFrame->GetRect();
         nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
         // see if there is an eligible row or we distribute to all rows
         if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
           float ratio;
           if (eligibleRows) {
             if (!expandEmptyRows) {
               // The amount of additional space each row gets is proportional to
@@ -3502,34 +3508,81 @@ int32_t nsTableFrame::GetColumnWidth(int
   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
   if (this == firstInFlow) {
     nsTableColFrame* colFrame = GetColFrame(aColIndex);
     return colFrame ? colFrame->GetFinalWidth() : 0;
   }
   return firstInFlow->GetColumnWidth(aColIndex);
 }
 
-// XXX: could cache this.  But be sure to check style changes if you do!
 nscoord nsTableFrame::GetCellSpacingX()
 {
   if (IsBorderCollapse())
     return 0;
 
   return StyleTableBorder()->mBorderSpacingX;
 }
 
-// XXX: could cache this. But be sure to check style changes if you do!
+// XXX: could cache this.  But be sure to check style changes if you do!
+nscoord nsTableFrame::GetCellSpacingX(int32_t aColIndex)
+{
+  NS_ASSERTION(aColIndex >= -1 && aColIndex <= GetColCount(),
+               "Column index exceeds the bounds of the table");
+  // Index is irrelevant for ordinary tables.  We check that it falls within
+  // appropriate bounds to increase confidence of correctness in situations
+  // where it does matter.
+  return GetCellSpacingX();
+}
+
+nscoord nsTableFrame::GetCellSpacingX(int32_t aStartColIndex,
+                                      int32_t aEndColIndex)
+{
+  NS_ASSERTION(aStartColIndex >= -1 && aStartColIndex <= GetColCount(),
+               "Start column index exceeds the bounds of the table");
+  NS_ASSERTION(aEndColIndex >= -1 && aEndColIndex <= GetColCount(),
+               "End column index exceeds the bounds of the table");
+  NS_ASSERTION(aStartColIndex <= aEndColIndex,
+               "End index must not be less than start index");
+  // Only one possible value so just multiply it out. Tables where index
+  // matters will override this function
+  return GetCellSpacingX() * (aEndColIndex - aStartColIndex);
+}
+
 nscoord nsTableFrame::GetCellSpacingY()
 {
   if (IsBorderCollapse())
     return 0;
 
   return StyleTableBorder()->mBorderSpacingY;
 }
 
+// XXX: could cache this. But be sure to check style changes if you do!
+nscoord nsTableFrame::GetCellSpacingY(int32_t aRowIndex)
+{
+  NS_ASSERTION(aRowIndex >= -1 && aRowIndex <= GetRowCount(),
+               "Row index exceeds the bounds of the table");
+  // Index is irrelevant for ordinary tables.  We check that it falls within
+  // appropriate bounds to increase confidence of correctness in situations
+  // where it does matter.
+  return GetCellSpacingY();
+}
+
+nscoord nsTableFrame::GetCellSpacingY(int32_t aStartRowIndex,
+                                      int32_t aEndRowIndex)
+{
+  NS_ASSERTION(aStartRowIndex >= -1 && aStartRowIndex <= GetRowCount(),
+               "Start row index exceeds the bounds of the table");
+  NS_ASSERTION(aEndRowIndex >= -1 && aEndRowIndex <= GetRowCount(),
+               "End row index exceeds the bounds of the table");
+  NS_ASSERTION(aStartRowIndex <= aEndRowIndex,
+               "End index must not be less than start index");
+  // Only one possible value so just multiply it out. Tables where index
+  // matters will override this function
+  return GetCellSpacingY() * (aEndRowIndex - aStartRowIndex);
+}
 
 /* virtual */ nscoord
 nsTableFrame::GetBaseline() const
 {
   nscoord ascent = 0;
   RowGroupArray orderedRowGroups;
   OrderRowGroups(orderedRowGroups);
   nsTableRowFrame* firstRow = nullptr;
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -368,22 +368,78 @@ public:
 #ifdef DEBUG_FRAME_DUMP
   /** @see nsIFrame::GetFrameName */
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
   /** return the width of the column at aColIndex    */
   int32_t GetColumnWidth(int32_t aColIndex);
 
-  /** helper to get the cell spacing X style value */
-  nscoord GetCellSpacingX();
+  /** Helper to get the cell spacing X style value.
+   *  The argument refers to the space between column aColIndex and column
+   *  aColIndex + 1.  An index of -1 indicates the padding between the table
+   *  and the left border, an index equal to the number of columns indicates
+   *  the padding between the table and the right border.
+   *
+   *  Although in this class cell spacing does not depend on the index, it
+   *  may be important for overriding classes.
+   */
+  virtual nscoord GetCellSpacingX(int32_t aColIndex);
+
+  /** Helper to find the sum of the cell spacing between arbitrary columns.
+   *  The argument refers to the space between column aColIndex and column
+   *  aColIndex + 1.  An index of -1 indicates the padding between the table
+   *  and the left border, an index equal to the number of columns indicates
+   *  the padding between the table and the right border.
+   *
+   *  This method is equivalent to
+   *  nscoord result = 0;
+   *  for (i = aStartColIndex; i < aEndColIndex; i++) {
+   *    result += GetCellSpacingX(i);
+   *  }
+   *  return result;
+   */
+  virtual nscoord GetCellSpacingX(int32_t aStartColIndex,
+                                  int32_t aEndColIndex);
 
-  /** helper to get the cell spacing Y style value */
+  /** Helper to get the cell spacing Y style value.
+   *  The argument refers to the space between row aRowIndex and row
+   *  aRowIndex + 1.  An index of -1 indicates the padding between the table
+   *  and the top border, an index equal to the number of rows indicates
+   *  the padding between the table and the bottom border.
+   *
+   *  Although in this class cell spacing does not depend on the index, it
+   *  may be important for overriding classes.
+   */
+  virtual nscoord GetCellSpacingY(int32_t aRowIndex);
+
+  /** Helper to find the sum of the cell spacing between arbitrary rows.
+   *  The argument refers to the space between row aRowIndex and row
+   *  aRowIndex + 1.  An index of -1 indicates the padding between the table
+   *  and the top border, an index equal to the number of rows indicates
+   *  the padding between the table and the bottom border.
+   *
+   *  This method is equivalent to
+   *  nscoord result = 0;
+   *  for (i = aStartRowIndex; i < aEndRowIndex; i++) {
+   *    result += GetCellSpacingY(i);
+   *  }
+   *  return result;
+   */
+  virtual nscoord GetCellSpacingY(int32_t aStartRowIndex,
+                                  int32_t aEndRowIndex);
+
+private:
+  /* For the base implementation of nsTableFrame, cell spacing does not depend
+   * on row/column indexing.
+   */
+  nscoord GetCellSpacingX();
   nscoord GetCellSpacingY();
- 
+
+public:
   virtual nscoord GetBaseline() const MOZ_OVERRIDE;
   /** return the row span of a cell, taking into account row span magic at the bottom
     * of a table. The row span equals the number of rows spanned by aCell starting at
     * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row
     * index in which aCell originates.
     *
     * @param aStartRowIndex the cell
     * @param aCell          the cell
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -271,26 +271,25 @@ nsTableRowFrame::GetUsedPadding() const
   return nsMargin(0,0,0,0);
 }
 
 nscoord 
 GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
                                  nsTableFrame&     aTableFrame)
 {
   nscoord height = 0;
-  nscoord cellSpacingY = aTableFrame.GetCellSpacingY();
   int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
   // add in height of rows spanned beyond the 1st one
   nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
   for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) {
     if (nsGkAtoms::tableRowFrame == nextRow->GetType()) {
       height += nextRow->GetSize().height;
       rowX++;
     }
-    height += cellSpacingY;
+    height += aTableFrame.GetCellSpacingY(rowX);
     nextRow = nextRow->GetNextSibling();
   }
   return height;
 }
 
 nsTableCellFrame*  
 nsTableRowFrame::GetFirstCell() 
 {
@@ -677,41 +676,39 @@ nsTableRowFrame::CalculateCellActualHeig
 
   return NS_OK;
 }
 
 // Calculates the available width for the table cell based on the known
 // column widths taking into account column spans and column spacing
 static nscoord
 CalcAvailWidth(nsTableFrame&     aTableFrame,
-               nsTableCellFrame& aCellFrame,
-               nscoord           aCellSpacingX)
+               nsTableCellFrame& aCellFrame)
 {
   nscoord cellAvailWidth = 0;
   int32_t colIndex;
   aCellFrame.GetColIndex(colIndex);
   int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
   NS_ASSERTION(colspan > 0, "effective colspan should be positive");
 
   for (int32_t spanX = 0; spanX < colspan; spanX++) {
     cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX);
     if (spanX > 0 &&
         aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
-      cellAvailWidth += aCellSpacingX;
+      cellAvailWidth += aTableFrame.GetCellSpacingX(colIndex + spanX - 1);
     }
   }
   return cellAvailWidth;
 }
 
 nscoord
 GetSpaceBetween(int32_t       aPrevColIndex,
                 int32_t       aColIndex,
                 int32_t       aColSpan,
                 nsTableFrame& aTableFrame,
-                nscoord       aCellSpacingX,
                 bool          aIsLeftToRight,
                 bool          aCheckVisibility)
 {
   nscoord space = 0;
   int32_t colX;
   if (aIsLeftToRight) {
     for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) {
       bool isCollapsed = false;
@@ -726,17 +723,17 @@ GetSpaceBetween(int32_t       aPrevColIn
         const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
         bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
                                 groupVis->mVisible);
         isCollapsed = collapseCol || collapseGroup;
         if (!isCollapsed)
           space += aTableFrame.GetColumnWidth(colX);
       }
       if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
-        space += aCellSpacingX;
+        space += aTableFrame.GetCellSpacingX(colX - 1);
       }
     }
   } 
   else {
     int32_t lastCol = aColIndex + aColSpan - 1;
     for (colX = aPrevColIndex - 1; colX > lastCol; colX--) {
       bool isCollapsed = false;
       if (!aCheckVisibility) {
@@ -750,17 +747,17 @@ GetSpaceBetween(int32_t       aPrevColIn
         const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
         bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
                                 groupVis->mVisible);
         isCollapsed = collapseCol || collapseGroup;
         if (!isCollapsed)
           space += aTableFrame.GetColumnWidth(colX);
       }
       if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
-        space += aCellSpacingX;
+        space += aTableFrame.GetCellSpacingX(colX - 1);
       }
     }
   }
   return space;
 }
 
 // subtract the heights of aRow's prev in flows from the unpaginated height
 static
@@ -788,17 +785,16 @@ nsTableRowFrame::ReflowChildren(nsPresCo
                                 nsReflowStatus&          aStatus)
 {
   aStatus = NS_FRAME_COMPLETE;
 
   // XXXldb Should we be checking constrained height instead?
   const bool isPaginated = aPresContext->IsPaginated();
   const bool borderCollapse = aTableFrame.IsBorderCollapse();
 
-  nscoord cellSpacingX = aTableFrame.GetCellSpacingX();
   int32_t cellColSpan = 1;  // must be defined here so it's set properly for non-cell kids
   
   nsTableIterator iter(*this);
   // remember the col index of the previous cell to handle rowspans into this row
   int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount();
   int32_t prevColIndex  = firstPrevColIndex;
   nscoord x = 0; // running total of children x offset
 
@@ -850,32 +846,32 @@ nsTableRowFrame::ReflowChildren(nsPresCo
     int32_t cellColIndex;
     cellFrame->GetColIndex(cellColIndex);
     cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame);
 
     // If the adjacent cell is in a prior row (because of a rowspan) add in the space
     if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
         (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) {
       x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, 
-                           cellSpacingX, iter.IsLeftToRight(), false);
+                           iter.IsLeftToRight(), false);
     }
 
     // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
     prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
 
     // Reflow the child frame
     nsRect kidRect = kidFrame->GetRect();
     nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
     bool firstReflow =
       (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
 
     if (doReflowChild) {
       // Calculate the available width for the table cell using the known column widths
       nscoord availCellWidth =
-        CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX);
+        CalcAvailWidth(aTableFrame, *cellFrame);
 
       nsHTMLReflowMetrics desiredSize(aReflowState);
 
       // If the avail width is not the same as last time we reflowed the cell or
       // the cell wants to be bigger than what was available last time or
       // it is a style change reflow or we are printing, then we must reflow the
       // cell. Otherwise we can skip the reflow.
       // XXXldb Why is this condition distinct from doReflowChild above?
@@ -974,17 +970,17 @@ nsTableRowFrame::ReflowChildren(nsPresCo
       // we need to account for the cell's width even if it isn't reflowed
       x += kidRect.width;
 
       if (kidFrame->GetNextInFlow()) {
         aStatus = NS_FRAME_NOT_COMPLETE;
       }
     }
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
-    x += cellSpacingX;
+    x += aTableFrame.GetCellSpacingX(cellColIndex);
   }
 
   // just set our width to what was available. The table will calculate the width and not use our value.
   aDesiredSize.Width() = aReflowState.AvailableWidth();
 
   if (aReflowState.mFlags.mSpecialHeightReflow) {
     aDesiredSize.Height() = mRect.height;
   }
@@ -1139,23 +1135,23 @@ nsTableRowFrame::CollapseRowIfNecessary(
   nsRect rowRect = GetRect();
   nsRect oldRect = rowRect;
   nsRect oldVisualOverflow = GetVisualOverflowRect();
   
   rowRect.y -= aRowOffset;
   rowRect.width  = aWidth;
   nsOverflowAreas overflow;
   nscoord shift = 0;
-  nscoord cellSpacingX = tableFrame->GetCellSpacingX();
-  nscoord cellSpacingY = tableFrame->GetCellSpacingY();
 
   if (aCollapseGroup || collapseRow) {
     nsTableCellFrame* cellFrame = GetFirstCell();
     aDidCollapse = true;
-    shift = rowRect.height + cellSpacingY;
+    int32_t rowIndex;
+    cellFrame->GetRowIndex(rowIndex);
+    shift = rowRect.height + tableFrame->GetCellSpacingY(rowIndex);
     while (cellFrame) {
       nsRect cRect = cellFrame->GetRect();
       // If aRowOffset != 0, there's no point in invalidating the cells, since
       // we've already invalidated our overflow area.  Note that we _do_ still
       // need to invalidate if our row is not moving, because the cell might
       // span out of this row, so invalidating our row rect won't do enough.
       if (aRowOffset == 0) {
         InvalidateFrame();
@@ -1172,33 +1168,31 @@ nsTableRowFrame::CollapseRowIfNecessary(
     // row
     int32_t firstPrevColIndex = (iter.IsLeftToRight()) ? -1 :
                                 tableFrame->GetColCount();
     int32_t prevColIndex  = firstPrevColIndex;
     nscoord x = 0; // running total of children x offset
 
     int32_t colIncrement = iter.IsLeftToRight() ? 1 : -1;
 
-    //nscoord x = cellSpacingX;
-
     nsIFrame* kidFrame = iter.First();
     while (kidFrame) {
       nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
       if (cellFrame) {
         int32_t cellColIndex;
         cellFrame->GetColIndex(cellColIndex);
         int32_t cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
 
         // If the adjacent cell is in a prior row (because of a rowspan) add in
         // the space
         if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
             (!iter.IsLeftToRight() &&
              (prevColIndex != cellColIndex + cellColSpan))) {
           x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
-                               *tableFrame, cellSpacingX, iter.IsLeftToRight(),
+                               *tableFrame, iter.IsLeftToRight(),
                                true);
         }
         nsRect cRect(x, 0, 0, rowRect.height);
 
         // remember the rightmost (ltr) or leftmost (rtl) column this cell
         // spans into
         prevColIndex = (iter.IsLeftToRight()) ?
                        cellColIndex + (cellColSpan - 1) : cellColIndex;
@@ -1223,33 +1217,34 @@ nsTableRowFrame::CollapseRowIfNecessary(
             isVisible = true;
             if ((actualColSpan > 1)) {
               nsTableColFrame* nextColFrame =
                 tableFrame->GetColFrame(colX + colIncrement);
               const nsStyleVisibility* nextColVis =
               nextColFrame->StyleVisibility();
               if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) &&
                   tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) {
-                cRect.width += cellSpacingX;
+                cRect.width += tableFrame->GetCellSpacingX(cellColIndex);
               }
             }
           }
         }
         x += cRect.width;
         if (isVisible)
-          x += cellSpacingX;
+          x += tableFrame->GetCellSpacingX(cellColIndex);
         int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
         nsTableRowFrame* rowFrame = GetNextRow();
         for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
           const nsStyleVisibility* nextRowVis = rowFrame->StyleVisibility();
           bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
                                     nextRowVis->mVisible);
           if (!collapseNextRow) {
             nsRect nextRect = rowFrame->GetRect();
-            cRect.height += nextRect.height + cellSpacingY;
+            cRect.height += nextRect.height +
+                            tableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
           }
           rowFrame = rowFrame->GetNextRow();
         }
 
         nsRect oldCellRect = cellFrame->GetRect();
         nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
 
         if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -336,17 +336,16 @@ nsTableRowGroupFrame::ReflowChildren(nsP
                                      nsReflowStatus&        aStatus,
                                      bool*                aPageBreakBeforeEnd)
 {
   if (aPageBreakBeforeEnd) 
     *aPageBreakBeforeEnd = false;
 
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   const bool borderCollapse = tableFrame->IsBorderCollapse();
-  nscoord cellSpacingY = tableFrame->GetCellSpacingY();
 
   // XXXldb Should we really be checking this rather than available height?
   // (Think about multi-column layout!)
   bool isPaginated = aPresContext->IsPaginated() && 
                        NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
 
   bool haveRow = false;
   bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
@@ -357,17 +356,17 @@ nsTableRowGroupFrame::ReflowChildren(nsP
   for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
        prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) {
     nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame);
     if (!rowFrame) {
       // XXXldb nsCSSFrameConstructor needs to enforce this!
       NS_NOTREACHED("yikes, a non-row child");
       continue;
     }
-
+    nscoord cellSpacingY = tableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
     haveRow = true;
 
     // Reflow the row frame
     if (reflowAllKids ||
         NS_SUBTREE_DIRTY(kidFrame) ||
         (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
          (isPaginated || (kidFrame->GetStateBits() &
                           NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
@@ -448,17 +447,18 @@ nsTableRowGroupFrame::ReflowChildren(nsP
       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
         aReflowState.availSize.height -= height;
       }
     }
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
   }
 
   if (haveRow)
-    aReflowState.y -= cellSpacingY;
+    aReflowState.y -= tableFrame->GetCellSpacingY(GetStartRowIndex() +
+                                                  GetRowCount());
 
   // Return our desired rect
   aDesiredSize.Width() = aReflowState.reflowState.AvailableWidth();
   aDesiredSize.Height() = aReflowState.y;
 
   if (aReflowState.reflowState.mFlags.mSpecialHeightReflow) {
     DidResizeRows(aDesiredSize);
     if (isPaginated) {
@@ -532,19 +532,16 @@ nsTableRowGroupFrame::DidResizeRows(nsHT
 void 
 nsTableRowGroupFrame::CalculateRowHeights(nsPresContext*           aPresContext, 
                                           nsHTMLReflowMetrics&     aDesiredSize,
                                           const nsHTMLReflowState& aReflowState)
 {
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   const bool isPaginated = aPresContext->IsPaginated();
 
-  // all table cells have the same top and bottom margins, namely cellSpacingY
-  nscoord cellSpacingY = tableFrame->GetCellSpacingY();
-
   int32_t numEffCols = tableFrame->GetEffectiveColCount();
 
   int32_t startRowIndex = GetStartRowIndex();
   // find the row corresponding to the row index we just found
   nsTableRowFrame* startRowFrame = GetFirstRow();
 
   if (!startRowFrame) return;
 
@@ -614,16 +611,17 @@ nsTableRowGroupFrame::CalculateRowHeight
     for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
       // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a 
       // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating
       // cells yet the row may have a continued cell which originates in it.
       if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) {
         nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
         // iteratate the row's cell frames 
         while (cellFrame) {
+          nscoord cellSpacingY = tableFrame->GetCellSpacingY(startRowIndex + rowIndex);
           int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
           if ((rowIndex + rowSpan) > numRows) {
             // there might be rows pushed already to the nextInFlow
             rowSpan = numRows - rowIndex;
           }
           if (rowSpan > 1) { // a cell with rowspan > 1, determine the height of the rows it spans
             nscoord heightOfRowsSpanned = 0;
             nscoord heightOfUnStyledRowsSpanned = 0;
@@ -731,17 +729,18 @@ nsTableRowGroupFrame::CalculateRowHeight
                          ? rInfo.pctHeight - rInfo.height: 0;
       rowExtra = std::min(rowExtra, extra);
       UpdateHeights(rInfo, rowExtra, heightOfRows, heightOfUnStyledRows);
       extra -= rowExtra;
     }
   }
 
   bool styleHeightAllocation = false;
-  nscoord rowGroupHeight = startRowGroupHeight + heightOfRows + ((numRows - 1) * cellSpacingY);
+  nscoord rowGroupHeight = startRowGroupHeight + heightOfRows +
+                           tableFrame->GetCellSpacingY(0, numRows-1);
   // if we have a style height, allocate the extra height to unconstrained rows
   if ((aReflowState.ComputedHeight() > rowGroupHeight) && 
       (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight())) {
     nscoord extraComputedHeight = aReflowState.ComputedHeight() - rowGroupHeight;
     nscoord extraUsed = 0;
     bool haveUnStyledRows = (heightOfUnStyledRows > 0);
     nscoord divisor = (haveUnStyledRows) 
                       ? heightOfUnStyledRows : heightOfRows;
@@ -790,17 +789,17 @@ nsTableRowGroupFrame::CalculateRowHeight
 
       nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
                                          false);
     }
     if (movedFrame) {
       nsTableFrame::RePositionViews(rowFrame);
       // XXXbz we don't need to update our overflow area?
     }
-    yOrigin += rowHeight + cellSpacingY;
+    yOrigin += rowHeight + tableFrame->GetCellSpacingY(startRowIndex + rowIndex);
   }
 
   if (isPaginated && styleHeightAllocation) {
     // since the row group has a style height, cache the row heights, so next in flows can honor them 
     CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
   }
 
   DidResizeRows(aDesiredSize);
@@ -834,17 +833,18 @@ nsTableRowGroupFrame::CollapseRowGroupIf
 
   nsRect groupRect = GetRect();
   nsRect oldGroupRect = groupRect;
   nsRect oldGroupVisualOverflow = GetVisualOverflowRect();
   
   groupRect.height -= yGroupOffset;
   if (didCollapse) {
     // add back the cellspacing between rowgroups
-    groupRect.height += tableFrame->GetCellSpacingY();
+    groupRect.height += tableFrame->GetCellSpacingY(GetStartRowIndex() +
+                                                    GetRowCount());
   }
 
   groupRect.y -= aYTotalOffset;
   groupRect.width = aWidth;
 
   if (aYTotalOffset != 0) {
     InvalidateFrameSubtree();
   }
@@ -1045,17 +1045,16 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
 
   nsTableRowFrame* prevRowFrame = nullptr;
   aDesiredSize.Height() = 0;
 
   nscoord availWidth  = aReflowState.AvailableWidth();
   nscoord availHeight = aReflowState.AvailableHeight();
   
   const bool borderCollapse = aTableFrame->IsBorderCollapse();
-  nscoord cellSpacingY = aTableFrame->GetCellSpacingY();
   
   // get the page height
   nscoord pageHeight = aPresContext->GetPageSize().height;
   NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE, 
                "The table shouldn't be split when there should be space");
 
   bool isTopOfPage = aReflowState.mFlags.mIsTopOfPage;
   nsTableRowFrame* firstRowThisPage = GetFirstRow();
@@ -1063,16 +1062,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
   // Need to dirty the table's geometry, or else the row might skip
   // reflowing its cell as an optimization.
   aTableFrame->SetGeometryDirty();
 
   // Walk each of the row frames looking for the first row frame that doesn't fit 
   // in the available space
   for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
     bool rowIsOnPage = true;
+    nscoord cellSpacingY = aTableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
     nsRect rowRect = rowFrame->GetRect();
     // See if the row fits on this page
     if (rowRect.YMost() > availHeight) {
       nsTableRowFrame* contRow = nullptr;
       // Reflow the row in the availabe space and have it split if it is the 1st
       // row (on the page) or there is at least 5% of the current page available 
       // XXX this 5% should be made a preference 
       if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) { 
@@ -1507,28 +1507,31 @@ nsTableRowGroupFrame::GetUsedPadding() c
   return nsMargin(0,0,0,0);
 }
 
 nscoord 
 nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState)
 {
   nscoord result = 0;
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  int32_t startRowIndex = GetStartRowIndex();
   if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
-    nscoord cellSpacing = std::max(0, GetRowCount() - 1) * tableFrame->GetCellSpacingY();
+    nscoord cellSpacing = tableFrame->GetCellSpacingY(startRowIndex,
+                                                      std::max(startRowIndex,
+                                                               startRowIndex + GetRowCount() - 1));
     result = aReflowState.ComputedHeight() - cellSpacing;
   }
   else {
     const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
     if (parentRS && (tableFrame != parentRS->frame)) {
       parentRS = parentRS->parentReflowState;
     }
     if (parentRS && (tableFrame == parentRS->frame) && 
         (parentRS->ComputedHeight() > 0) && (parentRS->ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
-      nscoord cellSpacing = std::max(0, tableFrame->GetRowCount() + 1) * tableFrame->GetCellSpacingY();
+      nscoord cellSpacing = tableFrame->GetCellSpacingY(-1, tableFrame->GetRowCount());
       result = parentRS->ComputedHeight() - cellSpacing;
     }
   }
 
   return result;
 }
 
 bool