Make OrderRowGroups type-safe. Bug 367706, r=bernd, sr=roc
authorbzbarsky@mit.edu
Tue, 05 Jun 2007 11:55:26 -0700
changeset 2141 1adc60ec025bc59f167f060ecb0e0d01ac4f1f33
parent 2140 2009b6ab7f051ddb19b272e6a5769c616dca7fee
child 2142 5dcd50ddc98b9f51ef6d16e97f1b834f375cffda
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbernd, roc
bugs367706
milestone1.9a6pre
Make OrderRowGroups type-safe. Bug 367706, r=bernd, sr=roc
layout/tables/nsCellMap.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTablePainter.cpp
--- a/layout/tables/nsCellMap.cpp
+++ b/layout/tables/nsCellMap.cpp
@@ -71,28 +71,24 @@ BCCellData::~BCCellData()
 // nsTableCellMap
 
 nsTableCellMap::nsTableCellMap(nsTableFrame&   aTableFrame,
                                PRBool          aBorderCollapse)
 :mTableFrame(aTableFrame), mFirstMap(nsnull), mBCInfo(nsnull)
 {
   MOZ_COUNT_CTOR(nsTableCellMap);
 
-  nsAutoVoidArray orderedRowGroups;
-  PRUint32 numRowGroups;
-  aTableFrame.OrderRowGroups(orderedRowGroups, numRowGroups);
+  nsTableFrame::RowGroupArray orderedRowGroups;
+  aTableFrame.OrderRowGroups(orderedRowGroups);
 
-  for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-    nsTableRowGroupFrame* rgFrame =
-      nsTableFrame::GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgX));
-    if (rgFrame) {
-      nsTableRowGroupFrame* prior = (0 == rgX)
-        ? nsnull : nsTableFrame::GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgX - 1));
-      InsertGroupCellMap(*rgFrame, prior);
-    }
+  nsTableRowGroupFrame* prior = nsnull;
+  for (PRUint32 rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
+    InsertGroupCellMap(*rgFrame, prior);
+    prior = rgFrame;
   }
   if (aBorderCollapse) {
     mBCInfo = new BCInfo();
   }
 }
 
 nsTableCellMap::~nsTableCellMap()
 {
@@ -291,68 +287,62 @@ nsTableCellMap::GetMapFor(const nsTableR
   if (map) {
     return map;
   }
   
   // if aRowGroup is a repeated header or footer find the header or footer it was repeated from
   if (aRowGroup->IsRepeatable()) {
     nsTableFrame* fifTable = NS_STATIC_CAST(nsTableFrame*, mTableFrame.GetFirstInFlow());
 
-    nsAutoVoidArray rowGroups;
-    PRUint32 numRowGroups;
-    nsTableRowGroupFrame *thead, *tfoot;
-    // find the original header/footer 
-    fifTable->OrderRowGroups(rowGroups, numRowGroups, &thead, &tfoot);
-
     const nsStyleDisplay* display = aRowGroup->GetStyleDisplay();
     nsTableRowGroupFrame* rgOrig = 
-      (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ? thead : tfoot; 
+      (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ?
+      fifTable->GetTHead() : fifTable->GetTFoot(); 
     // find the row group cell map using the original header/footer
     if (rgOrig && rgOrig != aRowGroup) {
       return GetMapFor(rgOrig, aStartHint);
     }
   }
 
   return nsnull;
 }
 
 void
 nsTableCellMap::Synchronize(nsTableFrame* aTableFrame)
 {
-  nsAutoVoidArray orderedRowGroups;
-  nsAutoVoidArray maps;
-  PRUint32 numRowGroups;
-  PRInt32 mapIndex;
+  nsTableFrame::RowGroupArray orderedRowGroups;
+  nsAutoTPtrArray<nsCellMap, 8> maps;
 
-  maps.Clear();
-  aTableFrame->OrderRowGroups(orderedRowGroups, numRowGroups);
-  if (!numRowGroups) {
+  aTableFrame->OrderRowGroups(orderedRowGroups);
+  if (!orderedRowGroups.Length()) {
     return;
   }
 
+  // XXXbz this fails if orderedRowGroups is missing some row groups
+  // (due to OOM when appending to the array, e.g. -- we leak maps in
+  // that case).
+  
   // Scope |map| outside the loop so we can use it as a hint.
   nsCellMap* map = nsnull;
-  for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-    nsTableRowGroupFrame* rgFrame =
-      nsTableFrame::GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgX));
-    if (rgFrame) {
-      map = GetMapFor(rgFrame, map);
-      if (map) {
-        if (!maps.AppendElement(map)) {
-          delete map;
-          NS_WARNING("Could not AppendElement");
-        }
+  for (PRUint32 rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
+    map = GetMapFor(rgFrame, map);
+    if (map) {
+      if (!maps.AppendElement(map)) {
+        delete map;
+        NS_WARNING("Could not AppendElement");
       }
     }
   }
-  mapIndex = maps.Count() - 1;
-  nsCellMap* nextMap = (nsCellMap*) maps.ElementAt(mapIndex);
+
+  PRInt32 mapIndex = maps.Length() - 1;  // Might end up -1
+  nsCellMap* nextMap = maps.ElementAt(mapIndex);
   nextMap->SetNextSibling(nsnull);
   for (mapIndex-- ; mapIndex >= 0; mapIndex--) {
-    nsCellMap* map = (nsCellMap*) maps.ElementAt(mapIndex);
+    nsCellMap* map = maps.ElementAt(mapIndex);
     map->SetNextSibling(nextMap);
     nextMap = map;
   }
   mFirstMap = nextMap;
 }
 
 PRBool
 nsTableCellMap::HasMoreThanOneCell(PRInt32 aRowIndex) const
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -535,49 +535,44 @@ PRInt32 nsTableFrame::GetEffectiveCOLSAt
   return result;
 }
 
 void nsTableFrame::AdjustRowIndices(PRInt32         aRowIndex,
                                     PRInt32         aAdjustment)
 {
   // Iterate over the row groups and adjust the row indices of all rows 
   // whose index is >= aRowIndex.
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups);
-
-  for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-    nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
-    rgFrame->AdjustRowIndices(aRowIndex, aAdjustment);
+  RowGroupArray rowGroups;
+  OrderRowGroups(rowGroups);
+
+  for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment);
   }
 }
 
 
 void nsTableFrame::ResetRowIndices(nsIFrame* aFirstRowGroupFrame,
                                    nsIFrame* aLastRowGroupFrame)
 {
   // Iterate over the row groups and adjust the row indices of all rows
   // omit the rowgroups that will be inserted later
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups, nsnull);
+  RowGroupArray rowGroups;
+  OrderRowGroups(rowGroups);
 
   PRInt32 rowIndex = 0;
   nsTableRowGroupFrame* newRgFrame = nsnull;
   nsIFrame* omitRgFrame = aFirstRowGroupFrame;
   if (omitRgFrame) {
     newRgFrame = GetRowGroupFrame(omitRgFrame);
     if (omitRgFrame == aLastRowGroupFrame)
       omitRgFrame = nsnull;
   }
 
