Bug 484448. Fix handling of whitespace kids of table-related frames. r=bernd, sr=roc
☠☠ backed out by 3c7cd3a8f785 ☠ ☠
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 08 Apr 2009 12:56:16 -0400
changeset 27086 0ea22856b5d9e25e2b687e3fb6f3da557274f42a
parent 27085 a41ec1a6b8fcf08db5ce425827c872899f429776
child 27087 c7018b3ca9416249d41901458cbe05ae2d462e32
child 27095 3c7cd3a8f785ec9a09fb4b5244f1c095f26c6fc1
push idunknown
push userunknown
push dateunknown
reviewersbernd, roc
bugs484448
milestone1.9.2a1pre
Bug 484448. Fix handling of whitespace kids of table-related frames. r=bernd, sr=roc
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericDOMDataNode.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsTextFrameThebes.cpp
layout/mathml/nsMathMLmtableFrame.h
layout/reftests/table-anonymous-boxes/3-blocks-ref.html
layout/reftests/table-anonymous-boxes/3-tables-ref.html
layout/reftests/table-anonymous-boxes/372641-1-ref.xhtml
layout/reftests/table-anonymous-boxes/372641-1a.xhtml
layout/reftests/table-anonymous-boxes/372641-1b.xhtml
layout/reftests/table-anonymous-boxes/372641-1c.xhtml
layout/reftests/table-anonymous-boxes/3x3-cols-ref.html
layout/reftests/table-anonymous-boxes/3x3-ref.html
layout/reftests/table-anonymous-boxes/dynamic-removal-14.html
layout/reftests/table-anonymous-boxes/infer-cells-1.html
layout/reftests/table-anonymous-boxes/reftest.list
layout/reftests/table-anonymous-boxes/white-space-1-ref.html
layout/reftests/table-anonymous-boxes/white-space-1.html
layout/reftests/table-anonymous-boxes/white-space-10.html
layout/reftests/table-anonymous-boxes/white-space-11.html
layout/reftests/table-anonymous-boxes/white-space-12.html
layout/reftests/table-anonymous-boxes/white-space-13.html
layout/reftests/table-anonymous-boxes/white-space-14.html
layout/reftests/table-anonymous-boxes/white-space-15.html
layout/reftests/table-anonymous-boxes/white-space-16.html
layout/reftests/table-anonymous-boxes/white-space-17.html
layout/reftests/table-anonymous-boxes/white-space-18.html
layout/reftests/table-anonymous-boxes/white-space-19.html
layout/reftests/table-anonymous-boxes/white-space-2.html
layout/reftests/table-anonymous-boxes/white-space-20.html
layout/reftests/table-anonymous-boxes/white-space-21.html
layout/reftests/table-anonymous-boxes/white-space-22.html
layout/reftests/table-anonymous-boxes/white-space-23.html
layout/reftests/table-anonymous-boxes/white-space-24.html
layout/reftests/table-anonymous-boxes/white-space-25.html
layout/reftests/table-anonymous-boxes/white-space-26.html
layout/reftests/table-anonymous-boxes/white-space-3.html
layout/reftests/table-anonymous-boxes/white-space-4.html
layout/reftests/table-anonymous-boxes/white-space-5.html
layout/reftests/table-anonymous-boxes/white-space-6.html
layout/reftests/table-anonymous-boxes/white-space-7.html
layout/reftests/table-anonymous-boxes/white-space-8.html
layout/reftests/table-anonymous-boxes/white-space-9.html
layout/reftests/table-anonymous-boxes/white-space-pre-1.html
layout/reftests/table-anonymous-boxes/white-space-pre-ref.html
layout/reftests/table-anonymous-boxes/white-space-ref.html
layout/tables/nsTableColFrame.h
layout/tables/nsTableColGroupFrame.h
layout/tables/nsTableFrame.h
layout/tables/nsTableOuterFrame.h
layout/tables/nsTableRowFrame.h
layout/tables/nsTableRowGroupFrame.h
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -580,16 +580,20 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
                    "Bound to wrong binding parent");
 
   return NS_OK;
 }
 
 void
 nsGenericDOMDataNode::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
+  // Unset FRAMETREE_DEPENDS_ON_CHARS; if we need it again later, it'll get set
+  // again.
+  UnsetFlags(FRAMETREE_DEPENDS_ON_CHARS);
+  
   nsIDocument *document = GetCurrentDoc();
   if (document) {
     // Notify XBL- & nsIAnonymousContentCreator-generated
     // anonymous content that the document is changing.
     // This is needed to update the insertion point.
     document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
   }
 
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -38,16 +38,19 @@
 /*
  * Base class for DOM Core's nsIDOMComment, nsIDOMDocumentType, nsIDOMText,
  * nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes.
  */
 
 #ifndef nsGenericDOMDataNode_h___
 #define nsGenericDOMDataNode_h___
 
+// This bit is set if the frame tree depends on whether this node is whitespace
+#define FRAMETREE_DEPENDS_ON_CHARS (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET)
+
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOM3Text.h"
 #include "nsTextFragment.h"
 #include "nsDOMError.h"
 #include "nsIEventListenerManager.h"
 #include "nsGenericElement.h"
 #include "nsCycleCollectionParticipant.h"
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -117,16 +117,17 @@
 #include "nsIObjectLoadingContent.h"
 #include "nsContentErrors.h"
 #include "nsIPrincipal.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsStyleUtil.h"
 #include "nsIFocusEventSuppressor.h"
 #include "nsBox.h"
 #include "nsTArray.h"
+#include "nsGenericDOMDataNode.h"
 
 #ifdef MOZ_XUL
 #include "nsIRootBox.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIXULDocument.h"
 #endif
 #ifdef ACCESSIBILITY
@@ -1918,23 +1919,16 @@ nsCSSFrameConstructor::CreateGeneratedCo
       container->AppendChildTo(content, PR_FALSE);
     }
   }
 
   AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
                                     kNameSpaceID_None, pseudoStyleContext,
                                     ITEM_IS_GENERATED_CONTENT, aItems);
 }
-
-static PRBool
-TextIsOnlyWhitespace(nsIContent* aContent)
-{
-  return aContent->IsNodeOfType(nsINode::eTEXT) &&
-         aContent->TextIsOnlyWhitespace();
-}
     
 /****************************************************
  **  BEGIN TABLE SECTION
  ****************************************************/
 
 // The term pseudo frame is being used instead of anonymous frame, since anonymous
 // frame has been used elsewhere to refer to frames that have generated content
 
@@ -2369,19 +2363,31 @@ NeedFrameFor(nsIFrame*   aParentFrame,
              nsIContent* aChildContent) 
 {
   // don't create a whitespace frame if aParentFrame doesn't want it.
   // always create frames for children in generated content. counter(),
   // quotes, and attr() content can easily change dynamically and we don't
   // want to be reconstructing frames. It's not even clear that these
   // should be considered ignorable just because they evaluate to
   // whitespace.
-  return !aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace)
-    || !TextIsOnlyWhitespace(aChildContent)
-    || aParentFrame->IsGeneratedContentFrame();
+
+  // We could handle all this in CreateNeededTablePseudos or some other place
+  // after we build our frame construction items, but that would involve
+  // creating frame construction items for whitespace kids of
+  // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
+  // all anyway, and involve an extra walk down the frame construction item
+  // list.
+  if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
+      aParentFrame->IsGeneratedContentFrame() ||
+      !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
+    return PR_TRUE;
+  }
+
+  aChildContent->SetFlags(FRAMETREE_DEPENDS_ON_CHARS);
+  return !aChildContent->TextIsOnlyWhitespace();
 }
 
 /***********************************************
  * END TABLE SECTION
  ***********************************************/
 
 static PRBool CheckOverflow(nsPresContext* aPresContext,
                             const nsStyleDisplay* aDisplay)
@@ -6165,16 +6171,17 @@ nsCSSFrameConstructor::ContentAppended(n
 
   nsIAtom* frameType = parentFrame->GetType();
   // We should never get here with fieldsets, since they have multiple
   // insertion points.
   NS_ASSERTION(frameType != nsGkAtoms::fieldSetFrame,
                "Unexpected parent");
 
   // Deal with possible :after generated content on the parent
+  nsIFrame* origParentFrame = parentFrame;
   nsIFrame* parentAfterFrame;
   parentFrame =
     ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
                                         aContainer, parentFrame,
                                         &parentAfterFrame);
   
   // Create some new frames
   nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
