Bug 487449. Figure out the correct prevSibling for the new frames in ContentAppended, always pass the correct prevSibling to WipeContainingBlock, and make its aIsAppend argument exactly correspond to whether we'll call nsCSSFrameConstructor::AppendFrames. Change nsCSSFrameConstructor::AppendFrames() to take the prevSibling for the new frames. r+sr=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 14 Apr 2009 09:30:20 -0400
changeset 27310 11714b1d9aa4a9b51aa7202ff86d6740da39e684
parent 27309 0354cfc5132000262b74aedf32ffe537e8670c1f
child 27311 96e707a8f72a0648f733fae074c6a81c0badef42
push id6486
push userbzbarsky@mozilla.com
push dateTue, 14 Apr 2009 13:31:20 +0000
treeherdermozilla-central@96e707a8f72a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs487449
milestone1.9.2a1pre
Bug 487449. Figure out the correct prevSibling for the new frames in ContentAppended, always pass the correct prevSibling to WipeContainingBlock, and make its aIsAppend argument exactly correspond to whether we'll call nsCSSFrameConstructor::AppendFrames. Change nsCSSFrameConstructor::AppendFrames() to take the prevSibling for the new frames. r+sr=roc
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/reftests/ib-split/insert-into-split-inline-15-ref.html
layout/reftests/ib-split/insert-into-split-inline-15.html
layout/reftests/ib-split/insert-into-split-inline-16-ref.html
layout/reftests/ib-split/insert-into-split-inline-16a.html
layout/reftests/ib-split/insert-into-split-inline-16b.html
layout/reftests/ib-split/reftest.list
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5724,59 +5724,79 @@ AdjustAppendParentForAfterContent(nsPres
     }
   }
 
   *aAfterFrame = nsnull;
   return aParentFrame;
 }
 
 /**
- * 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.
+ * 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).
+ */
+static nsIFrame*
+FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
+{
+  nsFrameList childList(aParentFrame->GetFirstChild(nsnull));
+  if (aAfterFrame) {
+    NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
+    return childList.GetPrevSiblingFor(aAfterFrame);
+  }
+
+  return childList.LastChild();
+}
+
+/**
+ * This function will get the next sibling for a frame insert operation given
+ * the parent and previous sibling.  aPrevSibling may be null.
+ */
+static nsIFrame*
+GetInsertNextSibling(nsIFrame* aParentFrame, nsIFrame* aPrevSibling)
+{
+  if (aPrevSibling) {
+    return aPrevSibling->GetNextSibling();
+  }
+
+  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.
  */
 nsresult
 nsCSSFrameConstructor::AppendFrames(nsFrameConstructorState&       aState,
-                                    nsIContent*                    aContainer,
                                     nsIFrame*                      aParentFrame,
                                     nsFrameItems&                  aFrameList,
-                                    nsIFrame*                      aAfterFrame)
-{
-#ifdef DEBUG
-  nsIFrame* debugAfterFrame;
-  nsIFrame* debugNewParent =
-    ::AdjustAppendParentForAfterContent(aState.mPresContext, aContainer,
-                                        aParentFrame, &debugAfterFrame);
-  NS_ASSERTION(debugNewParent == aParentFrame, "Incorrect parent");
-  NS_ASSERTION(debugAfterFrame == aAfterFrame, "Incorrect after frame");
-#endif
-
-  nsFrameManager* frameManager = aState.mFrameManager;
-  if (aAfterFrame) {
-    NS_ASSERTION(!IsFrameSpecial(aParentFrame) ||
-                 IsInlineFrame(aParentFrame) ||
-                 !IsInlineOutside(aAfterFrame),
-                 "Shouldn't have inline :after content on the block in an "
-                 "{ib} split");
-    nsFrameList frames(aParentFrame->GetFirstChild(nsnull));
-
-    // Insert the frames before the :after pseudo-element.
-    return frameManager->InsertFrames(aParentFrame, nsnull,
-                                      frames.GetPrevSiblingFor(aAfterFrame),
-                                      aFrameList.childList);
-  }
-
-  if (IsFrameSpecial(aParentFrame) &&
+                                    nsIFrame*                      aPrevSibling)
+{
+  NS_PRECONDITION(!IsFrameSpecial(aParentFrame) ||
+                  !GetSpecialSibling(aParentFrame) ||
+                  !GetSpecialSibling(aParentFrame)->GetFirstChild(nsnull),
+                  "aParentFrame has a special sibling with kids?");
+  NS_PRECONDITION(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame,
+                  "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)) {
-    NS_ASSERTION(!aParentFrame->GetNextContinuation() ||
-                 !aParentFrame->GetNextContinuation()->GetFirstChild(nsnull),
-                 "Shouldn't happen");
-    
     // We want to put some of the frames into the following inline frame.
     nsIFrame* lastBlock = FindLastBlock(aFrameList.childList);
     nsIFrame* firstTrailingInline;
     if (lastBlock) {
       firstTrailingInline = lastBlock->GetNextSibling();
       lastBlock->SetNextSibling(nsnull);
       aFrameList.lastChild = lastBlock;
     } else {
@@ -5799,18 +5819,19 @@ nsCSSFrameConstructor::AppendFrames(nsFr
                              aParentFrame, &targetState);
   }
     
   if (!aFrameList.childList) {
     // It all got eaten by the special inline
     return NS_OK;
   }
   
-  return frameManager->AppendFrames(aParentFrame, nsnull,
-                                    aFrameList.childList);
+  // Insert the frames after out aPrevSibling
+  return aState.mFrameManager->InsertFrames(aParentFrame, nsnull, aPrevSibling,
+                                            aFrameList.childList);
 }
 
 #define UNSET_DISPLAY 255
 
 // This gets called to see if the frames corresponding to aSiblingDisplay and aDisplay
 // should be siblings in the frame tree. Although (1) rows and cols, (2) row groups 
 // and col groups, (3) row groups and captions, (4) legends and content inside fieldsets, (5) popups and other kids of the menu
 // are siblings from a content perspective, they are not considered siblings in the 
@@ -6155,17 +6176,18 @@ nsCSSFrameConstructor::ContentAppended(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);
   }
 
-  // Get continuation that parents the last child
+  // Get continuation that parents the last child.  This MUST be done
+  // before the AdjustAppendParentForAfterContent call.
   parentFrame = nsLayoutUtils::GetLastContinuationWithChild(parentFrame);
 
   nsIAtom* frameType = parentFrame->GetType();
   // We should never get here with fieldsets, since they have multiple
   // insertion points.
   NS_ASSERTION(frameType != nsGkAtoms::fieldSetFrame,
                "Unexpected parent");
 
@@ -6200,23 +6222,25 @@ nsCSSFrameConstructor::ContentAppended(n
   FrameConstructionItemList items;
   for (PRUint32 i = aNewIndexInContainer, count = aContainer->GetChildCount();
        i < count;
        ++i) {
     AddFrameConstructionItems(state, aContainer->GetChildAt(i), parentFrame,
                               items);
   }
 
+  nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
+
   // 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)) {
+                          PR_TRUE, prevSibling)) {
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   nsFrameItems frameItems;
   ConstructFramesFromItemList(state, items, parentFrame, frameItems);
 
@@ -6249,36 +6273,31 @@ nsCSSFrameConstructor::ContentAppended(n
     // first-line frame. Look at them and see...
     AppendFirstLineFrames(state, containingBlock->GetContent(),
                           containingBlock, frameItems); 
   }
 
   nsresult result = NS_OK;
 
   // Notify the parent frame passing it the list of new frames
-  if (NS_SUCCEEDED(result) &&
-      (frameItems.childList || captionItems.childList)) {
-    // Append the flowed frames to the principal child list, tables need special treatment
-    if (nsGkAtoms::tableFrame == frameType) {
-      if (captionItems.childList) { // append the caption to the outer table
-        nsIFrame* outerTable = parentFrame->GetParent();
-        if (outerTable) { 
-          state.mFrameManager->AppendFrames(outerTable,
-                                            nsGkAtoms::captionList,
-                                            captionItems.childList);
-        }
-      }
-      if (frameItems.childList) { // append children of the inner table
-        AppendFrames(state, aContainer, parentFrame, frameItems,
-                     parentAfterFrame);
-      }
-    }
-    else {
-      AppendFrames(state, aContainer, parentFrame, frameItems,
-                   parentAfterFrame);
+  if (NS_SUCCEEDED(result)) {
+    // Append the flowed frames to the principal child list; captions
+    // need special treatment
+    if (captionItems.childList) { // append the caption to the outer table
+      NS_ASSERTION(nsGkAtoms::tableFrame == frameType, "how did that happen?");
+      nsIFrame* outerTable = parentFrame->GetParent();
+      if (outerTable) {
+        state.mFrameManager->AppendFrames(outerTable,
+                                          nsGkAtoms::captionList,
+                                          captionItems.childList);
+      }
+    }
+
+    if (frameItems.childList) { // append the in-flow kids
+      AppendFrames(state, parentFrame, frameItems, prevSibling);
     }
   }
 
   // Recover first-letter frames
   if (haveFirstLetterStyle) {
     RecoverLetterFrames(containingBlock);
   }
 
@@ -6484,17 +6503,16 @@ nsCSSFrameConstructor::ContentInserted(n
     NS_WARNING("Someone passed native anonymous content directly into frame "
                "construction.  Stop doing that!");
   }
 #endif
   
   nsIFrame* prevSibling = FindPreviousSibling(first, iter);
 
   PRBool    isAppend = PR_FALSE;
-  nsIFrame* appendAfterFrame;  // This is only looked at when isAppend is true
 
   // Now, find the geometric parent so that we can handle
   // continuations properly. Use the prev sibling if we have it;
   // otherwise use the next sibling.
   if (prevSibling) {
     parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
   }
   else {
@@ -6508,26 +6526,29 @@ nsCSSFrameConstructor::ContentInserted(n
       // No previous or next sibling, so treat this like an appended frame.
       isAppend = PR_TRUE;
       if (IsFrameSpecial(parentFrame)) {
         // 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);
       }
-      // Get continuation that parents the last child
+      // Get continuation that parents the last child.  This MUST be done
+      // before the AdjustAppendParentForAfterContent call.
       parentFrame = nsLayoutUtils::GetLastContinuationWithChild(parentFrame);
       // Deal with fieldsets
       parentFrame = ::GetAdjustedParentFrame(parentFrame,
                                              parentFrame->GetType(),
                                              aChild);
+      nsIFrame* appendAfterFrame;
       parentFrame =
         ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
                                             container, parentFrame,
                                             &appendAfterFrame);
+      prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
     }
   }
 
   if (parentFrame->GetType() == nsGkAtoms::frameSetFrame &&
       IsSpecialFramesetChild(aChild)) {
     // Just reframe the parent, since framesets are weird like that.
     return RecreateFramesForContent(parentFrame->GetContent());
   }
@@ -6611,43 +6632,31 @@ nsCSSFrameConstructor::ContentInserted(n
     nsIFrame* firstChild = parentFrame->GetFirstChild(nsnull);
 
     if (firstChild &&
         nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
                                              nsCSSPseudoElements::before)) {
       // Insert the new frames after the last continuation of the :before
       prevSibling = firstChild->GetTailContinuation();
       parentFrame = prevSibling->GetParent();
-      // We perhaps could leave this true and take the AppendFrames path
-      // below, but we'd have to update appendAfterFrame and it seems safer
-      // to force all insert-after-:before cases to take these to take the
-      // InsertFrames path.
-      // It's safe to skip AppendFrames here, even in append cases, because
-      // that only does two things: handles insertion before the :after, and
-      // handles moving trailing inline frames over to the last trailing
-      // inline.  For the former, we already have a prevSibling, so we'll put
-      // the new frames right after that prevSibling, so before the :after.
-      // For the latter, WipeContainingBlock will force a reframe of our
-      // container in the cases when the reparenting in AppendFrames would have
-      // been needed (in this case, when the :before frame is the block child
-      // at the end of the anonymous block of an {ib} split).
-      isAppend = PR_FALSE;
+      // Don't change isAppend here; we'll can call AppendFrames as needed, and
+      // the change to our prevSibling doesn't affect that.
     }
   }
 
   FrameConstructionItemList items;
   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, 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;
@@ -6663,21 +6672,22 @@ nsCSSFrameConstructor::ContentInserted(n
       frameItems = nsFrameItems();
     }
   }
 
   // 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).
+  // This can affect our prevSibling and isAppend, but should not have any
+  // effect on the WipeContainingBlock above, since this should only happen
+  // when neither parent is a special frame and should not affect whitespace
+  // handling inside table-related frames (and in fact, can only happen when
+  // one of the parents is an outer table and one is an inner table or when the
+  // parent is a fieldset or fieldset content frame).
   // 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();
@@ -6687,23 +6697,24 @@ nsCSSFrameConstructor::ContentInserted(n
                   frame2->GetType() == nsGkAtoms::tableOuterFrame) ||
                  (frame1->GetType() == nsGkAtoms::tableOuterFrame &&
                   frame2->GetType() == nsGkAtoms::tableFrame) ||
                  frame1->GetType() == nsGkAtoms::fieldSetFrame ||
                  (frame1->GetParent() &&
                   frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
                  "Unexpected frame types");
 #endif
-    prevSibling = nsnull;
     isAppend = PR_TRUE;
+    nsIFrame* appendAfterFrame;
     parentFrame =
       ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
                                           container,
                                           frameItems.childList->GetParent(),
                                           &appendAfterFrame);
