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 id6382
push userbzbarsky@mozilla.com
push dateWed, 08 Apr 2009 16:57:10 +0000
treeherdermozilla-central@0ea22856b5d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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;