Bug 502937. Part 4. Implement ContentRangeInserted to create frames for a range of nodes that aren't at the end of a childlist. r=bzbarsky sr=roc
authorTimothy Nikkel <tnikkel@gmail.com>
Thu, 21 Jan 2010 16:06:16 -0600
changeset 40939 7a19bc84e5729cf2eb9d33d5524bcd2c822eae3b
parent 40938 f80de1a7c60466ef95cb33152d4d6d0a4df72905
child 40940 699e6f9a366031743d1fd6c1ca4085113a28fa8b
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, roc
bugs502937
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 502937. Part 4. Implement ContentRangeInserted to create frames for a range of nodes that aren't at the end of a childlist. r=bzbarsky sr=roc
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5733,21 +5733,21 @@ nsCSSFrameConstructor::AppendFrames(nsFr
   
   // Insert the frames after our aPrevSibling
   return aState.mFrameManager->InsertFrames(aParentFrame, nsnull, aPrevSibling,
                                             aFrameList);
 }
 
 #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 
-// frame tree.
+// This gets called to see if the frames corresponding to aSibling and aContent
+// 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 frame tree.
 PRBool
 nsCSSFrameConstructor::IsValidSibling(nsIFrame*              aSibling,
                                       nsIContent*            aContent,
                                       PRUint8&               aDisplay)
 {
   nsIFrame* parentFrame = aSibling->GetParent();
   nsIAtom* parentType = nsnull;
   nsIAtom* grandparentType = nsnull;
@@ -5875,52 +5875,52 @@ nsCSSFrameConstructor::FindFrameForConte
     sibling = nsnull;
   }
 
   return sibling;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::FindPreviousSibling(const ChildIterator& aFirst,
-                                           ChildIterator aIter)
+                                           ChildIterator aIter,
+                                           PRUint8& aTargetContentDisplay)
 {
   nsIContent* child = *aIter;
 
-  PRUint8 childDisplay = UNSET_DISPLAY;
   // Note: not all content objects are associated with a frame (e.g., if it's
   // `display: none') so keep looking until we find a previous frame
   while (aIter-- != aFirst) {
     nsIFrame* prevSibling =
-      FindFrameForContentSibling(*aIter, child, childDisplay, PR_TRUE);
+      FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, PR_TRUE);
 
     if (prevSibling) {
       // Found a previous sibling, we're done!
       return prevSibling;
     }
   }
 
   return nsnull;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::FindNextSibling(ChildIterator aIter,
-                                       const ChildIterator& aLast)
+                                       const ChildIterator& aLast,
+                                       PRUint8& aTargetContentDisplay)
 {
   if (aIter == aLast) {
     // XXXbz Can happen when XBL lies to us about insertion points.  This check
     // might be able to go away once bug 474324 is fixed.
     return nsnull;
   }
 
   nsIContent* child = *aIter;
-  PRUint8 childDisplay = UNSET_DISPLAY;
 
   while (++aIter != aLast) {
     nsIFrame* nextSibling =
-      FindFrameForContentSibling(*aIter, child, childDisplay, PR_FALSE);
+      FindFrameForContentSibling(*aIter, child, aTargetContentDisplay, PR_FALSE);
 
     if (nextSibling) {
       // We found a next sibling, we're done!
       return nextSibling;
     }
   }
 
   return nsnull;
@@ -5948,62 +5948,99 @@ GetAdjustedParentFrame(nsIFrame*       a
   return (newParent) ? newParent : aParentFrame;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::GetInsertionPrevSibling(nsIFrame*& aParentFrame,
                                                nsIContent* aContainer,
                                                nsIContent* aChild,
                                                PRInt32 aIndexInContainer,
-                                               PRBool* aIsAppend)
-{
+                                               PRBool* aIsAppend,
+                                               PRBool* aIsRangeInsertSafe,
+                                               PRInt32 aStartSkipIndexInContainer,
+                                               nsIContent* aStartSkipChild,
+                                               PRInt32 aEndSkipIndexInContainer,
+                                               nsIContent *aEndSkipChild)
+{
+  NS_PRECONDITION((aStartSkipIndexInContainer >= 0) == !!aStartSkipChild,
+                  "aStartSkipIndexInContainer >= 0 iff aStartSkipChild");
+  NS_PRECONDITION((aEndSkipIndexInContainer >= 0) == !!aEndSkipChild,
+                  "aEndSkipIndexInContainer >= 0 iff aEndSkipChild");
+  NS_PRECONDITION((aStartSkipIndexInContainer >= 0 &&
+                   aEndSkipIndexInContainer >= 0 &&
+                   aEndSkipIndexInContainer > aStartSkipIndexInContainer) ||
+                  (aStartSkipIndexInContainer == -1 &&
+                   aEndSkipIndexInContainer == -1),
+                  "aStartSkipIndexInContainer and aEndSkipIndexInContainer "
+                  "should both be valid and in correct order or both invalid");
+
   *aIsAppend = PR_FALSE;
 
   // Find the frame that precedes the insertion point. Walk backwards
   // from the parent frame to get the parent content, because if an
   // XBL insertion point is involved, we'll need to use _that_ to find
   // the preceding frame.
 
   NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
   nsIContent* container = aParentFrame->GetContent();
 
   ChildIterator first, last;
   ChildIterator::Init(container, &first, &last);
   ChildIterator iter(first);
+  PRBool xblCase = PR_FALSE;
   if (iter.XBLInvolved() || container != aContainer) {
-    iter.seek(aChild);
+    xblCase = PR_TRUE;
+    if (aStartSkipChild) {
+      iter.seek(aStartSkipChild);
+    } else {
+      iter.seek(aChild);
+    }
     // Don't touch our aIndexInContainer, though it's almost certainly bogus in
     // this case.  If someone wants to use an index below, they should make
     // sure to use the right index (aIndexInContainer vs iter.position()) with
     // the right parent node.
   } else if (aIndexInContainer != -1) {
     // Do things the fast way if we can.  The check for -1 is because editor is
     // severely broken and calls us directly for native anonymous nodes that it
     // creates.
-    iter.seek(aIndexInContainer);
-    NS_ASSERTION(*iter == aChild, "Someone screwed up the indexing");
+    if (aStartSkipIndexInContainer >= 0) {
+      iter.seek(aStartSkipIndexInContainer);
+      NS_ASSERTION(*iter == aStartSkipChild, "Someone screwed up the indexing");
+    } else {
+      iter.seek(aIndexInContainer);
+      NS_ASSERTION(*iter == aChild, "Someone screwed up the indexing");
+    }
   }
 #ifdef DEBUG
   else {
     NS_WARNING("Someone passed native anonymous content directly into frame "
                "construction.  Stop doing that!");
   }
 #endif
 
-  nsIFrame* prevSibling = FindPreviousSibling(first, iter);
+  PRUint8 childDisplay = UNSET_DISPLAY;
+  nsIFrame* prevSibling = FindPreviousSibling(first, iter, childDisplay);
 
   // 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) {
     aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
   }
   else {
     // If there is no previous sibling, then find the frame that follows
-    nsIFrame* nextSibling = FindNextSibling(iter, last);
+    if (aEndSkipChild) {
+      if (xblCase) {
+        iter.seek(aEndSkipChild);
+        iter--;
+      } else {
+        iter.seek(aEndSkipIndexInContainer-1);
+      }
+    }
+    nsIFrame* nextSibling = FindNextSibling(iter, last, childDisplay);
 
     if (nextSibling) {
       aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
     }
     else {
       // No previous or next sibling, so treat this like an appended frame.
       *aIsAppend = PR_TRUE;
       if (IsFrameSpecial(aParentFrame)) {
@@ -6023,16 +6060,17 @@ nsCSSFrameConstructor::GetInsertionPrevS
       aParentFrame =
         ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
                                             container, aParentFrame,
                                             &appendAfterFrame);
       prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
     }
   }
 
+  *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
   return prevSibling;
 }
 
 static PRBool
 IsSpecialFramesetChild(nsIContent* aContent)
 {
   // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
   return aContent->IsHTML() &&
@@ -6041,26 +6079,31 @@ IsSpecialFramesetChild(nsIContent* aCont
 }
 
 static void
 InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node);
 
 #ifdef MOZ_XUL
 
 static
+PRBool
+IsXULListBox(nsIContent* aContainer)
+{
+  return (aContainer->IsXUL() && aContainer->Tag() == nsGkAtoms::listbox);
+}
+
+static
 nsListBoxBodyFrame*
 MaybeGetListBoxBodyFrame(nsIContent* aContainer, nsIContent* aChild)
 {
   if (!aContainer)
     return nsnull;
 
-  if (aContainer->IsXUL() &&
-      aChild->IsXUL() &&
-      aContainer->Tag() == nsGkAtoms::listbox &&
-      aChild->Tag() == nsGkAtoms::listitem) {
+  if (IsXULListBox(aContainer) &&
+      aChild->IsXUL() && aChild->Tag() == nsGkAtoms::listitem) {
     nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aContainer);
     nsCOMPtr<nsIBoxObject> boxObject;
     xulElement->GetBoxObject(getter_AddRefs(boxObject));
     nsCOMPtr<nsPIListBoxObject> listBoxObject = do_QueryInterface(boxObject);
     if (listBoxObject) {
       return listBoxObject->GetListBoxBody(PR_FALSE);
     }
   }
@@ -6229,21 +6272,19 @@ nsCSSFrameConstructor::CreateNeededFrame
       if (!inRun) {
         inRun = PR_TRUE;
         startOfRun = i;
         firstChildInRun = child;
       }
     } else {
       if (inRun) {
         inRun = PR_FALSE;
-        // generate a ContentInserted for each node in [startOfRun,i)
-        for (PRUint32 j = startOfRun; j < i; j++) {
-          ContentInserted(aContent, aContent->GetChildAt(j), j, nsnull,
-                          PR_FALSE);
-        }
+        // generate a ContentRangeInserted for [startOfRun,i)
+        ContentRangeInserted(aContent, firstChildInRun, startOfRun, i,
+                             nsnull, PR_FALSE);
       }
     }
   }
   if (inRun) {
     ContentAppended(aContent, startOfRun, PR_FALSE);
   }
 
   // Now descend.
