Fix bug 416073: invalidate more things as needed during table reflow to prevent glitches. r+sr=roc
authorbzbarsky@mit.edu
Fri, 08 Feb 2008 01:36:32 -0800
changeset 11384 277c4bcb59c4332ef3f9df085c0671f409be9f21
parent 11383 659eaac5c472e0f3648d1afd3dfd45e07ef64eb3
child 11385 c5eea6482b743df60fc5cd1396fde7ce82870b3e
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs416073
milestone1.9b4pre
Fix bug 416073: invalidate more things as needed during table reflow to prevent glitches. r+sr=roc
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowGroupFrame.cpp
layout/tables/nsTableRowGroupFrame.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2481,8 +2481,26 @@ nsLayoutUtils::GetTextRunFlagsForStyle(n
     }
     break;
   default:
     break;
   }
 #endif
   return result;
 }
+
+/* static */ void
+nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
+                                       nsRect* aHStrip, nsRect* aVStrip) {
+  NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
+               "expected rects at the same position");
+  nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width),
+                   PR_MAX(aR1.height, aR2.height));
+  nscoord VStripStart = PR_MIN(aR1.width, aR2.width);
+  nscoord HStripStart = PR_MIN(aR1.height, aR2.height);
+  *aVStrip = unionRect;
+  aVStrip->x += VStripStart;
+  aVStrip->width -= VStripStart;
+  *aHStrip = unionRect;
+  aHStrip->y += HStripStart;
+  aHStrip->height -= HStripStart;
+}
+
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -750,16 +750,25 @@ public:
    * -- TEXT_OPTIMIZE_SPEED if the text-rendering CSS property and font size
    * and prefs indicate we should be optimizing for speed over quality
    */
   static PRUint32 GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
                                           const nsStyleText* aStyleText,
                                           const nsStyleFont* aStyleFont);
 
   /**
+   * Takes two rectangles whose origins must be the same, and computes
+   * the difference between their union and their intersection as two
+   * rectangles. (This difference is a superset of the difference
+   * between the two rectangles.)
+   */
+  static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
+                                      nsRect* aHStrip, nsRect* aVStrip);
+
+  /**
    * Indicates if the nsIFrame::GetUsedXXX assertions in nsFrame.cpp should
    * disabled.
    */
 #ifdef DEBUG
   static PRBool sDisableGetUsedXAssertions;
 #endif
 };
 
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -2230,38 +2230,16 @@ nsBlockFrame::DeleteLine(nsBlockReflowSt
     // Mark the previous margin of the next line dirty since we need to
     // recompute its top position.
     if (aLine != aLineEnd)
       aLine->MarkPreviousMarginDirty();
   }
 }
 
 /**
- * Takes two rectangles whose origins must be the same, and computes
- * the difference between their union and their intersection as two
- * rectangles. (This difference is a superset of the difference
- * between the two rectangles.)
- */
-static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
-                                    nsRect* aHStrip, nsRect* aVStrip) {
-  NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
-               "expected rects at the same position");
-  nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width),
-                   PR_MAX(aR1.height, aR2.height));
-  nscoord VStripStart = PR_MIN(aR1.width, aR2.width);
-  nscoord HStripStart = PR_MIN(aR1.height, aR2.height);
-  *aVStrip = unionRect;
-  aVStrip->x += VStripStart;
-  aVStrip->width -= VStripStart;
-  *aHStrip = unionRect;
-  aHStrip->y += HStripStart;
-  aHStrip->height -= HStripStart;
-}
-
-/**
  * Reflow a line. The line will either contain a single block frame
  * or contain 1 or more inline frames. aKeepReflowGoing indicates
  * whether or not the caller should continue to reflow more lines.
  */
 nsresult
 nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
                          line_iterator aLine,
                          PRBool* aKeepReflowGoing)
@@ -2300,20 +2278,21 @@ nsBlockFrame::ReflowLine(nsBlockReflowSt
 #ifdef NOISY_BLOCK_INVALIDATE
       printf("%p invalidate 6 (%d, %d, %d, %d)\n",
              this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
 #endif
       Invalidate(dirtyRect);
     } else {
       nsRect combinedAreaHStrip, combinedAreaVStrip;
       nsRect boundsHStrip, boundsVStrip;
-      GetRectDifferenceStrips(oldBounds, newBounds,
-                              &boundsHStrip, &boundsVStrip);
-      GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea,
-                              &combinedAreaHStrip, &combinedAreaVStrip);
+      nsLayoutUtils::GetRectDifferenceStrips(oldBounds, newBounds,
+                                             &boundsHStrip, &boundsVStrip);
+      nsLayoutUtils::GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea,
+                                             &combinedAreaHStrip,
+                                             &combinedAreaVStrip);
 
 #ifdef NOISY_BLOCK_INVALIDATE
       printf("%p invalidate boundsVStrip (%d, %d, %d, %d)\n",
              this, boundsVStrip.x, boundsVStrip.y, boundsVStrip.width, boundsVStrip.height);
       printf("%p invalidate boundsHStrip (%d, %d, %d, %d)\n",
              this, boundsHStrip.x, boundsHStrip.y, boundsHStrip.width, boundsHStrip.height);
       printf("%p invalidate combinedAreaVStrip (%d, %d, %d, %d)\n",
              this, combinedAreaVStrip.x, combinedAreaVStrip.y, combinedAreaVStrip.width, combinedAreaVStrip.height);
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -738,16 +738,21 @@ nsContainerFrame::ReflowChild(nsIFrame* 
 
   nsresult  result;
 
   // Send the WillReflow() notification, and position the child frame
   // and its view if requested
   aKidFrame->WillReflow(aPresContext);
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
+    if ((aFlags & NS_FRAME_INVALIDATE_ON_MOVE) &&
+        !(aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
+        aKidFrame->GetPosition() != nsPoint(aX, aY)) {
+      aKidFrame->InvalidateOverflowRect();
+    }
     aKidFrame->SetPosition(nsPoint(aX, aY));
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
   }
 
   // Reflow the child frame
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -55,16 +55,19 @@
   // for frames that support overflow containers
 
 // Option flags for ReflowChild() and FinishReflowChild()
 // member functions
 #define NS_FRAME_NO_MOVE_VIEW         0x0001
 #define NS_FRAME_NO_MOVE_FRAME        (0x0002 | NS_FRAME_NO_MOVE_VIEW)
 #define NS_FRAME_NO_SIZE_VIEW         0x0004
 #define NS_FRAME_NO_VISIBILITY        0x0008
+// Only applies to ReflowChild: if true, invalidate the child if it's
+// being moved
+#define NS_FRAME_INVALIDATE_ON_MOVE   0x0010 
 
 class nsOverflowContinuationTracker;
 
 /**
  * Implementation of a container frame.
  */
 class nsContainerFrame : public nsSplittableFrame
 {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3596,16 +3596,31 @@ void
 nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY,
                              nsIFrame* aForChild, PRBool aImmediate)
 {
   GetParent()->
     InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aImmediate);
 }
 
 void
