Bug 728516. Refactor frame construction for things that want to wrap kids in a block. r=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 21 Feb 2012 18:01:40 -0500
changeset 87327 fb6e8ee380fee46afd7509b7b7bad6632639bb22
parent 87326 1eb78a62ba7548ffb1eea7e343c2ac47e63e91d5
child 87328 5a90ee43d1f00e6c45ad8e2d95dba3d680c1bf50
push id6300
push userbzbarsky@mozilla.com
push dateTue, 21 Feb 2012 23:02:09 +0000
treeherdermozilla-inbound@d0a7f8c60ccc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs728516
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 728516. Refactor frame construction for things that want to wrap kids in a block. r=roc
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/reftests/forms/button-first-letter-1-noref.html
layout/reftests/forms/button-first-letter-1-ref.html
layout/reftests/forms/button-first-letter-1.html
layout/reftests/forms/reftest.list
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -368,18 +368,21 @@ static PRInt32 FFWC_nextInFlows=0;
 static inline nsIFrame*
 GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame)
 {
   // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
   nsIFrame* firstChild = aFieldsetFrame->GetFirstPrincipalChild();
   return firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
 }
 
-#define FCDATA_DECL(_flags, _func) \
-  { _flags, { (FrameCreationFunc)_func } }
+#define FCDATA_DECL(_flags, _func)                          \
+  { _flags, { (FrameCreationFunc)_func }, nsnull, nsnull }
+#define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box)  \
+  { _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS,        \
+      { (FrameCreationFunc)_func }, nsnull, &_anon_box }
 
 //----------------------------------------------------------------------
 
 static bool
 IsInlineOutside(nsIFrame* aFrame)
 {
   return aFrame->GetStyleDisplay()->IsInlineOutside();
 }
@@ -2925,130 +2928,16 @@ ClearLazyBits(nsIContent* aStartContent,
                   "Must have start child if we have an end child");
   for (nsIContent* cur = aStartContent; cur != aEndContent;
        cur = cur->GetNextSibling()) {
     cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
   }
 }
 
 nsresult
