Bug 261037. Support scrolled fieldsets. r=mats
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 23 Oct 2013 00:46:40 +0200
changeset 166898 44de05b3239bac97f65da0d68462baa2a0ed3042
parent 166897 c79088ab4e0c3c44a3352610effae537dc5b698b
child 166899 c1578a4fc86d3d85fd2166947d55c078cc72391e
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs261037
milestone27.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 261037. Support scrolled fieldsets. r=mats
content/base/src/Element.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/forms/nsFieldSetFrame.cpp
layout/reftests/bugs/478811-1.html
layout/reftests/forms/fieldset/dynamic-legend-scroll-1-ref.html
layout/reftests/forms/fieldset/dynamic-legend-scroll-1.html
layout/reftests/forms/fieldset/fieldset-hidden-1-ref.html
layout/reftests/forms/fieldset/fieldset-hidden-1.html
layout/reftests/forms/fieldset/fieldset-intrinsic-width-1-ref.html
layout/reftests/forms/fieldset/fieldset-intrinsic-width-1.html
layout/reftests/forms/fieldset/fieldset-overflow-auto-1-ref.html
layout/reftests/forms/fieldset/fieldset-overflow-auto-1.html
layout/reftests/forms/fieldset/fieldset-percentage-padding-1-ref.html
layout/reftests/forms/fieldset/fieldset-percentage-padding-1.html
layout/reftests/forms/fieldset/fieldset-scroll-1-ref.html
layout/reftests/forms/fieldset/fieldset-scroll-1.html
layout/reftests/forms/fieldset/fieldset-scrolled-1-ref.html
layout/reftests/forms/fieldset/fieldset-scrolled-1.html
layout/reftests/forms/fieldset/positioned-container-1-ref.html
layout/reftests/forms/fieldset/positioned-container-1.html
layout/reftests/forms/fieldset/reftest.list
layout/reftests/forms/reftest.list
layout/style/forms.css
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -497,18 +497,18 @@ Element::GetScrollFrame(nsIFrame **aStyl
     *aStyledFrame = frame;
   }
   if (!frame) {
     return nullptr;
   }
 
   // menu frames implement GetScrollTargetFrame but we don't want
   // to use it here.  Similar for comboboxes.
-  if (frame->GetType() != nsGkAtoms::menuFrame &&
-      frame->GetType() != nsGkAtoms::comboboxControlFrame) {
+  nsIAtom* type = frame->GetType();
+  if (type != nsGkAtoms::menuFrame && type != nsGkAtoms::comboboxControlFrame) {
     nsIScrollableFrame *scrollFrame = frame->GetScrollTargetFrame();
     if (scrollFrame)
       return scrollFrame;
   }
 
   nsIDocument* doc = OwnerDoc();
   bool quirksMode = doc->GetCompatibilityMode() == eCompatibility_NavQuirks;
   Element* elementWithRootScrollInfo =
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3045,75 +3045,90 @@ nsCSSFrameConstructor::ConstructFieldSet
                                               FrameConstructionItem&   aItem,
                                               nsIFrame*                aParentFrame,
                                               const nsStyleDisplay*    aStyleDisplay,
                                               nsFrameItems&            aFrameItems)
 {
   nsIContent* const content = aItem.mContent;
   nsStyleContext* const styleContext = aItem.mStyleContext;
 
-  nsIFrame* newFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
+  nsIFrame* fieldsetFrame = NS_NewFieldSetFrame(mPresShell, styleContext);
 
   // Initialize it
   InitAndRestoreFrame(aState, content,
                       aState.GetGeometricParent(aStyleDisplay, aParentFrame),
-                      newFrame);
+                      fieldsetFrame);
 
   // Resolve style and initialize the frame
   nsRefPtr<nsStyleContext> fieldsetContentStyle;
   fieldsetContentStyle = mPresShell->StyleSet()->
     ResolveAnonymousBoxStyle(nsCSSAnonBoxes::fieldsetContent, styleContext);
 
+  const nsStyleDisplay* fieldsetContentDisplay = fieldsetContentStyle->StyleDisplay();
+  bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow();
+  nsIFrame* scrollFrame = nullptr;
+  if (isScrollable) {
+    fieldsetContentStyle =
+      BeginBuildingScrollFrame(aState, content, fieldsetContentStyle,
+                               fieldsetFrame, nsCSSAnonBoxes::scrolledContent,
+                               false, scrollFrame);
+  }
+
   nsIFrame* blockFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle,
                                           NS_BLOCK_FLOAT_MGR |
                                           NS_BLOCK_MARGIN_ROOT);