+nsIFrame::InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2)
+{
+  nsRect sizeHStrip, sizeVStrip;
+  nsLayoutUtils::GetRectDifferenceStrips(aR1, aR2, &sizeHStrip, &sizeVStrip);
+  Invalidate(sizeVStrip);
+  Invalidate(sizeHStrip);
+}
+
+void
+nsIFrame::InvalidateOverflowRect()
+{
+  Invalidate(GetOverflowRect());
+}
+
+void
 nsIFrame::InvalidateRoot(const nsRect& aDamageRect,
                          nscoord aX, nscoord aY, PRBool aImmediate)
 {
   PRUint32 flags = aImmediate ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC;
   nsIView* view = GetView();
   NS_ASSERTION(view, "This can only be called on frames with views");
   view->GetViewManager()->UpdateView(view, aDamageRect + nsPoint(aX, aY), flags);
 }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1651,16 +1651,31 @@ public:
    *   In case it's true, pending notifications will be flushed which
    *   could cause frames to be deleted (including |this|).
    */  
   virtual void InvalidateInternal(const nsRect& aDamageRect,
                                   nscoord aOffsetX, nscoord aOffsetY,
                                   nsIFrame* aForChild, PRBool aImmediate);
 
   /**
+   * Take two rectangles in the coordinate system of this frame which
+   * have the same origin and invalidate the difference between them.
+   * This is a helper method to be used when a frame is being resized.
+   *
+   * @param aR1 the first rectangle
+   * @param aR2 the second rectangle
+   */
+  void InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2);
+
+  /**
+   * Invalidate the overflow rect of this frame
+   */
+  void InvalidateOverflowRect();
+  
+  /**
    * Computes a rect that encompasses everything that might be painted by
    * this frame.  This includes this frame, all its descendent frames, this
    * frame's outline, and descentant frames' outline, but does not include
    * areas clipped out by the CSS "overflow" and "clip" properties.
    *
    * @return the rect relative to this frame's origin
    */
   nsRect GetOverflowRect() const;
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -588,27 +588,35 @@ void nsTableCellFrame::VerticallyAlignCh
     default:
     case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
       // Align the middle of the child frame with the middle of the content area, 
       kidYTop = (height - childHeight - bottomInset + topInset) / 2;
   }
   // if the content is larger than the cell height align from top
   kidYTop = PR_MAX(0, kidYTop);
 
+  if (kidYTop != kidRect.y) {
+    // Invalidate at the old position first
+    firstKid->InvalidateOverflowRect();
+  }
+  
   firstKid->SetPosition(nsPoint(kidRect.x, kidYTop));
   nsHTMLReflowMetrics desiredSize;
   desiredSize.width = mRect.width;
   desiredSize.height = mRect.height;
   GetSelfOverflow(desiredSize.mOverflowArea);
   ConsiderChildOverflow(desiredSize.mOverflowArea, firstKid);
   FinishAndStoreOverflow(&desiredSize);
   if (kidYTop != kidRect.y) {
     // Make sure any child views are correctly positioned. We know the inner table
     // cell won't have a view
     nsContainerFrame::PositionChildViews(firstKid);
+
+    // Invalidate new overflow rect
+    firstKid->InvalidateOverflowRect();
   }
   if (HasView()) {
     nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
                                                GetView(),
                                                &desiredSize.mOverflowArea, 0);
   }
 }
 
@@ -856,40 +864,46 @@ NS_METHOD nsTableCellFrame::Reflow(nsPre
       (GetFirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
     // We need to force the kid to have mVResize set if we've had a
     // special reflow in the past, since the non-special reflow needs to
     // resize back to what it was without the special height reflow.
     kidReflowState.mFlags.mVResize = PR_TRUE;
   }
 
   nsPoint kidOrigin(leftInset, topInset);
+  nsRect origRect = firstKid->GetRect();
+  PRBool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
 
   ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
-              kidOrigin.x, kidOrigin.y, 0, aStatus);
+              kidOrigin.x, kidOrigin.y, NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
   if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
     // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
     //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
     NS_FRAME_SET_INCOMPLETE(aStatus);
     printf("Set table cell incomplete %p\n", this);
   }
+
+  // XXXbz is this invalidate actually needed, really?
   if (GetStateBits() & NS_FRAME_IS_DIRTY) {
     Invalidate(GetOverflowRect(), PR_FALSE);
   }
 
 #ifdef NS_DEBUG
   DebugCheckChildSize(firstKid, kidSize, availSize);
 #endif
 
   // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode 
   // see testcase "emptyCells.html"
   SetContentEmpty(0 == kidSize.height);
 
   // Place the child
   FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize,
                     kidOrigin.x, kidOrigin.y, 0);