@@ -6270,16 +6311,135 @@ void nsCSSFrameConstructor::CreateNeeded
     "root content should not have frame created lazily");
   if (rootContent && rootContent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
     BeginUpdate();
     CreateNeededFrames(rootContent);
     EndUpdate();
   }
 }
 
+void
+nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
+                                                    PRInt32 aStartIndexInContainer,
+                                                    PRInt32 aEndIndexInContainer,
+                                                    PRBool aAllowLazyConstruction)
+{
+  for (PRUint32 i = aStartIndexInContainer;
+       i < (PRUint32)aEndIndexInContainer;
+       i++) {
+    nsIContent* child = aContainer->GetChildAt(i);
+    if ((child->GetPrimaryFrame() ||
+         mPresShell->FrameManager()->GetUndisplayedContent(child))
+#ifdef MOZ_XUL
+        //  Except listboxes suck, so do NOT skip anything here if
+        //  we plan to notify a listbox.
+        && !MaybeGetListBoxBodyFrame(aContainer, child)
+#endif
+        ) {
+      // Already have a frame or undisplayed entry for this content; a
+      // previous ContentInserted in this loop must have reconstructed
+      // its insertion parent.  Skip it.
+      continue;
+    }
+    // Call ContentInserted with this index.
+    ContentInserted(aContainer, child, i, mTempFrameTreeState,
+                    aAllowLazyConstruction);
+  }
+}
+
+nsIFrame*
+nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
+                                              nsIFrame* aParentFrame,
+                                              PRInt32 aStartIndexInContainer,
+                                              PRInt32 aEndIndexInContainer,
+                                              PRBool aAllowLazyConstruction)
+{
+  // See if we have an XBL insertion point. If so, then that's our
+  // real parent frame; if not, then the frame hasn't been built yet
+  // and we just bail.
+  nsIFrame* insertionPoint;
+  PRBool multiple = PR_FALSE;
+  GetInsertionPoint(aParentFrame, nsnull, &insertionPoint, &multiple);
+  if (! insertionPoint)
+    return nsnull; // Don't build the frames.
+ 
+  PRBool hasInsertion = PR_FALSE;
+  if (!multiple) {
+    nsIDocument* document = nsnull; 
+    nsIContent *firstAppendedChild =
+      aContainer->GetChildAt(aStartIndexInContainer);
+    if (firstAppendedChild) {
+      document = firstAppendedChild->GetDocument();
+    }
+    if (document &&
+        document->BindingManager()->GetInsertionParent(firstAppendedChild)) {
+      hasInsertion = PR_TRUE;
+    }
+  }
+
+  if (multiple || hasInsertion) {
+    // We have an insertion point.  There are some additional tests we need to do
+    // in order to ensure that an append is a safe operation.
+    PRUint32 childCount = 0;
+
+    if (!multiple) {
+      // We may need to make multiple ContentInserted calls instead.  A
+      // reasonable heuristic to employ (in order to maintain good performance)
+      // is to find out if the insertion point's content node contains any
+      // explicit children.  If it does not, then it is highly likely that 
+      // an append is occurring.  (Note it is not definite, and there are insane
+      // cases we will not deal with by employing this heuristic, but it beats
+      // always falling back to multiple ContentInserted calls).
+      //
+      // In the multiple insertion point case, we know we're going to need to do
+      // multiple ContentInserted calls anyway.
+      childCount = insertionPoint->GetContent()->GetChildCount();
+    }
+ 
+    // If we have multiple insertion points or if we have an insertion point
+    // and the operation is not a true append or if the insertion point already
+    // has explicit children, then we must fall back.
+    if (multiple || aEndIndexInContainer != -1 || childCount > 0) {
+      // Now comes the fun part.  For each appended child, make a
+      // ContentInserted call as if it had just gotten inserted at the index
+      // it's at in aContainer and let ContentInserted handle the mess.  If our
+      // insertion point is non-XBL that's the correct index, and otherwise
+      // ContentInserted will ignore the passed-in index.
+      PRUint32 endIndex = aEndIndexInContainer == -1 ?
+        aContainer->GetChildCount() : aEndIndexInContainer;
+      IssueSingleInsertNofications(aContainer, aStartIndexInContainer,
+                                   endIndex, aAllowLazyConstruction);
+      return nsnull;
+    }
+  }
+
+  return insertionPoint;
+}
+
+PRBool
+nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIContent* aContainer,
+                                                nsIFrame* aParentFrame,
+                                                PRUint32 aStartIndexInContainer,
+                                                PRUint32 aEndIndexInContainer)
+{
+  if (aParentFrame->GetType() == nsGkAtoms::frameSetFrame) {
+    // Check whether we have any kids we care about.
+    for (PRUint32 i = aStartIndexInContainer;
+         i < aEndIndexInContainer;
+         ++i) {
+      if (IsSpecialFramesetChild(aContainer->GetChildAt(i))) {
+        // Just reframe the parent, since framesets are weird like that.
+        RecreateFramesForContent(aParentFrame->GetContent(), PR_FALSE);
+        return PR_TRUE;
+      }
+    }
+  }
+  return PR_FALSE;
+}
+
 nsresult
 nsCSSFrameConstructor::ContentAppended(nsIContent*     aContainer,
                                        PRInt32         aNewIndexInContainer,
                                        PRBool          aAllowLazyConstruction)
 {
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
   NS_PRECONDITION(mUpdateCount != 0,
                   "Should be in an update while creating frames");
@@ -6317,108 +6477,32 @@ nsCSSFrameConstructor::ContentAppended(n
     return NS_OK;
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTAPPEND, aContainer, nsnull,
                            aNewIndexInContainer)) {
     return NS_OK;
   }
 