-nsCSSFrameConstructor::ConstructButtonFrame(nsFrameConstructorState& aState,
-                                            FrameConstructionItem&   aItem,
-                                            nsIFrame*                aParentFrame,
-                                            const nsStyleDisplay*    aStyleDisplay,
-                                            nsFrameItems&            aFrameItems,
-                                            nsIFrame**               aNewFrame)
-{
-  *aNewFrame = nsnull;
-  nsIFrame* buttonFrame = nsnull;
-  nsIContent* const content = aItem.mContent;
-  nsStyleContext* const styleContext = aItem.mStyleContext;
-
-  if (nsGkAtoms::button == aItem.mTag) {
-    buttonFrame = NS_NewHTMLButtonControlFrame(mPresShell, styleContext);
-  }
-  else {
-    buttonFrame = NS_NewGfxButtonControlFrame(mPresShell, styleContext);
-  }
-  if (NS_UNLIKELY(!buttonFrame)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  // Initialize the button frame
-  nsresult rv = InitAndRestoreFrame(aState, content,
-                                    aState.GetGeometricParent(aStyleDisplay, aParentFrame),
-                                    nsnull, buttonFrame);
-  if (NS_FAILED(rv)) {
-    buttonFrame->Destroy();
-    return rv;
-  }
-
-  nsRefPtr<nsStyleContext> innerBlockContext;
-  innerBlockContext =
-    mPresShell->StyleSet()->ResolveAnonymousBoxStyle(nsCSSAnonBoxes::buttonContent,
-                                                     styleContext);
-                                                               
-  nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, innerBlockContext,
-                                          NS_BLOCK_FLOAT_MGR);
-
-  if (NS_UNLIKELY(!blockFrame)) {
-    buttonFrame->Destroy();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  rv = InitAndRestoreFrame(aState, content, buttonFrame, nsnull, blockFrame);
-  if (NS_FAILED(rv)) {
-    blockFrame->Destroy();
-    buttonFrame->Destroy();
-    return rv;
-  }
-
-  rv = aState.AddChild(buttonFrame, aFrameItems, content, styleContext,
-                       aParentFrame);
-  if (NS_FAILED(rv)) {
-    blockFrame->Destroy();
-    buttonFrame->Destroy();
-    return rv;
-  }
-
-  bool isLeaf = buttonFrame->IsLeaf();
-#ifdef DEBUG
-  // Make sure that we're an anonymous content creator exactly when we're a
-  // leaf
-  nsIAnonymousContentCreator* creator = do_QueryFrame(buttonFrame);
-  NS_ASSERTION(!creator == !isLeaf,
-               "Should be creator exactly when we're a leaf");
-#endif
-  
-  if (!isLeaf) { 
-    // Process children
-    nsFrameConstructorSaveState absoluteSaveState;
-    nsFrameItems                childItems;
-
-    if (aStyleDisplay->IsPositioned()) {
-      aState.PushAbsoluteContainingBlock(buttonFrame, absoluteSaveState);
-    }
-
-#ifdef DEBUG
-    // Make sure that anonymous child creation will have no effect in this case
-    nsIAnonymousContentCreator* creator = do_QueryFrame(blockFrame);
-    NS_ASSERTION(!creator, "Shouldn't be an anonymous content creator!");
-#endif
-
-    rv = ProcessChildren(aState, content, styleContext, blockFrame, true,
-                         childItems, aStyleDisplay->IsBlockInside(),
-                         aItem.mPendingBinding);
-    if (NS_FAILED(rv)) return rv;
-  
-    // Set the areas frame's initial child lists
-    blockFrame->SetInitialChildList(kPrincipalList, childItems);
-  }
-
-  SetInitialSingleChild(buttonFrame, blockFrame);
-
-  if (isLeaf) {
-    ClearLazyBits(content->GetFirstChild(), nsnull);
-
-    nsFrameItems  anonymousChildItems;
-    // if there are any anonymous children create frames for them.  Note that
-    // we're doing this using a different parent frame from the one we pass to
-    // ProcessChildren!
-    CreateAnonymousFrames(aState, content, buttonFrame, aItem.mPendingBinding,
-                          anonymousChildItems);
-    if (anonymousChildItems.NotEmpty()) {
-      // the anonymous content is already parented to the area frame
-      AppendFrames(blockFrame, kPrincipalList, anonymousChildItems);
-    }
-  }
-
-  // our new button frame returned is the top frame. 
-  *aNewFrame = buttonFrame; 
-
-  return NS_OK;  
-}
-
-nsresult
 nsCSSFrameConstructor::ConstructSelectFrame(nsFrameConstructorState& aState,
                                             FrameConstructionItem&   aItem,
                                             nsIFrame*                aParentFrame,
                                             const nsStyleDisplay*    aStyleDisplay,
                                             nsFrameItems&            aFrameItems,
                                             nsIFrame**               aNewFrame)
 {
   nsresult rv = NS_OK;
@@ -3320,18 +3209,18 @@ FindAncestorWithGeneratedContentPseudo(n
     if (pseudo == nsCSSPseudoElements::before ||
         pseudo == nsCSSPseudoElements::after)
       return f;
   }
   return nsnull;
 }
 
 #define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
-#define FULL_CTOR_FCDATA(_flags, _func)                     \
-  { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nsnull }, _func }
+#define FULL_CTOR_FCDATA(_flags, _func)                             \
+  { _flags | FCDATA_FUNC_IS_FULL_CTOR, { nsnull }, _func, nsnull }
 
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindTextData(nsIFrame* aParentFrame)
 {
   if (aParentFrame && aParentFrame->IsFrameOfType(nsIFrame::eSVG)) {
     nsIFrame *ancestorFrame =
       nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
@@ -3518,17 +3407,20 @@ nsCSSFrameConstructor::FindHTMLData(Elem
     SIMPLE_TAG_CHAIN(applet, nsCSSFrameConstructor::FindObjectData),
     SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
     COMPLEX_TAG_CREATE(fieldset,
                        &nsCSSFrameConstructor::ConstructFieldSetFrame),
     { &nsGkAtoms::legend,
       FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES, NS_NewLegendFrame) },
     SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
     SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
-    COMPLEX_TAG_CREATE(button, &nsCSSFrameConstructor::ConstructButtonFrame),
+    { &nsGkAtoms::button,
+      FCDATA_WITH_WRAPPING_BLOCK(FCDATA_ALLOW_BLOCK_STYLES,
+                                 NS_NewHTMLButtonControlFrame,
+                                 nsCSSAnonBoxes::buttonContent) },
     SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData),
 #if defined(MOZ_MEDIA)
     SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
     SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
 #endif
     SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame)
   };
 