+
+  nsTableFrame::InvalidateFrame(firstKid, origRect, firstReflow);
     
   // first, compute the height which can be set w/o being restricted by aMaxSize.height
   nscoord cellHeight = kidSize.height;
 
   if (NS_UNCONSTRAINEDSIZE != cellHeight) {
     cellHeight += topInset + bottomInset;
   }
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1952,16 +1952,21 @@ NS_METHOD nsTableFrame::Reflow(nsPresCon
   if (!reflowedChildren) {
     // use the old overflow area
      nsRect* oldOverflowArea = GetOverflowAreaProperty();
      if (oldOverflowArea) {
        aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, *oldOverflowArea);
      }
   }
 
+  if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+    // Fulfill the promise InvalidateFrame makes.
+    Invalidate(aDesiredSize.mOverflowArea);
+  }
+
   FinishAndStoreOverflow(&aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return rv;
 }
 
 nsresult 
 nsTableFrame::ReflowTable(nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
@@ -2573,23 +2578,28 @@ nsTableFrame::InitChildReflowState(nsHTM
     aReflowState.mFlags.mHResize = PR_TRUE;
   }
 }
 
 // Position and size aKidFrame and update our reflow state. The origin of
 // aKidRect is relative to the upper-left origin of our frame
 void nsTableFrame::PlaceChild(nsTableReflowState&  aReflowState,
                               nsIFrame*            aKidFrame,
-                              nsHTMLReflowMetrics& aKidDesiredSize)
-{
+                              nsHTMLReflowMetrics& aKidDesiredSize,
+                              const nsRect&        aOriginalKidRect)
+{
+  PRBool isFirstReflow =
+    (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
   
   // Place and size the child
   FinishReflowChild(aKidFrame, PresContext(), nsnull, aKidDesiredSize,
                     aReflowState.x, aReflowState.y, 0);
 
+  InvalidateFrame(aKidFrame, aOriginalKidRect, isFirstReflow);
+
   // Adjust the running y-offset
   aReflowState.y += aKidDesiredSize.height;
 
   // If our height is constrained, then update the available height
   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
     aReflowState.availSize.height -= aKidDesiredSize.height;
   }
 }
@@ -2929,19 +2939,20 @@ nsTableFrame::ReflowChildren(nsTableRefl
         aReflowState.availSize.height -= cellSpacingY;
       }
       // record the presence of a next in flow, it might get destroyed so we
       // need to reorder the row group array
       nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
       PRBool reorder = PR_FALSE;
       if (kidFrame->GetNextInFlow())
         reorder = PR_TRUE;
-    
+
       rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
-                       aReflowState.x, aReflowState.y, 0, aStatus);
+                       aReflowState.x, aReflowState.y,
+                       NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
 
       if (reorder) {
         // reorder row groups the reflow may have changed the nextinflows
         numRowGroups = OrderRowGroups(rowGroups, &thead, &tfoot);
         childX = rowGroups.IndexOf(kidFrame);
         if (childX == RowGroupArray::NoIndex) {
           // XXXbz can this happen?
           childX = numRowGroups;
@@ -2952,17 +2963,17 @@ nsTableFrame::ReflowChildren(nsTableRefl
       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 < rowGroups.Length()) {
             nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
             if (nextRowGroupFrame) {
-              PlaceChild(aReflowState, kidFrame, desiredSize);
+              PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect);
               aStatus = NS_FRAME_NOT_COMPLETE;
               PushChildren(rowGroups, childX + 1);
               aLastChildReflowed = kidFrame;
               break;
             }
           }
         }
         else { // we are not on top, push this rowgroup onto the next page
@@ -2983,17 +2994,17 @@ nsTableFrame::ReflowChildren(nsTableRefl
       if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && 
           (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
         nsIFrame* nextKid =
           (childX + 1 < numRowGroups) ? rowGroups[childX + 1] : nsnull;
         pageBreak = PageBreakAfter(*kidFrame, nextKid);
       }
 
       // Place the child
-      PlaceChild(aReflowState, kidFrame, desiredSize);
+      PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect);
 
       // Remember where we just were in case we end up pushing children
       prevKidFrame = kidFrame;
 
       // Special handling for incomplete children
       if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {         
         kidNextInFlow = kidFrame->GetNextInFlow();
         if (!kidNextInFlow) {
@@ -3031,33 +3042,39 @@ nsTableFrame::ReflowChildren(nsTableRefl
         if (allowRepeatedFooter) {
           kidAvailSize.height = footerHeight;
           nsHTMLReflowState footerReflowState(presContext,
                                               aReflowState.reflowState,
                                               tfoot, kidAvailSize,
                                               -1, -1, PR_FALSE);
           InitChildReflowState(footerReflowState);
           aReflowState.y += cellSpacingY;
+
+          nsRect origTfootRect = tfoot->GetRect();
+          
           nsReflowStatus footerStatus;
           rv = ReflowChild(tfoot, presContext, desiredSize, footerReflowState,
-                           aReflowState.x, aReflowState.y, 0, footerStatus);
-          PlaceChild(aReflowState, tfoot, desiredSize);
+                           aReflowState.x, aReflowState.y,
+                           NS_FRAME_INVALIDATE_ON_MOVE, footerStatus);
+          PlaceChild(aReflowState, tfoot, desiredSize, origTfootRect);
         }
         break;
       }
     }
     else { // it isn't being reflowed
       aReflowState.y += cellSpacingY;
       nsRect kidRect = kidFrame->GetRect();
       if (kidRect.y != aReflowState.y) {
-        Invalidate(kidRect); // invalidate the old position
+        // invalidate the old position
+        kidFrame->InvalidateOverflowRect();
         kidRect.y = aReflowState.y;
         kidFrame->SetRect(kidRect);        // move to the new position
         RePositionViews(kidFrame);
-        Invalidate(kidRect); // invalidate the new position
+        // invalidate the new position
+        kidFrame->InvalidateOverflowRect();
       }
       aReflowState.y += kidRect.height;
 
       // If our height is constrained then update the available height.
       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
         aReflowState.availSize.height -= cellSpacingY + kidRect.height;
       }
     }
