Bug 501847 part 4. Change frame construction to create {ib} splits that have blocks wrapping runs of block kids and inlines wrapping runs of inline kids instead of wrapping all kids from the first block to the last block in a single block. r=tn,dbaron,roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 18 Sep 2009 14:00:21 -0400
changeset 34462 822ba7ef29bb7dbec1f4a7f2056721d8c5424034
parent 34461 ce946663a30ae4855fa79d7e54476f4d3f6694e1
child 34463 5bc1c85439bf3f4119de5cef6709376c73a7bf14
push id10058
push userbzbarsky@mozilla.com
push dateMon, 02 Nov 2009 03:43:27 +0000
treeherdermozilla-central@0f12f3edad42 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, dbaron, roc
bugs501847
milestone1.9.3a1pre
Bug 501847 part 4. Change frame construction to create {ib} splits that have blocks wrapping runs of block kids and inlines wrapping runs of inline kids instead of wrapping all kids from the first block to the last block in a single block. r=tn,dbaron,roc
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsIFrame.h
layout/reftests/bugs/424236-8-ref.html
layout/reftests/bugs/424236-8.html
layout/reftests/bugs/424236-9-ref.html
layout/reftests/bugs/424236-9.html
layout/reftests/ib-split/float-inside-inline-between-blocks-1-ref.html
layout/reftests/ib-split/ignored-margins-1-ref.html
layout/reftests/ib-split/ignored-margins-1a.html
layout/reftests/ib-split/ignored-margins-1b.html
layout/reftests/ib-split/ignored-margins-2-ref.html
layout/reftests/ib-split/ignored-margins-2a.html
layout/reftests/ib-split/ignored-margins-2b.html
layout/reftests/ib-split/insert-into-split-inline-1-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-10-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-11-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-12-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-13-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-14-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-15-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-16-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-2-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-3-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-4-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-5-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-6-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-7-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-7-ref.html
layout/reftests/ib-split/insert-into-split-inline-7.html
layout/reftests/ib-split/insert-into-split-inline-8-noib-ref.html
layout/reftests/ib-split/insert-into-split-inline-9-noib-ref.html
layout/reftests/ib-split/percent-height-1-ref.html
layout/reftests/ib-split/percent-height-1.html
layout/reftests/ib-split/reftest.list
layout/reftests/ib-split/remove-from-split-inline-1-noib-ref.html
layout/reftests/ib-split/remove-from-split-inline-3-noib-ref.html
layout/reftests/ib-split/remove-from-split-inline-4-noib-ref.html
layout/reftests/ib-split/remove-from-split-inline-5-noib-ref.html
layout/reftests/ib-split/remove-from-split-inline-6-noib-ref.html
layout/reftests/ib-split/remove-from-split-inline-6-ref.html
layout/reftests/ib-split/remove-from-split-inline-6.html
layout/reftests/ib-split/split-inner-inline-2-ref.html
layout/reftests/ib-split/split-inner-inline-2.html
layout/reftests/ib-split/trailing-inline-with-continuations-1-ref.html
layout/reftests/ib-split/trailing-inline-with-continuations-1.html
layout/reftests/ib-split/whitespace-present-1-ref.html
layout/reftests/ib-split/whitespace-present-1a.html
layout/reftests/ib-split/whitespace-present-1b.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -483,38 +483,43 @@ static nsIFrame* GetSpecialPrevSibling(n
   // frame in the continuation chain. Walk back to find that frame now.  
   return
     static_cast<nsIFrame*>
     (aFrame->GetFirstContinuation()->
        GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling));
 }
 
 static nsIFrame*