+    prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
   }
 
   if (haveFirstLineStyle && parentFrame == containingBlock) {
     // It's possible that the new frame goes into a first-line
     // frame. Look at it and see...
     if (isAppend) {
       // Use append logic when appending
       AppendFirstLineFrames(state, containingBlock->GetContent(),
@@ -6719,18 +6730,17 @@ nsCSSFrameConstructor::ContentInserted(n
     }
   }
       
   nsIFrame* const newFrame = frameItems.childList;
   if (NS_SUCCEEDED(rv) && newFrame) {
     NS_ASSERTION(!captionItems.childList, "leaking caption frames");
     // Notify the parent frame
     if (isAppend) {
-      AppendFrames(state, container, parentFrame, frameItems,
-                   appendAfterFrame);
+      AppendFrames(state, parentFrame, frameItems, prevSibling);
     } else {
       state.mFrameManager->InsertFrames(parentFrame,
                                         nsnull, prevSibling, newFrame);
     }
   }
   else {
     // we might have a caption treat it here
     nsIFrame* newCaptionFrame = captionItems.childList;
@@ -10848,53 +10858,55 @@ nsCSSFrameConstructor::WipeContainingBlo
     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 (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(
-          GetIBSplitSpecialPrevSiblingForAnonymousBlock(floatContainer));
-        if (!floatContainer) {
-          break;
-        }
-        if (!IsFrameSpecial(floatContainer)) {
-          return PR_FALSE;
-        }
-      } while (1);
-    }
-    
-    if (aPrevSibling && !aPrevSibling->GetNextSibling()) {
-      // This is an append that won't go through AppendFrames.  We can bail out
-      // if the last frame we're appending is not inline.
+    if (aPrevSibling || !aItems.IsStartInline()) {
+      // Not messing up the beginning.  Now let's look at the end.
+      nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling);
+      if (nextSibling) {
+        // Can't possibly screw up the end; bail out
+        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(
+            GetIBSplitSpecialPrevSiblingForAnonymousBlock(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()) {
         return PR_FALSE;
       }
-    } else {
-      // We can bail out if we're not inserting at the beginning or if
-      // the first frame we're inserting is not inline.
-      if (aPrevSibling || !aItems.IsStartInline()) {
-        return PR_FALSE;
-      }
     }
   }
 
   // If we don't have a containing block, start with aFrame and look for one.
   if (!aContainingBlock) {
     aContainingBlock = aFrame;
   }
   
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -390,24 +390,25 @@ 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.
+  // 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
+  // aFrameList is being added at the beginning of the child list.
   nsresult AppendFrames(nsFrameConstructorState&       aState,
-                        nsIContent*                    aContainer,
                         nsIFrame*                      aParentFrame,
                         nsFrameItems&                  aFrameList,
-                        nsIFrame*                      aAfterFrame);
+                        nsIFrame*                      aPrevSibling);
 
   // BEGIN TABLE SECTION
   /**
    * Construct an outer table frame.  This is the FrameConstructionData
    * callback used for the job.
    */
   nsresult ConstructTable(nsFrameConstructorState& aState,
                           FrameConstructionItem&   aItem,
@@ -1338,21 +1339,22 @@ private:
    */
   nsresult ConstructFramesFromItemList(nsFrameConstructorState& aState,
                                        FrameConstructionItemList& aItems,
                                        nsIFrame* aParentFrame,
                                        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.
+  // (and therefore need an {ib} split).  aPrevSibling must be correct, even in
+  // aIsAppend cases.  Passing aIsAppend false even when an append is happening
+  // is ok in terms of correctness, but can lead to unnecessary reframing.  If
+  // aIsAppend is true, then the caller MUST call
+  // nsCSSFrameConstructor::AppendFrames (as opposed to
+  // nsFrameManager::InsertFrames directly) to add the new frames.
   // @return PR_TRUE if we reconstructed the containing block, PR_FALSE
   // otherwise
   PRBool WipeContainingBlock(nsFrameConstructorState& aState,
                              nsIFrame*                aContainingBlock,
                              nsIFrame*                aFrame,
                              const FrameConstructionItemList& aItems,
                              PRBool                   aIsAppend,
                              nsIFrame*                aPrevSibling);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-15-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid"><div>One</div>Two<div>Three</div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-15.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #i { display: inline; border: 2px solid; }
+      #i::after { display: block; content: "Three"; }
+    </style>
+  </head>
+  <body onload="doTest()">
+    <div id="i"><script>document.body.offsetWidth</script><div>One</div>Two</div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-16-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="display: inline; border: 2px solid">One<div>Two</div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-16a.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #i { border: 2px solid; }
+      #i::after { display: block; content: "Two"; }
+    </style>
+    <script>
+      function doTest() {
+        var i = document.getElementById("i");
+        i.insertBefore(document.createTextNode("One"), i.firstChild);
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span id="i"><span style="display: none"></span></span>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/ib-split/insert-into-split-inline-16b.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      #i { border: 2px solid; }
+      #i::after { display: block; content: "Two"; }
+    </style>
+    <script>
+      function doTest() {
+        var i = document.getElementById("i");
+        i.appendChild(document.createTextNode("One"));
+      }
+    </script>
+  </head>
+  <body onload="doTest()">
+    <span id="i"></span>
+  </body>
+</html>
--- a/layout/reftests/ib-split/reftest.list
+++ b/layout/reftests/ib-split/reftest.list
@@ -34,11 +34,14 @@
 == 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-9.html insert-into-split-inline-9-ref.html
 == insert-into-split-inline-10.html insert-into-split-inline-10-ref.html
 == insert-into-split-inline-11.html insert-into-split-inline-11-ref.html
 == insert-into-split-inline-12.html insert-into-split-inline-12-ref.html
 == insert-into-split-inline-13.html insert-into-split-inline-13-ref.html
 == insert-into-split-inline-14.html insert-into-split-inline-14-ref.html
+== insert-into-split-inline-15.html insert-into-split-inline-15-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
 == 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
 == split-inner-inline-1.html split-inner-inline-1-ref.html