@@ -3221,46 +3238,65 @@ nsTableFrame::DistributeHeightToRows(con
     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) {
+            nsRect oldRowRect = rowRect;
             rowRect.height += amountForRow;
+            // XXXbz we don't need to change rowRect.y to be yOriginRow?
             rowFrame->SetRect(rowRect);
             yOriginRow += rowRect.height + cellSpacingY;
             yEndRG += rowRect.height + cellSpacingY;
             amountUsed += amountForRow;
             amountUsedByRG += amountForRow;
             //rowFrame->DidResize();        
             nsTableFrame::RePositionViews(rowFrame);
+
+            rgFrame->InvalidateRectDifference(oldRowRect, rowRect);
           }
         }
         else {
-          if (amountUsed > 0) {
+          if (amountUsed > 0 && yOriginRow != rowRect.y &&
+              !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+            rowFrame->InvalidateOverflowRect();
             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
             nsTableFrame::RePositionViews(rowFrame);
+            rowFrame->InvalidateOverflowRect();
           }
           yOriginRow += rowRect.height + cellSpacingY;
           yEndRG += rowRect.height + cellSpacingY;
         }
         rowFrame = rowFrame->GetNextRow();
       }
       if (amountUsed > 0) {
+        if (rgRect.y != yOriginRG) {
+          rgFrame->InvalidateOverflowRect();
+        }
+
+        nsRect origRgRect = rgRect;
+        
         rgRect.y = yOriginRG;
         rgRect.height += amountUsedByRG;
+        
         rgFrame->SetRect(rgRect);
+
+        nsTableFrame::InvalidateFrame(rgFrame, origRgRect, PR_FALSE);
       }
     }
-    else if (amountUsed > 0) {
+    else if (amountUsed > 0 && yOriginRG != rgFrame->GetPosition().y) {
+      NS_ASSERTION(rgFrame->GetPosition().x == 0, "Unexpected position");
+      rgFrame->InvalidateOverflowRect();
       rgFrame->SetPosition(nsPoint(0, yOriginRG));
       // Make sure child views are properly positioned
       nsTableFrame::RePositionViews(rgFrame);
+      rgFrame->InvalidateOverflowRect();
     }
     yOriginRG = yEndRG;
   }
 
   if (amountUsed >= aAmount) {
     ResizeCells(*this);
     return;
   }
@@ -3355,50 +3391,69 @@ nsTableFrame::DistributeHeightToRows(con
             // all rows get the same additional space
             ratio = 1.0f / float(divisor);
           }
           // give rows their additional space, except for the last row which
           // gets the remainder
           nscoord amountForRow = (rowFrame == lastEligibleRow) 
                                  ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
           amountForRow = PR_MIN(amountForRow, aAmount - amountUsed);
+
+          if (yOriginRow != rowRect.y) {
+            rowFrame->InvalidateOverflowRect();
+          }
+          
           // update the row height
-          nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, rowRect.height + amountForRow);
+          nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
+                            rowRect.height + amountForRow);
           rowFrame->SetRect(newRowRect);
+
           yOriginRow += newRowRect.height + cellSpacingY;
           yEndRG += newRowRect.height + cellSpacingY;
 
           amountUsed += amountForRow;
           amountUsedByRG += amountForRow;
           NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
           //rowFrame->DidResize();        
           nsTableFrame::RePositionViews(rowFrame);
+
+          nsTableFrame::InvalidateFrame(rowFrame, rowRect, PR_FALSE);
         }
         else {
-          if (amountUsed > 0) {
+          if (amountUsed > 0 && yOriginRow != rowRect.y) {
+            rowFrame->InvalidateOverflowRect();
             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
             nsTableFrame::RePositionViews(rowFrame);
+            rowFrame->InvalidateOverflowRect();
           }
           yOriginRow += rowRect.height + cellSpacingY;
           yEndRG += rowRect.height + cellSpacingY;
         }
         rowFrame = rowFrame->GetNextRow();
       }
       if (amountUsed > 0) {
-        rgRect.y = yOriginRG;
-        rgRect.height += amountUsedByRG;
-        rgFrame->SetRect(rgRect);
+        if (rgRect.y != yOriginRG) {
+          rgFrame->InvalidateOverflowRect();
+        }
+        
+        rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
+                                rgRect.height + amountUsedByRG));
+
+        nsTableFrame::InvalidateFrame(rgFrame, rgRect, PR_FALSE);
       }
       // Make sure child views are properly positioned
       // XXX what happens if childFrame is a scroll frame and this gets skipped? see also below
     }
-    else if (amountUsed > 0) {
+    else if (amountUsed > 0 && yOriginRG != rgFrame->GetPosition().y) {
+      NS_ASSERTION(rgFrame->GetPosition().x == 0, "Unexpected position");
+      rgFrame->InvalidateOverflowRect();
       rgFrame->SetPosition(nsPoint(0, yOriginRG));
       // Make sure child views are properly positioned
       nsTableFrame::RePositionViews(rgFrame);
+      rgFrame->InvalidateOverflowRect();
     }
     yOriginRG = yEndRG;
   }
 
   ResizeCells(*this);
 }
 
 PRBool 