-  InitAndRestoreFrame(aState, content, newFrame, blockFrame);
-
-  aState.AddChild(newFrame, aFrameItems, content, styleContext, aParentFrame);
+  InitAndRestoreFrame(aState, content,
+    scrollFrame ? scrollFrame : fieldsetFrame, blockFrame);
+
+  aState.AddChild(fieldsetFrame, aFrameItems, content, styleContext, aParentFrame);
   
   // Process children
   nsFrameConstructorSaveState absoluteSaveState;
   nsFrameItems                childItems;
 
-  newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
-  if (newFrame->IsPositioned()) {
-    aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
+  blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+  if (fieldsetFrame->IsPositioned()) {
+    aState.PushAbsoluteContainingBlock(blockFrame, fieldsetFrame, absoluteSaveState);
   }
 
   ProcessChildren(aState, content, styleContext, blockFrame, true,
                   childItems, true, aItem.mPendingBinding);
 
   nsFrameItems fieldsetKids;
-  fieldsetKids.AddChild(blockFrame);
+  fieldsetKids.AddChild(scrollFrame ? scrollFrame : blockFrame);
 
   for (nsFrameList::Enumerator e(childItems); !e.AtEnd(); e.Next()) {
     nsIFrame* child = e.get();
     if (child->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame) {
       // We want the legend to be the first frame in the fieldset child list.
       // That way the EventStateManager will do the right thing when tabbing
       // from a selection point within the legend (bug 236071), which is
       // used for implementing legend access keys (bug 81481).
       // GetAdjustedParentFrame() below depends on this frame order.
       childItems.RemoveFrame(child);
       // Make sure to reparent the legend so it has the fieldset as the parent.
-      fieldsetKids.InsertFrame(newFrame, nullptr, child);
+      fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
       break;
     }
   }
 
+  if (isScrollable) {
+    FinishBuildingScrollFrame(scrollFrame, blockFrame);
+  }
+
   // Set the inner frame's initial child lists
   blockFrame->SetInitialChildList(kPrincipalList, childItems);
 
   // Set the outer frame's initial child list
-  newFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
-
-  newFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
+  fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
+
+  fieldsetFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
 
   // Our new frame returned is the outer frame, which is the fieldset frame.
-  return newFrame; 
+  return fieldsetFrame; 
 }
 
 static nsIFrame*
 FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
 {
   for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
     NS_ASSERTION(f->IsGeneratedContentFrame(),
                  "should not have exited generated content");
@@ -3257,16 +3272,27 @@ nsCSSFrameConstructor::FindDataByTag(nsI
 
 #define SIMPLE_TAG_CREATE(_tag, _func)          \
   { &nsGkAtoms::_tag, SIMPLE_FCDATA(_func) }
 #define SIMPLE_TAG_CHAIN(_tag, _func)                                   \
   { &nsGkAtoms::_tag, FCDATA_DECL(FCDATA_FUNC_IS_DATA_GETTER,  _func) }
 #define COMPLEX_TAG_CREATE(_tag, _func)             \
   { &nsGkAtoms::_tag, FULL_CTOR_FCDATA(0, _func) }
 
+static bool
+IsFrameForFieldSet(nsIFrame* aFrame, nsIAtom* aFrameType)
+{
+  nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
+  if (pseudo == nsCSSAnonBoxes::fieldsetContent ||
+      pseudo == nsCSSAnonBoxes::scrolledContent) {
+    return IsFrameForFieldSet(aFrame->GetParent(), aFrame->GetParent()->GetType());
+  }
+  return aFrameType == nsGkAtoms::fieldSetFrame;
+}
+
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindHTMLData(Element* aElement,
                                     nsIAtom* aTag,
                                     int32_t aNameSpaceID,
                                     nsIFrame* aParentFrame,
                                     nsStyleContext* aStyleContext)
 {
@@ -3279,19 +3305,17 @@ nsCSSFrameConstructor::FindHTMLData(Elem
 
   NS_ASSERTION(!aParentFrame ||
                aParentFrame->StyleContext()->GetPseudo() !=
                  nsCSSAnonBoxes::fieldsetContent ||
                aParentFrame->GetParent()->GetType() == nsGkAtoms::fieldSetFrame,
                "Unexpected parent for fieldset content anon box");
   if (aTag == nsGkAtoms::legend &&
       (!aParentFrame ||
-       (aParentFrame->GetType() != nsGkAtoms::fieldSetFrame &&
-        aParentFrame->StyleContext()->GetPseudo() !=
-          nsCSSAnonBoxes::fieldsetContent) ||
+       !IsFrameForFieldSet(aParentFrame, aParentFrame->GetType()) ||
        !aElement->GetParent() ||
        !aElement->GetParent()->IsHTML(nsGkAtoms::fieldset) ||
        aStyleContext->StyleDisplay()->IsFloatingStyle() ||
        aStyleContext->StyleDisplay()->IsAbsolutelyPositionedStyle())) {
     // <legend> is only special inside fieldset, check both the frame tree
     // parent and content tree parent due to XBL issues. For floated or
     // absolutely positioned legends we want to construct by display type and
     // not do special legend stuff.
@@ -5914,23 +5938,18 @@ nsCSSFrameConstructor::AppendFramesToPar
 // content perspective, they are not considered siblings in the frame tree.
 bool
 nsCSSFrameConstructor::IsValidSibling(nsIFrame*              aSibling,
                                       nsIContent*            aContent,
                                       uint8_t&               aDisplay)
 {
   nsIFrame* parentFrame = aSibling->GetParent();
   nsIAtom* parentType = nullptr;
-  nsIAtom* grandparentType = nullptr;
   if (parentFrame) {
     parentType = parentFrame->GetType();
-    nsIFrame* grandparentFrame = parentFrame->GetParent();
-    if (grandparentFrame) {
-      grandparentType = grandparentFrame->GetType();
-    }
   }
 
   uint8_t siblingDisplay = aSibling->GetDisplay();
   if ((NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == siblingDisplay) ||
       (NS_STYLE_DISPLAY_TABLE_COLUMN       == siblingDisplay) ||
       (NS_STYLE_DISPLAY_TABLE_CAPTION      == siblingDisplay) ||
       (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == siblingDisplay) ||
       (NS_STYLE_DISPLAY_TABLE_ROW_GROUP    == siblingDisplay) ||
@@ -5979,19 +5998,17 @@ nsCSSFrameConstructor::IsValidSibling(ns
          aDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN)) {
       // One's a column or column group and the other is not.  Not valid
       // siblings.
       return false;
     }
 
     return true;
   }
-  else if (nsGkAtoms::fieldSetFrame == parentType ||
-           (nsGkAtoms::fieldSetFrame == grandparentType &&
-            nsGkAtoms::blockFrame == parentType)) {
+  else if (IsFrameForFieldSet(parentFrame, parentType)) {
     // Legends can be sibling of legends but not of other content in the fieldset
     nsIAtom* sibType = aSibling->GetContentInsertionFrame()->GetType();
     bool legendContent = aContent->IsHTML(nsGkAtoms::legend);
 
     if ((legendContent  && (nsGkAtoms::legendFrame != sibType)) ||
         (!legendContent && (nsGkAtoms::legendFrame == sibType)))
       return false;
   }
@@ -7078,17 +7095,17 @@ nsCSSFrameConstructor::ContentRangeInser
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   // 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 &&
+  if (IsFrameForFieldSet(parentFrame, frameType) &&
       aStartChild->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.
     LAYOUT_PHASE_TEMP_EXIT();
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2820,17 +2820,18 @@ GetIntrinsicCoord(const nsStyleCoord& aS
 
 #ifdef DEBUG_INTRINSIC_WIDTH
 static int32_t gNoiseIndent = 0;
 #endif
 
 /* static */ nscoord
 nsLayoutUtils::IntrinsicForContainer(nsRenderingContext *aRenderingContext,
                                      nsIFrame *aFrame,
-                                     IntrinsicWidthType aType)
+                                     IntrinsicWidthType aType,
+                                     uint32_t aFlags)
 {
   NS_PRECONDITION(aFrame, "null frame");
   NS_PRECONDITION(aType == MIN_WIDTH || aType == PREF_WIDTH, "bad type");
 
 #ifdef DEBUG_INTRINSIC_WIDTH
   nsFrame::IndentBy(stdout, gNoiseIndent);
   static_cast<nsFrame*>(aFrame)->ListTag(stdout);
   printf(" %s intrinsic width for container:\n",
@@ -2914,25 +2915,27 @@ nsLayoutUtils::IntrinsicForContainer(nsR
         switch (boxSizing) {
         case NS_STYLE_BOX_SIZING_BORDER: {
           const nsStyleBorder* styleBorder = aFrame->StyleBorder();
           heightTakenByBoxSizing +=
             styleBorder->GetComputedBorder().TopBottom();
           // fall through
         }
         case NS_STYLE_BOX_SIZING_PADDING: {
-          const nsStylePadding* stylePadding = aFrame->StylePadding();
-          nscoord pad;
-          if (GetAbsoluteCoord(stylePadding->mPadding.GetTop(), pad) ||
-              GetPercentHeight(stylePadding->mPadding.GetTop(), aFrame, pad)) {
-            heightTakenByBoxSizing += pad;
-          }
-          if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) ||
-              GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) {
-            heightTakenByBoxSizing += pad;
+          if (!(aFlags & IGNORE_PADDING)) {
+            const nsStylePadding* stylePadding = aFrame->StylePadding();
+            nscoord pad;
+            if (GetAbsoluteCoord(stylePadding->mPadding.GetTop(), pad) ||
+                GetPercentHeight(stylePadding->mPadding.GetTop(), aFrame, pad)) {
+              heightTakenByBoxSizing += pad;
+            }
+            if (GetAbsoluteCoord(stylePadding->mPadding.GetBottom(), pad) ||
+                GetPercentHeight(stylePadding->mPadding.GetBottom(), aFrame, pad)) {
+              heightTakenByBoxSizing += pad;
+            }
           }
           // fall through
         }
         case NS_STYLE_BOX_SIZING_CONTENT:
         default:
           break;
         }
 
@@ -2973,28 +2976,32 @@ nsLayoutUtils::IntrinsicForContainer(nsR
 
   // We also need to track what has been added on outside of the box
   // (controlled by 'box-sizing') where 'width', 'min-width' and
   // 'max-width' are applied.  We have to account for these properties
   // after getting all the offsets (margin, border, padding) because
   // percentages do not operate linearly.
   // Doing this is ok because although percentages aren't handled
   // linearly, they are handled monotonically.
-  nscoord coordOutsideWidth = offsets.hPadding;
-  float pctOutsideWidth = offsets.hPctPadding;
-
+  nscoord coordOutsideWidth = 0;
+  float pctOutsideWidth = 0;
   float pctTotal = 0.0f;
 
-  if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) {
-    min += coordOutsideWidth;
-    result = NSCoordSaturatingAdd(result, coordOutsideWidth);
-    pctTotal += pctOutsideWidth;
-
-    coordOutsideWidth = 0;
-    pctOutsideWidth = 0.0f;
+  if (!(aFlags & IGNORE_PADDING)) {
+    coordOutsideWidth += offsets.hPadding;
+    pctOutsideWidth += offsets.hPctPadding;
+
+    if (boxSizing == NS_STYLE_BOX_SIZING_PADDING) {
+      min += coordOutsideWidth;
+      result = NSCoordSaturatingAdd(result, coordOutsideWidth);
+      pctTotal += pctOutsideWidth;
+
+      coordOutsideWidth = 0;
+      pctOutsideWidth = 0.0f;
+    }
   }
 
   coordOutsideWidth += offsets.hBorder;
 
   if (boxSizing == NS_STYLE_BOX_SIZING_BORDER) {
     min += coordOutsideWidth;
     result = NSCoordSaturatingAdd(result, coordOutsideWidth);
     pctTotal += pctOutsideWidth;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -983,19 +983,23 @@ public:
 
   /**
    * Get the contribution of aFrame to its containing block's intrinsic
    * width.  This considers the child's intrinsic width, its 'width',
    * 'min-width', and 'max-width' properties, and its padding, border,
    * and margin.
    */
   enum IntrinsicWidthType { MIN_WIDTH, PREF_WIDTH };
+  enum {
+    IGNORE_PADDING = 0x01
+  };
   static nscoord IntrinsicForContainer(nsRenderingContext* aRenderingContext,
                                        nsIFrame* aFrame,
-                                       IntrinsicWidthType aType);
+                                       IntrinsicWidthType aType,
+                                       uint32_t aFlags = 0);
 
   /*
    * Convert nsStyleCoord to nscoord when percentages depend on the
    * containing block size.
    * @param aPercentBasis The width or height of the containing block
    * (whichever the client wants to use for resolving percentages).
    */
   static nscoord ComputeCBDependentValue(nscoord aPercentBasis,
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -10,16 +10,17 @@
 #include <algorithm>
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 #include "RestyleManager.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsDisplayList.h"
 #include "nsRenderingContext.h"
+#include "nsIScrollableFrame.h"
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
 using namespace mozilla::layout;
 
 class nsFieldSetFrame MOZ_FINAL : public nsContainerFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
@@ -73,16 +74,20 @@ public:
                          nsIFrame*      aOldFrame);
 
   virtual nsIAtom* GetType() const;
   virtual bool IsFrameOfType(uint32_t aFlags) const
   {
     return nsContainerFrame::IsFrameOfType(aFlags &
              ~nsIFrame::eCanContainOverflowContainers);
   }
+  virtual nsIScrollableFrame* GetScrollTargetFrame() MOZ_OVERRIDE
+  {
+    return do_QueryFrame(GetInner());
+  }
 
 #ifdef ACCESSIBILITY  
   virtual mozilla::a11y::AccType AccessibleType() MOZ_OVERRIDE;
 #endif
 
 #ifdef DEBUG
   NS_IMETHOD SetInitialChildList(ChildListID    aListID,
                                  nsFrameList&   aChildList);
@@ -351,20 +356,24 @@ nsFieldSetFrame::GetIntrinsicWidth(nsRen
   nscoord legendWidth = 0;
   nscoord contentWidth = 0;
   if (nsIFrame* legend = GetLegend()) {
     legendWidth =
       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, legend, aType);
   }
 
   if (nsIFrame* inner = GetInner()) {
+    // Ignore padding on the inner, since the padding will be applied to the
+    // outer instead, and the padding computed for the inner is wrong
+    // for percentage padding.
     contentWidth =
-      nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType);
+      nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType,
+                                           nsLayoutUtils::IGNORE_PADDING);
   }
-      
+
   return std::max(legendWidth, contentWidth);
 }
 
 
 nscoord
 nsFieldSetFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
 {
   nscoord result = 0;
@@ -439,33 +448,35 @@ nsFieldSetFrame::Reflow(nsPresContext*  
     reflowLegend = legend != nullptr;
   } else {
     reflowInner = inner && NS_SUBTREE_DIRTY(inner);
     reflowLegend = legend && NS_SUBTREE_DIRTY(legend);
   }
 
   // We don't allow fieldsets to break vertically. If we did, we'd
   // need logic here to push and pull overflow frames.
-  nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
+  // Since we're not applying our padding in this frame, we need to add it here
+  // to compute the available width for our children.
+  nsSize availSize(aReflowState.ComputedWidth() + aReflowState.mComputedPadding.LeftRight(),
+                   NS_UNCONSTRAINEDSIZE);
   NS_ASSERTION(!inner ||
       nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
                                            inner,
                                            nsLayoutUtils::MIN_WIDTH) <=
                availSize.width,
                "Bogus availSize.width; should be bigger");
   NS_ASSERTION(!legend ||
       nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
                                            legend,
                                            nsLayoutUtils::MIN_WIDTH) <=
                availSize.width,
                "Bogus availSize.width; should be bigger");
 
   // get our border and padding
-  const nsMargin &borderPadding = aReflowState.mComputedBorderPadding;
-  nsMargin border = borderPadding - aReflowState.mComputedPadding;  
+  nsMargin border = aReflowState.mComputedBorderPadding - aReflowState.mComputedPadding;
 
   // Figure out how big the legend is if there is one. 
   // get the legend's margin
   nsMargin legendMargin(0,0,0,0);
   // reflow the legend only if needed
   if (reflowLegend) {
     nsHTMLReflowState legendReflowState(aPresContext, aReflowState,
                                         legend, availSize);
@@ -476,17 +487,17 @@ nsFieldSetFrame::Reflow(nsPresContext*  
                 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
 #ifdef NOISY_REFLOW
     printf("  returned (%d, %d)\n", legendDesiredSize.width, legendDesiredSize.height);
 #endif
     // figure out the legend's rectangle
     legendMargin = legend->GetUsedMargin();
     mLegendRect.width  = legendDesiredSize.width + legendMargin.left + legendMargin.right;
     mLegendRect.height = legendDesiredSize.height + legendMargin.top + legendMargin.bottom;
-    mLegendRect.x = borderPadding.left;
+    mLegendRect.x = 0;
     mLegendRect.y = 0;
 
     nscoord oldSpace = mLegendSpace;
     mLegendSpace = 0;
     if (mLegendRect.height > border.top) {
       // center the border on the legend
       mLegendSpace = mLegendRect.height - border.top;
     } else {
@@ -508,76 +519,90 @@ nsFieldSetFrame::Reflow(nsPresContext*  
     // mLegendSpace and mLegendRect haven't changed, but we need
     // the used margin when placing the legend.
     legendMargin = legend->GetUsedMargin();
   }
 
   // reflow the content frame only if needed
   if (reflowInner) {
     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner,
-                                     availSize);
+                                     availSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
+    // Override computed padding, in case it's percentage padding
+    kidReflowState.Init(aPresContext, -1, -1, nullptr,
+                        &aReflowState.mComputedPadding);
     // Our child is "height:100%" but we actually want its height to be reduced
     // by the amount of content-height the legend is eating up, unless our
     // height is unconstrained (in which case the child's will be too).
     if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
-      kidReflowState.SetComputedHeight(std::max(0, aReflowState.ComputedHeight() - mLegendSpace));
+      kidReflowState.SetComputedHeight(
+         std::max(0, aReflowState.ComputedHeight() - mLegendSpace));
     }
 
-    kidReflowState.mComputedMinHeight =
-      std::max(0, aReflowState.mComputedMinHeight - mLegendSpace);
+    if (aReflowState.mComputedMinHeight > 0) {
+      kidReflowState.mComputedMinHeight =
+        std::max(0, aReflowState.mComputedMinHeight - mLegendSpace);
+    }
 
     if (aReflowState.mComputedMaxHeight != NS_UNCONSTRAINEDSIZE) {
       kidReflowState.mComputedMaxHeight =
         std::max(0, aReflowState.mComputedMaxHeight - mLegendSpace);
     }
 
     nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags);
     // Reflow the frame
     NS_ASSERTION(kidReflowState.mComputedMargin == nsMargin(0,0,0,0),
                  "Margins on anonymous fieldset child not supported!");
-    nsPoint pt(borderPadding.left, borderPadding.top + mLegendSpace);
+    nsPoint pt(border.left, border.top + mLegendSpace);
     ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState,
                 pt.x, pt.y, 0, aStatus);
 
     FinishReflowChild(inner, aPresContext, &kidReflowState, 
                       kidDesiredSize, pt.x, pt.y, 0);
     NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
   }
 
-  nsRect contentRect(0,0,0,0);
+  nsRect contentRect;
   if (inner) {
-    // We don't support margins on inner, so our "content rect" is just
-    // its rect.
+    // We don't support margins on inner, so our content rect is just the
+    // inner's border-box.
     contentRect = inner->GetRect();
   }
 
-  // use the computed width if the inner content does not fill it
-  if (aReflowState.ComputedWidth() > contentRect.width) {
-    contentRect.width = aReflowState.ComputedWidth();
+  // Our content rect must fill up the available width
+  if (availSize.width > contentRect.width) {
+    contentRect.width = availSize.width;
   }
 
   if (legend) {
-    // if the content rect is larger then the  legend we can align the legend
-    if (contentRect.width > mLegendRect.width) {
+    // the legend is postioned horizontally within the inner's content rect
+    // (so that padding on the fieldset affects the legend position).
+    nsRect innerContentRect = contentRect;
+    innerContentRect.Deflate(aReflowState.mComputedPadding);
+    // if the inner content rect is larger than the legend, we can align the legend
+    if (innerContentRect.width > mLegendRect.width) {
       int32_t align = static_cast<nsLegendFrame*>
         (legend->GetContentInsertionFrame())->GetAlign();
 
-      switch(align) {
+      switch (align) {
         case NS_STYLE_TEXT_ALIGN_RIGHT:
-          mLegendRect.x = contentRect.width - mLegendRect.width + borderPadding.left;
+          mLegendRect.x = innerContentRect.XMost() - mLegendRect.width;
           break;
         case NS_STYLE_TEXT_ALIGN_CENTER:
           // Note: rounding removed; there doesn't seem to be any need
-          mLegendRect.x = contentRect.width / 2 - mLegendRect.width / 2 + borderPadding.left;
+          mLegendRect.x = innerContentRect.width / 2 - mLegendRect.width / 2 + innerContentRect.x;
+          break;
+        default:
+          mLegendRect.x = innerContentRect.x;
           break;
       }
-  
     } else {
       // otherwise make place for the legend
-      contentRect.width = mLegendRect.width;
+      mLegendRect.x = innerContentRect.x;
+      innerContentRect.width = mLegendRect.width;
+      contentRect.width = mLegendRect.width + aReflowState.mComputedPadding.LeftRight();
     }
     // place the legend
     nsRect actualLegendRect(mLegendRect);
     actualLegendRect.Deflate(legendMargin);
 
     nsPoint curOrigin = legend->GetPosition();
 
     // only if the origin changed
@@ -589,26 +614,26 @@ nsFieldSetFrame::Reflow(nsPresContext*  
       // children since we're moving the frame after Reflow.
       nsContainerFrame::PositionChildViews(legend);
     }
   }
 
   // Return our size and our result
   if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
     aDesiredSize.height = mLegendSpace + 
-                          borderPadding.TopBottom() +
-                          contentRect.height;
+                          border.TopBottom() +
+                          (inner ? inner->GetRect().height : 0);
   } else {
-    nscoord min = borderPadding.TopBottom() + mLegendRect.height;
+    nscoord min = border.TopBottom() + mLegendRect.height;
     aDesiredSize.height =
-      aReflowState.ComputedHeight() + borderPadding.TopBottom();
+      aReflowState.ComputedHeight() + aReflowState.mComputedBorderPadding.TopBottom();
     if (aDesiredSize.height < min)
       aDesiredSize.height = min;
   }
-  aDesiredSize.width = contentRect.width + borderPadding.LeftRight();
+  aDesiredSize.width = contentRect.width + border.LeftRight();
   aDesiredSize.SetOverflowAreasToDesiredBounds();
   if (legend)
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend);
   if (inner)
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner);
 
   // Merge overflow container bounds and status.
   aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
@@ -676,14 +701,11 @@ nsFieldSetFrame::ReparentFrameList(const
     e.get()->SetParent(inner);
     restyleManager->ReparentStyleContext(e.get());
   }
 }
 
 nscoord
 nsFieldSetFrame::GetBaseline() const
 {
-  // We know inner is a block, so calling GetBaseline() on it will do
-  // the right thing (that being to return the baseline of the last line).
   nsIFrame* inner = GetInner();
-  NS_ASSERTION(nsLayoutUtils::GetAsBlock(inner), "Unexpected inner");
   return inner->GetPosition().y + inner->GetBaseline();
 }
--- a/layout/reftests/bugs/478811-1.html
+++ b/layout/reftests/bugs/478811-1.html
@@ -1,19 +1,21 @@
 <!DOCTYPE html>
-<html style="height: 100%">
+<html class="reftest-wait" style="height: 100%">
   <head>
     <script>
       function doIt() {
         var d = document.createElement("div");
         d.setAttribute("style",
                        "position: absolute; left: 0; right: 0; top: 0; " +
                        "bottom: 0; background: green");
         document.getElementById("x").appendChild(d);
+        document.documentElement.removeAttribute("class");
       }
+      window.addEventListener("MozReftestInvalidate", doIt);
     </script>
   </head>
-  <body style="height: 100%" onload="doIt()">
+  <body style="height: 100%">
     <fieldset id="x" style="position: relative; display: table; width: 50%;
                             height: 50%; top: 25%; left: 25%">
     </fieldset>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/dynamic-legend-scroll-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<style>
+fieldset { 
+  background:pink;
+  overflow:hidden;
+  height:100px;
+}
+legend::after { content:"legend"; }
+p {
+  background:lime;
+  height:20px;
+}
+</style>
+</head>
+<body>
+<fieldset id="f1"><legend></legend><p></p></fieldset>
+<br>
+<fieldset id="f2"><legend></legend><p></p></fieldset>
+<br>
+<fieldset id="f3"><legend></legend></fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/dynamic-legend-scroll-1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<style>
+fieldset { 
+  background:pink;
+  overflow:hidden;
+  height:100px;
+}
+legend::after { content:"legend"; }
+p {
+  background:lime;
+  height:20px;
+}
+</style>
+</head>
+<body>
+<fieldset id="f1"><p></p></fieldset>
+<br>
+<fieldset id="f2"><p></p></fieldset>
+<br>
+<fieldset id="f3"></fieldset>
+<script>
+function doTest() {
+  f1.appendChild(document.createElement('legend'));
+  f2.insertBefore(document.createElement('legend'), f2.firstChild);
+  f3.appendChild(document.createElement('legend'));
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-hidden-1-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="width:200px; height:200px; padding:0">
+  <legend>Legend</legend>
+  <div style="overflow:hidden; height:100%;">
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+  </div>
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-hidden-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="overflow:hidden; width:200px; height:200px; padding:0">
+  <legend>Legend</legend>
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-intrinsic-width-1-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="width:0; padding:0;">
+  <div style="width:400px; height:200px;"></div>
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-intrinsic-width-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="padding:50px; float:left;">
+  <div style="width:300px; height:100px;"></div>
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-overflow-auto-1-ref.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 261037</title>
+    <style type="text/css">
+fieldset, div { 
+  background:pink;
+  overflow:auto;
+  height:100px;
+  margin:0; padding:0; border:0;
+}
+p {
+  background:blue;
+  height:100px;
+  margin:0; padding:0;
+}
+.overflow {
+  height:110px;
+}
+.abs {
+  position:absolute;
+  width:100px;
+  top:250px;
+}
+p.abs {
+  top:0;left:0;
+}
+.b { border:10px solid black; }
+.p { padding: 7px 0; }
+.p p { height:114px; }
+    </style>
+</head>
+<body>
+
+<div><p></p></div>
+<br>
+<div><p class="overflow"></p></div>
+<br>
+<div class="abs"><p class="abs"></p></div>
+<br>
+<div class="abs" style="left:120px"><p class="abs overflow"></p></div>
+<br>
+<div class="abs b" style="left:240px"><p class="abs"></p></div>
+<br>
+<div class="abs b" style="left:370px"><p class="abs overflow"></p></div>
+<br>
+<div class="abs b p" style="left:510px"><p class="abs"></p></div>
+<br>
+<div class="abs b p" style="left:640px"><p class="abs overflow"></p></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-overflow-auto-1.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 261037</title>
+    <style type="text/css">
+fieldset, div { 
+  background:pink;
+  overflow:auto;
+  height:100px;
+  margin:0; padding:0; border:0;
+}
+p {
+  background:blue;
+  height:100px;
+  margin:0; padding:0;
+}
+.overflow {
+  height:110px;
+}
+.abs {
+  position:absolute;
+  width:100px;
+  top:250px;
+}
+p.abs {
+  top:0;left:0;
+}
+.b { border:10px solid black; }
+.p { padding: 7px 0; }
+.p p { height:114px; }
+    </style>
+</head>
+<body>
+
+<fieldset><p></p></fieldset>
+<br>
+<fieldset><p class="overflow"></p></fieldset>
+<br>
+<fieldset class="abs"><p class="abs"></p></fieldset>
+<br>
+<fieldset class="abs" style="left:120px"><p class="abs overflow"></p></fieldset>
+<br>
+<fieldset class="abs b" style="left:240px"><p class="abs"></p></fieldset>
+<br>
+<fieldset class="abs b" style="left:370px"><p class="abs overflow"></p></fieldset>
+<br>
+<fieldset class="abs b p" style="left:510px"><p class="abs"></p></fieldset>
+<br>
+<fieldset class="abs b p" style="left:640px"><p class="abs overflow"></p></fieldset>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-percentage-padding-1-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="width:700px; border:2px solid green; overflow:hidden">
+  <fieldset style="padding:140px; width:400px;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+  <fieldset style="padding:140px; display:inline;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+  <fieldset style="padding:140px; float:left;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-percentage-padding-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="width:700px; border:2px solid green; overflow:hidden">
+  <fieldset style="padding:20%; width:400px;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+  <fieldset style="padding:20%; display:inline;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+  <fieldset style="padding:20%; float:left;">
+    <legend>Legend</legend>
+    TextTextTextTextTextText
+  </fieldset>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-scroll-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="width:200px; height:200px; padding:0">
+  <legend>Legend</legend>
+  <div style="overflow:scroll; height:100%">
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-scroll-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="overflow:scroll; width:200px; height:200px; padding:0">
+  <legend>Legend</legend>
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-scrolled-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="width:200px; height:200px; padding:0">
+  <legend style="overflow:hidden">Legend</legend>
+  <div id="d" style="overflow:hidden; height:100%;">
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+    <p>Hello Kitty
+  </div>
+</fieldset>
+<script>
+d.scrollTop = 20;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/fieldset-scrolled-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body>
+<fieldset id="f" style="overflow:hidden; width:200px; height:200px; padding:0">
+  <legend style="overflow:hidden">Legend</legend>
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+  <p>Hello Kitty
+</fieldset>
+<script>
+f.scrollTop = 0;
+function doTest() {
+  f.scrollTop = 20;
+  document.documentElement.removeAttribute("class");
+}
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/positioned-container-1-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset style="position:relative; overflow:hidden; width:500px; height:500px;">
+  <legend>Legend</legend>
+  <div style="height:1000px;">
+    <div style="position:absolute; left:20px; top:20px; background:yellow;">Abs-pos</div>
+  </div>
+</fieldset>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/positioned-container-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<fieldset id="f" style="position:relative; overflow:hidden; width:500px; height:500px;">
+  <legend>Legend</legend>
+  <div style="height:1000px;">
+    <div style="position:absolute; left:20px; top:50px; background:yellow;">Abs-pos</div>
+  </div>
+</fieldset>
+<script>
+f.scrollTop = 30;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/fieldset/reftest.list
@@ -0,0 +1,8 @@
+== dynamic-legend-scroll-1.html dynamic-legend-scroll-1-ref.html
+== fieldset-hidden-1.html fieldset-hidden-1-ref.html
+== fieldset-intrinsic-width-1.html fieldset-intrinsic-width-1-ref.html
+== fieldset-percentage-padding-1.html fieldset-percentage-padding-1-ref.html
+== fieldset-scroll-1.html fieldset-scroll-1-ref.html
+== fieldset-scrolled-1.html fieldset-scrolled-1-ref.html
+== fieldset-overflow-auto-1.html fieldset-overflow-auto-1-ref.html
+== positioned-container-1.html positioned-container-1-ref.html
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -1,16 +1,19 @@
 skip-if(B2G) fails-if(Android) HTTP(..) == text-control-baseline-1.html text-control-baseline-1-ref.html
 
 # button element
 include button/reftest.list
 
 # legend element
 include legend/reftest.list
 
+# fieldset element
+include fieldset/reftest.list
+
 # placeholder
 include placeholder/reftest.list
 
 # input
 include input/reftest.list
 
 # meter element
 include meter/reftest.list
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -9,16 +9,19 @@
 
 @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */
 @namespace xul url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
 
 *|*::-moz-fieldset-content {
   display: block;
   unicode-bidi: inherit;
   text-overflow: inherit;
+  overflow: inherit;
+  padding: inherit;
+  position: inherit;
   height: 100%;   /* Need this so percentage heights of kids work right */
 }
 
 /* miscellaneous form elements */
 
 fieldset > legend {
   padding-left: 2px;
   padding-right: 2px;