-GetLastSpecialSibling(nsIFrame* aFrame, PRBool aIgnoreEmpty)
+GetLastSpecialSibling(nsIFrame* aFrame, PRBool aReturnEmptyTrailingInline)
 {
   for (nsIFrame *frame = aFrame, *next; ; frame = next) {
     next = GetSpecialSibling(frame);
     if (!next ||
-        (aIgnoreEmpty && !next->GetFirstChild(nsnull)))
+        (!aReturnEmptyTrailingInline && !next->GetFirstChild(nsnull) &&
+         !GetSpecialSibling(next))) {
+      NS_ASSERTION(!next || !IsInlineOutside(frame),
+                   "Should have a block here!");
       return frame;
+    }
   }
   NS_NOTREACHED("unreachable code");
   return nsnull;
 }
 
 static void
 SetFrameIsSpecial(nsIFrame* aFrame, nsIFrame* aSpecialSibling)
 {
   NS_PRECONDITION(aFrame, "bad args!");
 
   // We should be the only continuation
   NS_ASSERTION(!aFrame->GetPrevContinuation(),
                "assigning special sibling to other than first continuation!");
-  NS_ASSERTION(!aFrame->GetNextContinuation(),
-               "should have no continuations here");
+  NS_ASSERTION(!aFrame->GetNextContinuation() ||
+               IsFrameSpecial(aFrame->GetNextContinuation()),
+               "should have no non-special continuations here");
 
   // Mark the frame as "special".
   aFrame->AddStateBits(NS_FRAME_IS_SPECIAL);
 
   if (aSpecialSibling) {
     NS_ASSERTION(!aSpecialSibling->GetPrevContinuation(),
                  "assigning something other than the first continuation as the "
                  "special sibling");
@@ -577,32 +582,29 @@ FindFirstBlock(nsFrameList::FrameLinkEnu
 {
   for ( ; !aLink.AtEnd(); aLink.Next()) {
     if (!IsInlineOutside(aLink.NextFrame())) {
       return;
     }
   }
 }
 
-// This function returns an frame link enumerator pointing to the last link in
-// the list which has a block prev frame.  This means the enumerator might be
-// at end.  If there are no blocks at all, the returned enumerator will point
-// to the beginning of the list.
+// This function returns a frame link enumerator pointing to the first link in
+// the list for which the next frame is not block.  If there is no such link,
+// it points to the end of the list.
 static nsFrameList::FrameLinkEnumerator
-FindLastBlock(const nsFrameList& aList)
-{
-  nsFrameList::FrameLinkEnumerator cur(aList), last(aList);
-  for ( ; !cur.AtEnd(); cur.Next()) {
-    if (!IsInlineOutside(cur.NextFrame())) {
-      last = cur;
-      last.Next();
-    }
-  }
-
-  return last;
+FindFirstNonBlock(const nsFrameList& aList)
+{
+  nsFrameList::FrameLinkEnumerator link(aList);
+  for (; !link.AtEnd(); link.Next()) {
+    if (IsInlineOutside(link.NextFrame())) {
+      break;
+    }
+  }
+  return link;
 }
 
 inline void
 SetInitialSingleChild(nsIFrame* aParent, nsIFrame* aFrame)
 {
   NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
   nsFrameList temp(aFrame, aFrame);
   aParent->SetInitialChildList(nsnull, temp);
@@ -1161,27 +1163,16 @@ nsFrameConstructorState::AddChild(nsIFra
   }
 #endif
 
   if (aInsertAfter) {
     frameItems->InsertFrame(nsnull, aInsertAfterFrame, aNewFrame);
   } else {
     frameItems->AddChild(aNewFrame);
   }
-
-  // Now add the special siblings too.
-  nsIFrame* specialSibling = aNewFrame;
-  while (specialSibling && IsFrameSpecial(specialSibling)) {
-    specialSibling = GetSpecialSibling(specialSibling);
-    if (specialSibling) {
-      NS_ASSERTION(frameItems == &aFrameItems,
-                   "IB split ending up in an out-of-flow childlist?");
-      frameItems->AddChild(specialSibling);
-    }
-  }
   
   return NS_OK;
 }
 
 void
 nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
                                                 nsIAtom* aChildListName)
 {
@@ -5362,44 +5353,66 @@ nsCSSFrameConstructor::AddFrameConstruct
     AddPageBreakItem(aContent, aStyleContext, aItems);
   }
 
   if (bits & FCDATA_IS_INLINE) {
     // To correctly set item->mIsAllInline we need to build up our child items
     // right now.
     BuildInlineChildItems(aState, *item);
     item->mHasInlineEnds = PR_TRUE;
+    item->mIsBlock = PR_FALSE;
   } else {
-    item->mIsAllInline = item->mHasInlineEnds =
-      // Table-internal things are inline-outside if they're kids of
+    // Compute a boolean isInline which is guaranteed to be false for blocks
+    // (but may also be false for some inlines).
+    PRBool isInline =
+      // Table-internal things are inline-outside if and only if they're kids of
       // inlines, since they'll trigger construction of inline-table
       // pseudos.
       ((bits & FCDATA_IS_TABLE_PART) &&
        (!aParentFrame || // No aParentFrame means inline
         aParentFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_INLINE)) ||
       // Things that are inline-outside but aren't inline frames are inline
       display->IsInlineOutside() ||
-      // Things that we're guaranteed will end up out-of-flow are inline.  This
-      // is not a precise test, since one of our ancestor inlines might add an
-      // absolute containing block (if it's relatively positioned) or float
-      // containing block (the latter if it gets split by child blocks on both
-      // sides of us) when there wasn't such a containining block before.  But
-      // it's conservative in the sense that anything that will really end up
-      // as an in-flow non-inline will have false mIsAllInline.  It just might
-      // be that even an inline that has mIsAllInline false doesn't need an
-      // {ib} split.  So this is just an optimization to keep from doing too
-      // much work when that happens.
-      (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
-       aState.GetGeometricParent(display, nsnull)) ||
       // Popups that are certainly out of flow.
       isPopup;
+
+    // Set mIsAllInline conservatively.  It just might be that even an inline
+    // that has mIsAllInline false doesn't need an {ib} split.  So this is just
+    // an optimization to keep from doing too much work in cases when we can
+    // show that mIsAllInline is true..
+    item->mIsAllInline = item->mHasInlineEnds = isInline ||
+      // Figure out whether we're guaranteed this item will be out of flow.
+      // This is not a precise test, since one of our ancestor inlines might add
+      // an absolute containing block (if it's relatively positioned) when there
+      // wasn't such a containing block before.  But it's conservative in the
+      // sense that anything that will really end up as an in-flow non-inline
+      // will test false here.  In other words, if this test is true we're
+      // guaranteed to be inline; if it's false we don't know what we'll end up
+      // as.
+      //
+      // If we make this test precise, we can remove some of the code dealing
+      // with the imprecision in ConstructInline and adjust the comments on
+      // mIsAllInline and mIsBlock in the header.  And probably remove mIsBlock
+      // altogether, since then it will always be equal to !mHasInlineEnds.
+      (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) &&
+       aState.GetGeometricParent(display, nsnull));
+
+    // Set mIsBlock conservatively.  It's OK to set it false for some real
+    // blocks, but not OK to set it true for things that aren't blocks.  Since
+    // isOutOfFlow might be false even in cases when the frame will end up
+    // out-of-flow, we can't use it here.  But we _can_ say that the frame will
+    // for sure end up in-flow if it's not floated or absolutely positioned.
+    item->mIsBlock =
+      !isInline && !display->IsAbsolutelyPositioned() && !display->IsFloating();
   }
 
   if (item->mIsAllInline) {
     aItems.InlineItemAdded();
+  } else if (item->mIsBlock) {
+    aItems.BlockItemAdded();
   }
 
   // Our item should be treated as a line participant if we have the relevant
   // bit and are going to be in-flow.  Note that this really only matters if
   // our ancestor is a box or some such, so the fact that we might have an
   // inline ancestor that might become a containing block is not relevant here.
   if ((bits & FCDATA_IS_LINE_PARTICIPANT) &&
       ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
@@ -5666,16 +5679,29 @@ AdjustAppendParentForAfterContent(nsPres
     nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
     if (afterFrame) {
       *aAfterFrame = afterFrame;
       return afterFrame->GetParent();
     }
   }
 
   *aAfterFrame = nsnull;
+
+  if (IsFrameSpecial(aParentFrame)) {
+    // We might be in a situation where the last part of the {ib} split was
+    // empty.  Since we have no ::after pseudo-element, we do in fact want to be
+    // appending to that last part, so advance to it if needed.  Note that here
+    // aParentFrame is the result of a GetLastSpecialSibling call, so must be
+    // either the last or next to last special sibling.
+    nsIFrame* trailingInline = GetSpecialSibling(aParentFrame);
+    if (trailingInline) {
+      aParentFrame = trailingInline->GetLastContinuation();
+    }
+  }
+
   return aParentFrame;
 }
 
 /**
  * This function will get the previous sibling to use for an append operation.
  * it takes a parent frame (must not be null) and its :after frame (may be
  * null).
  */
@@ -5702,18 +5728,17 @@ GetInsertNextSibling(nsIFrame* aParentFr
   }
 
   return aParentFrame->GetFirstChild(nsnull);
 }
 
 /**
  * This function is called by ContentAppended() and ContentInserted() when
  * appending flowed frames to a parent's principal child list. It handles the
- * case where the parent frame has :after pseudo-element generated content and
- * the case where the parent is the block of an {ib} split.
+ * case where the parent is the trailing inline of an {ib} split.
  */
 nsresult
 nsCSSFrameConstructor::AppendFrames(nsFrameConstructorState&       aState,
                                     nsIFrame*                      aParentFrame,
                                     nsFrameItems&                  aFrameList,
                                     nsIFrame*                      aPrevSibling)
 {
   NS_PRECONDITION(!IsFrameSpecial(aParentFrame) ||
@@ -5724,45 +5749,56 @@ nsCSSFrameConstructor::AppendFrames(nsFr
                   "Parent and prevsibling don't match");
 
   nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling);
 
   NS_ASSERTION(nextSibling ||
                !aParentFrame->GetNextContinuation() ||
                !aParentFrame->GetNextContinuation()->GetFirstChild(nsnull),
                "aParentFrame has later continuations with kids?");
-
-  // If we we're inserting a list of frames that ends in inlines at the end of
-  // the block part of an {ib} split, we need to move them out to the beginning
-  // of a trailing inline part.
-  if (!nextSibling &&
-      IsFrameSpecial(aParentFrame) &&
-      !IsInlineFrame(aParentFrame) &&
-      IsInlineOutside(aFrameList.LastChild())) {
-    // We want to put some of the frames into the following inline frame.
-    nsFrameList::FrameLinkEnumerator lastBlock = FindLastBlock(aFrameList);
-    nsFrameList inlineKids = aFrameList.ExtractTail(lastBlock);
-
-    NS_ASSERTION(inlineKids.NotEmpty(), "How did that happen?");
-
-    nsIFrame* inlineSibling = GetSpecialSibling(aParentFrame);
-    NS_ASSERTION(inlineSibling, "How did that happen?");
-
-    nsIFrame* stateParent = inlineSibling->GetParent();
-
-    nsFrameConstructorState targetState(mPresShell, mFixedContainingBlock,
-                                        GetAbsoluteContainingBlock(stateParent),
-                                        GetFloatContainingBlock(stateParent));
-
-    MoveFramesToEndOfIBSplit(aState, inlineSibling, inlineKids,
-                             aParentFrame, &targetState);
-  }
-    
-  if (aFrameList.IsEmpty()) {
-    // It all got eaten by the special inline
+  NS_ASSERTION(nextSibling ||
+               !IsFrameSpecial(aParentFrame) ||
+               (IsInlineFrame(aParentFrame) &&
+                !GetSpecialSibling(aParentFrame) &&
+                !aParentFrame->GetNextContinuation()),
+               "aParentFrame is not last?");
+
+  // If we're inserting a list of frames at the end of the trailing inline
+  // of an {ib} split, we may need to create additional {ib} siblings to parent
+  // them.
+  if (!nextSibling && IsFrameSpecial(aParentFrame)) {
+    // We want to put some of the frames into this inline frame.
+    nsFrameList::FrameLinkEnumerator firstBlockEnumerator(aFrameList);
+    FindFirstBlock(firstBlockEnumerator);
+
+    nsFrameList inlineKids = aFrameList.ExtractHead(firstBlockEnumerator);
+    if (!inlineKids.IsEmpty()) {
+      aState.mFrameManager->AppendFrames(aParentFrame, nsnull, inlineKids);
+    }
+
+    if (!aFrameList.IsEmpty()) {
+      const nsStyleDisplay* parentDisplay = aParentFrame->GetStyleDisplay();
+      PRBool positioned =
+        parentDisplay->mPosition == NS_STYLE_POSITION_RELATIVE ||
+        parentDisplay->HasTransform();
+      nsFrameItems ibSiblings;
+      CreateIBSiblings(aState, aParentFrame, positioned, aFrameList,
+                       ibSiblings);
+
+      // Make sure to trigger reflow of the inline that used to be our
+      // last one and now isn't anymore, since its GetSkipSides() has
+      // changed.
+      mPresShell->FrameNeedsReflow(aParentFrame,
+                                   nsIPresShell::eTreeChange,
+                                   NS_FRAME_HAS_DIRTY_CHILDREN);
+
+      return aState.mFrameManager->InsertFrames(aParentFrame->GetParent(),
+                                                nsnull, aParentFrame, ibSiblings);
+    }
+
     return NS_OK;
   }
   
   // Insert the frames after out aPrevSibling
   return aState.mFrameManager->InsertFrames(aParentFrame, nsnull, aPrevSibling,
                                             aFrameList);
 }
 
@@ -5873,17 +5909,17 @@ nsCSSFrameConstructor::FindFrameForConte
 
   // The frame we have now should never be a continuation
   NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
 
   if (aPrevSibling) {
     // The frame may be a special frame (a split inline frame that
     // contains a block).  Get the last part of that split.
     if (IsFrameSpecial(sibling)) {
-      sibling = GetLastSpecialSibling(sibling, PR_FALSE);
+      sibling = GetLastSpecialSibling(sibling, PR_TRUE);
     }
 
     // The frame may have a continuation. If so, we want the last
     // non-overflow-container continuation as our previous sibling.
     sibling = sibling->GetTailContinuation();
   }
 
   if (aTargetContent &&
@@ -6021,17 +6057,17 @@ nsCSSFrameConstructor::GetInsertionPrevS
     }
     else {
       // No previous or next sibling, so treat this like an appended frame.
       *aIsAppend = PR_TRUE;
       if (IsFrameSpecial(aParentFrame)) {
         // Since we're appending, we'll walk to the last anonymous frame
         // that was created for the broken inline frame.  But don't walk
         // to the trailing inline if it's empty; stop at the block.
-        aParentFrame = GetLastSpecialSibling(aParentFrame, PR_TRUE);
+        aParentFrame = GetLastSpecialSibling(aParentFrame, PR_FALSE);
       }
       // Get continuation that parents the last child.  This MUST be done
       // before the AdjustAppendParentForAfterContent call.
       aParentFrame = nsLayoutUtils::GetLastContinuationWithChild(aParentFrame);
       // Deal with fieldsets
       aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
                                               aParentFrame->GetType(),
                                               aChild);
@@ -6282,17 +6318,17 @@ nsCSSFrameConstructor::ContentAppended(n
       nsFrame::ListTag(stdout, parentFrame);
       printf(" is special\n");
     }
 #endif
 
     // Since we're appending, we'll walk to the last anonymous frame
     // that was created for the broken inline frame.  But don't walk
     // to the trailing inline if it's empty; stop at the block.
-    parentFrame = GetLastSpecialSibling(parentFrame, PR_TRUE);
+    parentFrame = GetLastSpecialSibling(parentFrame, PR_FALSE);
   }
 
   // Get continuation that parents the last child.  This MUST be done
   // before the AdjustAppendParentForAfterContent call.
   parentFrame = nsLayoutUtils::GetLastContinuationWithChild(parentFrame);
 
   // We should never get here with fieldsets, since they have multiple
   // insertion points.
@@ -8991,28 +9027,29 @@ nsCSSFrameConstructor::MaybeRecreateCont
 
   // We might still need to reconstruct things if the parent of inFlowFrame is
   // special, since in that case the removal of aFrame might affect the
   // splitting of its parent.
   if (!IsFrameSpecial(parent)) {
     return PR_FALSE;
   }
 
-  // If inFlowFrame is an inline, then it cannot possibly have caused the
-  // splitting.  If the frame is being reconstructed and being changed to a
-  // block, the ContentInserted call will handle the containing block reframe.
-  // So in this case, we don't need to reframe.
-  if (IsInlineOutside(inFlowFrame)) {
+  // If inFlowFrame is not the only in-flow child of |parent|, then removing
+  // it will change nothing about the {ib} split.
+  if (inFlowFrame != parent->GetFirstChild(nsnull) ||
+      inFlowFrame->GetLastContinuation()->GetNextSibling()) {
     return PR_FALSE;
   }
 
-  // If aFrame is not the first or last block, then removing it is not
-  // going to affect the splitting.
-  if (inFlowFrame != parent->GetFirstChild(nsnull) &&
-      inFlowFrame->GetLastContinuation()->GetNextSibling()) {
+  // If the parent is the first or last part of the {ib} split, then
+  // removing one of its kids will have no effect on the splitting.
+  // Get the first continuation up front so we don't have to do it twice.
+  nsIFrame* parentFirstContinuation = parent->GetFirstContinuation();
+  if (!GetSpecialSibling(parentFirstContinuation) ||
+      !GetSpecialPrevSibling(parentFirstContinuation)) {
     return PR_FALSE;
   }
 
 #ifdef DEBUG
   if (gNoisyContentUpdates) {
     printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: "
            "frame=");
     nsFrame::ListTag(stdout, parent);
@@ -10641,16 +10678,72 @@ nsCSSFrameConstructor::ConstructBlock(ns
 nsresult
 nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState,
                                        FrameConstructionItem&   aItem,
                                        nsIFrame*                aParentFrame,
                                        const nsStyleDisplay*    aDisplay,
                                        nsFrameItems&            aFrameItems,
                                        nsIFrame**               aNewFrame)
 {
+  // If an inline frame has non-inline kids, then we chop up the child list
+  // into runs of blocks and runs of inlines, create anonymous block frames to
+  // contain the runs of blocks, inline frames with our style context for the
+  // runs of inlines, and put all these frames, in order, into aFrameItems.  We
+  // put the first one into *aNewFrame.  The whole setup is called an {ib}
+  // split; in what follows "frames in the split" refers to the anonymous blocks
+  // and inlines that contain our children.
+  //
+  // {ib} splits maintain the following invariants:
+  // 1) All frames in the split have the NS_FRAME_IS_SPECIAL bit set.
+  // 2) Each frame in the split has the nsGkAtoms::IBSplitSpecialSibling
+  //    property pointing to the next frame in the split, except for the last
+  //    one, which does not have it set.
+  // 3) Each frame in the split has the nsGkAtoms::IBSplitSpecialPrevSibling
+  //    property pointing to the previous frame in the split, except for the
+  //    first one, which does not have it set.
+  // 4) The first and last frame in the split are always inlines.
+  //
+  // An invariant that is NOT maintained is that the wrappers are actually
+  // linked via GetNextSibling linkage.  A simple example is an inline
+  // containing an inline that contains a block.  The three parts of the inner
+  // inline end up with three different parents.
+  //
+  // For example, this HTML:
+  // <span>
+  //   <div>a</div>
+  //   <span>
+  //     b
+  //     <div>c</div>
+  //   </span>
+  //   d
+  //   <div>e</div>
+  //   f
+  //  </span>
+  // Gives the following frame tree:
+  //
+  // Inline (outer span)
+  // Block (anonymous, outer span)
+  //   Block (div)
+  //     Text("a")
+  // Inline (outer span)
+  //   Inline (inner span)
+  //     Text("b")
+  // Block (anonymous, outer span)
+  //   Block (anonymous, inner span)
+  //     Block (div)
+  //       Text("c")
+  // Inline (outer span)
+  //   Inline (inner span)
+  //   Text("d")
+  // Block (anonymous, outer span)
+  //   Block (div)
+  //     Text("e")
+  // Inline (outer span)
+  //   Text("f")
+
   nsIContent* const content = aItem.mContent;
   nsStyleContext* const styleContext = aItem.mStyleContext;
 
   nsIFrame *newFrame;
 
   PRBool positioned =
     NS_STYLE_DISPLAY_INLINE == aDisplay->mDisplay &&
     (NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition ||
@@ -10701,142 +10794,133 @@ nsCSSFrameConstructor::ConstructInline(n
     newFrame->SetInitialChildList(nsnull, childItems);
     if (NS_SUCCEEDED(rv)) {
       aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
       *aNewFrame = newFrame;
     }
     return rv;
   }
 
-  // This inline frame contains several types of children. Therefore
-  // this frame has to be chopped into several pieces. We will produce
-  // as a result of this 3 lists of children. The first list contains
-  // all of the inline children that precede the first block child
-  // (and may be empty). The second list contains all of the block
-  // children and any inlines that are between them (and must not be
-  // empty, otherwise - why are we here?). The final list contains all
-  // of the inline children that follow the final block child.
+  // This inline frame contains several types of children. Therefore this frame
+  // has to be chopped into several pieces, as described above.
 
   // Grab the first inline's kids
   nsFrameList firstInlineKids = childItems.ExtractHead(firstBlockEnumerator);
   newFrame->SetInitialChildList(nsnull, firstInlineKids);
-                                             
-  // The kids between the first and last block belong to an anonymous block
-  // that we create right now. The anonymous block will be the parent of the
-  // block children of the inline.
-  nsIAtom* blockStyle;
-  nsRefPtr<nsStyleContext> blockSC;
-  nsIFrame* blockFrame;
-  if (positioned) {
-    blockStyle = nsCSSAnonBoxes::mozAnonymousPositionedBlock;
-    
-    blockSC = mPresShell->StyleSet()->
-      ResolvePseudoStyleFor(content, blockStyle, styleContext);
-  }
-  else {
-    blockStyle = nsCSSAnonBoxes::mozAnonymousBlock;
-
-    blockSC = mPresShell->StyleSet()->
-      ResolvePseudoStyleFor(content, blockStyle, styleContext);
-  }
-  blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
-
-  InitAndRestoreFrame(aState, content, aParentFrame, nsnull, blockFrame, PR_FALSE);
-
-  // Any inline frame could have a view (e.g., opacity)
-  nsHTMLContainerFrame::CreateViewForFrame(blockFrame, PR_FALSE);
-
-  // Find the last block child which defines the end of our block kids and the
-  // start of our trailing inline's kids
-  nsFrameList::FrameLinkEnumerator lastBlock = FindLastBlock(childItems);
-  nsFrameList blockKids = childItems.ExtractHead(lastBlock);
-
-  if (blockFrame->HasView() || newFrame->HasView()) {
-    // Move the block's child frames into the new view
-    nsHTMLContainerFrame::ReparentFrameViewList(aState.mPresContext, blockKids,
-                                                newFrame, blockFrame);
-  }
-
-  // Save the first frame in blockKids for the MoveChildrenTo call, since
-  // SetInitialChildList will empty blockKids.
-  nsIFrame* firstBlock = blockKids.FirstChild();
-  blockFrame->SetInitialChildList(nsnull, blockKids);
-
-  nsFrameConstructorState state(mPresShell, mFixedContainingBlock,
-                                GetAbsoluteContainingBlock(blockFrame),
-                                GetFloatContainingBlock(blockFrame));
-
-  // If we have an inline between two blocks all inside an inline and the inner
-  // inline contains a float, the float will end up in the float list of the
-  // parent block of the inline, but its parent pointer will be the anonymous
-  // block we create...  AdjustFloatParentPtrs() deals with this by moving the
-  // float from the outer state |aState| to the inner |state|.
-  MoveChildrenTo(state.mFrameManager, blockFrame, firstBlock, nsnull,
-                 &state, &aState);
-
-  // What's left in childItems belongs to our trailing inline frame
-  nsIFrame* inlineFrame;
-  if (positioned) {
-    inlineFrame = NS_NewPositionedInlineFrame(mPresShell, styleContext);
-  }
-  else {
-    inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
-  }
-
-  InitAndRestoreFrame(aState, content, aParentFrame, nsnull, inlineFrame,
-                      PR_FALSE);
-
-  // Any frame might need a view
-  nsHTMLContainerFrame::CreateViewForFrame(inlineFrame, PR_FALSE);
-
-  if (childItems.NotEmpty()) {
-    MoveFramesToEndOfIBSplit(aState, inlineFrame, childItems, blockFrame,
-                             nsnull);
-  }
-
-  // Mark the frames as special. That way if any of the append/insert/remove
-  // methods try to fiddle with the children, the containing block will be
-  // reframed instead.
-  SetFrameIsSpecial(newFrame, blockFrame);
-  SetFrameIsSpecial(blockFrame, inlineFrame);
-  SetFrameIsSpecial(inlineFrame, nsnull);
-
-#ifdef DEBUG
-  if (gNoisyInlineConstruction) {
-    printf("nsCSSFrameConstructor::ConstructInline:\n");
-    if (*aNewFrame) {
-      printf("  ==> leading inline frame:\n");
-      (*aNewFrame)->List(stdout, 2);
-    }
-    if (blockFrame) {
-      printf("  ==> block frame:\n");
-      blockFrame->List(stdout, 2);
-    }
-    if (inlineFrame) {
-      printf("  ==> trailing inline frame:\n");
-      inlineFrame->List(stdout, 2);
-    }
-  }
-#endif
-
-  if (NS_SUCCEEDED(rv)) {
-    aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
-    *aNewFrame = newFrame;
-  }
-  return rv;
+
+  aFrameItems.AddChild(newFrame);
+
+  CreateIBSiblings(aState, newFrame, positioned, childItems, aFrameItems);
+
+  *aNewFrame = newFrame;
+  return NS_OK;
+}
+
+void
+nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState,
+                                        nsIFrame* aInitialInline,
+                                        PRBool aIsPositioned,
+                                        nsFrameItems& aChildItems,
+                                        nsFrameItems& aSiblings)
+{
+  nsIContent* content = aInitialInline->GetContent();
+  nsStyleContext* styleContext = aInitialInline->GetStyleContext();
+  nsIFrame* parentFrame = aInitialInline->GetParent();
+
+  // Resolve the right style context for our anonymous blocks.
+  nsRefPtr<nsStyleContext> blockSC =
+    mPresShell->StyleSet()->
+      ResolvePseudoStyleFor(content,
+                            aIsPositioned ?
+                              nsCSSAnonBoxes::mozAnonymousPositionedBlock :
+                              nsCSSAnonBoxes::mozAnonymousBlock,
+                            styleContext);
+
+  nsIFrame* lastNewInline = aInitialInline->GetFirstContinuation();
+  do {
+    // On entry to this loop aChildItems is not empty and the first frame in it
+    // is block-level.
+    NS_PRECONDITION(aChildItems.NotEmpty(), "Should have child items");
+    NS_PRECONDITION(!IsInlineOutside(aChildItems.FirstChild()),
+                    "Must have list starting with block");
+
+    // The initial run of blocks belongs to an anonymous block that we create
+    // right now. The anonymous block will be the parent of these block
+    // children of the inline.
+    nsIFrame* blockFrame;
+    blockFrame = NS_NewBlockFrame(mPresShell, blockSC);
+
+    InitAndRestoreFrame(aState, content, parentFrame, nsnull, blockFrame,
+                        PR_FALSE);
+
+    // Any frame could have a view
+    nsHTMLContainerFrame::CreateViewForFrame(blockFrame, PR_FALSE);
+
+    // Find the first non-block child which defines the end of our block kids
+    // and the start of our next inline's kids
+    nsFrameList::FrameLinkEnumerator firstNonBlock =
+      FindFirstNonBlock(aChildItems);
+    nsFrameList blockKids = aChildItems.ExtractHead(firstNonBlock);
+
+    if (blockFrame->HasView() || aInitialInline->HasView()) {
+      // Move the block's child frames into the new view
+      nsHTMLContainerFrame::ReparentFrameViewList(aState.mPresContext,
+                                                  blockKids, aInitialInline,
+                                                  blockFrame);
+    }
+
+    // Save the first frame in blockKids for the MoveChildrenTo call, since
+    // SetInitialChildList will empty blockKids.
+    nsIFrame* firstBlock = blockKids.FirstChild();
+    blockFrame->SetInitialChildList(nsnull, blockKids);
+
+    // XXXbz can we simplify MoveChildrenTo somewhat?
+    MoveChildrenTo(aState.mFrameManager, blockFrame, firstBlock, nsnull,
+                   nsnull, nsnull);
+
+    SetFrameIsSpecial(lastNewInline, blockFrame);
+    aSiblings.AddChild(blockFrame);
+
+    // Now grab the initial inlines in aChildItems and put them into an inline
+    // frame
+    nsIFrame* inlineFrame;
+    if (aIsPositioned) {
+      inlineFrame = NS_NewPositionedInlineFrame(mPresShell, styleContext);
+    }
+    else {
+      inlineFrame = NS_NewInlineFrame(mPresShell, styleContext);
+    }
+
+    InitAndRestoreFrame(aState, content, parentFrame, nsnull, inlineFrame,
+                        PR_FALSE);
+
+    // Any frame might need a view
+    nsHTMLContainerFrame::CreateViewForFrame(inlineFrame, PR_FALSE);
+
+    if (aChildItems.NotEmpty()) {
+      nsFrameList::FrameLinkEnumerator firstBlock(aChildItems);
+      FindFirstBlock(firstBlock);
+      nsFrameList inlineKids = aChildItems.ExtractHead(firstBlock);
+      MoveFramesToEndOfIBSplit(aState, inlineFrame, inlineKids, nsnull);
+    }
+
+    SetFrameIsSpecial(blockFrame, inlineFrame);
+    aSiblings.AddChild(inlineFrame);
+    lastNewInline = inlineFrame;
+  } while (aChildItems.NotEmpty());
+
+  SetFrameIsSpecial(lastNewInline, nsnull);
 }
 
 void
 nsCSSFrameConstructor::MoveFramesToEndOfIBSplit(nsFrameConstructorState& aState,
                                                 nsIFrame* aExistingEndFrame,
                                                 nsFrameList& aFramesToMove,
-                                                nsIFrame* aBlockPart,
                                                 nsFrameConstructorState* aTargetState)
 {
-  NS_PRECONDITION(aBlockPart, "Must have a block part");
   NS_PRECONDITION(aExistingEndFrame, "Must have trailing inline");
   NS_PRECONDITION(aFramesToMove.NotEmpty(), "Must have frames to move");
 
   nsIFrame* newFirstChild = aFramesToMove.FirstChild();
   if (aExistingEndFrame->HasView() ||
       newFirstChild->GetParent()->HasView()) {
     // Move the frames into the new view
     nsHTMLContainerFrame::ReparentFrameViewList(aState.mPresContext,
@@ -10912,17 +10996,17 @@ nsCSSFrameConstructor::WipeContainingBlo
                                            FrameConstructionItemList& aItems,
                                            PRBool aIsAppend,
                                            nsIFrame* aPrevSibling)
 {
   if (aItems.IsEmpty()) {
     return PR_FALSE;
   }
   
-  // Before we go and append the frames, we must check for three
+  // Before we go and append the frames, we must check for several
   // special situations.
 
   // Situation #1 is a XUL frame that contains frames that are required
   // to be wrapped in blocks.
   if (aFrame->IsBoxFrame() &&
       !(aFrame->GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) &&
       aItems.AnyItemsNeedBlockParent()) {
     RecreateFramesForContent(aFrame->GetContent(), PR_TRUE);
@@ -11093,78 +11177,58 @@ nsCSSFrameConstructor::WipeContainingBlo
     if (!aItems.AllWantParentType(parentType)) {
       // Reframing aFrame->GetContent() is good enough, since the content of
       // table pseudo-frames is the ancestor content.
       RecreateFramesForContent(aFrame->GetContent(), PR_TRUE);
       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.
-
-  // We also need to check for an append of content ending in an
-  // inline to the block in an {ib} split or an insert of content
-  // starting with an inline to the start of that block.  If that
-  // happens, we also need to reframe, since that content needs to go
-  // into the following or preceding inline in the split.
-
-  if (IsInlineFrame(aFrame)) {
-    // Nothing to do if all kids are inline
-    if (aItems.AreAllItemsInline()) {
-      return PR_FALSE;
-    }
-  } else if (!IsFrameSpecial(aFrame)) {
-    return PR_FALSE;
-  } else {
-    // aFrame is the block in an {ib} split.  Check that we're not
-    // messing up either end of it.
-    if (aPrevSibling || !aItems.IsStartInline()) {
-      // Not messing up the beginning.  Now let's look at the end.
-      if (nextSibling) {
-        // Can't possibly screw up the end; bail out
+  // Now we have several cases involving {ib} splits.  Put them all in a
+  // do/while with breaks to take us to the "go and reconstruct" code.
+  do {
+    if (IsInlineFrame(aFrame)) {
+      if (aItems.AreAllItemsInline()) {
+        // We can just put the kids in.
         return PR_FALSE;
       }
 
-      if (aIsAppend) {
-        // Will be handled in AppendFrames(), except the case when we might have
-        // floats that we won't be able to move out because there is no float
-        // containing block to move them into.
-
-        // Walk up until we get a float containing block that's not part of an
-        // {ib} split, since otherwise we might have to ship floats out of it
-        // too.
-        // XXXbz we could keep track of whether we have any descendants with
-        // float style in the FrameConstructionItem if we really want, but it's
-        // not clear to me that we need to.  In any case, the right solution
-        // here is to construct with the right parents to start with.
-        nsIFrame* floatContainer = aFrame;
-        do {
-          floatContainer =
-            GetFloatContainingBlock(GetSpecialPrevSibling(floatContainer));
-          if (!floatContainer) {
-            break;
-          }
-          if (!IsFrameSpecial(floatContainer)) {
-            return PR_FALSE;
-          }
-        } while (1);
-      }
-
-      // This is an append that won't go through AppendFrames, or won't be able
-      // to ship floats out in AppendFrames.  We can bail out if the last frame
-      // we're appending is not inline.
-      if (!aItems.IsEndInline()) {
+      if (!IsFrameSpecial(aFrame)) {
+        // Need to go ahead and reconstruct.
+        break;
+      }
+
+      // Now we're adding kids including some blocks to an inline part of an
+      // {ib} split.  If we plan to call AppendFrames, and don't have a next
+      // sibling for the new frames, and our parent is the last continuation of
+      // the last part of the {ib} split, then AppendFrames will handle things
+      // for us.  Bail out in that case.
+      if (aIsAppend && !nextSibling && !aFrame->GetNextContinuation() &&
+          !GetSpecialSibling(aFrame)) {
         return PR_FALSE;
       }
-    }
-  }
+
+      // Need to reconstruct.
+      break;
+    }
+
+    // Now we know we have a block parent.  If it's not special, we're all set.
+    if (!IsFrameSpecial(aFrame)) {
+      return PR_FALSE;
+    }
+
+    // We're adding some kids to a block part of an {ib} split.  If all the
+    // kids are blocks, we don't need to reconstruct.
+    if (aItems.AreAllItemsBlock()) {
+      return PR_FALSE;
+    }
+
+    // We might have some inline kids for this block.  Just reconstruct.
+    break;
+  } while (0);
 
   // If we don't have a containing block, start with aFrame and look for one.
   if (!aContainingBlock) {
     aContainingBlock = aFrame;
   }
   
   // To find the right block to reframe, just walk up the tree until we find a
   // frame that is:
@@ -11807,16 +11871,19 @@ void
 nsCSSFrameConstructor::FrameConstructionItemList::
 AdjustCountsForItem(FrameConstructionItem* aItem, PRInt32 aDelta)
 {
   NS_PRECONDITION(aDelta == 1 || aDelta == -1, "Unexpected delta");
   mItemCount += aDelta;
   if (aItem->mIsAllInline) {
     mInlineCount += aDelta;
   }
+  if (aItem->mIsBlock) {
+    mBlockCount += aDelta;
+  }
   if (aItem->mIsLineParticipant) {
     mLineParticipantCount += aDelta;
   }
   mDesiredParentCounts[aItem->DesiredParentType()] += aDelta;
 }
 
 ////////////////////////////////////////////////////////////////////////
 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods //
@@ -11883,16 +11950,17 @@ Iterator::AppendItemsToList(const Iterat
   }
 
   // move over the list of items
   PR_INSERT_AFTER(&aTargetList.mItems, &mList.mItems);
   PR_REMOVE_LINK(&mList.mItems);
 
   // Copy over the various counters
   aTargetList.mInlineCount = mList.mInlineCount;
+  aTargetList.mBlockCount = mList.mBlockCount;
   aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
   aTargetList.mItemCount = mList.mItemCount;
   memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
          sizeof(aTargetList.mDesiredParentCounts));
 
   // reset mList
   new (&mList) FrameConstructionItemList();
 
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -434,20 +434,20 @@ private:
   // aFrame may be null; this method doesn't use it directly in any case.
   void CreateGeneratedContentItem(nsFrameConstructorState& aState,
                                   nsIFrame*                aFrame,
                                   nsIContent*              aContent,
                                   nsStyleContext*          aStyleContext,
                                   nsIAtom*                 aPseudoElement,
                                   FrameConstructionItemList& aItems);
 
-  // This method can change aFrameList: it can chop off the end and put it in a
-  // special sibling of aParentFrame.  It can also change aState by moving some
-  // floats out of it.  aPrevSibling must be the frame after which aFrameList
-  // is to be placed on aParentFrame's principal child list.  It may be null if
+  // This method can change aFrameList: it can chop off the beginning and put
+  // it in aParentFrame while putting the remainder into a special sibling of
+  // aParentFrame.  aPrevSibling must be the frame after which aFrameList is to
+  // be placed on aParentFrame's principal child list.  It may be null if
   // aFrameList is being added at the beginning of the child list.
   nsresult AppendFrames(nsFrameConstructorState&       aState,
                         nsIFrame*                      aParentFrame,
                         nsFrameItems&                  aFrameList,
                         nsIFrame*                      aPrevSibling);
 
   // BEGIN TABLE SECTION
   /**
@@ -715,16 +715,17 @@ private:
                   const FrameConstructionDataByTag* aDataPtr,
                   PRUint32 aDataLength);
 
   /* A class representing a list of FrameConstructionItems */
   class FrameConstructionItemList {
   public:
     FrameConstructionItemList() :
       mInlineCount(0),
+      mBlockCount(0),
       mLineParticipantCount(0),
       mItemCount(0),
       mLineBoundaryAtStart(PR_FALSE),
       mLineBoundaryAtEnd(PR_FALSE),
       mParentHasNoXBLChildren(PR_FALSE)
     {
       PR_INIT_CLIST(&mItems);
       memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts));
@@ -748,24 +749,17 @@ private:
       mParentHasNoXBLChildren = aHasNoXBLChildren;
     }
     PRBool HasLineBoundaryAtStart() { return mLineBoundaryAtStart; }
     PRBool HasLineBoundaryAtEnd() { return mLineBoundaryAtEnd; }
     PRBool ParentHasNoXBLChildren() { return mParentHasNoXBLChildren; }
     PRBool IsEmpty() const { return PR_CLIST_IS_EMPTY(&mItems); }
     PRBool AnyItemsNeedBlockParent() const { return mLineParticipantCount != 0; }
     PRBool AreAllItemsInline() const { return mInlineCount == mItemCount; }
-    PRBool IsStartInline() const {
-      NS_ASSERTION(!IsEmpty(), "Someone forgot to check IsEmpty()");
-      return ToItem(PR_LIST_HEAD(&mItems))->mHasInlineEnds;
-    }
-    PRBool IsEndInline() const {
-      NS_ASSERTION(!IsEmpty(), "Someone forgot to check IsEmpty()");
-      return ToItem(PR_LIST_TAIL(&mItems))->mHasInlineEnds;
-    }
+    PRBool AreAllItemsBlock() const { return mBlockCount == mItemCount; }
     PRBool AllWantParentType(ParentType aDesiredParentType) const {
       return mDesiredParentCounts[aDesiredParentType] == mItemCount;
     }
 
     // aContentIndex is the index of aContent in its parent's child list,
     // or -1 if aContent is not in its parent's child list, or the index
     // is not known.
     FrameConstructionItem* AppendItem(const FrameConstructionData* aFCData,
@@ -785,16 +779,17 @@ private:
       } else {
         // Clean up the style context
         nsRefPtr<nsStyleContext> sc(aStyleContext);
       }
       return item;
     }
 
     void InlineItemAdded() { ++mInlineCount; }
+    void BlockItemAdded() { ++mBlockCount; }
     void LineParticipantItemAdded() { ++mLineParticipantCount; }
 
     class Iterator;
     friend class Iterator;
 
     class Iterator {
     public:
       Iterator(FrameConstructionItemList& list) :
@@ -895,16 +890,17 @@ private:
     }
 
     // Adjust our various counts for aItem being added or removed.  aDelta
     // should be either +1 or -1 depending on which is happening.
     void AdjustCountsForItem(FrameConstructionItem* aItem, PRInt32 aDelta);
 
     PRCList mItems;
     PRUint32 mInlineCount;
+    PRUint32 mBlockCount;
     PRUint32 mLineParticipantCount;
     PRUint32 mItemCount;
     PRUint32 mDesiredParentCounts[eParentTypeCount];
     // True if there is guaranteed to be a line boundary before the
     // frames created by these items
     PRPackedBool mLineBoundaryAtStart;
     // True if there is guaranteed to be a line boundary after the
     // frames created by these items
@@ -927,17 +923,17 @@ private:
                           nsIAtom* aTag,
                           PRInt32 aNameSpaceID,
                           PRInt32 aContentIndex,
                           already_AddRefed<nsStyleContext> aStyleContext) :
       mFCData(aFCData), mContent(aContent), mTag(aTag),
       mNameSpaceID(aNameSpaceID), mContentIndex(aContentIndex),
       mStyleContext(aStyleContext),
       mIsText(PR_FALSE), mIsGeneratedContent(PR_FALSE),
-      mIsRootPopupgroup(PR_FALSE), mIsAllInline(PR_FALSE),
+      mIsRootPopupgroup(PR_FALSE), mIsAllInline(PR_FALSE), mIsBlock(PR_FALSE),
       mHasInlineEnds(PR_FALSE), mIsPopup(PR_FALSE),
       mIsLineParticipant(PR_FALSE)
     {}
     ~FrameConstructionItem() {
       if (mIsGeneratedContent) {
         mContent->UnbindFromTree();
         NS_RELEASE(mContent);
       }
@@ -948,17 +944,17 @@ private:
     }
 
     // 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;
 
     PRBool IsLineBoundary() const {
-      return !mHasInlineEnds || (mFCData->mBits & FCDATA_IS_LINE_BREAK);
+      return mIsBlock || (mFCData->mBits & FCDATA_IS_LINE_BREAK);
     }
 
     // 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;
@@ -972,18 +968,26 @@ private:
     // Whether this is a text content item.
     PRPackedBool mIsText;
     // Whether this is a generated content container.
     // If it is, mContent is a strong pointer.
     PRPackedBool mIsGeneratedContent;
     // Whether this is an item for the root popupgroup.
     PRPackedBool mIsRootPopupgroup;
     // Whether construction from this item will create only frames that are
-    // IsInlineOutside() in the principal child list.
+    // IsInlineOutside() in the principal child list.  This is not precise, but
+    // conservative: if true the frames will really be inline, whereas if false
+    // they might still all be inline.
     PRPackedBool mIsAllInline;
+    // Whether construction from this item will create only frames that are
+    // IsBlockOutside() in the principal child list.  This is not precise, but
+    // conservative: if true the frames will really be blocks, whereas if false
+    // they might still be blocks (and in particular, out-of-flows that didn't
+    // find a containing block).
+    PRPackedBool mIsBlock;
     // Whether construction from this item will give leading and trailing
     // inline frames.  This is equal to mIsAllInline, except for inline frame
     // items, where it's always true, whereas mIsAllInline might be false due
     // to {ib} splits.
     PRPackedBool mHasInlineEnds;
     // Whether construction from this item will create a popup that needs to
     // go into the global popup items.
     PRPackedBool mIsPopup;
@@ -1444,31 +1448,53 @@ private:
   nsresult ConstructInline(nsFrameConstructorState& aState,
                            FrameConstructionItem&   aItem,
                            nsIFrame*                aParentFrame,
                            const nsStyleDisplay*    aDisplay,
                            nsFrameItems&            aFrameItems,
                            nsIFrame**               aNewFrame);
 
   /**
+   * Create any additional {ib} siblings needed to contain aChildItems and put
+   * them in aSiblings.
+   *
+   * @param aState the frame constructor state
+   * @param aInitialInline is an already-existing inline frame that will be
+   *                       part of this {ib} split and come before everything
+   *                       in aSiblings.
+   * @param aIsPositioned true if aInitialInline is positioned.
+   * @param aChildItems is a child list starting with a block; this method
+   *                    assumes that the inline has already taken all the
+   *                    children it wants.  When the method returns aChildItems
+   *                    will be empty.
+   * @param aSiblings the nsFrameItems to put the newly-created siblings into.
+   *
+   * This method is responsible for making any SetFrameIsSpecial calls that are
+   * needed.
+   */
+  void CreateIBSiblings(nsFrameConstructorState& aState,
+                        nsIFrame* aInitialInline,
+                        PRBool aIsPositioned,
+                        nsFrameItems& aChildItems,
+                        nsFrameItems& aSiblings);
+
+  /**
    * Move an already-constructed framelist into the inline frame at
    * the tail end of an {ib} split.
    *
    * @param aState the frame construction state we're using right now.
    * @param aExistingEndFrame the already-existing end frame.
    * @param aFramesToMove The frame list to move over.  Must be nonempty.
-   * @param aBlockPart the block part of the {ib} split.
    * @param aTargetState if non-null, the target state to pass to
    *        MoveChildrenTo for float reparenting.
    * XXXbz test float reparenting?
    */
   void MoveFramesToEndOfIBSplit(nsFrameConstructorState& aState,
                                 nsIFrame* aExistingEndFrame,
                                 nsFrameList& aFramesToMove,
-                                nsIFrame* aBlockPart,
                                 nsFrameConstructorState* aTargetState);
 
   /**
    * For an inline aParentItem, construct its list of child
    * FrameConstructionItems and set its mIsAllInline flag appropriately.
    */
   void BuildInlineChildItems(nsFrameConstructorState& aState,
                              FrameConstructionItem& aParentItem);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -232,16 +232,18 @@ enum {
   NS_FRAME_HAS_VIEW =                           0x00002000,
 
   // If this bit is set, the frame was created from anonymous content.
   NS_FRAME_INDEPENDENT_SELECTION =              0x00004000,
 
   // If this bit is set, the frame is "special" (lame term, I know),
   // which means that it is part of the mangled frame hierarchy that
   // results when an inline has been split because of a nested block.
+  // See the comments in nsCSSFrameConstructor::ConstructInline for
+  // more details.
   NS_FRAME_IS_SPECIAL =                         0x00008000,
 
   // If this bit is set, the frame may have a transform that it applies
   // to its coordinate system (e.g. CSS transform, SVG foreignObject).
   // This is used primarily in GetTransformMatrix to optimize for the
   // common case.
   // ALSO, if this bit is set, the frame's first-continuation may
   // have an associated nsSVGRenderingObserverList.
--- a/layout/reftests/bugs/424236-8-ref.html
+++ b/layout/reftests/bugs/424236-8-ref.html
@@ -1,23 +1,25 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
-	"http://www.w3.org/TR/html4/strict.dtd">
+        "http://www.w3.org/TR/html4/strict.dtd">
 <html lang="en-US">
 <head>
-	<title>reftest, bug 424236</title>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-	<meta http-equiv="Content-Style-Type" content="text/css">
-	<style type="text/css">
+        <title>reftest, bug 424236</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta http-equiv="Content-Style-Type" content="text/css">
+        <style type="text/css">
 
-	* { -moz-outline-offset: 3px; outline-offset: 3px; }
-	body > span { outline: 1px dotted black; }
-	/* can't compare to border combined with negative margin because of
-	   margin collapsing */
-	body > div { display: block; outline: 1px dotted black; width: 15em; }
+        * { -moz-outline-offset: 3px; outline-offset: 3px; }
+        body > span { outline: 1px dotted black; }
+        /* can't compare to border combined with negative margin because of
+           margin collapsing */
+        body > div { display: block; outline: 1px dotted black; }
+        body > div { width: 10em; }
+        body > div + span + div { width: 15em }
 
-	</style>
+        </style>
 </head>
 <body>
 
-<span></span><div>text<br>text<br>text</div><span></span>
+<span></span><div>text</div><span>text</span><div>text</div><span></span>
 
 </body>
 </html>
--- a/layout/reftests/bugs/424236-8.html
+++ b/layout/reftests/bugs/424236-8.html
@@ -1,23 +1,23 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
-	"http://www.w3.org/TR/html4/strict.dtd">
+        "http://www.w3.org/TR/html4/strict.dtd">
 <html lang="en-US">
 <head>
-	<title>reftest, bug 424236</title>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-	<meta http-equiv="Content-Style-Type" content="text/css">
-	<style type="text/css">
+        <title>reftest, bug 424236</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta http-equiv="Content-Style-Type" content="text/css">
+        <style type="text/css">
 
-	* { -moz-outline-offset: 3px; outline-offset: 3px; }
-	body > span { outline: 1px dotted black; }
-	body > span > span { display: block; width: 10em; }
-	body > span > span + span { display:inline; }
-	body > span > span + span + span { display: block; width: 15em; }
+        * { -moz-outline-offset: 3px; outline-offset: 3px; }
+        body > span { outline: 1px dotted black; }
+        body > span > span { display: block; width: 10em; }
+        body > span > span + span { display:inline; }
+        body > span > span + span + span { display: block; width: 15em; }
 
-	</style>
+        </style>
 </head>
 <body>
 
 <span><span>text</span><span>text</span><span>text</span></span>
 
 </body>
 </html>
--- a/layout/reftests/bugs/424236-9-ref.html
+++ b/layout/reftests/bugs/424236-9-ref.html
@@ -1,24 +1,25 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
-	"http://www.w3.org/TR/html4/strict.dtd">
+        "http://www.w3.org/TR/html4/strict.dtd">
 <html lang="en-US">
 <head>
-	<title>reftest, bug 424236</title>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-	<meta http-equiv="Content-Style-Type" content="text/css">
-	<style type="text/css">
+        <title>reftest, bug 424236</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta http-equiv="Content-Style-Type" content="text/css">
+        <style type="text/css">
 
-	body { font-size: 16px; }
-	* { -moz-outline-offset: -1px; outline-offset: -1px; }
-	body > span { outline: 1px dotted black; }
-	/* can't compare to border combined with negative margin because of
-	   margin collapsing */
-	body > div { display: block; outline: 1px dotted black; width: -moz-max-content; width: max-content; }
+        body { font-size: 16px; }
+        * { -moz-outline-offset: -1px; outline-offset: -1px; }
+        body > span { outline: 1px dotted black; }
+        /* can't compare to border combined with negative margin because of
+           margin collapsing */
+        body > div { display: block; outline: 1px dotted black; width: 10em; }
+        body > div + span + div { width: 5em; }
 
-	</style>
+        </style>
 </head>
 <body>
 
-<span></span><div>text<br>Some words that must be wider than 10em<br>text</div>
+<span></span><div>text</div><span>Some words that must be wider than 10em</span><div>text</div>
 
 </body>
 </html>
--- a/layout/reftests/bugs/424236-9.html
+++ b/layout/reftests/bugs/424236-9.html
@@ -1,24 +1,24 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
-	"http://www.w3.org/TR/html4/strict.dtd">
+        "http://www.w3.org/TR/html4/strict.dtd">
 <html lang="en-US">
 <head>
-	<title>reftest, bug 424236</title>
-	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-	<meta http-equiv="Content-Style-Type" content="text/css">
-	<style type="text/css">
+        <title>reftest, bug 424236</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta http-equiv="Content-Style-Type" content="text/css">
+        <style type="text/css">
 
-	body { font-size: 16px; }
-	* { -moz-outline-offset: -1px; outline-offset: -1px; }
-	body > span { outline: 1px dotted black; }
-	body > span > span { display: block; width: 10em; }
-	body > span > span + span { display:inline; }
-	body > span > span + span + span { display: block; width: 5em; }
+        body { font-size: 16px; }
+        * { -moz-outline-offset: -1px; outline-offset: -1px; }
+        body > span { outline: 1px dotted black; }
+        body > span > span { display: block; width: 10em; }
+        body > span > span + span { display:inline; }
+        body > span > span + span + span { display: block; width: 5em; }
 
-	</style>
+        </style>
 </head>
 <body>
 
 <span><span>text</span><span>Some words that must be wider than 10em</span><span>text</span></span>
 
 </body>
 </html>
--- a/layout/reftests/ib-split/float-inside-inline-between-blocks-1-ref.html
+++ b/layout/reftests/ib-split/float-inside-inline-between-blocks-1-ref.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
   <body>
     <div style="position: relative; left: 100px">
-      <div>
-        aaa
-      </div>
+      aaa
+    </div>
+    <span  style="position: relative; left: 100px">
       <span style="float: left">bbb</span>
-      <div>
-        aaa
-      </div>
+    </span>
+    <div style="position: relative; left: 100px">
+      aaa
     </div>
   </body>
 </html> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-1-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="width: 100px; border: 1px solid green;">
+      <div style="display: block; height: 20px; width: 80px; margin: 10px 0 10px 10px; border: 5px solid black"></div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-1a.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: ltr; width: 100px; border: 1px solid green;">
+      <span>
+        <span style="display: block; height: 20px; width: 80px; margin: 10px; border: 5px solid black">
+        </span>
+      </span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-1b.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: ltr; width: 100px; border: 1px solid green;">
+      <span style="direction: rtl">
+        <span style="display: block; height: 20px; width: 80px; margin: 10px; border: 5px solid black">
+        </span>
+      </span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-2-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="width: 100px; border: 1px solid green;">
+      <div style="display: block; height: 20px; width: 80px; margin: 10px 10px 10px 0; border: 5px solid black"></div>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-2a.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: rtl; width: 100px; border: 1px solid green;">
+      <span>
+        <span style="display: block; height: 20px; width: 80px; margin: 10px; border: 5px solid black">
+        </span>
+      </span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/ignored-margins-2b.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: rtl; width: 100px; border: 1px solid green;">
+      <span style="direction: ltr">
+        <span style="display: block; height: 20px; width: 80px; margin: 10px; border: 5px solid black">
+        </span>
+      </span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-1-noib-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+   <span class="notend">
+     <span>One</span><span>Two</span><span>Three</span>
+   </span>
+   <div>Four</div>
+   <span class="notstart notend"></span>
+   <div>Five</div>
+   <span class="notstart notend">
+     <span>Six</span>
+   </span>
+   <div>Seven</div>
+   <span class="notstart notend"></span>
+   <div>Eight</div>
+   <span class="notstart">
+     <span>Nine</span><span>Ten</span><span>Eleven</span>
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-10-noib-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   body > span > span { border: 3px solid cyan }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span class="notend">
+      <span>One</span><span>Two</span><span>Three</span>
+    </span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span class="notstart notend">
+      <span>Six</span>
+    </span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Eight</div>
+  <span class="notstart">
+    <span class="notstart">
+      <span>Nine</span><span>Ten</span>
+    </span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-11-noib-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   body > span > span { border: 3px solid cyan }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span class="notend">
+      <span>One</span><span>Two</span><span>Three</span>
+    </span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span class="notstart notend">
+      <span>Six</span>
+    </span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Eight</div>
+  <span class="notstart notend">
+    <span class="notstart">
+      <span>Nine</span>
+    </span>
+  </span>
+  <div>Ten</div>
+  <span class="notstart"></span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-12-noib-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid; border-right: none">One</div>
+    <div>Two</div>
+    <div style="display: inline; border: 2px; border-style: solid none"></div>
+    <div>Three</div>
+    <div style="display: inline; border: 2px solid; border-left: none"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-13-noib-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid; border-right: none"></div>
+    <div>One</div>
+    <div style="display: inline; border: 2px solid; border-left: none">TwoThree</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-14-noib-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid; border-right: none"></div>
+    <div>One</div>
+    <div style="display: inline; border: 2px solid; border-left: none">Two</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-15-noib-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid; border-right:none"></div>
+    <div>One</div>
+    <div style="display: inline; border: 2px solid; border-right:none; border-left: none">Two</div>
+    <div>Three</div>
+    <div style="display: inline; border: 2px solid; border-left: none"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-16-noib-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid; border-right: none">One</div>
+    <div>Two</div>
+    <div style="display: inline; border: 2px solid; border-left: none"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-2-noib-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span>One</span><span>Two</span><span>Three</span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span>Six</span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"></span>
+  <div>Eight</div>
+  <span class="notstart"></span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-3-noib-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span>One</span><span>Two</span><span>Three</span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span>Six</span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"></span>
+  <div>Eight</div>
+  <span class="notstart">
+    <span>Nine</span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-4-noib-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span>One</span><span>Two</span><span>Three</span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span>Six</span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"></span>
+  <div>Eight</div>
+  <span class="notstart">
+    <span>Nine</span><span>Ten</span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-5-noib-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span>One</span><span>Two</span><span>Three</span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span>Six</span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"></span>
+  <div>Eight</div>
+  <span class="notstart">
+    <span>Nine</span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-6-noib-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+   <span class="notend"></span>
+   <div>One</div>
+   <span class="notstart notend"></span>
+   <div>Two</div>
+   <span class="notstart"></span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-7-noib-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+   <span class="notend"></span>
+   <div>One</div>
+   <span class="notstart">
+     <span>Two</span>
+   </span>
+ </body>
+</html>
--- a/layout/reftests/ib-split/insert-into-split-inline-7-ref.html
+++ b/layout/reftests/ib-split/insert-into-split-inline-7-ref.html
@@ -3,12 +3,12 @@
  <head>
   <style>
    body > span { border: 3px solid blue }
   </style>
  </head>
  <body>
    <span
    ><div>One</div
-   ><span>Two</div
+   ><span>Two</span
   ></span>
  </body>
 </html>
--- a/layout/reftests/ib-split/insert-into-split-inline-7.html
+++ b/layout/reftests/ib-split/insert-into-split-inline-7.html
@@ -10,12 +10,12 @@
    }
   </script>
   <style>
    body > span { border: 3px solid blue }
   </style>
  </head>
  <body onload='doit()'>
   <span id="target"
-  ><span id="insertion">Two</div
+  ><span id="insertion">Two</span
  ></span>
  </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-8-noib-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span>One</span>
+  </span>
+  <div>Two</div>
+  <span class="notstart notend">
+    <span>Three</span>
+  </span>
+  <div>Four</div>
+  <span class="notstart">
+    <span>Five</span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-9-noib-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   body > span > span { border: 3px solid cyan }
+   .notstart { border-left: none; }
+   .notend { border-right: none; }
+  </style>
+ </head>
+ <body>
+  <span class="notend">
+    <span class="notend">
+      <span>One</span><span>Two</span><span>Three</span>
+    </span>
+  </span>
+  <div>Four</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Five</div>
+  <span class="notstart notend">
+    <span class="notstart notend">
+      <span>Six</span>
+    </span>
+  </span>
+  <div>Seven</div>
+  <span class="notstart notend"><span class="notstart notend"></span></span>
+  <div>Eight</div>
+  <span class="notstart">
+    <span class="notstart">
+      <span>Nine</span>
+    </span>
+  </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/percent-height-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="height: 100px; border: 10px solid black"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/percent-height-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+  <body style="height: 200px">
+    <span>
+      <span style="display: block; height: 50%; border: 10px solid black">
+      </span>
+    </span>
+  </body>
+</html>
--- a/layout/reftests/ib-split/reftest.list
+++ b/layout/reftests/ib-split/reftest.list
@@ -1,51 +1,82 @@
 == remove-split-inline-1.html remove-split-inline-1-ref.html
 == remove-from-split-inline-1.html remove-from-split-inline-1-ref.html
+== remove-from-split-inline-1-ref.html remove-from-split-inline-1-noib-ref.html
 == remove-from-split-inline-2.html remove-from-split-inline-2-ref.html
 == remove-from-split-inline-3.html remove-from-split-inline-3-ref.html
+== remove-from-split-inline-3-ref.html remove-from-split-inline-3-noib-ref.html
 == remove-from-split-inline-4.html remove-from-split-inline-4-ref.html
+== remove-from-split-inline-4-ref.html remove-from-split-inline-4-noib-ref.html
 == remove-from-split-inline-5.html remove-from-split-inline-5-ref.html
+== remove-from-split-inline-5-ref.html remove-from-split-inline-5-noib-ref.html
+== remove-from-split-inline-6.html remove-from-split-inline-6-ref.html
+== remove-from-split-inline-6-ref.html remove-from-split-inline-6-noib-ref.html
 == insert-into-split-inline-1a.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1b.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1c.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1d.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1e.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1f.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1g.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1h.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1i.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1j.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1k.html insert-into-split-inline-1-ref.html
 == insert-into-split-inline-1l.html insert-into-split-inline-1-ref.html
+== insert-into-split-inline-1-ref.html insert-into-split-inline-1-noib-ref.html
 == insert-into-split-inline-2a.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2b.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2c.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2d.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2e.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2f.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2g.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2h.html insert-into-split-inline-2-ref.html
 == insert-into-split-inline-2i.html insert-into-split-inline-2-ref.html
+== insert-into-split-inline-2-ref.html insert-into-split-inline-2-noib-ref.html
 == insert-into-split-inline-3.html insert-into-split-inline-3-ref.html
+== insert-into-split-inline-3-ref.html insert-into-split-inline-3-noib-ref.html
 == insert-into-split-inline-4.html insert-into-split-inline-4-ref.html
+== insert-into-split-inline-4-ref.html insert-into-split-inline-4-noib-ref.html
 == insert-into-split-inline-5.html insert-into-split-inline-5-ref.html
+== insert-into-split-inline-5-ref.html insert-into-split-inline-5-noib-ref.html
 == insert-into-split-inline-6.html insert-into-split-inline-6-ref.html
+== insert-into-split-inline-6-ref.html insert-into-split-inline-6-noib-ref.html
 == insert-into-split-inline-7.html insert-into-split-inline-7-ref.html
+== insert-into-split-inline-7-ref.html insert-into-split-inline-7-noib-ref.html
 == insert-into-split-inline-8a.html insert-into-split-inline-8-ref.html
 == insert-into-split-inline-8b.html insert-into-split-inline-8-ref.html
 == insert-into-split-inline-8c.html insert-into-split-inline-8-ref.html
+== insert-into-split-inline-8-ref.html insert-into-split-inline-8-noib-ref.html
 == insert-into-split-inline-9.html insert-into-split-inline-9-ref.html
+== insert-into-split-inline-9-ref.html insert-into-split-inline-9-noib-ref.html
 == insert-into-split-inline-10.html insert-into-split-inline-10-ref.html
+== insert-into-split-inline-10-ref.html insert-into-split-inline-10-noib-ref.html
 == insert-into-split-inline-11.html insert-into-split-inline-11-ref.html
+== insert-into-split-inline-11-ref.html insert-into-split-inline-11-noib-ref.html
 == insert-into-split-inline-12.html insert-into-split-inline-12-ref.html
+== insert-into-split-inline-12-ref.html insert-into-split-inline-12-noib-ref.html
 == insert-into-split-inline-13.html insert-into-split-inline-13-ref.html
+== insert-into-split-inline-13-ref.html insert-into-split-inline-13-noib-ref.html
 == insert-into-split-inline-14.html insert-into-split-inline-14-ref.html
+== insert-into-split-inline-14-ref.html insert-into-split-inline-14-noib-ref.html
 == insert-into-split-inline-15.html insert-into-split-inline-15-ref.html
+== insert-into-split-inline-15-ref.html insert-into-split-inline-15-noib-ref.html
 == insert-into-split-inline-16a.html insert-into-split-inline-16-ref.html
 == insert-into-split-inline-16b.html insert-into-split-inline-16-ref.html
+== insert-into-split-inline-16-ref.html insert-into-split-inline-16-noib-ref.html
 == float-inside-inline-between-blocks-1.html float-inside-inline-between-blocks-1-ref.html
 == table-pseudo-in-part3-1.html table-pseudo-in-part3-1-ref.html
 == emptyspan-1.html emptyspan-1-ref.html
 == emptyspan-2.html emptyspan-2-ref.html
 == emptyspan-3.html emptyspan-3-ref.html
 == emptyspan-4.html emptyspan-4-ref.html
 == split-inner-inline-1.html split-inner-inline-1-ref.html
+== split-inner-inline-2.html split-inner-inline-2-ref.html
+== whitespace-present-1a.html whitespace-present-1-ref.html
+== whitespace-present-1b.html whitespace-present-1-ref.html
+== percent-height-1.html percent-height-1-ref.html
+== ignored-margins-1a.html ignored-margins-1-ref.html
+== ignored-margins-1b.html ignored-margins-1-ref.html
+== ignored-margins-2a.html ignored-margins-2-ref.html
+== ignored-margins-2b.html ignored-margins-2-ref.html
+== trailing-inline-with-continuations-1.html trailing-inline-with-continuations-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-1-noib-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   #start { border-right: none; }
+   #two { border-left: none; }
+  </style>
+ </head>
+ <body>
+   <span id="start"></span>
+   <div>One</div>
+   <span id="two">
+    Two
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-3-noib-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   #one { border-right: none; }
+   #tail { border-left: none; }
+  </style>
+ </head>
+ <body>
+   <span id="one">
+    One
+   </span>
+   <div>Two</div>
+   <span id="tail"></span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-4-noib-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   #one { border-right: none; }
+   #four { border-left: none; }
+  </style>
+ </head>
+ <body>
+   <span id="one">
+     One
+     Two
+   </span>
+   <div>Three</div>
+   <span id="four">
+     Four
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-5-noib-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   #one { border-right: none; }
+   #three { border-left: none; }
+  </style>
+ </head>
+ <body>
+   <span id="one">
+     One
+   </span>
+   <div>Two</div>
+   <span id="three">
+     Three
+     Four
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-6-noib-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+   #one { border-right: none; }
+   #four { border-left: none; }
+  </style>
+ </head>
+ <body>
+   <span id="one">
+     One
+   </span>
+   <div>Two</div>
+   <div>Three</div>
+   <span id="four">
+     Four
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-6-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+   body > span { border: 3px solid blue }
+  </style>
+ </head>
+ <body>
+   <span>
+     One
+     <div>Two</div>
+     <div>Three</div>
+     Four
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/remove-from-split-inline-6.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <script>
+   function doit() {
+     var target = document.getElementById("target");
+     target.parentNode.removeChild(target);
+   }
+  </script>
+  <style>
+   body > span { border: 3px solid blue }
+  </style>
+ </head>
+ <body onload='doit()'>
+   <span>
+     One
+     <div>Two</div>
+     <span id="target">Five</span>
+     <div>Three</div>
+     Four
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/split-inner-inline-2-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span>First line
+      <span style="border: 5px solid blue; border-right: none"></span>
+    </span>
+    <div>Second line</div>
+    <span>
+      <span style="border: 5px solid blue; border-left: none"></span>
+      Third line, yes
+    </span>
+  </body>
+</html> 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/split-inner-inline-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <span>
+      First line
+      <span style="border: 5px solid blue">
+        <span style="display: block">
+          Second line
+        </span>
+      </span>
+      Third line, yes
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/trailing-inline-with-continuations-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+  <body style="width: 0">
+    <span style="border: 2px solid blue; border-right: none"></span>
+    <span style="display: block"></span>
+    <span style="border: 2px solid blue; border-left: none; border-right: none">
+      a b
+    </span>
+    <span style="display: block"></span>
+    <span style="border: 2px solid blue; border-left: none;">
+      c
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/trailing-inline-with-continuations-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <body style="width: 0">
+    <span style="border: 2px solid blue;">
+      <span style="display: block"></span>
+      a b
+      <script>document.body.offsetWidth</script>
+      <span style="display: block"></span>
+      c
+    </span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/whitespace-present-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+   <style>
+     body > span { border: 3px solid blue }
+     .notstart { border-left: none; }
+     .notend { border-right: none; }
+   </style>
+ </head>
+ <body>
+   <span class="notend"></span>
+   <div>One</div>
+   <span class="notstart notend"></span>
+   <div>Two</div>
+   <span class="notstart"></span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/whitespace-present-1a.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+    body > span { border: 3px solid blue }
+  </style>
+ </head>
+ <body>
+   <span>
+     <div>One</div>
+     <div>Two</div>
+   </span>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/whitespace-present-1b.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <style>
+    body > span { border: 3px solid blue }
+  </style>
+  <script>
+    function doIt() {
+      var t = document.createTextNode("   ");
+      var d = document.getElementById("d");
+      d.parentNode.insertBefore(t, d);
+    }
+  </script>
+ </head>
+ <body onload="doIt()">
+   <span>
+     <div>One</div><div id="d">Two</div>
+   </span>
+ </body>
+</html>