@@ -6713,16 +6768,37 @@ nsTableFrame::GetProperty(nsIFrame*     
     if (value) {
       propTable->SetProperty(aFrame, aPropertyName, value, dtorFunc, nsnull);
     }
     return value;
   }
   return nsnull;
 }
 
+/* static */
+void
+nsTableFrame::InvalidateFrame(nsIFrame* aFrame, const nsRect& aOrigRect,
+                              PRBool aIsFirstReflow)
+{
+  nsIFrame* parent = aFrame->GetParent();
+  NS_ASSERTION(parent, "What happened here?");
+
+  if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
+    // Don't bother; we'll invalidate the parent's overflow rect when
+    // we finish reflowing it.
+    return;
+  }
+  
+  if (aIsFirstReflow || aOrigRect.TopLeft() != aFrame->GetPosition()) {
+    aFrame->InvalidateOverflowRect();
+  } else {
+    parent->InvalidateRectDifference(aOrigRect, aFrame->GetRect());
+  }    
+}
+
 #ifdef DEBUG
 #define MAX_SIZE  128
 #define MIN_INDENT 30
 
 static 
 void DumpTableFramesRecur(nsIFrame*       aFrame,
                           PRUint32        aIndent)
 {
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -475,16 +475,30 @@ public:
   PRInt32 GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const;
 
   PRBool HasPctCol() const;
   void SetHasPctCol(PRBool aValue);
 
   PRBool HasCellSpanningPctCol() const;
   void SetHasCellSpanningPctCol(PRBool aValue);
 
+  /**
+   * To be called on a frame by its parent after setting its size/position and
+   * calling DidReflow (possibly via FinishReflowChild()).  This can also be
+   * used for child frames which are not being reflown but did have their size
+   * or position changed.
+   *
+   * @param aFrame The frame to invalidate
+   * @param aOrigRect The original rect of aFrame (before the change).
+   * @param aIsFirstReflow True if the size/position change is due to the
+   *                       first reflow of aFrame.
+   */
+  static void InvalidateFrame(nsIFrame* aFrame, const nsRect& aOrigRect,
+                              PRBool aIsFirstReflow);
+
 protected:
 
   /** protected constructor. 
     * @see NewFrame
     */
   nsTableFrame(nsStyleContext* aContext);
 
   /** destructor, responsible for mColumnLayoutData */
@@ -562,17 +576,18 @@ protected:
 
   // The following is a helper for CalcDesiredHeight 
  
   void DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
                               nscoord                  aAmount);
 
   void PlaceChild(nsTableReflowState&  aReflowState,
                   nsIFrame*            aKidFrame,
-                  nsHTMLReflowMetrics& aKidDesiredSize);
+                  nsHTMLReflowMetrics& aKidDesiredSize,
+                  const nsRect&        aOriginalKidRect);
 
   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.
    */
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -348,25 +348,33 @@ nsTableRowFrame::DidResize()
                                       desiredSize.height);
 
   while (childFrame) {
     if (IS_TABLE_CELL(childFrame->GetType())) {
       nsTableCellFrame* cellFrame = (nsTableCellFrame*)childFrame;
       nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame);
 
       // resize the cell's height
-      //if (cellFrameSize.height!=cellHeight)
+      nsRect cellRect = cellFrame->GetRect();
+      if (cellRect.height != cellHeight)
       {
-        cellFrame->SetSize(nsSize(cellFrame->GetSize().width, cellHeight));
-        // realign cell content based on the new height
-        cellFrame->VerticallyAlignChild(mMaxCellAscent);
-        ConsiderChildOverflow(desiredSize.mOverflowArea, cellFrame);
-        // Note that if the cell's *content* needs to change in response
-        // to this height, it will get a special height reflow.
+        cellFrame->SetSize(nsSize(cellRect.width, cellHeight));
+        nsTableFrame::InvalidateFrame(cellFrame, cellRect, PR_FALSE);
       }
+
+      // realign cell content based on the new height.  We might be able to
+      // skip this if the height didn't change... maybe.  Hard to tell.
+      cellFrame->VerticallyAlignChild(mMaxCellAscent);
+      
+      // Always store the overflow, even if the height didn't change, since
+      // we'll lose part of our overflow area otherwise.
+      ConsiderChildOverflow(desiredSize.mOverflowArea, cellFrame);
+
+      // Note that if the cell's *content* needs to change in response
+      // to this height, it will get a special height reflow.
     }
     // Get the next child
     childFrame = iter.Next();
   }
   FinishAndStoreOverflow(&desiredSize);
   if (HasView()) {
     nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(), &desiredSize.mOverflowArea, 0);
   }