-  // See if we have an XBL insertion point. If so, then that's our
-  // real parent frame; if not, then the frame hasn't been built yet
-  // and we just bail.
-  //
-  nsIFrame* insertionPoint;
-  PRBool multiple = PR_FALSE;
-  GetInsertionPoint(parentFrame, nsnull, &insertionPoint, &multiple);
-  if (! insertionPoint)
-    return NS_OK; // Don't build the frames.
-
-  PRBool hasInsertion = PR_FALSE;
-  if (!multiple) {
-    nsIDocument* document = nsnull; 
-    nsIContent *firstAppendedChild =
-      aContainer->GetChildAt(aNewIndexInContainer);
-    if (firstAppendedChild) {
-      document = firstAppendedChild->GetDocument();
-    }
-    if (document &&
-        document->BindingManager()->GetInsertionParent(firstAppendedChild)) {
-      hasInsertion = PR_TRUE;
-    }
-  }
-  
-  if (multiple || hasInsertion) {
-    // We have an insertion point.  There are some additional tests we need to do
-    // in order to ensure that an append is a safe operation.
-    PRUint32 childCount = 0;
-      
-    if (!multiple) {
-      // We may need to make multiple ContentInserted calls instead.  A
-      // reasonable heuristic to employ (in order to maintain good performance)
-      // is to find out if the insertion point's content node contains any
-      // explicit children.  If it does not, then it is highly likely that 
-      // an append is occurring.  (Note it is not definite, and there are insane
-      // cases we will not deal with by employing this heuristic, but it beats
-      // always falling back to multiple ContentInserted calls).
-      //
-      // In the multiple insertion point case, we know we're going to need to do
-      // multiple ContentInserted calls anyway.
-      childCount = insertionPoint->GetContent()->GetChildCount();
-    }
-
-    if (multiple || childCount > 0) {
-      // Now comes the fun part.  For each appended child, make a
-      // ContentInserted call as if it had just gotten inserted at the index
-      // it's at in aContainer and let ContentInserted handle the mess.  If our
-      // insertion point is non-XBL that's the correct index, and otherwise
-      // ContentInserted will ignore the passed-in index.
-      PRUint32 containerCount = aContainer->GetChildCount();
-      for (PRUint32 i = aNewIndexInContainer; i < containerCount; i++) {
-        nsIContent* content = aContainer->GetChildAt(i);
-        if ((content->GetPrimaryFrame() ||
-             mPresShell->FrameManager()->GetUndisplayedContent(content))
-#ifdef MOZ_XUL
-            //  Except listboxes suck, so do NOT skip anything here if
-            //  we plan to notify a listbox.
-            && !MaybeGetListBoxBodyFrame(aContainer, content)
-#endif
-            ) {
-          // Already have a frame or undisplayed entry for this content; a
-          // previous ContentInserted in this loop must have reconstructed
-          // its insertion parent.  Skip it.
-          continue;
-        }
-        LAYOUT_PHASE_TEMP_EXIT();
-        // Call ContentInserted with this index.
-        ContentInserted(aContainer, content, i, mTempFrameTreeState,
-                        aAllowLazyConstruction);
-        LAYOUT_PHASE_TEMP_REENTER();
-      }
-
-      return NS_OK;
-    }
-  }
-
-  parentFrame = insertionPoint;
-
-  if (parentFrame->GetType() == nsGkAtoms::frameSetFrame) {
-    // Check whether we have any kids we care about.
-    PRUint32 count = aContainer->GetChildCount();
-    for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
-      if (IsSpecialFramesetChild(aContainer->GetChildAt(i))) {
-        // Just reframe the parent, since framesets are weird like that.
-        LAYOUT_PHASE_TEMP_EXIT();
-        nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
-        LAYOUT_PHASE_TEMP_REENTER();
-        return rv;
-      }
-    }
-  }
-  
+  LAYOUT_PHASE_TEMP_EXIT();
+  parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
+                  aNewIndexInContainer, -1, aAllowLazyConstruction);
+  LAYOUT_PHASE_TEMP_REENTER();
+  if (!parentFrame) {
+    return NS_OK;
+  }
+
+  LAYOUT_PHASE_TEMP_EXIT();
+  if (MaybeRecreateForFrameset(aContainer, parentFrame, aNewIndexInContainer,
+                               aContainer->GetChildCount())) {
+    LAYOUT_PHASE_TEMP_REENTER();
+    return NS_OK;
+  }
+  LAYOUT_PHASE_TEMP_REENTER();  
+
   if (parentFrame->IsLeaf()) {
     // Nothing to do here; we shouldn't be constructing kids of leaves
     // Clear lazy bits so we don't try to construct again.
     ClearLazyBitsInChildren(aContainer, aNewIndexInContainer,
                             aContainer->GetChildCount());
     return NS_OK;
   }
   