@@ -3575,22 +3467,25 @@ nsCSSFrameConstructor::FindInputData(Ele
     SIMPLE_INT_CHAIN(NS_FORM_INPUT_IMAGE,
                      nsCSSFrameConstructor::FindImgControlData),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_EMAIL, NS_NewTextControlFrame),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_SEARCH, NS_NewTextControlFrame),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_TEXT, NS_NewTextControlFrame),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_TEL, NS_NewTextControlFrame),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_URL, NS_NewTextControlFrame),
     SIMPLE_INT_CREATE(NS_FORM_INPUT_PASSWORD, NS_NewTextControlFrame),
-    COMPLEX_INT_CREATE(NS_FORM_INPUT_SUBMIT,
-                       &nsCSSFrameConstructor::ConstructButtonFrame),
-    COMPLEX_INT_CREATE(NS_FORM_INPUT_RESET,
-                       &nsCSSFrameConstructor::ConstructButtonFrame),
-    COMPLEX_INT_CREATE(NS_FORM_INPUT_BUTTON,
-                       &nsCSSFrameConstructor::ConstructButtonFrame)
+    { NS_FORM_INPUT_SUBMIT,
+      FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
+                                 nsCSSAnonBoxes::buttonContent) },
+    { NS_FORM_INPUT_RESET,
+      FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
+                                 nsCSSAnonBoxes::buttonContent) },
+    { NS_FORM_INPUT_BUTTON,
+      FCDATA_WITH_WRAPPING_BLOCK(0, NS_NewGfxButtonControlFrame,
+                                 nsCSSAnonBoxes::buttonContent) }
     // Keeping hidden inputs out of here on purpose for so they get frames by
     // display (in practice, none).
   };
 
   nsCOMPtr<nsIFormControl> control = do_QueryInterface(aElement);
   NS_ASSERTION(control, "input doesn't implement nsIFormControl?");
 
   return FindDataByInt(control->GetType(), aElement, aStyleContext,
@@ -3670,16 +3565,20 @@ nsCSSFrameConstructor::ConstructFrameFro
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
                      FCDATA_DISALLOW_GENERATED_CONTENT);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
+  CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
+                     FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
+  CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
+                     FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
 #undef CHECK_ONLY_ONE_BIT
   NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
                ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
                 data->mFullConstructor ==
                   &nsCSSFrameConstructor::ConstructNonScrollableBlock),
                "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
 
   // Don't create a subdocument frame for iframes if we're creating extra frames
@@ -3743,16 +3642,55 @@ nsCSSFrameConstructor::ConstructFrameFro
     }
 
     // Use frameToAddToList as the primary frame.  In the non-scrollframe case
     // they're equal, but in the scrollframe case newFrame is the scrolled
     // frame, while frameToAddToList is the scrollframe (and should be the
     // primary frame).
     primaryFrame = frameToAddToList;
 