@@ -6200,23 +6207,37 @@ nsCSSFrameConstructor::ContentAppended(n
   FrameConstructionItemList items;
   for (PRUint32 i = aNewIndexInContainer, count = aContainer->GetChildCount();
        i < count;
        ++i) {
     AddFrameConstructionItems(state, aContainer->GetChildAt(i), parentFrame,
                               items);
   }
 
+  // Determine whether this is an insert at the beginning
+  PRBool isInsertAtStart = PR_TRUE;
+  nsIFrame* curParent = parentFrame;
+  nsIFrame* curAfterChild = parentAfterFrame;
+  do {
+    if (curParent->GetPrevContinuation() ||
+        curParent->GetFirstChild(nsnull) != curAfterChild) {
+      isInsertAtStart = PR_FALSE;
+      break;
+    }
+    curAfterChild = curParent;
+    curParent = curParent->GetParent();
+  } while (curAfterChild != origParentFrame);
+
   // Perform special check for diddling around with the frames in
   // a special inline frame.
   // If we're appending before :after content, then we're not really
   // appending, so let WipeContainingBlock know that.
   LAYOUT_PHASE_TEMP_EXIT();
   if (WipeContainingBlock(state, containingBlock, parentFrame, items,
-                          !parentAfterFrame, nsnull)) {
+                          !parentAfterFrame, isInsertAtStart, nsnull)) {
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   nsFrameItems frameItems;
   ConstructFramesFromItemList(state, items, parentFrame, frameItems);
 
@@ -6622,17 +6643,19 @@ nsCSSFrameConstructor::ContentInserted(n
   AddFrameConstructionItems(state, aChild, parentFrame, items);
 
   // Perform special check for diddling around with the frames in
   // a special inline frame.
   // If we're appending before :after content, then we're not really
   // appending, so let WipeContainingBlock know that.
   LAYOUT_PHASE_TEMP_EXIT();
   if (WipeContainingBlock(state, containingBlock, parentFrame, items,
-                          isAppend && !appendAfterFrame, prevSibling)) {
+                          isAppend && !appendAfterFrame,
+                          prevSibling == nsnull,
+                          prevSibling)) {
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   // if the container is a table and a caption will be appended, it needs to be
   // put in the outer table frame's additional child list.
   nsFrameItems frameItems, captionItems;
@@ -6652,17 +6675,20 @@ nsCSSFrameConstructor::ContentInserted(n
   // If the parent of our current prevSibling is different from the frame we'll
   // actually use as the parent, then the calculated insertion point is now
   // invalid and as it is unknown where to insert correctly we append instead
   // (bug 341858).
   // This can affect our appendAfterFrame, but should not have any effect on
   // the WipeContainingBlock above, since this should only happen when neither
   // parent is a special frame (and in fact, only when one is an outer table
   // and one is an inner table or when the parent is a fieldset or fieldset
-  // content frame).
+  // content frame).  So it won't affect the {ib} or XUL box cases in
+  // WipeContainingBlock(), and the table pseudo handling will only be affected
+  // by us maybe thinking we're not inserting at the beginning, whereas we
+  // really are.  That would have made us reframe unnecessarily, but that's ok.
   // XXXbz we should push our frame construction item code up higher, so we
   // know what our items are by the time we start figuring out previous
   // siblings
   if (prevSibling && frameItems.childList &&
       frameItems.childList->GetParent() != prevSibling->GetParent()) {
 #ifdef DEBUG
     nsIFrame* frame1 = frameItems.childList->GetParent();
     nsIFrame* frame2 = prevSibling->GetParent();
@@ -7450,16 +7476,25 @@ nsCSSFrameConstructor::StyleChangeReflow
 
 nsresult
 nsCSSFrameConstructor::CharacterDataChanged(nsIContent* aContent,
                                             PRBool aAppend)
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
   nsresult      rv = NS_OK;
 
+  if (aContent->HasFlag(FRAMETREE_DEPENDS_ON_CHARS)) {
+#ifdef DEBUG
+    nsIFrame* frame = mPresShell->GetPrimaryFrameFor(aContent);
+    NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(),
+                 "Bit should never be set on generated content");
+#endif
+    return RecreateFramesForContent(aContent);
+  }
+
   // Find the child frame
   nsIFrame* frame = mPresShell->GetPrimaryFrameFor(aContent);
 
   // Notify the first frame that maps the content. It will generate a reflow
   // command
 
   // It's possible the frame whose content changed isn't inserted into the
   // frame hierarchy yet, or that there is no frame that maps the content
@@ -8706,16 +8741,38 @@ nsCSSFrameConstructor::MaybeRecreateFram
     frameManager->ChangeUndisplayedContent(aContent, newContext);
     if (newContext->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE) {
       result = RecreateFramesForContent(aContent);
     }
   }
   return result;
 }
 
+static nsIFrame*
+FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
+{
+  nsIFrame* f = aParentFrame->GetFirstChild(nsnull);
+  while (f && f->GetType() == nsGkAtoms::textFrame &&
+         f->GetContent()->TextIsOnlyWhitespace()) {
+    f = f->GetNextSibling();
+  }
+  return f;
+}
+
+static nsIFrame*
+FindNextNonWhitespaceSibling(nsIFrame* aFrame)
+{
+  nsIFrame* f = aFrame;
+  do {
+    f = f->GetNextSibling();
+  } while (f && f->GetType() == nsGkAtoms::textFrame &&
+           f->GetContent()->TextIsOnlyWhitespace());
+  return f;
+}
+
 PRBool
 nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
                                                              nsresult* aResult)
 {
   NS_PRECONDITION(aFrame, "Must have a frame");
   NS_PRECONDITION(aFrame->GetParent(), "Frame shouldn't be root");
   NS_PRECONDITION(aResult, "Null out param?");
   NS_PRECONDITION(aFrame == aFrame->GetFirstContinuation(),
@@ -8739,18 +8796,18 @@ nsCSSFrameConstructor::MaybeRecreateCont
 
   // Now check for possibly needing to reconstruct due to a pseudo parent
   nsIFrame* inFlowFrame =
     (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
       mPresShell->FrameManager()->GetPlaceholderFrameFor(aFrame) : aFrame;
   NS_ASSERTION(inFlowFrame, "How did that happen?");
   nsIFrame* parent = inFlowFrame->GetParent();
   if (IsTablePseudo(parent)) {
-    if (parent->GetFirstChild(nsnull) == inFlowFrame ||
-        !inFlowFrame->GetLastContinuation()->GetNextSibling() ||
+    if (FindFirstNonWhitespaceChild(parent) == inFlowFrame ||
+        !FindNextNonWhitespaceSibling(inFlowFrame->GetLastContinuation()) ||
         // If we're a table-column-group, then the GetFirstChild check above is
         // not going to catch cases when we're the first child.
         (inFlowFrame->GetType() == nsGkAtoms::tableColGroupFrame &&
          parent->GetFirstChild(nsGkAtoms::colGroupList) == inFlowFrame) ||
         // Similar if we're a table-caption.
         (inFlowFrame->GetType() == nsGkAtoms::tableCaptionFrame &&
          parent->GetFirstChild(nsGkAtoms::captionList) == inFlowFrame)) {
       // We're the first or last frame in the pseudo.  Need to reframe.
@@ -9052,117 +9109,114 @@ nsCSSFrameConstructor::CreateNeededTable
   ParentType ourParentType = GetParentType(aParentFrame);
   if (aItems.AllWantParentType(ourParentType)) {
     // Nothing to do here
     return NS_OK;
   }
 
   FCItemIterator iter(aItems);
   do {
-    NS_ASSERTION(!iter.IsDone(), "How did that happen?");
-
-    // Advance to the next item that wants a different parent type.
-    while (iter.item().DesiredParentType() == ourParentType) {
-      iter.Next();
-      if (iter.IsDone()) {
-        // Nothing else to do here; we're finished
-        return NS_OK;
-      }
-    }
-
-    NS_ASSERTION(!iter.IsDone() &&
-                 iter.item().DesiredParentType() != ourParentType,
-                 "Why did we stop?");
+    if (iter.SkipItemsWantingParentType(ourParentType)) {
+      // Nothing else to do here; we're finished
+      return NS_OK;
+    }
 
     // Now we're pointing to the first child that wants a different parent
-    // type.  Except for generated content, we should have already enforced the
-    // fact that no such items are whitespace (either in this method when
-    // constructing wrapping items, or when constructing the original
-    // FrameConstructionItemList).
-    NS_ASSERTION(aParentFrame->IsGeneratedContentFrame() ||
-                 !iter.item().mIsText ||
-                 !iter.item().mContent->TextIsOnlyWhitespace(),
-                 "Why do we have whitespace under a known-table parent?");
+    // type.
 
     // Now try to figure out what kids we can group together.  We can generally
     // group everything that has a different desired parent type from us.  Two
     // exceptions to this:
     // 1) If our parent type is table, we can't group columns with anything
     //    else other than whitespace.
-    // 2) Whitespace that lies between two things we can group should
-    //    be dropped, even if we can't group them with each other.
-    // XXXbz it's not clear to me that rule 2 is a good one, it's not called
-    // for by the (admittedly vague) spec, and in fact it leads to some pretty
-    // crappy behavior if you have some inlines and whitespace as kids of a
-    // table-row, say, but it's more or less what we used to do.  More
-    // precisely, we shipped all whitespace out to the nearest block parent of
-    // the whole mess, sort of.  In any case this aspect of things, and in fact
-    // this whole function might need changes as the spec here gets
-    // clarified...  I happen to think we should not drop whitespace that comes
-    // between things that want a block parent.
+    // 2) Whitespace that lies between two things we can group which both want
+    //    a non-block parent should be dropped, even if we can't group them
+    //    with each other and even if the whitespace wants a parent of
+    //    ourParentType.  Ends of the list count as things that don't want a
+    //    block parent (so that for example we'll drop a whitespace-only list).
 
     FCItemIterator endIter(iter); /* iterator to find the end of the group */
     ParentType groupingParentType = endIter.item().DesiredParentType();
-    // If we decide to, we could optimize this by checking whether
-    // aItems.AllWantParentType(groupingParentType) and if so just setting
-    // endIter to the end of the list, which is an O(1) operation.  That
-    // requires not dropping whitespace between items that want a block parent,
-    // though, per the XXX comment above, since a whole bunch of spans and
-    // whitespace would test true to all wanting a block parent.
-    do {
-      endIter.Next();
-      if (endIter.IsDone()) {
-        break;
-      }
-
-      if (!aParentFrame->IsGeneratedContentFrame() &&
-          endIter.item().IsWhitespace()) {
-        // Whitespace coming after some groupable items
-        FCItemIterator textSkipIter(endIter);
-        do {
-          textSkipIter.Next();
-        } while (!textSkipIter.IsDone() && textSkipIter.item().IsWhitespace());
-
-        PRBool trailingSpace = textSkipIter.IsDone();
-        if (// Trailing whitespace we can't handle
-            (trailingSpace && ourParentType != eTypeBlock) ||
-            // Whitespace before kids needing wrapping
-            (!trailingSpace &&
-             textSkipIter.item().DesiredParentType() != ourParentType)) {
-          // Drop all the whitespace here so that |endIter| now points to the
-          // same thing as |textSkipIter|.  This doesn't affect where |iter|
-          // points, since that's guaranted to point to before endIter.
-          do {
-            endIter.DeleteItem();
-          } while (endIter != textSkipIter);
-
-          NS_ASSERTION(endIter.IsDone() == trailingSpace,
-                       "endIter == skipIter now!");
-          if (trailingSpace) {
-            break; // The loop advancing endIter
+    if (aItems.AllWantParentType(groupingParentType) &&
+        groupingParentType != eTypeBlock) {
+      // Just group them all and be done with it.  We need the check for
+      // eTypeBlock here to catch the "all the items are whitespace" case
+      // described above.
+      endIter.SetToEnd();
+    } else {
+      // Locate the end of the group.
+
+      // Keep track of the type the previous item wanted, in case we have to
+      // deal with whitespace.  Start it off with ourParentType, since that's
+      // the last thing |iter| would have skipped over.
+      ParentType prevParentType = ourParentType;
+      do {
+        /* Walk an iterator past any whitespace that we might be able to drop from the list */
+        FCItemIterator spaceEndIter(endIter);
+        if (prevParentType != eTypeBlock &&
+            !aParentFrame->IsGeneratedContentFrame() &&
+            spaceEndIter.item().IsWhitespace()) {
+          PRBool trailingSpaces = spaceEndIter.SkipWhitespace();
+
+          // See whether we can drop the whitespace
+          if (trailingSpaces ||
+              spaceEndIter.item().DesiredParentType() != eTypeBlock) {
+            PRBool updateStart = (iter == endIter);
+            endIter.DeleteItemsTo(spaceEndIter);
+            NS_ASSERTION(trailingSpaces == endIter.IsDone(), "These should match");
+
+            if (updateStart) {
+              iter = endIter;
+            }
+
+            if (trailingSpaces) {
+              break; /* Found group end */
+            }
+
+            if (updateStart) {
+              // Update groupingParentType, since it might have been eTypeBlock
+              // just because of the whitespace.
+              groupingParentType = iter.item().DesiredParentType();
+            }
           }
         }
-      }
-
-      ParentType itemParentType = endIter.item().DesiredParentType();
-
-      if (itemParentType == ourParentType) {
-        break;
-      }
-
-      if (ourParentType == eTypeTable &&
-          (itemParentType == eTypeColGroup) !=
-          (groupingParentType == eTypeColGroup)) {
-        // Either we started with columns and now found something else, or vice
-        // versa.  In any case, end the grouping.
-        break;
-      }
-    } while (1);
-
-    NS_ASSERTION(iter != endIter, "How did that happen?");
+
+        // Now endIter points to a non-whitespace item or a non-droppable
+        // whitespace item. In the latter case, if this is the end of the group
+        // we'll traverse this whitespace again.  But it'll all just be quick
+        // DesiredParentType() checks which will match ourParentType (that's
+        // what it means that this is the group end), so it's OK.
+        prevParentType = endIter.item().DesiredParentType();
+        if (prevParentType == ourParentType) {
+          // End the group at endIter.
+          break;
+        }
+
+        if (ourParentType == eTypeTable &&
+            (prevParentType == eTypeColGroup) !=
+            (groupingParentType == eTypeColGroup)) {
+          // Either we started with columns and now found something else, or vice
+          // versa.  In any case, end the grouping.
+          break;
+        }
+
+        // Include the whitespace we didn't drop (if any) in the group, since
+        // this is not the end of the group.  Note that this doesn't change
+        // prevParentType, since if we didn't drop the whitespace then we ended
+        // at something that wants a block parent.
+        endIter = spaceEndIter;
+
+        endIter.Next();
+      } while (!endIter.IsDone());
+    }
+
+    if (iter == endIter) {
+      // Nothing to wrap here; just skipped some whitespace
+      continue;
+    }
 
     // Now group together all the items between iter and endIter.  The right
     // parent type to use depends on ourParentType.
     ParentType wrapperType;
     switch (ourParentType) {
       case eTypeBlock:
         wrapperType = eTypeTable;
         break;
@@ -10768,18 +10822,19 @@ nsCSSFrameConstructor::BuildInlineChildI
 
   aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline();
 }
 
 PRBool
 nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState,
                                            nsIFrame* aContainingBlock,
                                            nsIFrame* aFrame,
-                                           const FrameConstructionItemList& aItems,
+                                           FrameConstructionItemList& aItems,
                                            PRBool aIsAppend,
+                                           PRBool aIsInsertAtStart,
                                            nsIFrame* aPrevSibling)
 {
   if (aItems.IsEmpty()) {
     return PR_FALSE;
   }
   
   // Before we go and append the frames, we must check for three
   // special situations.
@@ -10797,28 +10852,112 @@ nsCSSFrameConstructor::WipeContainingBlo
   ParentType parentType = GetParentType(aFrame);
   // If all the kids want a parent of the type that aFrame is, then we're all
   // set to go.  Indeed, there won't be any table pseudo-frames created between
   // aFrame and the kids, so those won't need to be merged with any table
   // pseudo-frames that might already be kids of aFrame.  If aFrame itself is a
   // table pseudo-frame, then all the kids in this list would have wanted a
   // frame of that type wrapping them anyway, so putting them inside it is ok.
   if (!aItems.AllWantParentType(parentType)) {
+    // Don't give up yet.  If parentType is not eTypeBlock and the parent is
+    // not a generated content frame, then try filtering whitespace out of the
+    // list.
+    if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) {
+      // For leading whitespace followed by a kid that wants our parent type,
+      // there are three cases:
+      // 1) We have a previous sibling.  That means that previous sibling
+      //    wanted a (non-block) parent of the type we're looking at.  Then the
+      //    whitespace comes between two table-internal elements, so should be
+      //    collapsed out.
+      // 2) We have no previous sibling but aIsInsertAtStart is true.  That
+      //    means that we'll be at the beginning of our non-block-type parent,
+      //    and the whitespace is OK to collapse out.  If something is ever
+      //    inserted before us, it'll find our own parent as its parent and if
+      //    it's something that would care about the whitespace it'll want a
+      //    block parent, so it'll trigger a reframe at that point.
+      // 3) We have no previous sibling and aIsInsertAtStart is false.  In this
+      //    case it might be that we're actually in ContentAppended and have a
+      //    previous sibling that will want the whitespace.  We can't filter it
+      //    out in this case.
+      //
+      // XXXbz really, we should figure out the correct prevSibling in
+      // ContentAppended and pass it in here, I would think.  We end up having
+      // to sort it out anyway, eventually...  Then we could just check the
+      // prevSibling to decide on what to do with the whitespace.
+      //
+      // It's always OK to drop whitespace between any two items that want a
+      // parent of type parentType.
+      //
+      // For trailing whitespace, the situation is more complicated.  We might
+      // in fact have a next sibling that would care about the whitespace.  We
+      // just don't know anything about that here.  So leave trailing
+      // whitespace be, unless aIsAppend is true.  If it's true, we have no
+      // next sibling, and if one ever gets added that would care about the
+      // whitespace it'll get us as a previous sibling and trigger a reframe.
+
+      FCItemIterator iter(aItems);
+      FCItemIterator start(iter);
+      do {
+        if (iter.SkipItemsWantingParentType(parentType)) {
+          break;
+        }
+
+        // iter points to an item that wants a different parent.  If it's not
+        // whitespace, we're done; no more point scanning the list.
+        if (!iter.item().IsWhitespace()) {
+          break;
+        }
+
+        if (iter == start && !aPrevSibling && !aIsInsertAtStart) {
+          // Leading whitespace, not inserting at the start, and don't know
+          // whether our previous sibling might want this whitespace.  See the
+          // long comment above.  Need to reframe.
+          break;
+        }
+
+        FCItemIterator spaceEndIter(iter);
+        // Advance spaceEndIter past any whitespace
+        PRBool trailingSpaces = spaceEndIter.SkipWhitespace();
+
+        if ((!trailingSpaces &&
+             spaceEndIter.item().DesiredParentType() == parentType) ||
+            (trailingSpaces && aIsAppend)) {
+          // Drop the whitespace
+          iter.DeleteItemsTo(spaceEndIter);
+        } else {
+          // We're done: we don't want to drop the whitespace, and it has the
+          // wrong parent type.
+          break;
+        }
+
+        // Now loop, since |iter| points to item right after the whitespace we
+        // removed.
+      } while (!iter.IsDone());
+    }
+
     // We might be able to figure out some sort of optimizations here, but they
     // would have to depend on having a correct aPrevSibling and a correct next
     // sibling.  For example, we can probably avoid reframing if none of
     // aFrame, aPrevSibling, and next sibling are table pseudo-frames.  But it
     // doesn't seem worth it to worry about that for now, especially since we
     // in fact do not have a reliable aPrevSibling, nor any next sibling, in
     // this method.
 
-    // Reframing aFrame->GetContent() is good enough, since the content of
-    // table pseudo-frames is the ancestor content.
-    RecreateFramesForContent(aFrame->GetContent());
-    return PR_TRUE;
+    // aItems might have changed, so recheck the parent type thing.  In fact,
+    // it might be empty, so recheck that too.
+    if (aItems.IsEmpty()) {
+      return PR_FALSE;
+    }
+
+    if (!aItems.AllWantParentType(parentType)) {
+      // Reframing aFrame->GetContent() is good enough, since the content of
+      // table pseudo-frames is the ancestor content.
+      RecreateFramesForContent(aFrame->GetContent());
+      return PR_TRUE;
+    }
   }
 
   // Situation #3 is an inline frame that will now contain block
   // frames. This is a no-no and the frame construction logic knows
   // how to fix this.  See defition of IsInlineFrame() for what "an
   // inline" is.  Whether we have "a block" is tested for by
   // AreAllItemsInline.
 
@@ -11531,16 +11670,29 @@ nsCSSFrameConstructor::LazyGenerateChild
 
     // call XBL constructors after the frames are created
     mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
   }
 
   return NS_OK;
 }
 
+//////////////////////////////////////////////////////////
+// nsCSSFrameConstructor::FrameConstructionItem methods //
+//////////////////////////////////////////////////////////
+PRBool
+nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace() const
+{
+  if (!mIsText) {
+    return PR_FALSE;
+  }
+  mContent->SetFlags(FRAMETREE_DEPENDS_ON_CHARS);
+  return mContent->TextIsOnlyWhitespace();
+}
+
 //////////////////////////////////////////////////////////////
 // nsCSSFrameConstructor::FrameConstructionItemList methods //
 //////////////////////////////////////////////////////////////
 void
 nsCSSFrameConstructor::FrameConstructionItemList::
 AdjustCountsForItem(FrameConstructionItem* aItem, PRInt32 aDelta)
 {
   NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
@@ -11552,16 +11704,46 @@ AdjustCountsForItem(FrameConstructionIte
     mLineParticipantCount += aDelta;
   }
   mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
 ////////////////////////////////////////////////////////////////////////
+inline PRBool
+nsCSSFrameConstructor::FrameConstructionItemList::
+Iterator::SkipItemsWantingParentType(ParentType aParentType)
+{
+  NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
+  while (item().DesiredParentType() == aParentType) {
+    Next();
+    if (IsDone()) {
+      return PR_TRUE;
+    }
+  }
+  return PR_FALSE;
+}
+
+inline PRBool
+nsCSSFrameConstructor::FrameConstructionItemList::
+Iterator::SkipWhitespace()
+{
+  NS_PRECONDITION(!IsDone(), "Shouldn't be done yet");
+  NS_PRECONDITION(item().IsWhitespace(), "Not pointing to whitespace?");
+  do {
+    Next();
+    if (IsDone()) {
+      return PR_TRUE;
+    }
+  } while (item().IsWhitespace());
+
+  return PR_FALSE;
+}
+
 void
 nsCSSFrameConstructor::FrameConstructionItemList::
 Iterator::AppendItemToList(FrameConstructionItemList& aTargetList)
 {
   NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
   NS_PRECONDITION(!IsDone(), "should not be done");
 
   FrameConstructionItem* item = ToItem(mCurrent);
@@ -11614,16 +11796,23 @@ Iterator::InsertItem(FrameConstructionIt
   // Just insert the item before us.  There's no magic here.
   PR_INSERT_BEFORE(aItem, mCurrent);
   mList.AdjustCountsForItem(aItem, 1);
 
   NS_POSTCONDITION(PR_NEXT_LINK(aItem) == mCurrent, "How did that happen?");
 }
 
 void
-nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItem()
-{
-  FrameConstructionItem* item = ToItem(mCurrent);
-  Next();
-  PR_REMOVE_LINK(item);
-  mList.AdjustCountsForItem(item, -1);
-  delete item;
-}
+nsCSSFrameConstructor::FrameConstructionItemList::
+Iterator::DeleteItemsTo(const Iterator& aEnd)
+{
+  NS_PRECONDITION(mEnd == aEnd.mEnd, "end iterator for some other list?");
+  NS_PRECONDITION(*this != aEnd, "Shouldn't be at aEnd yet");
+
+  do {
+    NS_ASSERTION(!IsDone(), "Ran off end of list?");
+    FrameConstructionItem* item = ToItem(mCurrent);
+    Next();
+    PR_REMOVE_LINK(item);
+    mList.AdjustCountsForItem(item, -1);
+    delete item;
+  } while (*this != aEnd);
+}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -742,30 +742,46 @@ private:
 
       PRBool operator==(const Iterator& aOther) const {
         NS_ASSERTION(mEnd == aOther.mEnd, "Iterators for different lists?");
         return mCurrent == aOther.mCurrent;
       }
       PRBool operator!=(const Iterator& aOther) const {
         return !(*this == aOther);
       }
+      Iterator& operator=(const Iterator& aOther) {
+        NS_ASSERTION(mEnd == aOther.mEnd, "Iterators for different lists?");
+        mCurrent = aOther.mCurrent;
+        return *this;
+      }
 
       operator FrameConstructionItem& () {
         return item();
       }
 
       FrameConstructionItem& item() {
         return *FrameConstructionItemList::ToItem(mCurrent);
       }
       PRBool IsDone() const { return mCurrent == mEnd; }
       PRBool AtStart() const { return mCurrent == PR_NEXT_LINK(mEnd); }
       void Next() {
         NS_ASSERTION(!IsDone(), "Should have checked IsDone()!");
         mCurrent = PR_NEXT_LINK(mCurrent);
       }
+      void SetToEnd() { mCurrent = mEnd; }
+
+      // Skip over all items that want a parent type different from the given
+      // one.  Return whether the iterator is done after doing that.  The
+      // iterator must not be done when this is called.
+      inline PRBool SkipItemsWantingParentType(ParentType aParentType);
+
+      // Skip over whitespace.  Return whether the iterator is done after doing
+      // that.  The iterator must not be done, and must be pointing to a
+      // whitespace item when this is called.
+      inline PRBool SkipWhitespace();
 
       // Remove the item pointed to by this iterator from its current list and
       // Append it to aTargetList.  This iterator is advanced to point to the
       // next item in its list.  aIter must not be done.  aOther must not be
       // the list this iterator is iterating over..
       void AppendItemToList(FrameConstructionItemList& aTargetList);
 
       // As above, but moves all items starting with this iterator until we
@@ -779,19 +795,22 @@ private:
 
       // Insert aItem in this iterator's list right before the item pointed to
       // by this iterator.  After the insertion, this iterator will continue to
       // point to the item it now points to (the one just after the
       // newly-inserted item).  This iterator is allowed to be done; in that
       // case this call just appends the given item to the list.
       void InsertItem(FrameConstructionItem* aItem);
 
-      // Delete the item pointed to by this iterator, and point ourselves to
-      // the next item in the list.
-      void DeleteItem();
+      // Delete the items between this iterator and aEnd, including the item
+      // this iterator currently points to but not including the item pointed
+      // to by aEnd.  When this returns, this iterator will point to the same
+      // item as aEnd.  This iterator must not equal aEnd when this method is
+      // called.
+      void DeleteItemsTo(const Iterator& aEnd);
 
     private:
       PRCList* mCurrent;
       PRCList* mEnd;
       FrameConstructionItemList& mList;
     };
 
   private:
@@ -835,19 +854,21 @@ private:
         mContent->UnbindFromTree();
         NS_RELEASE(mContent);
       }
     }
 
     ParentType DesiredParentType() {
       return FCDATA_DESIRED_PARENT_TYPE(mFCData->mBits);
     }
-    PRBool IsWhitespace() const {
-      return mIsText && mContent->TextIsOnlyWhitespace();
-    }
+
+    // Don't call this unless the frametree really depends on the answer!
+    // Especially so for generated content, where we don't want to reframe
+    // things.
+    PRBool IsWhitespace() const;
 
     // The FrameConstructionData to use.
     const FrameConstructionData* mFCData;
     // The nsIContent node to use when initializing the new frame.
     nsIContent* mContent;
     // The XBL-resolved tag name to use for frame construction.
     nsIAtom* mTag;
     // The XBL-resolved namespace to use for frame construction.
@@ -1342,24 +1363,27 @@ private:
                                        nsFrameItems& aFrameItems);
 
   // Determine whether we need to wipe out what we just did and start over
   // because we're doing something like adding block kids to an inline frame
   // (and therefore need an {ib} split).  If aIsAppend is true, aPrevSibling is
   // ignored.  Otherwise it may be used to determine whether to reframe when
   // inserting into the block of an {ib} split.  Passing a null aPrevSibling in
   // the non-append case is ok in terms of correctness.  It might reframe when
-  // we don't really need to, but that's it.
+  // we don't really need to, but that's it.  Passing in a correct
+  // aIsInsertAtStart is desirable for performance reasons, but if it's hard to
+  // determine passing false is safe in terms of correctness.
   // @return PR_TRUE if we reconstructed the containing block, PR_FALSE
   // otherwise
   PRBool WipeContainingBlock(nsFrameConstructorState& aState,
                              nsIFrame*                aContainingBlock,
                              nsIFrame*                aFrame,
-                             const FrameConstructionItemList& aItems,
+                             FrameConstructionItemList& aItems,
                              PRBool                   aIsAppend,
+                             PRBool                   aIsInsertAtStart,
                              nsIFrame*                aPrevSibling);
 
   nsresult ReframeContainingBlock(nsIFrame* aFrame);
 
   nsresult StyleChangeReflow(nsIFrame* aFrame);
 
   /** Helper function that searches the immediate child frames 
     * (and their children if the frames are "special")
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -3417,16 +3417,18 @@ nsTextFrame::Init(nsIContent*      aCont
   // We're not a continuing frame.
   // mContentOffset = 0; not necessary since we get zeroed out at init
   return nsFrame::Init(aContent, aParent, aPrevInFlow);
 }
 
 void
 nsTextFrame::Destroy()
 {
+  // We might want to clear FRAMETREE_DEPENDS_ON_CHARS on mContent here, since
+  // our parent frame type might be changing.  Not clear whether it's worth it.
   ClearTextRun();
   if (mNextContinuation) {
     mNextContinuation->SetPrevInFlow(nsnull);
   }
   // Let the base class destroy the frame
   nsFrame::Destroy();
 }
 
--- a/layout/mathml/nsMathMLmtableFrame.h
+++ b/layout/mathml/nsMathMLmtableFrame.h
@@ -79,18 +79,17 @@ public:
 
   NS_IMETHOD
   AttributeChanged(PRInt32  aNameSpaceID,
                    nsIAtom* aAttribute,
                    PRInt32  aModType);
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsTableOuterFrame::IsFrameOfType(aFlags &
-      ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
+    return nsTableOuterFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
 protected:
   nsMathMLmtableOuterFrame(nsStyleContext* aContext) : nsTableOuterFrame(aContext) {}
   virtual ~nsMathMLmtableOuterFrame();
 
   // helper to find the row frame at a given index, positive or negative, e.g.,
   // 1..n means the first row down to the last row, -1..-n means the last row
@@ -138,18 +137,17 @@ public:
   {
     nsresult rv = nsTableFrame::RemoveFrame(aListName, aOldFrame);
     RestyleTable();
     return rv;
   }
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsTableFrame::IsFrameOfType(aFlags &
-      ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
+    return nsTableFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
   // helper to restyle and reflow the table when a row is changed -- since MathML
   // attributes are inter-dependent and row/colspan can affect the table, it is
   // safer (albeit grossly suboptimal) to just relayout the whole thing.
   void RestyleTable();
 
 protected:
@@ -196,18 +194,17 @@ public:
   {
     nsresult rv = nsTableRowFrame::RemoveFrame(aListName, aOldFrame);
     RestyleTable();
     return rv;
   }
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsTableRowFrame::IsFrameOfType(aFlags &
-      ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
+    return nsTableRowFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
   // helper to restyle and reflow the table -- @see nsMathMLmtableFrame.
   void RestyleTable()
   {
     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
     if (tableFrame && tableFrame->IsFrameOfType(nsIFrame::eMathML)) {
       // relayout the table
@@ -233,18 +230,17 @@ public:
   AttributeChanged(PRInt32  aNameSpaceID,
                    nsIAtom* aAttribute,
                    PRInt32  aModType);
 
   virtual PRInt32 GetRowSpan();
   virtual PRInt32 GetColSpan();
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsTableCellFrame::IsFrameOfType(aFlags &
-      ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
+    return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
 protected:
   nsMathMLmtdFrame(nsStyleContext* aContext) : nsTableCellFrame(aContext) {}
   virtual ~nsMathMLmtdFrame();
 }; // class nsMathMLmtdFrame
 
 // --------------
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/3-blocks-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <body style="font-family: monospace">
+    <div>
+      Row 1, Col 1Row 1, Col 2Row 1, Col 3
+    </div>
+    <div>
+      Row 22, Col 1Row 22, Col 2Row 22, Col 3
+    </div>
+    <div>
+      Row 333, Col 1Row 333, Col 2Row 333, Col 3
+    </div>    
+  </body>
+</html>
--- a/layout/reftests/table-anonymous-boxes/3-tables-ref.html
+++ b/layout/reftests/table-anonymous-boxes/3-tables-ref.html
@@ -1,26 +1,26 @@
 <!DOCTYPE html>
 <html>
   <body style="font-family: monospace">
-    <table cellpadding="0" cellspacing="0">
+    <table cellpadding="0" cellspacing="0" style="margin: 0; padding: 0; border: none">
       <tr>
         <td>Row 1, Col 1</td>
         <td>Row 1, Col 2</td>
         <td>Row 1, Col 3</td>
       </tr>
     </table>
-    <table cellpadding="0" cellspacing="0">
+    <table cellpadding="0" cellspacing="0" style="margin: 0; padding: 0; border: none">
       <tr>
         <td>Row 22, Col 1</td>
         <td>Row 22, Col 2</td>
         <td>Row 22, Col 3</td>
       </tr>
     </table>
-    <table cellpadding="0" cellspacing="0">
+    <table cellpadding="0" cellspacing="0" style="margin: 0; padding: 0; border: none">
       <tr>
         <td>Row 333, Col 1</td>
         <td>Row 333, Col 2</td>
         <td>Row 333, Col 3</td>
       </tr>
     </table>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/372641-1-ref.xhtml
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+
+<table border="1"><tbody><tr><td>TD</td></tr></tbody></table>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/372641-1a.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+
+<table border="1" id="table">XXX<tbody><tr><td>TD</td></tr></tbody></table>
+
+<script>
+document.body.offsetWidth;
+document.getElementById("table").firstChild.data = '';
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/372641-1b.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+
+<table border="1"><tbody id="tbody">XXX<tr><td>TD</td></tr></tbody></table>
+
+<script>
+document.body.offsetWidth;
+document.getElementById("tbody").firstChild.data = '';
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/372641-1c.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+
+<table border="1"><tbody><tr id="tr">XXX<td>TD</td></tr></tbody></table>
+
+<script>
+document.body.offsetWidth;
+document.getElementById("tr").firstChild.data = '';
+</script>
+
+</body>
+</html>
--- a/layout/reftests/table-anonymous-boxes/3x3-cols-ref.html
+++ b/layout/reftests/table-anonymous-boxes/3x3-cols-ref.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
   <body style="font-family: monospace">
-    <table cellpadding="0" cellspacing="0">
+    <table cellpadding="0" cellspacing="0" style="margin: 0; padding: 0; border: none">
       <colgroup><col style="background: yellow"><col style="background: cyan"><col style="background: lime"></colgroup>
       <tr>
         <td>Row 1, Col 1</td>
         <td>Row 1, Col 2</td>
         <td>Row 1, Col 3</td>
       </tr>
       <tr>
         <td>Row 22, Col 1</td>
--- a/layout/reftests/table-anonymous-boxes/3x3-ref.html
+++ b/layout/reftests/table-anonymous-boxes/3x3-ref.html
@@ -1,12 +1,12 @@
 <!DOCTYPE html>
 <html>
   <body style="font-family: monospace">
-    <table cellpadding="0" cellspacing="0">
+    <table cellpadding="0" cellspacing="0" style="margin: 0; padding: 0; border: none">
       <tr>
         <td>Row 1, Col 1</td>
         <td>Row 1, Col 2</td>
         <td>Row 1, Col 3</td>
       </tr>
       <tr>
         <td>Row 22, Col 1</td>
         <td>Row 22, Col 2</td>
--- a/layout/reftests/table-anonymous-boxes/dynamic-removal-14.html
+++ b/layout/reftests/table-anonymous-boxes/dynamic-removal-14.html
@@ -1,14 +1,16 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
   <head>
     <script>
       function doTest() {
-        document.normalize();
+        try {
+          document.normalize();
+        } catch(e) {}
         var n = document.getElementById("t").nextSibling;
         n.parentNode.removeChild(n);
         document.documentElement.className = "";
       }
     </script>
   </head>
   <body style="font-family: monospace" onload="doTest()">
     <span style="display: table-row">
--- a/layout/reftests/table-anonymous-boxes/infer-cells-1.html
+++ b/layout/reftests/table-anonymous-boxes/infer-cells-1.html
@@ -1,17 +1,18 @@
 <!DOCTYPE html>
 <html>
   <body style="font-family: monospace">
     <!-- The test in the first row might not be correct, depending on spec
          clarifications -->
     <span style="display: table-row"> 
-      <span>Row 1, Col 1</span>
-      <span>Row 1, Col 2</span>
-      <span>Row 1, Col 3</span>
+      <span>Row 1,</span>
+      <span>Col 1Row 1,</span>
+      <span>Col 2Row 1,</span>
+      <span>Col 3</span>
     </span>
     <span style="display: table-row"> 
       <span style="display: block">Row 22, Col 1Row 22, Col 2Row 22, Col 3</span>
     </span>
     <span style="display: table-row">
       <span>Row 333, Col 1</span><span>Row 333, Col 2</span><span>Row 333, Col 3</span>
     </span>
   </body>
--- a/layout/reftests/table-anonymous-boxes/reftest.list
+++ b/layout/reftests/table-anonymous-boxes/reftest.list
@@ -1,31 +1,34 @@
 == 121142-1a.html 121142-1-ref.html
 == 121142-1b.html 121142-1-ref.html
 == 121142-2.html 121142-2-ref.html
 fails == 156888-1.html 156888-1-ref.html # bug 484825
 == 156888-2.html 156888-2-ref.html
 == 162063-1.xhtml about:blank
-== 203923-1.html white-space-1-ref.html
-== 203923-2.html white-space-1-ref.html
+== 203923-1.html white-space-ref.html
+== 203923-2.html white-space-ref.html
 == 208305-1.html 208305-1-ref.html
-== 208305-2.html white-space-1-ref.html
-== 208305-3.html white-space-1-ref.html
-== 208305-4.html white-space-1-ref.html
+== 208305-2.html white-space-ref.html
+== 208305-3.html white-space-ref.html
+== 208305-4.html white-space-ref.html
 == 277995-1.html 277995-1-ref.html
 == 293576-1.html 293576-1-ref.html
 == 302113-1.html 302113-1-ref.html
 == 315146-1.xhtml 315146-1-ref.xhtml
 == 325543-1a.html 325543-1-ref.html
 == 325543-1b.html 325543-1-ref.html
 == 338735-1.html 338735-1-ref.html
 == 339388-1a.html 339388-1-ref.html
 == 339388-1b.html 339388-1-ref.html
 == 368932-1.html 368932-1-ref.html
 == 371054-1.html 371054-1-ref.html
+== 372641-1a.xhtml 372641-1-ref.xhtml
+== 372641-1b.xhtml 372641-1-ref.xhtml
+== 372641-1c.xhtml 372641-1-ref.xhtml
 == 372649-1.html 372649-1-ref.html
 == 373379-1.html 373379-1-ref.html
 == 394402-1a.html 394402-1-ref.html
 == 394402-1b.html 394402-1-ref.html
 == 407115-1.html 407115-1-ref.html
 == 448111-1.html 448111-1-ref.html
 == infer-first-row.html 3x3-ref.html
 == infer-first-row-and-table.html 3x3-ref.html
@@ -33,16 +36,17 @@ fails == 156888-1.html 156888-1-ref.html
 == infer-second-row-and-table.html 3x3-ref.html
 == infer-table-around-headers-footers-1.html 3x3-ref.html
 == infer-table-around-headers-footers-2.html 3x3-ref.html
 == infer-table-around-headers-footers-3.html 3x3-ref.html
 == infer-rows-inside-rowgroups.html 3x3-ref.html
 == infer-table-row-cell.html 3x3-ref.html # Or should it be?  Spec is unclear.
 == infer-table.html 3x3-ref.html
 != 3-tables-ref.html 3x3-ref.html
+== 3-tables-ref.html 3-blocks-ref.html
 == blocks-divide-tables-1.html 3-tables-ref.html
 == blocks-divide-tables-2.html 3-tables-ref.html
 == infer-cells-1.html 3-tables-ref.html
 == infer-cells-2.html 3x3-ref.html
 == infer-cells-3.html 3x3-ref.html
 == infer-cells-4.html 3x3-ref.html
 == cols-test-1.html 3x3-cols-ref.html
 == cols-test-2.html 3x3-cols-ref.html
@@ -50,26 +54,52 @@ fails == 156888-1.html 156888-1-ref.html
 == dynamic-removal-1.html 3x3-ref.html
 == dynamic-removal-2.html 3x3-ref.html
 == dynamic-removal-3.html 3x3-ref.html
 == dynamic-removal-4.html 3x3-ref.html
 == dynamic-removal-5.html 3x3-ref.html
 == dynamic-removal-6.html 3x3-ref.html
 == dynamic-removal-7.html 3x3-ref.html
 == dynamic-removal-8.html 3x3-ref.html
-== dynamic-removal-9.html white-space-1-ref.html
-== dynamic-removal-10.html white-space-1-ref.html
-== dynamic-removal-11.html white-space-1-ref.html
-== dynamic-removal-12.html white-space-1-ref.html
+== dynamic-removal-9.html white-space-ref.html
+== dynamic-removal-10.html white-space-ref.html
+== dynamic-removal-11.html white-space-ref.html
+== dynamic-removal-12.html white-space-ref.html
 == dynamic-removal-13.html 3x3-ref.html
 == dynamic-removal-14.html 3x3-ref.html
 == dynamic-insert-cell-1.html 3x3-ref.html
 == dynamic-switch-block-to-cell-1.html 3x3-ref.html
 == dynamic-switch-block-to-cell-2.html 3x3-ref.html
 == dynamic-switch-block-to-cell-3.html 3x3-ref.html
 == dynamic-switch-block-to-cell-4.html 3x3-ref.html
 == dynamic-switch-block-to-cell-5.html 3x3-ref.html
 == dynamic-switch-inline-to-cell-1.html 3x3-ref.html
 == dynamic-switch-inline-to-cell-2.html 3x3-ref.html
 == dynamic-switch-inline-to-cell-3.html 3x3-ref.html
 == dynamic-switch-inline-to-cell-4.html 3x3-ref.html
 == dynamic-switch-inline-to-cell-5.html 3x3-ref.html
-== white-space-1.html white-space-1-ref.html
+== white-space-1.html 3-tables-ref.html
+== white-space-2.html 3x3-ref.html
+== white-space-3.html 3x3-ref.html
+== white-space-4.html 3x3-ref.html
+== white-space-5.html 3x3-ref.html
+== white-space-6.html 3x3-ref.html
+== white-space-7.html white-space-ref.html
+== white-space-8.html white-space-ref.html
+== white-space-9.html white-space-ref.html
+== white-space-10.html white-space-ref.html
+== white-space-11.html white-space-ref.html
+== white-space-12.html white-space-ref.html
+== white-space-13.html white-space-ref.html
+== white-space-14.html white-space-ref.html
+== white-space-15.html white-space-ref.html
+== white-space-16.html white-space-ref.html
+== white-space-17.html white-space-ref.html
+== white-space-18.html white-space-ref.html
+== white-space-19.html white-space-ref.html
+== white-space-20.html white-space-ref.html
+== white-space-21.html white-space-ref.html
+== white-space-22.html white-space-ref.html
+== white-space-23.html white-space-ref.html
+== white-space-24.html white-space-ref.html
+== white-space-25.html white-space-ref.html
+== white-space-26.html white-space-ref.html
+== white-space-pre-1.html white-space-pre-ref.html
--- a/layout/reftests/table-anonymous-boxes/white-space-1.html
+++ b/layout/reftests/table-anonymous-boxes/white-space-1.html
@@ -1,11 +1,20 @@
-<!DOCTYPE HTML>
-<html>
-  <body>
-    <span>
-      <span>a</span>
-      <span style="display: table-cell">b</span>
-      <span style="display: table-cell">c</span>
-      <span>d</span>
-    </span>
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var n = document.getElementById("t").nextSibling;
+        n.data = "Row 22, Col 1Row 22, Col 2Row 22, Col 3";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-cell">Row 1, Col 1</span>
+    <span style="display: table-cell">Row 1, Col 2</span>
+    <span style="display: table-cell" id="t">Row 1, Col 3</span>
+    <span style="display: table-cell">Row 333, Col 1</span>
+    <span style="display: table-cell">Row 333, Col 2</span>
+    <span style="display: table-cell">Row 333, Col 3</span>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-10.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var t = document.getElementById("t1");
+        t.parentNode.removeChild(t);
+        t = document.getElementById("t2");
+        t.parentNode.removeChild(t);
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span>
+      <span>a</span><span style="display: table-cell" id="t1">e</span>
+      <span style="display: table-cell" id="t2">f</span><span>bc d</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-11.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var t = document.getElementById("t1");
+        t.parentNode.removeChild(t);
+        t = document.getElementById("t2");
+        t.parentNode.removeChild(t);
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span>
+      <span>a</span><span style="display: table-cell" id="t2">e</span>
+      <span style="display: table-cell" id="t1">f</span><span>bc d</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-12.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span>
+      a
+      <span style="display: table-cell">b</span><span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-13.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span>
+      a
+      <span style="display: table-cell">b</span>
+      <span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-14.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:after { content: "d" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      a
+      <span style="display: table-cell">b</span><span style="display: table-cell">c</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-15.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:after { content: "d" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      a
+      <span style="display: table-cell">b</span>
+      <span style="display: table-cell">c</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-16.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:after { content: "d" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      a
+      <span style="display: table-cell">b</span><script>document.body.offsetWidth</script><span style="display: table-cell">c</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-17.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:after { content: "d" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      a
+      <span style="display: table-cell">b</span>
+      <script>document.body.offsetWidth</script>
+      <span style="display: table-cell">c</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-18.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:before { content: "a" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      <span style="display: table-cell">b</span><span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-19.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:before { content: "a" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      <span style="display: table-cell">b</span>
+      <span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        try {
+          document.normalize();
+        } catch (e) {}
+        var n = document.getElementById("t").nextSibling;
+        n.data = " ";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-row">
+      <span style="display: table-cell">Row 1, Col 1</span>
+      <span style="display: table-cell">Row 1, Col 2</span>
+      <span style="display: table-cell">Row 1, Col 3</span>
+    </span>
+    <span style="display: table-row" id="t">
+      <span style="display: table-cell">Row 22, Col 1</span>
+      <span style="display: table-cell">Row 22, Col 2</span>
+      <span style="display: table-cell">Row 22, Col 3</span>
+    </span>This is a test<span style="display: table-row">
+      <span style="display: table-cell">Row 333, Col 1</span>
+      <span style="display: table-cell">Row 333, Col 2</span>
+      <span style="display: table-cell">Row 333, Col 3</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-20.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:before { content: "a" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      <span style="display: table-cell">b</span><script>document.body.offsetWidth</script><span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-21.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:before { content: "a" }
+    </style>
+  </head>  
+  <body>
+    <span id="t">
+      <span style="display: table-cell">b</span>
+      <script>document.body.offsetWidth</script>
+      <span style="display: table-cell">c</span>
+      d
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-22.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span style="display:table-row; white-space: pre"><span style="display: table-cell">a</span> bc <span style="display: table-cell">d</span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-23.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span style="display:table-row; white-space: pre"><span style="display: table-cell">a</span> <span>bc</span> <span style="display: table-cell">d</span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-24.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span style="display:table-row; white-space: pre"><span style="display: table-cell">a</span> <script>document.body.offsetWidth</script>bc<script>document.body.offsetWidth</script> <span style="display: table-cell">d</span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-25.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var t = document.getElementById("t");
+        var parent = t.parentNode;
+        parent.insertBefore(document.createTextNode(" "), t);
+        parent.insertBefore(document.createTextNode("bc"), t);
+        parent.insertBefore(document.createTextNode(" "), t);
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span style="display:table-row; white-space: pre"><span style="display: table-cell">a</span><span id="t" style="display: table-cell">d</span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-26.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #t:after { display: table-cell; content: "d"; }
+    </style>
+  </head>
+  <body>
+    <span id="t">
+      a<script>document.body.offsetWidth;</script> <span style="display: table-cell; white-space: pre">bc </span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-3.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        try {
+          document.normalize();
+        } catch (e) {}
+        var n = document.getElementById("t").nextSibling;
+        n.data = " ";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-row-group">
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 1, Col 1</span>
+        <span style="display: table-cell">Row 1, Col 2</span>
+        <span style="display: table-cell">Row 1, Col 3</span>
+      </span>
+      <span style="display: table-row">
+        <span style="display: table-cell" id="t">Row 22, Col 1</span>This is a test<span style="display: table-cell">Row 22, Col 2</span>
+        <span style="display: table-cell">Row 22, Col 3</span>
+      </span>
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 333, Col 1</span>
+        <span style="display: table-cell">Row 333, Col 2</span>
+        <span style="display: table-cell">Row 333, Col 3</span>
+      </span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-4.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var n = document.getElementById("t").nextSibling;
+        n.data = "Row 22, Col 1";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-row-group">
+      <span style="display: table-row" id="t">
+        <span style="display: table-cell">Row 1, Col 1</span>
+        <span style="display: table-cell">Row 1, Col 2</span>
+        <span style="display: table-cell">Row 1, Col 3</span>
+      </span>
+      <span style="display: table-cell">Row 22, Col 2</span>
+      <span style="display: table-cell">Row 22, Col 3</span>
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 333, Col 1</span>
+        <span style="display: table-cell">Row 333, Col 2</span>
+        <span style="display: table-cell">Row 333, Col 3</span>
+      </span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-5.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var n = document.getElementById("t").nextSibling;
+        n.data = "Row 22, Col 2";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-row-group">
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 1, Col 1</span>
+        <span style="display: table-cell">Row 1, Col 2</span>
+        <span style="display: table-cell">Row 1, Col 3</span>
+      </span>
+      <span style="display: table-cell" id="t">Row 22, Col 1</span>
+      <span style="display: table-cell">Row 22, Col 3</span>
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 333, Col 1</span>
+        <span style="display: table-cell">Row 333, Col 2</span>
+        <span style="display: table-cell">Row 333, Col 3</span>
+      </span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-6.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var n = document.getElementById("t").nextSibling;
+        n.data = "Row 22, Col 3";
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body style="font-family: monospace" onload="doTest()">
+    <span style="display: table-row-group">
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 1, Col 1</span>
+        <span style="display: table-cell">Row 1, Col 2</span>
+        <span style="display: table-cell">Row 1, Col 3</span>
+      </span>
+      <span style="display: table-cell">Row 22, Col 1</span>
+      <span style="display: table-cell" id="t">Row 22, Col 2</span>
+      <span style="display: table-row">
+        <span style="display: table-cell">Row 333, Col 1</span>
+        <span style="display: table-cell">Row 333, Col 2</span>
+        <span style="display: table-cell">Row 333, Col 3</span>
+      </span>
+    </span>
+  </body>
+</html>
copy from layout/reftests/table-anonymous-boxes/white-space-1.html
copy to layout/reftests/table-anonymous-boxes/white-space-7.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-8.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var t = document.getElementById("t");
+        t.parentNode.removeChild(t);
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span>
+      <span>a</span><span style="display: table-cell" id="t">e</span>
+      <span style="display: table-cell">b</span><span>c d</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-9.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function doTest() {
+        var t = document.getElementById("t");
+        t.parentNode.removeChild(t);
+        document.documentElement.className = "";
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span>
+      <span>a b</span><span style="display: table-cell">c</span>
+      <span style="display: table-cell" id="t">e</span><span>d</span>
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-pre-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #x:after { content: " cd"; display: table-cell; }
+    </style>
+    <script>
+      function doTest() {
+        var f = document.getElementById("f");
+        f.parentNode.removeChild(f);
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <div style="font-family: monospace; width: 10em; white-space: pre-wrap"><span id="f" style="float: left; width: 80%; height: 0.5em"></span><span id="x">a <script>document.body.offsetWidth; dump('aa');</script> <span style="display: table-cell">b</span></span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/white-space-pre-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="font-family: monospace; white-space: pre-wrap">a  b cd</div>
+  </body>
+</html>
rename from layout/reftests/table-anonymous-boxes/white-space-1-ref.html
rename to layout/reftests/table-anonymous-boxes/white-space-ref.html
--- a/layout/tables/nsTableColFrame.h
+++ b/layout/tables/nsTableColFrame.h
@@ -85,22 +85,16 @@ public:
 
   /**
    * Table columns never paint anything, nor receive events.
    */
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists) { return NS_OK; }
 
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsSplittableFrame::IsFrameOfType(aFlags &
-      ~(nsIFrame::eExcludesIgnorableWhitespace));
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableColFrame
    */
   virtual nsIAtom* GetType() const;
   
 #ifdef DEBUG
--- a/layout/tables/nsTableColGroupFrame.h
+++ b/layout/tables/nsTableColGroupFrame.h
@@ -142,22 +142,16 @@ public:
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   /* needed only because we use Reflow in a hacky way, see
      nsTableFrame::ReflowColGroups */
   virtual PRBool IsContainingBlock() const;
 
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsHTMLContainerFrame::IsFrameOfType(aFlags &
-      ~nsIFrame::eExcludesIgnorableWhitespace);
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableColGroupFrame
    */
   virtual nsIAtom* GetType() const;
 
   /** Add column frames to the table storages: colframe cache and cellmap
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -369,22 +369,16 @@ public:
                        nsReflowStatus&          aStatus);
 
   nsFrameList& GetColGroups();
 
   NS_IMETHOD GetParentStyleContextFrame(nsPresContext* aPresContext,
                                         nsIFrame**      aProviderFrame,
                                         PRBool*         aIsChild);
 
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsHTMLContainerFrame::IsFrameOfType(aFlags &
-      ~nsIFrame::eExcludesIgnorableWhitespace);
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableFrame
    */
   virtual nsIAtom* GetType() const;
 
 #ifdef DEBUG
--- a/layout/tables/nsTableOuterFrame.h
+++ b/layout/tables/nsTableOuterFrame.h
@@ -95,22 +95,16 @@ public:
     * @return           the frame that was created
     */
   friend nsIFrame* NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
   
   // nsIFrame overrides - see there for a description
 
   virtual void Destroy();
   
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsHTMLContainerFrame::IsFrameOfType(aFlags &
-      ~nsIFrame::eExcludesIgnorableWhitespace);
-  }
-
   virtual PRBool IsContainingBlock() const;
 
   NS_IMETHOD SetInitialChildList(nsIAtom*        aListName,
                                  nsIFrame*       aChildList);
  
   virtual nsIFrame* GetFirstChild(nsIAtom* aListName) const;
 
   virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const;
--- a/layout/tables/nsTableRowFrame.h
+++ b/layout/tables/nsTableRowFrame.h
@@ -119,22 +119,16 @@ public:
     */
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   void DidResize();
 
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsHTMLContainerFrame::IsFrameOfType(aFlags &
-      ~nsIFrame::eExcludesIgnorableWhitespace);
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableRowFrame
    */
   virtual nsIAtom* GetType() const;
 
 #ifdef DEBUG
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -137,22 +137,16 @@ public:
     *
     * @see nsIFrame::Reflow
     */
   NS_IMETHOD Reflow(nsPresContext*           aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
-  virtual PRBool IsFrameOfType(PRUint32 aFlags) const
-  {
-    return nsHTMLContainerFrame::IsFrameOfType(aFlags &
-      ~nsIFrame::eExcludesIgnorableWhitespace);
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableRowGroupFrame
    */
   virtual nsIAtom* GetType() const;
 
   virtual PRBool IsContainingBlock() const;