@@ -6574,34 +6658,30 @@ nsCSSFrameConstructor::ContentAppended(n
   
   if (haveFirstLineStyle && parentFrame == containingBlock) {
     // It's possible that some of the new frames go into a
     // 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)) {
-    // Append the flowed frames to the principal child list; captions
-    // need special treatment
-    if (captionItems.NotEmpty()) { // 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);
-      }
-    }
-
-    if (frameItems.NotEmpty()) { // append the in-flow kids
-      AppendFrames(state, parentFrame, frameItems, prevSibling);
-    }
+  // Append the flowed frames to the principal child list; captions
+  // need special treatment
+  if (captionItems.NotEmpty()) { // 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);
+    }
+  }
+
+  if (frameItems.NotEmpty()) { // append the in-flow kids
+    AppendFrames(state, parentFrame, frameItems, prevSibling);
   }
 
   // Recover first-letter frames
   if (haveFirstLetterStyle) {
     RecoverLetterFrames(containingBlock);
   }
 
 #ifdef DEBUG
@@ -6659,51 +6739,112 @@ PRBool NotifyListBoxBody(nsPresContext* 
 
 nsresult
 nsCSSFrameConstructor::ContentInserted(nsIContent*            aContainer,
                                        nsIContent*            aChild,
                                        PRInt32                aIndexInContainer,
                                        nsILayoutHistoryState* aFrameState,
                                        PRBool                 aAllowLazyConstruction)
 {
+  return ContentRangeInserted(aContainer,
+                              aChild,
+                              aIndexInContainer,
+                              aIndexInContainer+1,
+                              aFrameState,
+                              aAllowLazyConstruction);
+}
+
+// ContentRangeInserted handles creating frames for a range of nodes that
+// aren't at the end of their childlist. ContentRangeInserted isn't a real
+// content notification, but rather it handles regular ContentInserted calls
+// for a single node as well as the lazy construction of frames for a range of
+// nodes when called from CreateNeededFrames. For a range of nodes to be
+// suitable to have its frames constructed all at once they must meet the same
+// conditions that ContentAppended imposes (GetRangeInsertionPoint checks
+// these), plus more. Namely when finding the insertion prevsibling we must not
+// need to consult something specific to any one node in the range, so that the
+// insertion prevsibling would be the same for each node in the range. So we
+// pass the first node in the range to GetInsertionPrevSibling, and if
+// IsValidSibling (the only place GetInsertionPrevSibling might look at the
+// passed in node itself) needs to resolve style on the node we record this and
+// return that this range needs to be split up and inserted separately. Table
+// captions need extra attention as we need to determine where to insert them
+// in the caption list, while skipping any nodes in the range being inserted
+// (because when we treat the caption frames the other nodes have had their
+// frames constructed but not yet inserted into the frame tree).
+nsresult
+nsCSSFrameConstructor::ContentRangeInserted(nsIContent*            aContainer,
+                                            nsIContent*            aChild,
+                                            PRInt32                aIndexInContainer,
+                                            PRInt32                aEndIndexInContainer,
+                                            nsILayoutHistoryState* aFrameState,
+                                            PRBool                 aAllowLazyConstruction)
+{
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
   NS_PRECONDITION(mUpdateCount != 0,
                   "Should be in an update while creating frames");
 
+  NS_PRECONDITION(aChild, "must always pass a child");
+
   // XXXldb Do we need to re-resolve style to handle the CSS2 + combinator and
   // the :empty pseudo-class?
 #ifdef DEBUG
   if (gNoisyContentUpdates) {
-    printf("nsCSSFrameConstructor::ContentInserted container=%p child=%p "
-           "index=%d lazy=%d\n",
-           static_cast<void*>(aContainer),
-           static_cast<void*>(aChild),
-           aIndexInContainer,
-           aAllowLazyConstruction);
+    printf("nsCSSFrameConstructor::ContentRangeInserted container=%p child=%p"
+           "index=%d endindex=%d lazy=%d\n",
+           static_cast<void*>(aContainer), static_cast<void*>(aChild),
+           aIndexInContainer, aEndIndexInContainer, aAllowLazyConstruction);
     if (gReallyNoisyContentUpdates) {
-      (aContainer ? aContainer : aChild)->List(stdout, 0);
+      if (aContainer) {
+        aContainer->List(stdout,0);
+      } else {
+        aChild->List(stdout, 0);
+      }
     }
   }
 #endif
 
   nsresult rv = NS_OK;
 
+  PRBool isSingleInsert = (aIndexInContainer + 1 == aEndIndexInContainer);
+  NS_ASSERTION(isSingleInsert || !aAllowLazyConstruction,
+               "range insert shouldn't be lazy");
+  NS_ASSERTION(isSingleInsert || !aContainer ||
+               aEndIndexInContainer < aContainer->GetChildCount(),
+               "end index should not include all nodes");
+
 #ifdef MOZ_XUL
-  // aIndexInContainer might be bogus here, but it's not used by
-  // NotifyListBoxBody's CONTENT_INSERTED handling in any case.
-  if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer, aChild,
-                        aIndexInContainer, 
-                        mDocument, nsnull, CONTENT_INSERTED))
-    return NS_OK;
+  if (aContainer && IsXULListBox(aContainer)) {
+    if (isSingleInsert) {
+      // aIndexInContainer might be bogus here, but it's not used by
+      // NotifyListBoxBody's CONTENT_INSERTED handling in any case.
+      if (NotifyListBoxBody(mPresShell->GetPresContext(), aContainer, aChild,
+                            aIndexInContainer, 
+                            mDocument, nsnull, CONTENT_INSERTED)) {
+        return NS_OK;
+      }
+    } else {
+      // We don't handle a range insert to a listbox parent, issue single
+      // ContertInserted calls for each node inserted.
+      LAYOUT_PHASE_TEMP_EXIT();
+      IssueSingleInsertNofications(aContainer, aIndexInContainer,
+                                   aEndIndexInContainer,
+                                   aAllowLazyConstruction);
+      LAYOUT_PHASE_TEMP_REENTER();
+      return NS_OK;
+    }
+  }
 #endif // MOZ_XUL
   
   // If we have a null parent, then this must be the document element being
   // inserted, or some other child of the document in the DOM (might be a PI,
   // say).
   if (! aContainer) {
+    NS_ASSERTION(isSingleInsert,
+                 "root node insertion should be a single insertion");
     nsIContent *docElement = mDocument->GetRootContent();
 
     if (aChild != docElement) {
       // Not the root element; just bail out
       return NS_OK;
     }
 
     NS_PRECONDITION(nsnull == mRootElementFrame,
@@ -6712,17 +6853,17 @@ nsCSSFrameConstructor::ContentInserted(n
     // Create frames for the document element and its child elements
     nsIFrame*               docElementFrame;
     rv = ConstructDocElementFrame(docElement, aFrameState, &docElementFrame);
 
     if (NS_SUCCEEDED(rv) && docElementFrame) {
       InvalidateCanvasIfNeeded(mPresShell, aChild);
 #ifdef DEBUG
       if (gReallyNoisyContentUpdates) {
-        printf("nsCSSFrameConstructor::ContentInserted: resulting frame "
+        printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame "
                "model:\n");
         mFixedContainingBlock->List(stdout, 0);
       }
 #endif
     }
 
     return NS_OK;
   }
@@ -6733,43 +6874,79 @@ nsCSSFrameConstructor::ContentInserted(n
     return NS_OK;
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTINSERT, aContainer, aChild,
                            aIndexInContainer)) {
     return NS_OK;
   }
 
-  // See if we have an XBL insertion point. If so, then that's our
-  // real parent frame; if not, then the frame hasn't been built yet
-  // and we just bail.
-  nsIFrame* insertionPoint;
-  GetInsertionPoint(parentFrame, aChild, &insertionPoint);
-  if (! insertionPoint)
-    return NS_OK; // Don't build the frames.
-
-  parentFrame = insertionPoint;
-
-  PRBool isAppend;
+  if (isSingleInsert) {
+    // See if we have an XBL insertion point. If so, then that's our
+    // real parent frame; if not, then the frame hasn't been built yet
+    // and we just bail.
+    nsIFrame* insertionPoint;
+    GetInsertionPoint(parentFrame, aChild, &insertionPoint);
+    if (! insertionPoint)
+      return NS_OK; // Don't build the frames.
+
+    parentFrame = insertionPoint;
+  } else {
+    // Get our insertion point. If we need to issue single ContentInserted's
+    // GetRangeInsertionPoint will take care of that for us.
+    LAYOUT_PHASE_TEMP_EXIT();
+    parentFrame = GetRangeInsertionPoint(aContainer, parentFrame,
+      aIndexInContainer, aEndIndexInContainer, aAllowLazyConstruction);
+    LAYOUT_PHASE_TEMP_REENTER();
+    if (!parentFrame) {
+      return NS_OK;
+    }
+  }
+
+  PRBool isAppend, isRangeInsertSafe;
   nsIFrame* prevSibling =
     GetInsertionPrevSibling(parentFrame, aContainer, aChild, aIndexInContainer,
-                            &isAppend);
+                            &isAppend, &isRangeInsertSafe);
+
+  // check if range insert is safe
+  if (!isSingleInsert && !isRangeInsertSafe) {
+    // must fall back to a single ContertInserted for each child in the range
+    LAYOUT_PHASE_TEMP_EXIT();
+    IssueSingleInsertNofications(aContainer, aIndexInContainer,
+                                 aEndIndexInContainer, aAllowLazyConstruction);
+    LAYOUT_PHASE_TEMP_REENTER();
+    return NS_OK;
+  }
 
   nsIContent* container = parentFrame->GetContent();
 
   nsIAtom* frameType = parentFrame->GetType();