+    // If we need to create a block formatting context to wrap our
+    // kids, do it now.
+    const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
+    nsIFrame* maybeAbsoluteContainingBlock = newFrame;
+    nsIFrame* possiblyLeafFrame = newFrame;
+    if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
+      nsRefPtr<nsStyleContext> blockContext;
+      blockContext =
+        mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo,
+                                                         styleContext);
+      nsIFrame* blockFrame =
+        NS_NewBlockFormattingContext(mPresShell, blockContext);
+      if (NS_UNLIKELY(!blockFrame)) {
+        primaryFrame->Destroy();
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+
+      rv = InitAndRestoreFrame(aState, content, newFrame, nsnull, blockFrame);
+      if (NS_FAILED(rv)) {
+        blockFrame->Destroy();
+        primaryFrame->Destroy();
+        return rv;
+      }
+
+      SetInitialSingleChild(newFrame, blockFrame);
+
+      // Now figure out whether newFrame or blockFrame should be the
+      // absolute container.  It should be the latter if it's
+      // positioned, otherwise the former.
+      const nsStyleDisplay* blockDisplay = blockContext->GetStyleDisplay();
+      if (blockDisplay->IsPositioned()) {
+        maybeAbsoluteContainingBlockDisplay = blockDisplay;
+        maybeAbsoluteContainingBlock = blockFrame;
+      }
+      
+      // Our kids should go into the blockFrame
+      newFrame = blockFrame;
+    }
+
     rv = aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
                          aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
 #ifdef MOZ_XUL
     // Icky XUL stuff, sadly
@@ -3768,30 +3706,32 @@ nsCSSFrameConstructor::ConstructFrameFro
 #endif /* MOZ_XUL */
 
     // Process the child content if requested
     nsFrameItems childItems;
     nsFrameConstructorSaveState absoluteSaveState;
 
     if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) {
       aState.PushAbsoluteContainingBlock(nsnull, absoluteSaveState);
-    } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH) && display->IsPositioned()) {
-      aState.PushAbsoluteContainingBlock(newFrame, absoluteSaveState);
+    } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH) &&
+               maybeAbsoluteContainingBlockDisplay->IsPositioned()) {
+      aState.PushAbsoluteContainingBlock(maybeAbsoluteContainingBlock,
+                                         absoluteSaveState);
     }
 
     if (bits & FCDATA_USE_CHILD_ITEMS) {
       rv = ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
                                        childItems);
     } else {
       // Process the child frames.
       rv = ProcessChildren(aState, content, styleContext, newFrame,
                            !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
                            childItems,
                            (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
-                           aItem.mPendingBinding);
+                           aItem.mPendingBinding, possiblyLeafFrame);
     }
 
 #ifdef MOZ_XUL
     // More icky XUL stuff
     if (aItem.mNameSpaceID == kNameSpaceID_XUL &&
         (aItem.mTag == nsGkAtoms::treechildren || // trees always need titletips
          content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext) ||
          content->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltip))) {
@@ -4856,18 +4796,19 @@ nsCSSFrameConstructor::FindSVGData(Eleme
     SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(path, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame),
     SIMPLE_SVG_CREATE(generic, NS_NewSVGGenericContainerFrame),
     { &nsGkAtoms::foreignObject,
-      FULL_CTOR_FCDATA(FCDATA_DISALLOW_OUT_OF_FLOW,
-                       &nsCSSFrameConstructor::ConstructSVGForeignObjectFrame) },
+      FCDATA_WITH_WRAPPING_BLOCK(FCDATA_DISALLOW_OUT_OF_FLOW,
+                                 NS_NewSVGForeignObjectFrame,
+                                 nsCSSAnonBoxes::mozSVGForeignContent) },
     SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame),
     SIMPLE_SVG_CREATE(altGlyph, NS_NewSVGTSpanFrame),
     SIMPLE_SVG_CREATE(text, NS_NewSVGTextFrame),
     SIMPLE_SVG_CREATE(tspan, NS_NewSVGTSpanFrame),
     SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame),
     SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame),
     SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame),
     SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame),
@@ -4910,69 +4851,16 @@ nsCSSFrameConstructor::FindSVGData(Eleme
 
   if (!data) {
     data = &sContainerData;
   }
 
   return data;
 }
 