@@ -850,16 +858,20 @@ nsTableRowFrame::ReflowChildren(nsPresCo
       x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame, 
                            cellSpacingX, iter.IsLeftToRight(), PR_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();
+    PRBool firstReflow =
+      (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
+
     if (doReflowChild) {
       // Calculate the available width for the table cell using the known column widths
       nscoord availColWidth, availCellWidth;
       CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX,
                      availColWidth, availCellWidth);
 
       nsHTMLReflowMetrics desiredSize;
 
@@ -884,25 +896,29 @@ nsTableRowFrame::ReflowChildren(nsPresCo
         // Reflow the child
         nsTableCellReflowState kidReflowState(aPresContext, aReflowState, 
                                               kidFrame, kidAvailSize, PR_FALSE);
         InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
                              kidReflowState);
 
         nsReflowStatus status;
         rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
-                         x, 0, 0, status);
+                         x, 0, NS_FRAME_INVALIDATE_ON_MOVE, status);
 
         // allow the table to determine if/how the table needs to be rebalanced
         // If any of the cells are not complete, then we're not complete
         if (NS_FRAME_IS_NOT_COMPLETE(status)) {
           aStatus = NS_FRAME_NOT_COMPLETE;
         }
       }
       else {
+        if (x != kidRect.x) {
+          kidFrame->InvalidateOverflowRect();
+        }
+        
         desiredSize.width = cellDesiredSize.width;
         desiredSize.height = cellDesiredSize.height;
         nsRect *overflowArea =
           cellFrame->GetOverflowAreaProperty();
         if (overflowArea)
           desiredSize.mOverflowArea = *overflowArea;
         else
           desiredSize.mOverflowArea.SetRect(0, 0, cellDesiredSize.width,
@@ -944,27 +960,30 @@ nsTableRowFrame::ReflowChildren(nsPresCo
       }
 
       // Place the child
       if (NS_UNCONSTRAINEDSIZE != availColWidth) {
         desiredSize.width = PR_MAX(availCellWidth, availColWidth);
       }
 
       FinishReflowChild(kidFrame, aPresContext, nsnull, desiredSize, x, 0, 0);
+
+      nsTableFrame::InvalidateFrame(kidFrame, kidRect, firstReflow);
       
       x += desiredSize.width;  
     }
     else {
-      nsRect kidRect = kidFrame->GetRect();
       if (kidRect.x != x) {
-        Invalidate(kidRect); // invalidate the old position
-        kidRect.x = x;
-        kidFrame->SetRect(kidRect);        // move to the new position
+        // Invalidate the old position
+        kidFrame->InvalidateOverflowRect();
+        // move to the new position
+        kidFrame->SetPosition(nsPoint(x, kidRect.y));
         nsTableFrame::RePositionViews(kidFrame);
-        Invalidate(kidRect); // invalidate the new position
+        // invalidate the new position
+        kidFrame->InvalidateOverflowRect();
       }
       // 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;
       }
     }
@@ -1061,35 +1080,39 @@ nsTableRowFrame::ReflowCellFrame(nsPresC
                                  nscoord                  aAvailableHeight,
                                  nsReflowStatus&          aStatus)
 {
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   if (!tableFrame)
     ABORT1(NS_ERROR_NULL_POINTER);
 
   // Reflow the cell frame with the specified height. Use the existing width
-  nsSize cellSize = aCellFrame->GetSize();
+  nsRect cellRect = aCellFrame->GetRect();
   
-  nsSize  availSize(cellSize.width, aAvailableHeight);
+  nsSize  availSize(cellRect.width, aAvailableHeight);
   PRBool borderCollapse = ((nsTableFrame*)tableFrame->GetFirstInFlow())->IsBorderCollapse();
   nsTableCellReflowState cellReflowState(aPresContext, aReflowState,
                                          aCellFrame, availSize, PR_FALSE);
   InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState);
   cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage;
 
   nsHTMLReflowMetrics desiredSize;
 
   ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState,
               0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
   PRBool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus);
   if (fullyComplete) {
     desiredSize.height = aAvailableHeight;
   }
-  aCellFrame->SetSize(nsSize(cellSize.width, desiredSize.height));
+  aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.height));
 
+  nsTableFrame::InvalidateFrame(aCellFrame, cellRect,
+                                (aCellFrame->GetStateBits() &
+                                   NS_FRAME_FIRST_REFLOW) != 0);
+  
   // XXX What happens if this cell has 'vertical-align: baseline' ?
   // XXX Why is it assumed that the cell's ascent hasn't changed ?
   if (fullyComplete) {
     aCellFrame->VerticallyAlignChild(mMaxCellAscent);
   }
   aCellFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
 
   return desiredSize.height;
@@ -1105,30 +1128,44 @@ nsTableRowFrame::CollapseRowIfNecessary(
   PRBool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
   nsTableFrame* tableFrame = static_cast<nsTableFrame*>(nsTableFrame::GetTableFrame(this)->GetFirstInFlow());
   if (!tableFrame)
       return 0;
   if (collapseRow) {
     tableFrame->SetNeedToCollapse(PR_TRUE);
   }
 
+  if (aRowOffset != 0) {
+    // We're moving, so invalidate our old position
+    InvalidateOverflowRect();
+  }
+  
   nsRect rowRect = GetRect();
+  nsRect oldRect = rowRect;
+  
   rowRect.y -= aRowOffset;
   rowRect.width  = aWidth;
   nsRect overflowArea(0, 0, 0, 0);
   nscoord shift = 0;
   nscoord cellSpacingX = tableFrame->GetCellSpacingX();
   nscoord cellSpacingY = tableFrame->GetCellSpacingY();
 
   if (aCollapseGroup || collapseRow) {
     nsTableCellFrame* cellFrame = GetFirstCell();
     aDidCollapse = PR_TRUE;
     shift = rowRect.height + cellSpacingY;
     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) {
+        Invalidate(cRect);
+      }
       cRect.height = 0;
       cellFrame->SetRect(cRect);
       cellFrame = cellFrame->GetNextCell();
     }
     rowRect.height = 0;
   }
   else { // row is not collapsed
     nsTableIterator iter(*this);
@@ -1156,17 +1193,17 @@ nsTableRowFrame::CollapseRowIfNecessary(
         // the space
         if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
             (!iter.IsLeftToRight() &&
              (prevColIndex != cellColIndex + cellColSpan))) {
           x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
                                *tableFrame, cellSpacingX, iter.IsLeftToRight(),
                                PR_TRUE);
         }
-        nsRect cRect(x, 0, 0,rowRect.height);
+        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;
         PRInt32 startIndex = (iter.IsLeftToRight()) ?
                              cellColIndex : cellColIndex + (cellColSpan - 1);
         PRInt32 actualColSpan = cellColSpan;