-  if (frameType == nsGkAtoms::frameSetFrame &&
-      IsSpecialFramesetChild(aChild)) {
-    // Just reframe the parent, since framesets are weird like that.
+  if (isSingleInsert) {
+    if (frameType == nsGkAtoms::frameSetFrame &&
+        IsSpecialFramesetChild(aChild)) {
+      // Just reframe the parent, since framesets are weird like that.
+      LAYOUT_PHASE_TEMP_EXIT();
+      nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
+      LAYOUT_PHASE_TEMP_REENTER();
+      return rv;
+    }
+  } else {
     LAYOUT_PHASE_TEMP_EXIT();
-    nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
+    if (MaybeRecreateForFrameset(aContainer, parentFrame,
+          aIndexInContainer, aEndIndexInContainer)) {
+      LAYOUT_PHASE_TEMP_REENTER();
+      return NS_OK;
+    }
     LAYOUT_PHASE_TEMP_REENTER();
-    return rv;
-  }
-
+  }
+
+  // We should only get here with fieldsets when doing a single insert, because
+  // fieldsets have multiple insertion points.
+  NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
+               "Unexpected parent");
   if (frameType == nsGkAtoms::fieldSetFrame &&
       aChild->Tag() == nsGkAtoms::legend) {
     // Just reframe the parent, since figuring out whether this
     // should be the new legend and then handling it is too complex.
     // We could do a little better here --- check if the fieldset already
     // has a legend which occurs earlier in its child list than this node,
     // and if so, proceed. But we'd have to extend nsFieldSetFrame
     // to locate this legend in the inserted frames and extract it.
@@ -6777,17 +6954,22 @@ nsCSSFrameConstructor::ContentInserted(n
     nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
     LAYOUT_PHASE_TEMP_REENTER();
     return rv;
   }
 
   // Don't construct kids of leaves
   if (parentFrame->IsLeaf()) {
     // Clear lazy bits so we don't try to construct again.
-    aChild->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
+    if (isSingleInsert) {
+      aChild->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
+    } else {
+      ClearLazyBitsInChildren(aContainer, aIndexInContainer,
+                              aEndIndexInContainer);
+    }
     return NS_OK;
   }
 
 #ifdef MOZ_MATHML
   if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
     LAYOUT_PHASE_TEMP_EXIT();
     nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), PR_FALSE);
     LAYOUT_PHASE_TEMP_REENTER();