-  for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-    nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
+  for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     if (rgFrame == newRgFrame) {
       // omit the new rowgroup
       if (omitRgFrame) {
         omitRgFrame = omitRgFrame->GetNextSibling();
         if (omitRgFrame) {
           newRgFrame  = GetRowGroupFrame(omitRgFrame);
           if (omitRgFrame == aLastRowGroupFrame)
             omitRgFrame = nsnull;
@@ -978,23 +973,22 @@ void nsTableFrame::RemoveCell(nsTableCel
       SetBCDamageArea(damageArea);
     }
   }
 }
 
 PRInt32
 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame& aRowGroupFrame)
 {
-  nsAutoVoidArray orderedRowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(orderedRowGroups, numRowGroups);
+  RowGroupArray orderedRowGroups;
+  OrderRowGroups(orderedRowGroups);
 
   PRInt32 rowIndex = 0;
-  for (PRUint32 rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex));
+  for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
+    nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
     if (rgFrame == &aRowGroupFrame) {
       break;
     }
     PRInt32 numRows = rgFrame->GetRowCount();
     rowIndex += numRows;
   }
   return rowIndex;
 }
@@ -1172,31 +1166,31 @@ nsTableFrame::InsertRowGroups(nsIFrame* 
                               nsIFrame* aLastRowGroupFrame)
 {
 #ifdef DEBUG_TABLE_CELLMAP
   printf("=== insertRowGroupsBefore\n");
   Dump(PR_TRUE, PR_FALSE, PR_TRUE);
 #endif
   nsTableCellMap* cellMap = GetCellMap();
   if (cellMap) {
-    nsAutoVoidArray orderedRowGroups;
-    PRUint32 numRowGroups;
-    OrderRowGroups(orderedRowGroups, numRowGroups);
+    RowGroupArray orderedRowGroups;
+    OrderRowGroups(orderedRowGroups);
+
     nsAutoVoidArray rows;
     // Loop over the rowgroups and check if some of them are new, if they are
     // insert cellmaps in the order that is predefined by OrderRowGroups,
     PRUint32 rgIndex;
-    for (rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
+    for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
       nsIFrame* kidFrame = aFirstRowGroupFrame;
       while (kidFrame) {
         nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
 
-        if (GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex)) == rgFrame) {
-          nsTableRowGroupFrame* priorRG = (0 == rgIndex)
-            ? nsnull : GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex - 1)); 
+        if (orderedRowGroups[rgIndex] == rgFrame) {
+          nsTableRowGroupFrame* priorRG =
+            (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1]; 
           // create and add the cell map for the row group
           cellMap->InsertGroupCellMap(*rgFrame, priorRG);
         
           break;
         }
         else {
           if (kidFrame == aLastRowGroupFrame) {
             break;
@@ -1204,24 +1198,24 @@ nsTableFrame::InsertRowGroups(nsIFrame* 
           kidFrame = kidFrame->GetNextSibling();
         }
       }
     }
     cellMap->Synchronize(this);
     ResetRowIndices(aFirstRowGroupFrame, aLastRowGroupFrame);
 
     //now that the cellmaps are reordered too insert the rows
-    for (rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
+    for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
       nsIFrame* kidFrame = aFirstRowGroupFrame;
       while (kidFrame) {
         nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
 
-        if (GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex)) == rgFrame) {
-          nsTableRowGroupFrame* priorRG = (0 == rgIndex)
-            ? nsnull : GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex - 1)); 
+        if (orderedRowGroups[rgIndex] == rgFrame) {
+          nsTableRowGroupFrame* priorRG =
+            (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1]; 
           // collect the new row frames in an array and add them to the table
           PRInt32 numRows = CollectRows(kidFrame, rows);
           if (numRows > 0) {
             PRInt32 rowIndex = 0;
             if (priorRG) {
               PRInt32 priorNumRows = priorRG->GetRowCount();
               rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
             }
@@ -1554,23 +1548,22 @@ nsTableFrame::SetColumnDimensions(nscoor
 // XXX this could be made more general to handle row modifications that change the
 // table height, but first we need to scrutinize every Invalidate
 static void
 ProcessRowInserted(nsTableFrame&   aTableFrame,
                    PRBool          aInvalidate,
                    nscoord         aNewHeight)
 {
   aTableFrame.SetRowInserted(PR_FALSE); // reset the bit that got us here
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  aTableFrame.OrderRowGroups(rowGroups, numRowGroups);
+  nsTableFrame::RowGroupArray rowGroups;
+  aTableFrame.OrderRowGroups(rowGroups);
   // find the row group containing the inserted row
-  for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-    nsTableRowGroupFrame* rgFrame = (nsTableRowGroupFrame*)rowGroups.ElementAt(rgX);
-    if (!rgFrame) continue; // should never happen
+  for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
+    NS_ASSERTION(rgFrame, "Must have rgFrame here");
     nsIFrame* childFrame = rgFrame->GetFirstChild(nsnull);
     // find the row that was inserted first
     while (childFrame) {
       if (nsGkAtoms::tableRowFrame == childFrame->GetType()) {
         nsTableRowFrame* rowFrame = (nsTableRowFrame*)childFrame;
         if (rowFrame->IsFirstInserted()) {
           rowFrame->SetFirstInserted(PR_FALSE);
           if (aInvalidate) {
@@ -2065,32 +2058,35 @@ nsTableFrame::GetFirstBodyRowGroupFrame(
   }
 
   return nsnull;
 }
 
 // Table specific version that takes into account repeated header and footer
 // frames when continuing table frames
 void
-nsTableFrame::PushChildren(const nsAutoVoidArray& aFrames,
+nsTableFrame::PushChildren(const FrameArray& aFrames,
                            PRInt32 aPushFrom)
 {
   NS_PRECONDITION(aPushFrom > 0, "pushing first child");
 
   // extract the frames from the array into a sibling list
   nsFrameList frames;
   nsIFrame* lastFrame = nsnull;
   PRUint32 childX;
-  nsIFrame* prevSiblingHint =
-    NS_STATIC_CAST(nsIFrame*, aFrames.ElementAt(aPushFrom - 1));
-  for (childX = aPushFrom; childX < aFrames.Count(); ++childX) {
-    nsIFrame* f = NS_STATIC_CAST(nsIFrame*, aFrames.FastElementAt(childX));
-    // Don't push repeatable frames, do push non-rowgroup frames
-    if (f->GetType() != nsGkAtoms::tableRowGroupFrame ||
-        !NS_STATIC_CAST(nsTableRowGroupFrame*, f)->IsRepeatable()) {
+  nsIFrame* prevSiblingHint = aFrames.SafeElementAt(aPushFrom - 1);
+  for (childX = aPushFrom; childX < aFrames.Length(); ++childX) {
+    nsIFrame* f = aFrames[childX];
+    // Don't push repeatable frames, do push non-rowgroup frames.
+    // XXXbz Need to push the non-rowgroup frames, even though we don't reflow
+    // them, so that we don't lose them.  Of course there shouldn't be any
+    // non-rowgroup frames here...
+    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(f);
+    NS_ASSERTION(rgFrame, "Unexpected non-row-group frame");
+    if (!rgFrame || !rgFrame->IsRepeatable()) {
       mFrames.RemoveFrame(f, prevSiblingHint);
       frames.InsertFrame(nsnull, lastFrame, f);
       lastFrame = f;
     }
   }
 
   if (nsnull != GetNextInFlow()) {
     nsTableFrame* nextInFlow = (nsTableFrame*)GetNextInFlow();
@@ -2160,27 +2156,25 @@ nsTableFrame::AdjustForCollapsingRowsCol
 {
   nscoord yTotalOffset = 0; // total offset among all rows in all row groups
 
   // reset the bit, it will be set again if row/rowgroup is collapsed
   SetNeedToCollapse(PR_FALSE);
   
   // collapse the rows and/or row groups as necessary
   // Get the ordered children
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups);
+  RowGroupArray rowGroups;
+  OrderRowGroups(rowGroups);
   nscoord width = GetCollapsedWidth(aBorderPadding);
   nscoord rgWidth = width - 2 * GetCellSpacingX();
   nsRect overflowArea(0, 0, 0, 0);
   // Walk the list of children
-  for (PRUint32 childX = 0; childX < numRowGroups; childX++) {
-    nsIFrame* childFrame = (nsIFrame*)rowGroups.ElementAt(childX);
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(childFrame);
-    if (!rgFrame) continue; // skip foreign frame types
+  for (PRUint32 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(overflowArea, rgFrame);
   } 
 
   aDesiredSize.height -= yTotalOffset;
   aDesiredSize.width   = width;
   overflowArea.UnionRect(nsRect(0, 0, aDesiredSize.width, aDesiredSize.height),
                          overflowArea);
@@ -2425,18 +2419,16 @@ nsTableFrame::RemoveFrame(nsIAtom*      
       CreateAnonymousColFrames(numAnonymousColsToAdd,
                                eColAnonymousCell, PR_TRUE);
     }
 
   } else {
     NS_ASSERTION(!aListName, "unexpected child list");
     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aOldFrame);
     if (rgFrame) {
-      PRInt32 startRowIndex = rgFrame->GetStartRowIndex();
-      PRInt32 numRows = rgFrame->GetRowCount();
       // remove the row group from the cell map
       nsTableCellMap* cellMap = GetCellMap();
       if (cellMap) {
         cellMap->RemoveGroupCellMap(rgFrame);
       }
 
        // remove the row group frame from the sibling chain
       mFrames.DestroyFrame(aOldFrame);
@@ -2630,89 +2622,201 @@ void nsTableFrame::PlaceChild(nsTableRef
 
   // If our height is constrained, then update the available height
   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
     aReflowState.availSize.height -= aKidDesiredSize.height;
   }
 }
 
 void
-nsTableFrame::OrderRowGroups(nsVoidArray&           aChildren,
-                             PRUint32&              aNumRowGroups,
-                             nsTableRowGroupFrame** aHead,
-                             nsTableRowGroupFrame** aFoot) const
+nsTableFrame::OrderRowGroups(RowGroupArray& aChildren) const
 {
   aChildren.Clear();
-  nsIFrame* head = nsnull;
-  nsIFrame* foot = nsnull;
-  // initialize out parameters, if present
-  if (aHead)      *aHead      = nsnull;
-  if (aFoot)      *aFoot      = nsnull;
+  nsTableRowGroupFrame* head = nsnull;
+  nsTableRowGroupFrame* foot = nsnull;
   
   nsIFrame* kidFrame = mFrames.FirstChild();
-  nsAutoVoidArray nonRowGroups;
-  // put the tbodies first, and the non row groups last
   while (kidFrame) {
     const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
-    if (IsRowGroup(kidDisplay->mDisplay)) {
+    nsTableRowGroupFrame* rowGroup = GetRowGroupFrame(kidFrame);
+    if (NS_LIKELY(rowGroup)) {
       switch(kidDisplay->mDisplay) {
       case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
         if (head) { // treat additional thead like tbody
-          aChildren.AppendElement(kidFrame);
+          aChildren.AppendElement(rowGroup);
         }
         else {
-          head = kidFrame;
-          if (aHead) {
-            *aHead = (nsTableRowGroupFrame*)head;
-          }
+          head = rowGroup;
         }
         break;
       case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
-        if (foot) {
-          aChildren.AppendElement(kidFrame);
+        if (foot) { // treat additional tfoot like tbody
+          aChildren.AppendElement(rowGroup);
         }
         else {
-          foot = kidFrame;
-          if (aFoot) {
-            *aFoot = (nsTableRowGroupFrame*)foot;
-          }
+          foot = rowGroup;
         }
         break;
+      case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
+        aChildren.AppendElement(rowGroup);
+        break;
       default:
-        aChildren.AppendElement(kidFrame);
+        NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
+        // Just ignore it
+        break;
       }
     }
-    else {
-      nonRowGroups.AppendElement(kidFrame);
-    }
     // Get the next sibling but skip it if it's also the next-in-flow, since
     // a next-in-flow will not be part of the current table.
     while (kidFrame) {
       nsIFrame* nif = kidFrame->GetNextInFlow();
       kidFrame = kidFrame->GetNextSibling();
       if (kidFrame != nif) 
         break;
     }
   }
-  aNumRowGroups = aChildren.Count();
+
   // put the thead first
   if (head) {
-    aChildren.InsertElementAt(head, 0);
-    aNumRowGroups++;
-  }
+    aChildren.InsertElementAt(0, head);
+  }
+
   // put the tfoot after the last tbody
   if (foot) {
-    aChildren.InsertElementAt(foot, aNumRowGroups);
-    aNumRowGroups++;
-  }
-  // put the non row groups at the end
-  PRInt32 numNonRowGroups = nonRowGroups.Count();
-  for (PRInt32 i = 0; i < numNonRowGroups; i++) {
-    aChildren.AppendElement(nonRowGroups.ElementAt(i));
-  }
+    aChildren.AppendElement(foot);
+  }
+}
+
+PRUint32
+nsTableFrame::OrderRowGroups(FrameArray& aChildren,
+                             nsTableRowGroupFrame** aHead,
+                             nsTableRowGroupFrame** aFoot) const
+{
+  aChildren.Clear();
+  // initialize out parameters
+  *aHead = nsnull;
+  *aFoot = nsnull;
+
+  FrameArray nonRowGroups;
+
+  nsIFrame* head = nsnull;
+  nsIFrame* foot = nsnull;
+  
+  nsIFrame* kidFrame = mFrames.FirstChild();
+  while (kidFrame) {
+    const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
+    nsTableRowGroupFrame* rowGroup = GetRowGroupFrame(kidFrame);
+    if (NS_LIKELY(rowGroup)) {
+      switch(kidDisplay->mDisplay) {
+      case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
+        if (head) { // treat additional thead like tbody
+          aChildren.AppendElement(kidFrame);
+        }
+        else {
+          head = kidFrame;
+          *aHead = rowGroup;
+        }
+        break;
+      case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
+        if (foot) { // treat additional tfoot like tbody
+          aChildren.AppendElement(kidFrame);
+        }
+        else {
+          foot = kidFrame;
+          *aFoot = rowGroup;
+        }
+        break;
+      case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
+        aChildren.AppendElement(kidFrame);
+        break;
+      default:
+        break;
+      }
+    } else {
+      NS_NOTREACHED("Non-row-group primary frame list child of an "
+                    "nsTableFrame?  How come?");
+      nonRowGroups.AppendElement(kidFrame);
+    }
+
+    // Get the next sibling but skip it if it's also the next-in-flow, since
+    // a next-in-flow will not be part of the current table.
+    while (kidFrame) {
+      nsIFrame* nif = kidFrame->GetNextInFlow();
+      kidFrame = kidFrame->GetNextSibling();
+      if (kidFrame != nif) 
+        break;
+    }
+  }
+  
+  // put the thead first
+  if (head) {
+    aChildren.InsertElementAt(0, head);
+  }
+
+  // put the tfoot after the last tbody
+  if (foot) {
+    aChildren.AppendElement(foot);
+  }
+
+  PRUint32 rowGroupCount = aChildren.Length();
+  aChildren.AppendElements(nonRowGroups);
+
+  return rowGroupCount;
+}
+
+nsTableRowGroupFrame*
+nsTableFrame::GetTHead() const
+{
+  nsIFrame* kidFrame = mFrames.FirstChild();
+  while (kidFrame) {
+    if (kidFrame->GetStyleDisplay()->mDisplay ==
+          NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) {
+      nsTableRowGroupFrame* rg = GetRowGroupFrame(kidFrame);
+      if (rg) {
+        return rg;
+      }
+    }
+
+    // Get the next sibling but skip it if it's also the next-in-flow, since
+    // a next-in-flow will not be part of the current table.
+    while (kidFrame) {
+      nsIFrame* nif = kidFrame->GetNextInFlow();
+      kidFrame = kidFrame->GetNextSibling();
+      if (kidFrame != nif) 
+        break;
+    }
+  }
+
+  return nsnull;
+}
+
+nsTableRowGroupFrame*
+nsTableFrame::GetTFoot() const
+{
+  nsIFrame* kidFrame = mFrames.FirstChild();
+  while (kidFrame) {
+    if (kidFrame->GetStyleDisplay()->mDisplay ==
+          NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) {
+      nsTableRowGroupFrame* rg = GetRowGroupFrame(kidFrame);
+      if (rg) {
+        return rg;
+      }
+    }
+
+    // Get the next sibling but skip it if it's also the next-in-flow, since
+    // a next-in-flow will not be part of the current table.
+    while (kidFrame) {
+      nsIFrame* nif = kidFrame->GetNextInFlow();
+      kidFrame = kidFrame->GetNextSibling();
+      if (kidFrame != nif) 
+        break;
+    }
+  }
+
+  return nsnull;
 }
 
 static PRBool
 IsRepeatable(nsTableRowGroupFrame& aHeaderOrFooter,
              nscoord               aPageHeight)
 {
   return aHeaderOrFooter.GetSize().height < (aPageHeight / 4);
 }
@@ -2737,23 +2841,22 @@ nsTableFrame::ReflowChildren(nsTableRefl
   PRBool isPaginated = presContext->IsPaginated();
 
   aOverflowArea = nsRect (0, 0, 0, 0);
 
   PRBool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
                          mBits.mResizedColumns ||
                          IsGeometryDirty();
 
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
+  FrameArray rowGroups;
   nsTableRowGroupFrame *thead, *tfoot;
-  OrderRowGroups(rowGroups, numRowGroups, &thead, &tfoot);
+  PRUint32 numRowGroups = OrderRowGroups(rowGroups, &thead, &tfoot);
   PRBool pageBreak = PR_FALSE;
   for (PRUint32 childX = 0; childX < numRowGroups; childX++) {
-    nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(childX);
+    nsIFrame* kidFrame = rowGroups[childX];
     // 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) {
@@ -2765,18 +2868,20 @@ nsTableFrame::ReflowChildren(nsTableRefl
       nsSize kidAvailSize(aReflowState.availSize);
       // if the child is a tbody in paginated mode reduce the height by a repeated footer
       // XXXldb Shouldn't this check against |thead| and |tfoot| from
       // OrderRowGroups?
       nsIFrame* repeatedFooter = nsnull;
       nscoord repeatedFooterHeight = 0;
       if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
         if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == kidFrame->GetStyleDisplay()->mDisplay) { // the child is a tbody
-          nsIFrame* lastChild = (nsIFrame*)rowGroups.ElementAt(numRowGroups - 1);
+          nsIFrame* lastChild = rowGroups[rowGroups.Length() - 1];
           if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == lastChild->GetStyleDisplay()->mDisplay) { // the last child is a tfoot
+            // XXXbz what if lastChild is a scrollable tfoot?  Bogus!!
+            // dbaron is right -- this should be using thead/tfoot!
             if (((nsTableRowGroupFrame*)lastChild)->IsRepeatable()) {
               repeatedFooterHeight = lastChild->GetSize().height;
               if (repeatedFooterHeight + cellSpacingY < kidAvailSize.height) {
                 repeatedFooter = lastChild;
                 kidAvailSize.height -= repeatedFooterHeight + cellSpacingY;
               }
             }
           }
@@ -2812,31 +2917,32 @@ nsTableFrame::ReflowChildren(nsTableRefl
       if (kidFrame->GetNextInFlow())
         reorder = PR_TRUE;
     
       rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
                        aReflowState.x, aReflowState.y, 0, aStatus);
 
       if (reorder) {
         // reorder row groups the reflow may have changed the nextinflows
-        OrderRowGroups(rowGroups, numRowGroups, &thead, &tfoot);
-        for (childX = 0; childX < numRowGroups; childX++) {
-          if (kidFrame == (nsIFrame*)rowGroups.ElementAt(childX))
-            break;
+        numRowGroups = OrderRowGroups(rowGroups, &thead, &tfoot);
+        childX = rowGroups.IndexOf(kidFrame);
+        if (childX == RowGroupArray::NoIndex) {
+          // XXXbz can this happen?
+          childX = numRowGroups;
         }
       }
       // see if the rowgroup did not fit on this page might be pushed on
       // the next page
       if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
           (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight) &&
           kidReflowState.availableHeight < desiredSize.height) {
         // if we are on top of the page place with dataloss
         if (kidReflowState.mFlags.mIsTopOfPage) {
-          if (childX+1 < numRowGroups) {
-            nsIFrame* nextRowGroupFrame = (nsIFrame*) rowGroups.ElementAt(childX +1);
+          if (childX+1 < rowGroups.Length()) {
+            nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
             if (nextRowGroupFrame) {
               PlaceChild(aReflowState, kidFrame, desiredSize);
               aStatus = NS_FRAME_NOT_COMPLETE;
               PushChildren(rowGroups, childX + 1);
               aLastChildReflowed = kidFrame;
               break;
             }
           }
@@ -2852,17 +2958,18 @@ nsTableFrame::ReflowChildren(nsTableRefl
       }
 
       aLastChildReflowed   = kidFrame;
 
       pageBreak = PR_FALSE;
       // see if there is a page break after this row group or before the next one
       if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && 
           (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
-        nsIFrame* nextKid = (childX + 1 < numRowGroups) ? (nsIFrame*)rowGroups.ElementAt(childX + 1) : nsnull;
+        nsIFrame* nextKid =
+          (childX + 1 < numRowGroups) ? rowGroups[childX + 1] : nsnull;
         pageBreak = PageBreakAfter(*kidFrame, nextKid);
       }
 
       // Place the child
       PlaceChild(aReflowState, kidFrame, desiredSize);
 
       // Remember where we just were in case we end up pushing children
       prevKidFrame = kidFrame;
@@ -2885,21 +2992,21 @@ nsTableFrame::ReflowChildren(nsTableRefl
 
           // Add the continuing frame to the sibling list
           continuingFrame->SetNextSibling(kidFrame->GetNextSibling());
           kidFrame->SetNextSibling(continuingFrame);
           // Update rowGroups with the new rowgroup, just as it
           // would have been if we had called OrderRowGroups
           // again. Note that rowGroups doesn't get used again after
           // we PushChildren below, anyway.
-          rowGroups.InsertElementAt(continuingFrame, childX + 1);
+          rowGroups.InsertElementAt(childX + 1, continuingFrame);
         }
         else {
           // put the nextinflow so that it will get pushed
-          rowGroups.InsertElementAt(kidNextInFlow, childX + 1);
+          rowGroups.InsertElementAt(childX + 1, kidNextInFlow);
         }
         // We've used up all of our available space so push the remaining
         // children to the next-in-flow
         nsIFrame* nextSibling = kidFrame->GetNextSibling();
         if (nsnull != nextSibling) {
           PushChildren(rowGroups, childX + 1);
         }
         if (repeatedFooter) {
@@ -2987,20 +3094,19 @@ nsTableFrame::CalcDesiredHeight(const ns
     NS_ASSERTION(PR_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 or scroll frame) rect
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups);
-  if (numRowGroups <= 0) {
+  RowGroupArray rowGroups;
+  OrderRowGroups(rowGroups);
+  if (rowGroups.Length() == 0) {
     // tables can be used as rectangular items without content
     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
     if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
         (tableSpecifiedHeight > 0) &&
         eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
           // empty tables should not have a size in quirks mode
       aDesiredSize.height = tableSpecifiedHeight;
     } 
@@ -3008,21 +3114,18 @@ nsTableFrame::CalcDesiredHeight(const ns
       aDesiredSize.height = 0;
     return;
   }
   PRInt32 rowCount = cellMap->GetRowCount();
   PRInt32 colCount = cellMap->GetColCount();
   nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
   if (rowCount > 0 && colCount > 0) {
     desiredHeight += cellSpacingY;
-    for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-      nsIFrame* rg = (nsIFrame*)rowGroups.ElementAt(rgX);
-      if (rg) {
-        desiredHeight += rg->GetSize().height + cellSpacingY;
-      }
+    for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+      desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
     }
   }
 
   // see if a specified table height requires dividing additional space to rows
   if (!GetPrevInFlow()) {
     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
     if ((tableSpecifiedHeight > 0) && 
         (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
@@ -3038,28 +3141,27 @@ nsTableFrame::CalcDesiredHeight(const ns
     }
   }
   aDesiredSize.height = desiredHeight;
 }
 
 static
 void ResizeCells(nsTableFrame& aTableFrame)
 {
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  aTableFrame.OrderRowGroups(rowGroups, numRowGroups);
+  nsTableFrame::RowGroupArray rowGroups;
+  aTableFrame.OrderRowGroups(rowGroups);
   nsHTMLReflowMetrics tableDesiredSize;
   nsRect tableRect = aTableFrame.GetRect();
   tableDesiredSize.width = tableRect.width;
   tableDesiredSize.height = tableRect.height;
   tableDesiredSize.mOverflowArea = nsRect(0, 0, tableRect.width,
                                           tableRect.height);
 
-  for (PRUint32 rgX = 0; (rgX < numRowGroups); rgX++) {
-    nsTableRowGroupFrame* rgFrame = aTableFrame.GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
+  for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
    
     nsRect rowGroupRect = rgFrame->GetRect();
     nsHTMLReflowMetrics groupDesiredSize;
     groupDesiredSize.width = rowGroupRect.width;
     groupDesiredSize.height = rowGroupRect.height;
     groupDesiredSize.mOverflowArea = nsRect(0, 0, groupDesiredSize.width,
                                       groupDesiredSize.height);
     nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
@@ -3082,34 +3184,33 @@ void ResizeCells(nsTableFrame& aTableFra
 void
 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
                                      nscoord                  aAmount)
 {
   nscoord cellSpacingY = GetCellSpacingY();
 
   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
   
-  nsVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups);
+  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.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1));
   nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
   nscoord yEndRG = yOriginRG;
   PRUint32 rgX;
-  for (rgX = 0; (rgX < numRowGroups); rgX++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
+  for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgRect = rgFrame->GetRect();
-    if (rgFrame && !rgFrame->HasStyleHeight()) {
+    if (!rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         nsRect rowRect = rowFrame->GetRect();
         if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
           nscoord pctHeight = rowFrame->GetHeight(pctBasis);
           nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height);
           if (amountForRow > 0) {
             rowRect.height += amountForRow;
@@ -3149,38 +3250,38 @@ nsTableFrame::DistributeHeightToRows(con
   if (amountUsed >= aAmount) {
     ResizeCells(*this);
     return;
   }
 
   // get the first row without a style height where its row group has an unconstrianed height
   nsTableRowGroupFrame* firstUnStyledRG  = nsnull;
   nsTableRowFrame*      firstUnStyledRow = nsnull;
-  for (rgX = 0; (rgX < numRowGroups) && !firstUnStyledRG; rgX++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
-    if (rgFrame && !rgFrame->HasStyleHeight()) {
+  for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
+    if (!rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         if (!rowFrame->HasStyleHeight()) {
           firstUnStyledRG = rgFrame;
           firstUnStyledRow = rowFrame;
           break;
         }
         rowFrame = rowFrame->GetNextRow();
       }
     }
   }
 
   nsTableRowFrame* lastElligibleRow = nsnull;
   // accumulate the correct divisor. This will be the total of all unstyled rows inside 
   // unstyled row groups, unless there are none, in which case, it will be all rows
   nscoord divisor = 0;
-  for (rgX = 0; rgX < numRowGroups; rgX++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
-    if (rgFrame && (!firstUnStyledRG || !rgFrame->HasStyleHeight())) {
+  for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
+    if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
           divisor += rowFrame->GetSize().height;
           lastElligibleRow = rowFrame;
         }
         rowFrame = rowFrame->GetNextRow();
       }
@@ -3190,19 +3291,18 @@ nsTableFrame::DistributeHeightToRows(con
     NS_ERROR("invalid divisor");
     return;
   }
 
   // allocate the extra height to the unstyled row groups and rows
   pctBasis = aAmount - amountUsed;
   yOriginRG = borderPadding.top + cellSpacingY;
   yEndRG = yOriginRG;
-  for (rgX = 0; rgX < numRowGroups; rgX++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
-    if (!rgFrame) continue; 
+  for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgRect = rgFrame->GetRect();
     // see if there is an eligible row group
     if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         nsRect rowRect = rowFrame->GetRect();
@@ -3325,22 +3425,23 @@ nscoord nsTableFrame::GetCellSpacingY()
   return GetStyleTableBorder()->mBorderSpacingY.GetCoordValue();
 }
 
 
 /* virtual */ nscoord
 nsTableFrame::GetBaseline() const
 {
   nscoord ascent = 0;
-  nsAutoVoidArray orderedRowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(orderedRowGroups, numRowGroups);
+  RowGroupArray orderedRowGroups;
+  OrderRowGroups(orderedRowGroups);
   nsTableRowFrame* firstRow = nsnull;
-  for (PRUint32 rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex));
+  for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
+    // XXXbz Do we really want to just let through the scrollable
+    // rowgroups and use their ascent?
+    nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
     if (rgFrame->GetRowCount()) {
       firstRow = rgFrame->GetFirstRow(); 
       ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline();
       break;
     }
   }
   if (!firstRow)
     ascent = GetRect().height;
@@ -3890,17 +3991,17 @@ private:
                BCMapCellInfo&   aMapInfo,
                nsCellMap*       aCellMap = nsnull);
 
   PRBool SetNewRow(nsTableRowFrame* row = nsnull);
   PRBool SetNewRowGroup(PRBool aFindFirstDamagedRow);
 
   nsTableFrame&         mTableFrame;
   nsTableCellMap*       mTableCellMap;
-  nsVoidArray           mRowGroups;
+  nsTableFrame::RowGroupArray mRowGroups;
   nsTableRowGroupFrame* mRowGroup;
   PRInt32               mRowGroupIndex;
   PRUint32              mNumRows;
   nsTableRowFrame*      mRow;
   nsTableRowFrame*      mPrevRow;
   PRBool                mIsNewRow;
   PRInt32               mRowIndex;
   PRUint32              mNumCols;
@@ -3923,18 +4024,17 @@ BCMapCellIterator::BCMapCellIterator(nsT
   mNumRows       = mTableFrame.GetRowCount();
   mRow           = nsnull;
   mRowIndex      = 0;
   mNumCols       = mTableFrame.GetColCount();
   mColIndex      = 0;
   mRowGroupIndex = -1;
 
   // Get the ordered row groups 
-  PRUint32 numRowGroups;
-  aTableFrame.OrderRowGroups(mRowGroups, numRowGroups);
+  aTableFrame.OrderRowGroups(mRowGroups);
 
   mAtEnd = PR_TRUE; // gets reset when First() is called
 }
 
 void 
 BCMapCellIterator::SetInfo(nsTableRowFrame* aRow,
                            PRInt32          aColIndex,
                            CellData*        aCellData,
@@ -4060,36 +4160,41 @@ BCMapCellIterator::SetNewRow(nsTableRowF
   return !mAtEnd;
 }
 
 PRBool
 BCMapCellIterator::SetNewRowGroup(PRBool aFindFirstDamagedRow)
 {
   mAtEnd = PR_TRUE;
   mRowGroupIndex++;
-  PRInt32 numRowGroups = mRowGroups.Count();
+  PRInt32 numRowGroups = mRowGroups.Length();
   mCellMap = nsnull;
   for (PRInt32 rgX = mRowGroupIndex; rgX < numRowGroups; rgX++) {
-    nsIFrame* frame = (nsIFrame*)mRowGroups.ElementAt(mRowGroupIndex); if (!frame) ABORT1(PR_FALSE);
-    mRowGroup = mTableFrame.GetRowGroupFrame(frame); if (!mRowGroup) ABORT1(PR_FALSE);
+    // XXXbz do we really want the group at mRowGroupIndex?  If so, what's the
+    // point of this rgX loop variable, exactly?  Why not just do a while
+    // (mRowGroupIndex < numRowGroups) loop or something?  I suspect this means
+    // to be getting the rowgroup at rgX.
+    mRowGroup = mRowGroups[mRowGroupIndex];
     PRInt32 rowCount = mRowGroup->GetRowCount();
     mRowGroupStart = mRowGroup->GetStartRowIndex();
     mRowGroupEnd   = mRowGroupStart + rowCount - 1;
-    if (rowCount > 0) {
+    if (rowCount > 0) { // XXXbz and if it's not we just keep looping?  What's
+                        // the point?
       mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
       if (!mCellMap) ABORT1(PR_FALSE);
       nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
       if (aFindFirstDamagedRow) {
         if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
           // the damage area starts in the row group 
           if (aFindFirstDamagedRow) {
             // find the correct first damaged row
             PRInt32 numRows = mAreaStart.y - mRowGroupStart;
             for (PRInt32 i = 0; i < numRows; i++) {
-              firstRow = firstRow->GetNextRow(); if (!frame) ABORT1(PR_FALSE);
+              firstRow = firstRow->GetNextRow();
+              if (!firstRow) ABORT1(PR_FALSE);
             }
           }
         }
         else {
           mRowGroupIndex++;
           continue;
         }
       }
@@ -4194,18 +4299,17 @@ BCMapCellIterator::PeekBottom(BCMapCellI
   PRInt32 rgRowIndex = rowIndex - mRowGroupStart;
   nsTableRowGroupFrame* rg = mRowGroup;
   nsCellMap* cellMap = mCellMap;
   nsTableRowFrame* nextRow = nsnull;
   if (rowIndex > mRowGroupEnd) {
     PRInt32 nextRgIndex = mRowGroupIndex;
     do {
       nextRgIndex++;
-      nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(nextRgIndex); if (!frame) ABORT0();
-      rg = mTableFrame.GetRowGroupFrame(frame);
+      rg = mRowGroups.SafeElementAt(nextRgIndex);
       if (rg) {
         cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
         rgRowIndex = 0;
         nextRow = rg->GetFirstRow();
       }
     }
     while (rg && !nextRow);
     if(!rg) return;
@@ -4798,25 +4902,23 @@ nsTableFrame::ExpandBCDamageArea(nsRect&
   // make the damage area as big as the table, similarly to the way the cell map decides whether
   // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
   // no spanners, but it may not be worth the effort in general, and it would need to be done in the
   // cell map as well.
   PRBool haveSpanner = PR_FALSE;
   if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
     nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
     // Get the ordered row groups 
-    PRUint32 numRowGroups;
-    nsVoidArray rowGroups;
-    OrderRowGroups(rowGroups, numRowGroups);
+    RowGroupArray rowGroups;
+    OrderRowGroups(rowGroups);
 
     // Scope outside loop to be used as hint.
     nsCellMap* cellMap = nsnull;
-    for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
-      nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
-      nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0();
+    for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
+      nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
       PRInt32 rgStartY = rgFrame->GetStartRowIndex();
       PRInt32 rgEndY   = rgStartY + rgFrame->GetRowCount() - 1;
       if (dEndY < rgStartY) 
         break;
       cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
       if (!cellMap) ABORT0();
       // check for spanners from above and below
       if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
@@ -5527,17 +5629,17 @@ public:
              const nsRect&         aDamageArea);
   void First();
   void Next();
 
   nsTableFrame*         table;
   nsTableCellMap*       tableCellMap;
   nsCellMap*            cellMap;
 
-  nsVoidArray           rowGroups;
+  nsTableFrame::RowGroupArray rowGroups;
   nsTableRowGroupFrame* prevRg;
   nsTableRowGroupFrame* rg;
   PRInt32               rowGroupIndex;
   PRInt32               fifRowGroupStart;
   PRInt32               rowGroupStart;
   PRInt32               rowGroupEnd;
   PRInt32               numRows; // number of rows in the table and all continuations
 
@@ -5615,18 +5717,17 @@ BCMapBorderIterator::Reset(nsTableFrame&
   rowGroupIndex = -1;
   prevCell      = nsnull;
   cell          = nsnull;
   prevCellData  = nsnull;
   cellData      = nsnull;
   bcData        = nsnull;
 
   // Get the ordered row groups 
-  PRUint32 numRowGroups;
-  table->OrderRowGroups(rowGroups, numRowGroups);
+  table->OrderRowGroups(rowGroups);
 }
 
 void 
 BCMapBorderIterator::SetNewData(PRInt32 aY,
                                 PRInt32 aX)
 {
   if (!tableCellMap || !tableCellMap->mBCInfo) ABORT0();
 
@@ -5693,20 +5794,19 @@ BCMapBorderIterator::SetNewRow(nsTableRo
 PRBool
 BCMapBorderIterator::SetNewRowGroup()
 {
   rowGroupIndex++;
 
   isRepeatedHeader = PR_FALSE;
   isRepeatedFooter = PR_FALSE;
 
-  if (rowGroupIndex < rowGroups.Count()) {
+  if (rowGroupIndex < rowGroups.Length()) {
     prevRg = rg;
-    nsIFrame* frame = (nsTableRowGroupFrame*)rowGroups.ElementAt(rowGroupIndex); if (!frame) ABORT1(PR_FALSE);
-    rg = table->GetRowGroupFrame(frame); if (!rg) ABORT1(PR_FALSE);
+    rg = rowGroups[rowGroupIndex];
     fifRowGroupStart = ((nsTableRowGroupFrame*)rg->GetFirstInFlow())->GetStartRowIndex();
     rowGroupStart    = rg->GetStartRowIndex(); 
     rowGroupEnd      = rowGroupStart + rg->GetRowCount() - 1;
 
     if (SetNewRow(rg->GetFirstRow())) {
       cellMap =
         tableCellMap->GetMapFor((nsTableRowGroupFrame*)rg->GetFirstInFlow(),
                                 nsnull);
@@ -5731,35 +5831,32 @@ BCMapBorderIterator::SetNewRowGroup()
 
 void 
 BCMapBorderIterator::First()
 {
   if (!table || (startX >= numCols) || (startY >= numRows)) ABORT0();
 
   atEnd = PR_FALSE;
 
-  PRUint32 numRowGroups = rowGroups.Count();
+  PRUint32 numRowGroups = rowGroups.Length();
   for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) { 
-    nsIFrame* frame = (nsIFrame*)rowGroups.ElementAt(rgX);
-    nsTableRowGroupFrame* rowG = table->GetRowGroupFrame(frame);
-    if (rowG) {
-      PRInt32 start = rowG->GetStartRowIndex();
-      PRInt32 end   = start + rowG->GetRowCount() - 1;
-      if ((startY >= start) && (startY <= end)) {
-        rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex
-        if (SetNewRowGroup()) { 
-          while ((y < startY) && !atEnd) {
-            SetNewRow();
-          }
-          if (!atEnd) {
-            SetNewData(startY, startX);
-          }
+    nsTableRowGroupFrame* rowG = rowGroups[rgX];
+    PRInt32 start = rowG->GetStartRowIndex();
+    PRInt32 end   = start + rowG->GetRowCount() - 1;
+    if ((startY >= start) && (startY <= end)) {
+      rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex
+      if (SetNewRowGroup()) { 
+        while ((y < startY) && !atEnd) {
+          SetNewRow();
         }
-        return;
+        if (!atEnd) {
+          SetNewData(startY, startX);
+        }
       }
+      return;
     }
   }
   atEnd = PR_TRUE;
 }
 
 void 
 BCMapBorderIterator::Next()
 {
@@ -6026,30 +6123,29 @@ nsTableFrame::PaintBCBorders(nsIRenderin
 
   PRInt32 startRowY = (GetPrevInFlow()) ? 0 : childAreaOffset.top; // y position of first row in damage area
 
   const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground(mStyleContext);
   // determine the damage area in terms of rows and columns and finalize startColX and startRowY
   PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex;
   startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
 
-  nsAutoVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  OrderRowGroups(rowGroups, numRowGroups);
+  RowGroupArray rowGroups;
+  OrderRowGroups(rowGroups);
   PRBool done = PR_FALSE;
   PRBool haveIntersect = PR_FALSE;
   nsTableRowGroupFrame* inFlowRG  = nsnull;
   nsTableRowFrame*      inFlowRow = nsnull;
   // find startRowIndex, endRowIndex, startRowY
   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   PRInt32 rowY = startRowY;
-  for (PRUint32 rgX = 0; (rgX < numRowGroups) && !done; rgX++) {
-    nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
-    nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0();
-    for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) {
+  for (PRUint32 rgX = 0; rgX < rowGroups.Length() && !done; rgX++) {
+    nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
+    for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
+         rowFrame = rowFrame->GetNextRow()) {
       // conservatively estimate the half border widths outside the row
       nscoord topBorderHalf    = (GetPrevInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); 
       nscoord bottomBorderHalf = (GetNextInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1);
       // get the row rect relative to the table rather than the row group
       nsSize rowSize = rowFrame->GetSize();
       if (haveIntersect) {
         if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) {
           nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0();
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -553,26 +553,53 @@ protected:
 
   nsIFrame* GetFirstBodyRowGroupFrame();
   PRBool MoveOverflowToChildList(nsPresContext* aPresContext);
   /**
    * Push all our child frames from the aFrames array, in order, starting from the
    * frame at aPushFrom to the end of the array. The frames are put on our overflow
    * list or moved directly to our next-in-flow if one exists.
    */
-  void PushChildren(const nsAutoVoidArray& aFrames, PRInt32 aPushFrom);
+  typedef nsAutoTPtrArray<nsIFrame, 8> FrameArray;
+  void PushChildren(const FrameArray& aFrames, PRInt32 aPushFrom);
 
 public:
-  // put the children frames in the display order (e.g. thead before tbody before tfoot)
-  // and put the non row group frames at the end. Also return the number of row group frames.
-  void OrderRowGroups(nsVoidArray&           aChildren,
-                      PRUint32&              aNumRowGroups,
-                      nsTableRowGroupFrame** aHead      = nsnull,
-                      nsTableRowGroupFrame** aFoot      = nsnull) const;
+  // put the children frames in the display order (e.g. thead before tbodies
+  // before tfoot). This will handle calling GetRowGroupFrame() on the
+  // children, and not append nulls, so the array is guaranteed to contain
+  // nsTableRowGroupFrames.  If there are multiple theads or tfoots, all but
+  // the first one are treated as tbodies instead.
+  typedef nsAutoTPtrArray<nsTableRowGroupFrame, 8> RowGroupArray;
+  void OrderRowGroups(RowGroupArray& aChildren) const;
+
+  // Return the thead, if any
+  nsTableRowGroupFrame* GetTHead() const;
+
+  // Return the tfoot, if any
+  nsTableRowGroupFrame* GetTFoot() const;
 
+protected:
+  // As above, but does NOT actually call GetRowGroupFrame() on the kids, so
+  // returns an array of nsIFrames.  This is to be used when you really want
+  // the flowable kids of the table, not the rowgroups.  This outputs the thead
+  // and tfoot if they happen to be rowgroups.  All the child nsIFrames of the
+  // table that return null if you call GetRowGroupFrame() on them will appear
+  // at the end of the array, after the tfoot, if any.
+  //
+  // aHead and aFoot must not be null.
+  //
+  // @return the number of frames in aChildren which return non-null if you
+  // call GetRowGroupFrame() on them.
+  //
+  // XXXbz why do we really care about the non-rowgroup kids?
+  PRUint32 OrderRowGroups(FrameArray& aChildren,
+                          nsTableRowGroupFrame** aHead,
+                          nsTableRowGroupFrame** aFoot) const;
+
+public:
   // Returns PR_TRUE if there are any cells above the row at
   // aRowIndex and spanning into the row at aRowIndex, the number of
   // effective columns limits the search up to that column
   PRBool RowIsSpannedInto(PRInt32 aRowIndex, PRInt32 aNumEffCols);
 
   // Returns PR_TRUE if there is a cell originating in aRowIndex
   // which spans into the next row,  the number of effective
   // columns limits the search up to that column
--- a/layout/tables/nsTablePainter.cpp
+++ b/layout/tables/nsTablePainter.cpp
@@ -347,29 +347,26 @@ TableBackgroundPainter::TranslateContext
 }
 
 nsresult
 TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame,
                                    nsMargin*     aDeflate)
 {
   NS_PRECONDITION(aTableFrame, "null table frame");
 
-  nsVoidArray rowGroups;
-  PRUint32 numRowGroups;
-  aTableFrame->OrderRowGroups(rowGroups, numRowGroups);
+  nsTableFrame::RowGroupArray rowGroups;
+  aTableFrame->OrderRowGroups(rowGroups);
 
-  if (numRowGroups < 1) { //degenerate case
-    PaintTableFrame(aTableFrame,nsnull, nsnull, nsnull);
+  if (rowGroups.Length() < 1) { //degenerate case
+    PaintTableFrame(aTableFrame, nsnull, nsnull, nsnull);
     /* No cells; nothing else to paint */
     return NS_OK;
   }
 
-  PaintTableFrame(aTableFrame,
-                  aTableFrame->GetRowGroupFrame(NS_STATIC_CAST(nsIFrame*, rowGroups.ElementAt(0))),
-                  aTableFrame->GetRowGroupFrame(NS_STATIC_CAST(nsIFrame*, rowGroups.ElementAt(numRowGroups - 1))),
+  PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1],
                   aDeflate);
 
   /*Set up column background/border data*/
   if (mNumCols > 0) {
     nsFrameList& colGroupList = aTableFrame->GetColGroups();
     NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup");
 
     mCols = new ColData[mNumCols];
@@ -433,18 +430,18 @@ TableBackgroundPainter::PaintTable(nsTab
 
       if (!cgDataOwnershipTaken) {
         cgData->Destroy(mPresContext);
         delete cgData;
       }
     }
   }
 
-  for (PRUint32 i = 0; i < numRowGroups; i++) {
-    nsTableRowGroupFrame* rg = nsTableFrame::GetRowGroupFrame(NS_STATIC_CAST(nsIFrame*, rowGroups.ElementAt(i)));
+  for (PRUint32 i = 0; i < rowGroups.Length(); i++) {
+    nsTableRowGroupFrame* rg = rowGroups[i];
     mRowGroup.SetFrame(rg);
     // Need to compute the right rect via GetOffsetTo, since the row
     // group may not be a child of the table.
     mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
     if (mRowGroup.mRect.Intersects(mDirtyRect)) {
       nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
       if (NS_FAILED(rv)) return rv;
     }