@@ -1211,33 +1248,50 @@ nsTableRowFrame::CollapseRowIfNecessary(
           PRBool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
                                     nextRowVis->mVisible);
           if (!collapseNextRow) {
             nsRect nextRect = rowFrame->GetRect();
             cRect.height += nextRect.height + cellSpacingY;
           }
           rowFrame = rowFrame->GetNextRow();
         }
+
+        nsRect oldCellRect = cellFrame->GetRect();
+
+        if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
+          // We're moving the cell.  Invalidate the old overflow area
+          cellFrame->InvalidateOverflowRect();
+        }
+        
         cellFrame->SetRect(cRect);
+
+        // XXXbz This looks completely bogus in the cases when we didn't
+        // collapse the cell!
         nsRect cellOverflow = nsRect(0, 0, cRect.width, cRect.height);
         cellFrame->FinishAndStoreOverflow(&cellOverflow, nsSize(cRect.width,
                                               cRect.height));
         nsTableFrame::RePositionViews(cellFrame);
         ConsiderChildOverflow(overflowArea, cellFrame);
-        
+                
+        if (aRowOffset == 0) {
+          nsTableFrame::InvalidateFrame(cellFrame, oldCellRect, PR_FALSE);
+        }
       }
       kidFrame = iter.Next(); // Get the next child
     }
   }
+
   SetRect(rowRect);
   overflowArea.UnionRect(nsRect(0,0,rowRect.width, rowRect.height),
                          overflowArea);
   FinishAndStoreOverflow(&overflowArea, nsSize(rowRect.width,
                                               rowRect.height));
+
   nsTableFrame::RePositionViews(this);
+  nsTableFrame::InvalidateFrame(this, oldRect, PR_FALSE);
   return shift;
 }
 
 
 /**
  * These 3 functions are called by the row group frame's SplitRowGroup() code when
  * it creates a continuing cell frame and wants to insert it into the row's child list
  */
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -289,20 +289,27 @@ nsTableRowGroupFrame::GetSkipSides() con
 }
 
 // Position and size aKidFrame and update our reflow state. The origin of
 // aKidRect is relative to the upper-left origin of our frame
 void 
 nsTableRowGroupFrame::PlaceChild(nsPresContext*        aPresContext,
                                  nsRowGroupReflowState& aReflowState,
                                  nsIFrame*              aKidFrame,
-                                 nsHTMLReflowMetrics&   aDesiredSize)
+                                 nsHTMLReflowMetrics&   aDesiredSize,
+                                 const nsRect&          aOriginalKidRect)
 {
+  PRBool isFirstReflow =
+    (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
+
   // Place and size the child
-  FinishReflowChild(aKidFrame, aPresContext, nsnull, aDesiredSize, 0, aReflowState.y, 0);
+  FinishReflowChild(aKidFrame, aPresContext, nsnull, aDesiredSize, 0,
+                    aReflowState.y, 0);
+
+  nsTableFrame::InvalidateFrame(aKidFrame, aOriginalKidRect, isFirstReflow);
 
   // Adjust the running y-offset
   aReflowState.y += aDesiredSize.height;
 
   // If our height is constrained then update the available height
   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
     aReflowState.availSize.height -= aDesiredSize.height;
   }
@@ -379,17 +386,17 @@ nsTableRowGroupFrame::ReflowChildren(nsP
     haveRow = PR_TRUE;
 
     // Reflow the row frame
     if (reflowAllKids ||
         NS_SUBTREE_DIRTY(kidFrame) ||
         (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
          (isPaginated || (kidFrame->GetStateBits() &
                           NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
-      nsSize oldKidSize = kidFrame->GetSize();
+      nsRect oldKidRect = kidFrame->GetRect();
 
       // XXXldb We used to only pass aDesiredSize.mFlags through for the
       // incremental reflow codepath.
       nsHTMLReflowMetrics desiredSize(aDesiredSize.mFlags);
       desiredSize.width = desiredSize.height = 0;
   
       // Reflow the child into the available space, giving it as much height as
       // it wants. We'll deal with splitting later after we've computed the row
@@ -405,20 +412,22 @@ nsTableRowGroupFrame::ReflowChildren(nsP
         kidReflowState.mFlags.mHResize = PR_TRUE;
      
       // If this isn't the first row, then we can't be at the top of the page
       if (kidFrame != GetFirstFrame()) {
         kidReflowState.mFlags.mIsTopOfPage = PR_FALSE;
       }
 
       rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
-                       0, aReflowState.y, 0, aStatus);
+                       0, aReflowState.y, NS_FRAME_INVALIDATE_ON_MOVE,
+                       aStatus);
 
       // Place the child
-      PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize);
+      PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
+                 oldKidRect);
       aReflowState.y += cellSpacingY;
 
       if (!reflowAllKids) {
         if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) {
           // Inform the row of its new height.
           ((nsTableRowFrame*)kidFrame)->DidResize();
           // the overflow area may have changed inflate the overflow area
           if (aReflowState.tableFrame->IsAutoHeight()) {
@@ -431,17 +440,17 @@ nsTableRowGroupFrame::ReflowChildren(nsP
             // Invalidate the area we're offseting. Note that we only
             // repaint within our existing frame bounds.
             if (kidRect.YMost() < mRect.height) {
               nsRect  dirtyRect(0, kidRect.YMost(),
                                 mRect.width, mRect.height - kidRect.YMost());
               Invalidate(dirtyRect);
             }
           }
-          else if (oldKidSize.height != desiredSize.height)
+          else if (oldKidRect.height != desiredSize.height)
             needToCalcRowHeights = PR_TRUE;
         } else {
           needToCalcRowHeights = PR_TRUE;
         }
       }
 
       if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
         nsTableRowFrame* nextRow = ((nsTableRowFrame*)kidFrame)->GetNextRow();
@@ -793,23 +802,29 @@ nsTableRowGroupFrame::CalculateRowHeight
   // update the rows with their (potentially) new heights
   for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
     nsRect rowBounds = rowFrame->GetRect(); 
 
     PRBool movedFrame = (rowBounds.y != yOrigin);  
     nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
     
     if (movedFrame || (rowHeight != rowBounds.height)) {
-      // Resize the row to its final size and position
-      rowBounds.y = yOrigin;
-      rowBounds.height = rowHeight;
-      rowFrame->SetRect(rowBounds);
+      // Resize/move the row to its final size and position
+      if (movedFrame) {
+        rowFrame->InvalidateOverflowRect();
+      }
+      
+      rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
+                               rowHeight));
+
+      nsTableFrame::InvalidateFrame(rowFrame, rowBounds, PR_FALSE);
     }
     if (movedFrame) {
       nsTableFrame::RePositionViews(rowFrame);
+      // XXXbz we don't need to update our overflow area?
     }
     yOrigin += rowHeight + cellSpacingY;
   }
 
   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());
   }