@@ -6850,26 +7032,40 @@ nsCSSFrameConstructor::ContentInserted(n
       // Remove the old letter frames before doing the insertion
       RemoveLetterFrames(state.mPresContext, mPresShell,
                          state.mFrameManager,
                          state.mFloatedItems.containingBlock);
 
       // Removing the letterframes messes around with the frame tree, removing
       // and creating frames.  We need to reget our prevsibling, parent frame,
       // etc.
-      prevSibling =
-        GetInsertionPrevSibling(parentFrame, aContainer, aChild,
-                                aIndexInContainer, &isAppend);
+      prevSibling = GetInsertionPrevSibling(parentFrame, aContainer, aChild,
+        aIndexInContainer, &isAppend, &isRangeInsertSafe);
+
+      // Need check whether a range insert is still safe.
+      if (!isSingleInsert && !isRangeInsertSafe) {
+        // Need to recover the letter frames first.
+        RecoverLetterFrames(state.mFloatedItems.containingBlock);
+
+        // must fall back to a single ContertInserted for each child in the range
+        LAYOUT_PHASE_TEMP_EXIT();
+        IssueSingleInsertNofications(aContainer, aIndexInContainer,
+                                     aEndIndexInContainer,
+                                     aAllowLazyConstruction);
+        LAYOUT_PHASE_TEMP_REENTER();
+        return NS_OK;
+      }
+
       container = parentFrame->GetContent();
       frameType = parentFrame->GetType();
     }
   }
 
   if (!prevSibling) {
-    // We're inserting the new frame as the first child. See if the
+    // We're inserting the new frames as the first child. See if the
     // parent has a :before pseudo-element
     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();
@@ -6880,34 +7076,43 @@ nsCSSFrameConstructor::ContentInserted(n
   }
 
   FrameConstructionItemList items;
   ParentType parentType = GetParentType(frameType);
   PRBool haveNoXBLChildren =
     mDocument->BindingManager()->GetXBLChildNodesFor(aContainer) == nsnull;
   if (aIndexInContainer > 0 && parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the
-    // new node, and it has no frame, make a frame construction item for
+    // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
     AddTextItemIfNeeded(state, parentFrame, aContainer, aIndexInContainer - 1,
                         items);
   }
 
-  AddFrameConstructionItems(state, aChild, aIndexInContainer, parentFrame, items);
-
-  if (aIndexInContainer + 1 < PRInt32(aContainer->GetChildCount()) &&
+  if (isSingleInsert) {
+    AddFrameConstructionItems(state, aChild, aIndexInContainer, parentFrame, items);
+  } else {
+    for (PRUint32 i = aIndexInContainer;
+         i < (PRUint32)aEndIndexInContainer;
+         ++i) {
+      nsIContent* child = aContainer->GetChildAt(i);
+      AddFrameConstructionItems(state, child, i, parentFrame, items);
+    }
+  }
+
+  if (aEndIndexInContainer < PRInt32(aContainer->GetChildCount()) &&
       parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just after the
-    // new node, and it has no frame, make a frame construction item for
+    // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
-    AddTextItemIfNeeded(state, parentFrame, aContainer, aIndexInContainer + 1,
+    AddTextItemIfNeeded(state, parentFrame, aContainer, aEndIndexInContainer,
                         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();
@@ -6921,23 +7126,29 @@ nsCSSFrameConstructor::ContentInserted(n
   // 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.
   // We make no attempt here to set flags to indicate whether the list
   // will be at the start or end of a block. It doesn't seem worthwhile.
   nsFrameItems frameItems, captionItems;
   ConstructFramesFromItemList(state, items, parentFrame, frameItems);
 
   if (frameItems.NotEmpty()) {
-    InvalidateCanvasIfNeeded(mPresShell, aChild);
-    
-    if (nsGkAtoms::tableCaptionFrame == frameItems.FirstChild()->GetType()) {
-      NS_ASSERTION(frameItems.OnlyChild(),
-                   "adding a non caption frame to the caption childlist?");
-      captionItems.AddChild(frameItems.FirstChild());
-      frameItems.Clear();
+    if (isSingleInsert) {
+      InvalidateCanvasIfNeeded(mPresShell, aChild);
+    } else {
+      for (PRUint32 i = aIndexInContainer;
+           i < (PRUint32)aEndIndexInContainer;
+           ++i) {
+        InvalidateCanvasIfNeeded(mPresShell, aContainer->GetChildAt(i));
+      }
+    }
+
+    if (nsGkAtoms::tableFrame == frameType ||
+        nsGkAtoms::tableOuterFrame == frameType) {
+      PullOutCaptionFrames(frameItems, captionItems);
     }
   }
 
   // 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 prevSibling and isAppend, but should not have any
@@ -6992,67 +7203,93 @@ nsCSSFrameConstructor::ContentInserted(n
       // XXXbz this method is a no-op, so it's easy for the args being passed
       // here to make no sense without anyone noticing...  If it ever stops
       // being a no-op, vet them carefully!
       InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
                             prevSibling, frameItems);
     }
   }
       
-  if (NS_SUCCEEDED(rv) && frameItems.NotEmpty()) {
-    NS_ASSERTION(captionItems.IsEmpty(), "leaking caption frames");
+  // We might have captions; put them into the caption list of the
+  // outer table frame.
+  if (captionItems.NotEmpty()) {
+    NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
+                 nsGkAtoms::tableOuterFrame == frameType,
+                 "parent for caption is not table?");
+    // We need to determine where to put the caption items; start with the
+    // the parent frame that has already been determined and get the insertion
+    // prevsibling of the first caption item.
+    nsIFrame* captionParent = parentFrame;
+    PRBool captionIsAppend;
+    nsIFrame* captionPrevSibling = nsnull;
+
+    // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
+    PRBool ignored;
+    if (isSingleInsert) {
+      captionPrevSibling =
+        GetInsertionPrevSibling(captionParent, aContainer, aChild,
+                                aIndexInContainer, &captionIsAppend, &ignored);
+    } else {
+      nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
+      // It is very important here that we skip the children in
+      // [aIndexInContainer,aEndIndexInContainer) when looking for a
+      // prevsibling.
+      captionPrevSibling =
+        GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
+          aContainer->IndexOf(firstCaption), &captionIsAppend, &ignored,
+          aIndexInContainer, aChild,
+          aEndIndexInContainer, aContainer->GetChildAt(aEndIndexInContainer));
+    }
+
+    nsIFrame* outerTable = nsnull;
+    if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
+                                 &outerTable)) {
+      // If the parent is not an outer table frame we will try to add frames
+      // to a named child list that the parent does not honour and the frames
+      // will get lost
+      NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
+                   "Pseudo frame construction failure; "
+                   "a caption can be only a child of an outer table frame");
+
+      // 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 (bug 341382).
+      if (captionPrevSibling &&
+          captionPrevSibling->GetParent() != outerTable) {
+          captionPrevSibling = nsnull;
+      }
+      if (captionIsAppend) {
+        state.mFrameManager->AppendFrames(outerTable, nsGkAtoms::captionList,
+                                          captionItems);
+      } else {
+        state.mFrameManager->InsertFrames(outerTable, nsGkAtoms::captionList,
+                                          captionPrevSibling, captionItems);
+      }
+    }
+  }
+
+  if (frameItems.NotEmpty()) {
     // Notify the parent frame
     if (isAppend) {
       AppendFrames(state, parentFrame, frameItems, prevSibling);
     } else {
       state.mFrameManager->InsertFrames(parentFrame, nsnull, prevSibling,
                                         frameItems);
     }
   }
-  else {
-    // we might have a caption treat it here
-    if (NS_SUCCEEDED(rv) && captionItems.NotEmpty()) {
-      nsIFrame* outerTableFrame;
-      if (GetCaptionAdjustedParent(parentFrame, captionItems.FirstChild(),
-                                   &outerTableFrame)) {
-        // 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 (bug 341382).
-        if (prevSibling && prevSibling->GetParent() != outerTableFrame) {
-          prevSibling = nsnull;
-        }
-        // If the parent is not a outer table frame we will try to add frames
-        // to a named child list that the parent does not honour and the frames
-        // will get lost
-        NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTableFrame->GetType(),
-                     "Pseudo frame construction failure, "
-                     "a caption can be only a child of a outer table frame");
-        if (isAppend) {
-          state.mFrameManager->AppendFrames(outerTableFrame,
-                                            nsGkAtoms::captionList,
-                                            captionItems);
-        }
-        else {
-          state.mFrameManager->InsertFrames(outerTableFrame,
-                                            nsGkAtoms::captionList,
-                                            prevSibling, captionItems);
-        }
-      }
-    }
-  }
 
   if (haveFirstLetterStyle) {
     // Recover the letter frames for the containing block when
     // it has first-letter style.
     RecoverLetterFrames(state.mFloatedItems.containingBlock);
   }
 
 #ifdef DEBUG
   if (gReallyNoisyContentUpdates && parentFrame) {
-    printf("nsCSSFrameConstructor::ContentInserted: resulting frame model:\n");
+    printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
     parentFrame->List(stdout, 0);
   }
 #endif
 
   return NS_OK;
 }
 
 nsresult
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -133,16 +133,41 @@ private:
     CONTENTINSERT
   };
 
   PRBool MaybeConstructLazily(Operation aOperation,
                               nsIContent* aContainer,
                               nsIContent* aChild,
                               PRInt32 aIndex);
 