-nsresult
-nsCSSFrameConstructor::ConstructSVGForeignObjectFrame(nsFrameConstructorState& aState,
-                                                      FrameConstructionItem&   aItem,
-                                                      nsIFrame* aParentFrame,
-                                                      const nsStyleDisplay* aStyleDisplay,
-                                                      nsFrameItems& aFrameItems,
-                                                      nsIFrame** aNewFrame)
-{
-  nsIContent* const content = aItem.mContent;
-  nsStyleContext* const styleContext = aItem.mStyleContext;
-
-  nsIFrame* newFrame = NS_NewSVGForeignObjectFrame(mPresShell, styleContext);
-  if (NS_UNLIKELY(!newFrame)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  // We don't allow this frame to be out of flow
-  InitAndRestoreFrame(aState, content, aParentFrame, nsnull, newFrame);
-
-  nsresult rv = aState.AddChild(newFrame, aFrameItems, content, styleContext,
-                                aParentFrame, false, false);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsRefPtr<nsStyleContext> innerPseudoStyle;
-  innerPseudoStyle = mPresShell->StyleSet()->
-    ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozSVGForeignContent, styleContext);
-
-  nsIFrame* blockFrame = NS_NewBlockFormattingContext(mPresShell,
-                                                      innerPseudoStyle);
-  if (NS_UNLIKELY(!blockFrame)) {
-    newFrame->Destroy();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsFrameItems childItems;
-  // We should be relatively positioned so that we end up being the
-  // absolute containing block.
-  const nsStyleDisplay* disp = innerPseudoStyle->GetStyleDisplay();
-  NS_ASSERTION(disp->IsPositioned(), "How did this get to not be positioned?");
-  rv = ConstructBlock(aState, innerPseudoStyle->GetStyleDisplay(), content,
-                      newFrame, newFrame, innerPseudoStyle,
-                      &blockFrame, childItems, true,
-                      aItem.mPendingBinding);
-
-  newFrame->SetInitialChildList(kPrincipalList, childItems);
-
-  *aNewFrame = newFrame;
-
-  return rv;
-}
-
 void
 nsCSSFrameConstructor::AddPageBreakItem(nsIContent* aContent,
                                         nsStyleContext* aMainStyleContext,
                                         FrameConstructionItemList& aItems)
 {
   nsRefPtr<nsStyleContext> pseudoStyle;
   // Use the same parent style context that |aMainStyleContext| has, since
   // that's easier to re-resolve and it doesn't matter in practice.
@@ -9516,23 +9404,28 @@ nsCSSFrameConstructor::ConstructFramesFr
 nsresult
 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
                                        nsIContent*              aContent,
                                        nsStyleContext*          aStyleContext,
                                        nsIFrame*                aFrame,
                                        const bool               aCanHaveGeneratedContent,
                                        nsFrameItems&            aFrameItems,
                                        const bool               aAllowBlockStyles,
-                                       PendingBinding*          aPendingBinding)
+                                       PendingBinding*          aPendingBinding,
+                                       nsIFrame*                aPossiblyLeafFrame)
 {
   NS_PRECONDITION(aFrame, "Must have parent frame here");
   NS_PRECONDITION(aFrame->GetContentInsertionFrame() == aFrame,
                   "Parent frame in ProcessChildren should be its own "
                   "content insertion frame");
 
+  if (!aPossiblyLeafFrame) {
+    aPossiblyLeafFrame = aFrame;
+  }
+
   // XXXbz ideally, this would do all the pushing of various
   // containing blocks as needed, so callers don't have to do it...
 
   bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
   if (aAllowBlockStyles) {
     ShouldHaveSpecialBlockStyle(aContent, aStyleContext, &haveFirstLetterStyle,
                                 &haveFirstLineStyle);
   }
@@ -9558,17 +9451,17 @@ nsCSSFrameConstructor::ProcessChildren(n
     itemsToConstruct.SetLineBoundaryAtStart(true);
     itemsToConstruct.SetLineBoundaryAtEnd(true);
   }
 
   // Create any anonymous frames we need here.  This must happen before the
   // non-anonymous children are processed to ensure that popups are never
   // constructed before the popupset.
   nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
-  GetAnonymousContent(aContent, aFrame, anonymousItems);
+  GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
   for (PRUint32 i = 0; i < anonymousItems.Length(); ++i) {
     nsIContent* content = anonymousItems[i].mContent;
 #ifdef DEBUG
     nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
     NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
                  "If you need to use CreateFrameFor, you need to call "
                  "CreateAnonymousFrames manually and not follow the standard "
                  "ProcessChildren() codepath for this frame");
@@ -9595,17 +9488,17 @@ nsCSSFrameConstructor::ProcessChildren(n
 
     AddFrameConstructionItemsInternal(aState, content, aFrame,
                                       content->Tag(), content->GetNameSpaceID(),
                                       true, styleContext,
                                       ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK,
                                       itemsToConstruct);
   }
 
-  if (!aFrame->IsLeaf()) {
+  if (!aPossiblyLeafFrame->IsLeaf()) {
     // :before/:after content should have the same style context parent
     // as normal kids.
     // Note that we don't use this style context for looking up things like
     // special block styles because in some cases involving table pseudo-frames
     // it has nothing to do with the parent frame's desired behavior.
     nsStyleContext* styleContext;
 
     if (aCanHaveGeneratedContent) {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -722,16 +722,21 @@ private:
      FrameConstructionItem instead of trying to process the content's children.
      This can be used with or without FCDATA_FUNC_IS_FULL_CTOR.
      The child items might still need table pseudo processing. */
 #define FCDATA_USE_CHILD_ITEMS 0x10000
   /* If FCDATA_FORCED_NON_SCROLLABLE_BLOCK is set, then this block
      would have been scrollable but has been forced to be
      non-scrollable due to being in a paginated context. */
 #define FCDATA_FORCED_NON_SCROLLABLE_BLOCK 0x20000
+  /* If FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, then create a
+     block formatting context wrapper around the kids of this frame
+     using the FrameConstructionData's mPseudoAtom for its anonymous
+     box type. */
+#define FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS 0x40000
 
   /* Structure representing information about how a frame should be
      constructed.  */
   struct FrameConstructionData {
     // Flag bits that can modify the way the construction happens
     PRUint32 mBits;
     // We have exactly one of three types of functions, so use a union for
     // better cache locality for the ones that aren't pointer-to-member.  That
@@ -740,16 +745,19 @@ private:
     // constructor and all the resulting generated code.  See documentation
     // above for FrameCreationFunc, FrameConstructionDataGetter, and
     // FrameFullConstructor to see what the functions would do.
     union Func {
       FrameCreationFunc mCreationFunc;
       FrameConstructionDataGetter mDataGetter;
     } mFunc;
     FrameFullConstructor mFullConstructor;
+    // For cases when FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, the
+    // anonymous box type to use for that wrapper.
+    nsICSSAnonBoxPseudo * const * const mAnonBoxPseudo;
   };
 
   /* Structure representing a mapping of an atom to a FrameConstructionData.
      This can be used with non-static atoms, assuming that the nsIAtom* is
      stored somewhere that this struct can point to (that is, a static
      nsIAtom*) and that it's allocated before the struct is ever used. */
   struct FrameConstructionDataByTag {
     // Pointer to nsIAtom* is used because we want to initialize this
@@ -1125,25 +1133,16 @@ protected:
                                             nsIFrame*        aFrame,
                                             nsStyleContext*  aStyleContext,
                                             nsIFrame*        aParentFrame,
                                             nsIFrame*        aPrevInFlow,
                                             nsFrameState     aTypeBit,
                                             nsIFrame**       aPlaceholderFrame);
 
 private:
-  // ConstructButtonFrame puts the new frame in aFrameItems and
-  // handles the kids of the button.
-  nsresult ConstructButtonFrame(nsFrameConstructorState& aState,
-                                FrameConstructionItem&    aItem,
-                                nsIFrame*                aParentFrame,
-                                const nsStyleDisplay*    aStyleDisplay,
-                                nsFrameItems&            aFrameItems,
-                                nsIFrame**               aNewFrame);
-
   // ConstructSelectFrame puts the new frame in aFrameItems and
   // handles the kids of the select.
   nsresult ConstructSelectFrame(nsFrameConstructorState& aState,
                                 FrameConstructionItem&   aItem,
                                 nsIFrame*                aParentFrame,
                                 const nsStyleDisplay*    aStyleDisplay,
                                 nsFrameItems&            aFrameItems,
                                 nsIFrame**               aNewFrame);
@@ -1323,23 +1322,16 @@ private:
 
 // SVG - rods
   static const FrameConstructionData* FindSVGData(Element* aElement,
                                                   nsIAtom* aTag,
                                                   PRInt32 aNameSpaceID,
                                                   nsIFrame* aParentFrame,
                                                   nsStyleContext* aStyleContext);
 
-  nsresult ConstructSVGForeignObjectFrame(nsFrameConstructorState& aState,
-                                          FrameConstructionItem&   aItem,
-                                          nsIFrame* aParentFrame,
-                                          const nsStyleDisplay* aStyleDisplay,
-                                          nsFrameItems& aFrameItems,
-                                          nsIFrame** aNewFrame);
-
   /* Not static because it does PropagateScrollToViewport.  If this
      changes, make this static */
   const FrameConstructionData*
     FindDisplayData(const nsStyleDisplay* aDisplay, Element* aElement,
                     nsStyleContext* aStyleContext);
 
   /**
    * Construct a scrollable block frame
@@ -1383,25 +1375,29 @@ private:
    *        (not necessary aFrame's style context).
    * @param aCanHaveGeneratedContent Whether to allow :before and
    *        :after styles on the parent.
    * @param aFrameItems the list in which we should place the in-flow children
    * @param aAllowBlockStyles Whether to allow first-letter and first-line
    *        styles on the parent.
    * @param aPendingBinding Make sure to push this into aState before doing any
    *        child item construction.
+   * @param aPossiblyLeafFrame if non-null, this should be used for the isLeaf
+   *        test and the anonymous content creation.  If null, aFrame will be
+   *        used.
    */
   nsresult ProcessChildren(nsFrameConstructorState& aState,
                            nsIContent*              aContent,
                            nsStyleContext*          aStyleContext,
                            nsIFrame*                aFrame,
                            const bool               aCanHaveGeneratedContent,
                            nsFrameItems&            aFrameItems,
                            const bool               aAllowBlockStyles,
-                           PendingBinding*          aPendingBinding);
+                           PendingBinding*          aPendingBinding,
+                           nsIFrame*                aPossiblyLeafFrame = nsnull);
 
   nsIFrame* GetFrameFor(nsIContent* aContent);
 
   /**
    * These two functions are used when we start frame creation from a non-root
    * element. They should recreate the same state that we would have
    * arrived at if we had built frames from the root frame to aFrame.
    * Therefore, any calls to PushFloatContainingBlock and
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button-first-letter-1-noref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<style>
+  body { line-height: 0; }
+</style>
+<button>Some text</button>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button-first-letter-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<style>
+  button { display: block; }
+  button::first-letter { color: green; }
+  body { line-height: 0; }
+</style>
+<button>Some text</button>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button-first-letter-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+  button::first-letter { color: green; }
+  body { line-height: 0; }
+</style>
+<button>Some text</button>
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -56,16 +56,19 @@ fails-if(Android) != textarea-rtl.html t
 != checkbox-checked-native-notref.html about:blank
 != radio-checked-native.html about:blank
 != radio-checked-native-notref.html about:blank
 
 == select-multiple.html select-multiple-ref.html
 == select-boguskids.html select-boguskids-ref.html
 == select-dynamic-boguskids.html select-boguskids-ref.html
 
+asserts(2) == button-first-letter-1.html button-first-letter-1-ref.html
+asserts(1) != button-first-letter-1.html button-first-letter-1-noref.html
+
 # placeholder
 include placeholder/reftest.list
 
 # input
 include input/reftest.list
 
 # output element
 include output/reftest.list