@@ -840,30 +855,39 @@ nsTableRowGroupFrame::CollapseRowGroupIf
     yGroupOffset += rowFrame->CollapseRowIfNecessary(yGroupOffset,
                                                      aWidth, collapseGroup,
                                                      didCollapse);
     ConsiderChildOverflow(overflowArea, rowFrame);
     rowFrame = rowFrame->GetNextRow();
   }
 
   nsRect groupRect = GetRect();
+  nsRect oldGroupRect = groupRect;
+  
   groupRect.height -= yGroupOffset;
   if (didCollapse) {
     // add back the cellspacing between rowgroups
     groupRect.height += tableFrame->GetCellSpacingY();
   }
 
   groupRect.y -= aYTotalOffset;
   groupRect.width = aWidth;
+
+  if (aYTotalOffset != 0) {
+    InvalidateOverflowRect();
+  }
+  
   SetRect(groupRect);
   overflowArea.UnionRect(nsRect(0, 0, groupRect.width, groupRect.height),
                          overflowArea);
   FinishAndStoreOverflow(&overflowArea, nsSize(groupRect.width,
                                               groupRect.height));
   nsTableFrame::RePositionViews(this);
+  nsTableFrame::InvalidateFrame(this, oldGroupRect, PR_FALSE);
+
   return yGroupOffset;
 }
 
 // Move a child that was skipped during an incremental reflow.
 // This function is not used for paginated mode so we don't need to deal
 // with continuing frames, and it's only called if aKidFrame has no
 // cells that span into it and no cells that span across it. That way
 // we don't have to deal with rowspans
@@ -875,18 +899,20 @@ nsTableRowGroupFrame::SlideChild(nsRowGr
   NS_PRECONDITION(NS_UNCONSTRAINEDSIZE == aReflowState.reflowState.availableHeight,
                   "we're not in galley mode");
 
   // Move the frame if we need to
   nsPoint oldPosition = aKidFrame->GetPosition();
   nsPoint newPosition = oldPosition;
   newPosition.y = aReflowState.y;
   if (oldPosition.y != newPosition.y) {
+    aKidFrame->InvalidateOverflowRect();
     aKidFrame->SetPosition(newPosition);
     nsTableFrame::RePositionViews(aKidFrame);
+    aKidFrame->InvalidateOverflowRect();
   }
 }
 
 // Create a continuing frame, add it to the child list, and then push it
 // and the frames that follow
 void 
 nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext,
                                                nsIFrame&       aRowFrame,
@@ -1074,25 +1100,30 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
         nsHTMLReflowState rowReflowState(aPresContext, aReflowState,
                                          rowFrame, availSize,
                                          -1, -1, PR_FALSE);
                                          
         InitChildReflowState(*aPresContext, borderCollapse, rowReflowState);
         rowReflowState.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
         nsHTMLReflowMetrics rowMetrics;
 
+        // Get the old size before we reflow.
+        nsRect oldRowRect = rowFrame->GetRect();
+
         // Reflow the cell with the constrained height. A cell with rowspan >1 will get this
         // reflow later during SplitSpanningCells.
         rv = ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowState,
                          0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
         if (NS_FAILED(rv)) return rv;
         rowFrame->SetSize(nsSize(rowMetrics.width, rowMetrics.height));
         rowFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
         rowFrame->DidResize();
 
+        nsTableFrame::InvalidateFrame(rowFrame, oldRowRect, PR_FALSE);
+
         if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
           // The row frame is incomplete and all of the rowspan 1 cells' block frames split
           if ((rowMetrics.height <= rowReflowState.availableHeight) || isTopOfPage) {
             // The row stays on this page because either it split ok or we're on the top of page.
             // If top of page and the height exceeded the avail height, then there will be data loss
             NS_ASSERTION(rowMetrics.height <= rowReflowState.availableHeight, 
                          "data loss - incomplete row needed more height than available, on top of page");
             CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow);
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -308,17 +308,18 @@ protected:
                             nsHTMLReflowState& aReflowState);
   
   /** implement abstract method on nsHTMLContainerFrame */
   virtual PRIntn GetSkipSides() const;
 
   void PlaceChild(nsPresContext*        aPresContext,
                   nsRowGroupReflowState& aReflowState,
                   nsIFrame*              aKidFrame,
-                  nsHTMLReflowMetrics&   aDesiredSize);
+                  nsHTMLReflowMetrics&   aDesiredSize,
+                  const nsRect&          aOriginalKidRect);
 
   void CalculateRowHeights(nsPresContext*          aPresContext, 
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState);
 
   void DidResizeRows(nsHTMLReflowMetrics& aDesiredSize);
 
   void SlideChild(nsRowGroupReflowState& aReflowState,