+  // Issues a single ContentInserted for each child of aContainer in the range
+  // [aStartIndexInContainer, aEndIndexInContainer).
+  void IssueSingleInsertNofications(nsIContent* aContainer,
+                                    PRInt32 aStartIndexInContainer,
+                                    PRInt32 aEndIndexInContainer,
+                                    PRBool aAllowLazyConstruction);
+  
+  // Checks if the children of aContainer in the range
+  // [aStartIndexInContainer, aEndIndexInContainer) can be inserted/appended
+  // to one insertion point together. If so, returns that insertion point. If
+  // not, returns null and issues single ContentInserted calls for each child.
+  // aEndIndexInContainer = -1 is a special value that indicates it is an
+  // append and the range includes the last child.
+  nsIFrame* GetRangeInsertionPoint(nsIContent* aContainer,
+                                   nsIFrame* aParentFrame,
+                                   PRInt32 aStartIndexInContainer,
+                                   PRInt32 aEndIndexInContainer,
+                                   PRBool aAllowLazyConstruction);
+
+  // Returns true if parent was recreated due to frameset child, false otherwise.
+  PRBool MaybeRecreateForFrameset(nsIContent* aContainer,
+                                  nsIFrame* aParentFrame,
+                                  PRUint32 aStartIndexInContainer,
+                                  PRUint32 aEndIndexInContainer);
+
 public:
   /**
    * Lazy frame construction is controlled by the aAllowLazyConstruction bool
    * parameter of nsCSSFrameConstructor::ContentAppended/Inserted. It is true
    * for all inserts/appends as passed from the presshell, except for the
    * insert of the root element, which is always non-lazy. Even if the
    * aAllowLazyConstruction passed to ContentAppended/Inserted is true we still
    * may not be able to construct lazily, so we call MaybeConstructLazily.
@@ -167,17 +192,17 @@ public:
    * node with that bit set. Then it posts a restyle event to ensure that a
    * flush happens to construct those frames.
    *
    * When the flush happens the presshell calls
    * nsCSSFrameConstructor::CreateNeededFrames. CreateNeededFrames follows any
    * nodes with NODE_DESCENDANTS_NEED_FRAMES set down the content tree looking
    * for nodes with NODE_NEEDS_FRAME set. It calls ContentAppended for any runs
    * of nodes with NODE_NEEDS_FRAME set that are at the end of their childlist,
-   * and ContentInserted for the others.
+   * and ContentRangeInserted for any other runs that aren't.
    *
    * If a node is removed from the document then we don't bother unsetting any
    * of the lazy bits that might be set on it, its descendants, or any of its
    * ancestor nodes because that is a slow operation, the work might be wasted
    * if another node gets inserted in its place, and we can clear the bits
    * quicker by processing the content tree from top down the next time we call
    * CreateNeededFrames. (We do clear the bits when BindToTree is called on any
    * nsIContent; so any nodes added to the document will not have any lazy bits
@@ -193,16 +218,31 @@ public:
   // If aAllowLazyConstruction is true then frame construction of the new child
   // can be done lazily.
   nsresult ContentInserted(nsIContent*            aContainer,
                            nsIContent*            aChild,
                            PRInt32                aIndexInContainer,
                            nsILayoutHistoryState* aFrameState,
                            PRBool                 aAllowLazyConstruction);
 
+  // Like ContentInserted but handles inserting the children of aContainer in
+  // the range [aIndexInContainer, aEndIndexInContainer).
+  // aChild must be non-null. For inserting a single node it should be that
+  // node. For inserting more than one node, aChild must be the first child
+  // being inserted.
+  // If aAllowLazyConstruction is true then frame construction of the new
+  // children can be done lazily. It is only allowed to be true when inserting
+  // a single node.
+  nsresult ContentRangeInserted(nsIContent*            aContainer,
+                                nsIContent*            aChild,
+                                PRInt32                aIndexInContainer,
+                                PRInt32                aEndIndexInContainer,
+                                nsILayoutHistoryState* aFrameState,
+                                PRBool                 aAllowLazyConstruction);
+
   enum RemoveFlags { REMOVE_CONTENT, REMOVE_FOR_RECONSTRUCTION };
   nsresult ContentRemoved(nsIContent* aContainer,
                           nsIContent* aChild,
                           PRInt32     aIndexInContainer,
                           RemoveFlags aFlags,
                           PRBool*     aDidReconstruct);
 
   nsresult CharacterDataChanged(nsIContent* aContent,
@@ -1716,44 +1756,61 @@ private:
                                  nsFrameItems&            aFrameItems);
 
   // Find the right frame to use for aContent when looking for sibling
   // frames for aTargetContent.  If aPrevSibling is true, this
   // will look for last continuations, etc, as necessary.  This calls
   // IsValidSibling as needed; if that returns false it returns null.
   //
   // @param aTargetContentDisplay the CSS display enum for aTargetContent if
-  // already known, UNSET_DISPLAY otherwise.
+  // already known, UNSET_DISPLAY otherwise. It will be filled in if needed.
   nsIFrame* FindFrameForContentSibling(nsIContent* aContent,
                                        nsIContent* aTargetContent,
                                        PRUint8& aTargetContentDisplay,
                                        PRBool aPrevSibling);
 
   // Find the ``rightmost'' frame for the content immediately preceding the one
   // aIter points to, following continuations if necessary.  aIter is passed by
-  // value on purpose, so as not to modify the callee's iterator.
+  // value on purpose, so as not to modify the caller's iterator.
   nsIFrame* FindPreviousSibling(const ChildIterator& aFirst,
-                                ChildIterator aIter);
+                                ChildIterator aIter,
+                                PRUint8& aTargetContentDisplay);
 
   // Find the frame for the content node immediately following the one aIter
   // points to, following continuations if necessary.  aIter is passed by value
-  // on purpose, so as not to modify the callee's iterator.
+  // on purpose, so as not to modify the caller's iterator.
   nsIFrame* FindNextSibling(ChildIterator aIter,
-                            const ChildIterator& aLast);
+                            const ChildIterator& aLast,
+                            PRUint8& aTargetContentDisplay);
 
   // Find the right previous sibling for an insertion.  This also updates the
   // parent frame to point to the correct continuation of the parent frame to
   // use, and returns whether this insertion is to be treated as an append.
   // aChild is the child being inserted and aIndexInContainer its index in
   // aContainer (which is aChild's DOM parent).
+  // aIsRangeInsertSafe returns whether it is safe to do a range insert with
+  // aChild being the first child in the range. It is the callers'
+  // responsibility to check whether a range insert is safe with regards to
+  // fieldsets.
+  // The skip parameters are used to ignore a range of children when looking
+  // for a sibling. All nodes starting from aStartSkipChild (which is in
+  // aContainer's regular child list at aStartSkipIndexInContainer) and up to
+  // but not including aEndSkipChild (which is at aEndSkipIndexInContainer in
+  // aContainer) will be skipped over when looking for sibling frames. Skipping
+  // a range can deal with XBL but not when there are multiple insertion points.
   nsIFrame* GetInsertionPrevSibling(nsIFrame*& aParentFrame, /* inout */
                                     nsIContent* aContainer,
                                     nsIContent* aChild,
                                     PRInt32 aIndexInContainer,
-                                    PRBool* aIsAppend);
+                                    PRBool* aIsAppend,
+                                    PRBool* aIsRangeInsertSafe,
+                                    PRInt32 aStartSkipIndexInContainer = -1,
+                                    nsIContent* aStartSkipChild = nsnull,
+                                    PRInt32 aEndSkipIndexInContainer = -1,
+                                    nsIContent *aEndSkipChild = nsnull);
 
   // see if aContent and aSibling are legitimate siblings due to restrictions
   // imposed by table columns
   // XXXbz this code is generally wrong, since the frame for aContent
   // may be constructed based on tag, not based on aDisplay!
   PRBool IsValidSibling(nsIFrame*              aSibling,
                         nsIContent*            aContent,
                         PRUint8&               aDisplay);