Make 'top' and 'bottom' captions follow the CSS2.1 behavior and put the old behavior into 'top-outside' and 'bottom-outside'. b=363248,386704 r+sr=roc a=blocking1.9+
authordbaron@dbaron.org
Tue, 26 Feb 2008 18:02:20 -0800
changeset 12309 e56591cd45442e3cea5c37986dc90addd45ec359
parent 12308 cb136f670ea866f08ccd8a9a4c56a0b8640f0108
child 12310 dd3c51e8966fb585b4834a534d5ba9e6d143bbfc
push idunknown
push userunknown
push dateunknown
reviewersblocking1.9
bugs363248, 386704
milestone1.9b4pre
Make 'top' and 'bottom' captions follow the CSS2.1 behavior and put the old behavior into 'top-outside' and 'bottom-outside'. b=363248,386704 r+sr=roc a=blocking1.9+
content/html/content/src/nsHTMLTableCaptionElement.cpp
layout/base/nsStyleConsts.h
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/test/property_database.js
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
--- a/content/html/content/src/nsHTMLTableCaptionElement.cpp
+++ b/content/html/content/src/nsHTMLTableCaptionElement.cpp
@@ -103,20 +103,20 @@ NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLA
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLTableCaptionElement)
 
 
 NS_IMPL_STRING_ATTR(nsHTMLTableCaptionElement, Align, align)
 
 
 static const nsAttrValue::EnumTable kCaptionAlignTable[] = {
-  { "left",  NS_SIDE_LEFT },
-  { "right", NS_SIDE_RIGHT },
-  { "top",   NS_SIDE_TOP},
-  { "bottom",NS_SIDE_BOTTOM},
+  { "left",   NS_STYLE_CAPTION_SIDE_LEFT },
+  { "right",  NS_STYLE_CAPTION_SIDE_RIGHT },
+  { "top",    NS_STYLE_CAPTION_SIDE_TOP },
+  { "bottom", NS_STYLE_CAPTION_SIDE_BOTTOM },
   { 0 }
 };
 
 PRBool
 nsHTMLTableCaptionElement::ParseAttribute(PRInt32 aNamespaceID,
                                           nsIAtom* aAttribute,
                                           const nsAString& aValue,
                                           nsAttrValue& aResult)
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -623,17 +623,22 @@
 
 #define NS_STYLE_TABLE_LAYOUT_AUTO              0
 #define NS_STYLE_TABLE_LAYOUT_FIXED             1
 
 #define NS_STYLE_TABLE_EMPTY_CELLS_HIDE            0
 #define NS_STYLE_TABLE_EMPTY_CELLS_SHOW            1
 #define NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND 2
 
-// CAPTION_SIDE uses NS_SIDE_*
+#define NS_STYLE_CAPTION_SIDE_TOP               0
+#define NS_STYLE_CAPTION_SIDE_RIGHT             1
+#define NS_STYLE_CAPTION_SIDE_BOTTOM            2
+#define NS_STYLE_CAPTION_SIDE_LEFT              3
+#define NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE       4
+#define NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE    5
 
 // constants for cell "scope" attribute
 #define NS_STYLE_CELL_SCOPE_ROW                 0
 #define NS_STYLE_CELL_SCOPE_COL                 1
 #define NS_STYLE_CELL_SCOPE_ROWGROUP            2
 #define NS_STYLE_CELL_SCOPE_COLGROUP            3
 
 // See nsStylePage
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -204,16 +204,17 @@ CSS_KEY(blink, blink)
 CSS_KEY(block, block)
 CSS_KEY(block-axis, block_axis)
 CSS_KEY(bold, bold)
 CSS_KEY(bolder, bolder)
 CSS_KEY(border, border)
 CSS_KEY(border-box, border_box)
 CSS_KEY(both, both)
 CSS_KEY(bottom, bottom)
+CSS_KEY(bottom-outside, bottom_outside)
 CSS_KEY(bounding-box, bounding_box)
 CSS_KEY(button, button)
 CSS_KEY(buttonface, buttonface)
 CSS_KEY(buttonhighlight, buttonhighlight)
 CSS_KEY(buttonshadow, buttonshadow)
 CSS_KEY(buttontext, buttontext)
 CSS_KEY(capitalize, capitalize)
 CSS_KEY(caption, caption)
@@ -434,16 +435,17 @@ CSS_KEY(thick, thick)
 CSS_KEY(thin, thin)
 CSS_KEY(threeddarkshadow, threeddarkshadow)
 CSS_KEY(threedface, threedface)
 CSS_KEY(threedhighlight, threedhighlight)
 CSS_KEY(threedlightshadow, threedlightshadow)
 CSS_KEY(threedshadow, threedshadow)
 CSS_KEY(toggle, toggle)
 CSS_KEY(top, top)
+CSS_KEY(top-outside, top_outside)
 CSS_KEY(transparent, transparent)
 CSS_KEY(tri-state, tri_state)
 CSS_KEY(ultra-condensed, ultra_condensed)
 CSS_KEY(ultra-expanded, ultra_expanded)
 CSS_KEY(underline, underline)
 CSS_KEY(upper-alpha, upper_alpha)
 CSS_KEY(upper-latin, upper_latin)
 CSS_KEY(upper-roman, upper_roman)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -377,21 +377,23 @@ const PRInt32 nsCSSProps::kBoxPropSource
 const PRInt32 nsCSSProps::kBoxSizingKTable[] = {
   eCSSKeyword_content_box,  NS_STYLE_BOX_SIZING_CONTENT,
   eCSSKeyword_border_box,   NS_STYLE_BOX_SIZING_BORDER,
   eCSSKeyword_padding_box,  NS_STYLE_BOX_SIZING_PADDING,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const PRInt32 nsCSSProps::kCaptionSideKTable[] = {
-  eCSSKeyword_top,    NS_SIDE_TOP,
-  eCSSKeyword_right,  NS_SIDE_RIGHT,
-  eCSSKeyword_bottom, NS_SIDE_BOTTOM,
-  eCSSKeyword_left,   NS_SIDE_LEFT,
-  eCSSKeyword_UNKNOWN,-1
+  eCSSKeyword_top,                  NS_STYLE_CAPTION_SIDE_TOP,
+  eCSSKeyword_right,                NS_STYLE_CAPTION_SIDE_RIGHT,
+  eCSSKeyword_bottom,               NS_STYLE_CAPTION_SIDE_BOTTOM,
+  eCSSKeyword_left,                 NS_STYLE_CAPTION_SIDE_LEFT,
+  eCSSKeyword_top_outside,          NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
+  eCSSKeyword_bottom_outside,       NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
+  eCSSKeyword_UNKNOWN,              -1
 };
 
 const PRInt32 nsCSSProps::kClearKTable[] = {
   eCSSKeyword_left, NS_STYLE_CLEAR_LEFT,
   eCSSKeyword_right, NS_STYLE_CLEAR_RIGHT,
   eCSSKeyword_both, NS_STYLE_CLEAR_LEFT_AND_RIGHT,
   eCSSKeyword_UNKNOWN,-1
 };
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4181,17 +4181,17 @@ nsRuleNode::ComputeTableBorderData(void*
   if (eCSSUnit_Enumerated == tableData.mCaptionSide.GetUnit()) {
     table->mCaptionSide = tableData.mCaptionSide.GetIntValue();
   }
   else if (eCSSUnit_Inherit == tableData.mCaptionSide.GetUnit()) {
     inherited = PR_TRUE;
     table->mCaptionSide = parentTable->mCaptionSide;
   }
   else if (eCSSUnit_Initial == tableData.mCaptionSide.GetUnit()) {
-    table->mCaptionSide = NS_SIDE_TOP;
+    table->mCaptionSide = NS_STYLE_CAPTION_SIDE_TOP;
   }
 
   // empty-cells: enum, inherit
   if (eCSSUnit_Enumerated == tableData.mEmptyCells.GetUnit()) {
     table->mEmptyCells = tableData.mEmptyCells.GetIntValue();
   }
   else if (eCSSUnit_Inherit == tableData.mEmptyCells.GetUnit()) {
     inherited = PR_TRUE;
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -996,17 +996,17 @@ nsStyleTableBorder::nsStyleTableBorder(n
   mBorderCollapse = NS_STYLE_BORDER_SEPARATE;
 
   nsCompatibility compatMode = eCompatibility_FullStandards;
   if (aPresContext)
     compatMode = aPresContext->CompatibilityMode();
   mEmptyCells = (compatMode == eCompatibility_NavQuirks)
                   ? NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND     
                   : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
-  mCaptionSide = NS_SIDE_TOP;
+  mCaptionSide = NS_STYLE_CAPTION_SIDE_TOP;
   mBorderSpacingX.SetCoordValue(0);
   mBorderSpacingY.SetCoordValue(0);
 }
 
 nsStyleTableBorder::~nsStyleTableBorder(void) 
 { 
 }
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -745,17 +745,17 @@ var gCSSProperties = {
 		other_values: [ "32px", "-3em", "12%" ],
 		invalid_values: []
 	},
 	"caption-side": {
 		domProp: "captionSide",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "top" ],
-		other_values: [ "right", "left", "bottom" ],
+		other_values: [ "right", "left", "bottom", "top-outside", "bottom-outside" ],
 		invalid_values: []
 	},
 	"clear": {
 		domProp: "clear",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "left", "right", "both" ],
--- a/layout/tables/nsTableOuterFrame.cpp
+++ b/layout/tables/nsTableOuterFrame.cpp
@@ -85,32 +85,40 @@ nsTableOuterFrame::GetBaseline() const
   if (!kid) {
     NS_NOTREACHED("no inner table");
     return nsHTMLContainerFrame::GetBaseline();
   }
 
   return kid->GetBaseline() + kid->GetPosition().y;
 }
 
-inline PRBool IsSideCaption(nsIFrame* aCaptionFrame)
-{
-  PRUint8 captionSide = aCaptionFrame->GetStyleTableBorder()->mCaptionSide;
-  return captionSide == NS_SIDE_LEFT || captionSide == NS_SIDE_RIGHT;
-}
-
 /* virtual */ nsSize
 nsTableCaptionFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
                                      nsSize aCBSize, nscoord aAvailableWidth,
                                      nsSize aMargin, nsSize aBorder,
                                      nsSize aPadding, PRBool aShrinkWrap)
 {
   nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize,
                     aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
-  if (IsSideCaption(this)) {
+  PRUint8 captionSide = GetStyleTableBorder()->mCaptionSide;
+  if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+      captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
     result.width = GetMinWidth(aRenderingContext);
+  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+    // The outer frame constrains our available width to the width of
+    // the table.  Grow if our min-width is bigger than that, but not
+    // larger than the containing block width.  (It would really be nice
+    // to transmit that information another way, so we could grow up to
+    // the table's available width, but that's harder.)
+    nscoord min = GetMinWidth(aRenderingContext);
+    if (min > aCBSize.width)
+      min = aCBSize.width;
+    if (min > result.width)
+      result.width = min;
   }
   return result;
 }
 
 NS_IMETHODIMP 
 nsTableCaptionFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
                                                 nsIFrame**      aProviderFrame,
                                                 PRBool*         aIsChild)
@@ -342,19 +350,17 @@ nsTableOuterFrame::InsertFrames(nsIAtom*
 NS_IMETHODIMP
 nsTableOuterFrame::RemoveFrame(nsIAtom*        aListName,
                                nsIFrame*       aOldFrame)
 {
   // We only have two child frames: the inner table and one caption frame.
   // The inner frame can't be removed so this should be the caption
   NS_PRECONDITION(nsGkAtoms::captionList == aListName, "can't remove inner frame");
 
-  PRUint8 captionSide = GetCaptionSide();
-
-  if (NS_SIDE_LEFT == captionSide || NS_SIDE_RIGHT == captionSide) {
+  if (HasSideCaption()) {
     // The old caption width had an effect on the inner table width so
     // we're going to need to reflow it. Mark it dirty
     mInnerTableFrame->AddStateBits(NS_FRAME_IS_DIRTY);
   }
 
   // Remove the frame and destroy it
   mCaptionFrames.DestroyFrame(aOldFrame);
   mCaptionFrame = mCaptionFrames.FirstChild();
@@ -547,53 +553,58 @@ nsTableOuterFrame::InvalidateDamage(PRUi
     nsRect innerRect = mInnerTableFrame->GetRect();
     if (mCaptionFrame) {
       captionRect = mCaptionFrame->GetRect();
     }
     
     damage.x = 0;
     damage.width  = aOuterSize.width;
     switch(aCaptionSide) {
-    case NS_SIDE_BOTTOM:
+    case NS_STYLE_CAPTION_SIDE_BOTTOM:
+    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
       if (aCaptionChanged) {
         damage.y = innerRect.y;
         damage.height = aOuterSize.height - damage.y;
       }
       else { // aInnerChanged 
         damage.y = 0;
         damage.height = captionRect.y;
       }
       break;
-    case NS_SIDE_LEFT:
+    case NS_STYLE_CAPTION_SIDE_LEFT:
       if (aCaptionChanged) {
         damage.width = innerRect.x;
         damage.y = 0;
         damage.height = captionRect.YMost();
       }
       else { // aInnerChanged
         damage.x = captionRect.XMost();
         damage.width = innerRect.XMost() - damage.x;
         damage.y = 0;
         damage.height = innerRect.YMost();
       }
       break;
-    case NS_SIDE_RIGHT:
+    case NS_STYLE_CAPTION_SIDE_RIGHT:
      if (aCaptionChanged) {
         damage.x = innerRect.XMost();
         damage.width -= damage.x;
         damage.y = 0;
         damage.height = captionRect.YMost();
       }
      else { // aInnerChanged
         damage.width -= captionRect.width;
         damage.y = 0;
         damage.height = innerRect.YMost();
       }
       break;
-    default: // NS_SIDE_TOP
+    default:
+      NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+                   aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
+                   aCaptionSide == NO_SIDE,
+                   "unexpected caption side");
       if (aCaptionChanged) {
         damage.y = 0;
         damage.height = innerRect.y;
       }
       else { // aInnerChanged
         damage.y = captionRect.y;
         damage.height = aOuterSize.height - damage.y;
       }
@@ -614,22 +625,19 @@ nsTableOuterFrame::GetMinWidth(nsIRender
 {
   nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                     mInnerTableFrame, nsLayoutUtils::MIN_WIDTH);
   DISPLAY_MIN_WIDTH(this, width);
   if (mCaptionFrame) {
     nscoord capWidth =
       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
                                            nsLayoutUtils::MIN_WIDTH);
-    switch(GetCaptionSide()) {
-    case NS_SIDE_LEFT:
-    case NS_SIDE_RIGHT:
+    if (HasSideCaption()) {
       width += capWidth;
-      break;
-    default:
+    } else {
       if (capWidth > width) {
         width = capWidth;
       }
     }
   }
   return width;
 }
 
@@ -639,34 +647,45 @@ nsTableOuterFrame::GetPrefWidth(nsIRende
   nscoord maxWidth;
   DISPLAY_PREF_WIDTH(this, maxWidth);
 
   maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                mInnerTableFrame, nsLayoutUtils::PREF_WIDTH);
   if (mCaptionFrame) {
     PRUint8 captionSide = GetCaptionSide();
     switch(captionSide) {
-    case NS_SIDE_LEFT:
-    case NS_SIDE_RIGHT:
+    case NS_STYLE_CAPTION_SIDE_LEFT:
+    case NS_STYLE_CAPTION_SIDE_RIGHT:
       {
         nscoord capMin =
           nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
                                                nsLayoutUtils::MIN_WIDTH);
         maxWidth += capMin;
       }
       break;
-    case NS_SIDE_TOP:
-    case NS_SIDE_BOTTOM:
-    default:  // no caption 
+    default:
       {
+        nsLayoutUtils::IntrinsicWidthType iwt;
+        if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+            captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+          // Don't let the caption's pref width expand the table's pref
+          // width.
+          iwt = nsLayoutUtils::MIN_WIDTH;
+        } else {
+          NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
+                       captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
+                       "unexpected caption side");
+          iwt = nsLayoutUtils::PREF_WIDTH;
+        }
         nscoord capPref =
           nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
-                                               nsLayoutUtils::PREF_WIDTH);
+                                               iwt);
         maxWidth = PR_MAX(maxWidth, capPref);
       }
+      break;
     }
   }
   return maxWidth;
 }
 
 /* virtual */ nsSize
 nsTableOuterFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
                                    nsSize aCBSize, nscoord aAvailableWidth,
@@ -697,31 +716,33 @@ nsTableOuterFrame::ComputeAutoSize(nsIRe
                               innerOffsets.mComputedPadding.TopBottom()),
                        aShrinkWrap);
   nscoord width = tableSize.width + innerOffsets.mComputedMargin.LeftRight() +
                   innerOffsets.mComputedBorderPadding.LeftRight();
 
   if (mCaptionFrame) {
     nsCSSOffsetState capOffsets(mCaptionFrame, aRenderingContext,
                                 aCBSize.width);
+    PRUint8 captionSide = GetCaptionSide();
+    // Should we adjust aAvailableWidth before using it (based on captionSide)?
     nsSize capSize = mCaptionFrame->ComputeSize(aRenderingContext, aCBSize,
                        aAvailableWidth,
                        nsSize(capOffsets.mComputedMargin.LeftRight(),
                               capOffsets.mComputedMargin.TopBottom()),
                        nsSize(capOffsets.mComputedBorderPadding.LeftRight() -
                                 capOffsets.mComputedPadding.LeftRight(),
                               capOffsets.mComputedBorderPadding.TopBottom() -
                                 capOffsets.mComputedPadding.TopBottom()),
                        nsSize(capOffsets.mComputedPadding.LeftRight(),
                               capOffsets.mComputedPadding.TopBottom()),
                        aShrinkWrap);
-    PRUint8 captionSide = GetCaptionSide();
     nscoord capWidth = capSize.width + capOffsets.mComputedMargin.LeftRight() +
                        capOffsets.mComputedBorderPadding.LeftRight();
-    if (captionSide == NS_SIDE_LEFT || captionSide == NS_SIDE_RIGHT) {
+    if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+        captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
       width += capWidth;
     } else {
       if (capWidth > width)
         width = capWidth;
     }
   }
 
   return nsSize(width, NS_UNCONSTRAINEDSIZE);
@@ -761,21 +782,21 @@ nsTableOuterFrame::SetDesiredSize(PRUint
 
   nsRect captionRect(0,0,0,0);
   nscoord captionWidth = 0;
   if (mCaptionFrame) {
     captionRect = mCaptionFrame->GetRect();
     captionWidth = captionRect.width;
   }
   switch(aCaptionSide) {
-    case NS_SIDE_LEFT:
+    case NS_STYLE_CAPTION_SIDE_LEFT:
       aWidth = PR_MAX(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
                innerWidth + aInnerMargin.right;
       break;
-    case NS_SIDE_RIGHT:
+    case NS_STYLE_CAPTION_SIDE_RIGHT:
       aWidth = PR_MAX(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
                innerWidth + aInnerMargin.left;
       break;
     default:
       aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
       aWidth = PR_MAX(aWidth, captionRect.XMost() + aCaptionMargin.right);
   }
   aHeight = innerRect.YMost() + aInnerMargin.bottom;
@@ -831,17 +852,17 @@ nsTableOuterFrame::BalanceLeftRightCapti
       return;
   }
 
   if ((capPercent <= 0.0) && (innerPercent <= 0.0))
     return;
 
   
   if (innerPercent <= 0.0) {
-    if (NS_SIDE_LEFT == aCaptionSide) 
+    if (NS_STYLE_CAPTION_SIDE_LEFT == aCaptionSide) 
       aCaptionWidth= (nscoord) ((capPercent / (1.0 - capPercent)) * (aCaptionMargin.left + aCaptionMargin.right + 
                                                           aInnerWidth + aInnerMargin.right));
     else
       aCaptionWidth= (nscoord) ((capPercent / (1.0 - capPercent)) * (aCaptionMargin.left + aCaptionMargin.right + 
                                                           aInnerWidth + aInnerMargin.left)); 
   } 
   else {
     aCaptionWidth = (nscoord) ((capPercent / innerPercent) * aInnerWidth);
@@ -860,17 +881,18 @@ nsTableOuterFrame::GetCaptionOrigin(PRUi
   aOrigin.x = aOrigin.y = 0;
   if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||  
       (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
     return NS_OK;
   }
   if (!mCaptionFrame) return NS_OK;
 
   switch(aCaptionSide) {
-  case NS_SIDE_BOTTOM: {
+  case NS_STYLE_CAPTION_SIDE_BOTTOM:
+  case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
     if (NS_AUTOMARGIN == aCaptionMargin.left) {
       aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
                                            aContainBlockSize.width, aCaptionSize.width);
     }
     aOrigin.x = aCaptionMargin.left;
     if (NS_AUTOMARGIN == aCaptionMargin.top) {
       aCaptionMargin.top = 0;
     }
@@ -880,17 +902,17 @@ nsTableOuterFrame::GetCaptionOrigin(PRUi
     nscoord collapseMargin = marg.get();
     if (NS_AUTOMARGIN == aCaptionMargin.bottom) {
       nscoord height = aInnerSize.height + collapseMargin + aCaptionSize.height;
       aCaptionMargin.bottom = CalcAutoMargin(aCaptionMargin.bottom, aInnerMargin.top,
                                              aContainBlockSize.height, height);
     }
     aOrigin.y = aInnerMargin.top + aInnerSize.height + collapseMargin;
   } break;
-  case NS_SIDE_LEFT: {
+  case NS_STYLE_CAPTION_SIDE_LEFT: {
     if (NS_AUTOMARGIN == aCaptionMargin.left) {
       if (NS_AUTOMARGIN != aInnerMargin.left) {
         aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
                                              aInnerMargin.left, aCaptionSize.width);
       } 
       else {
         // zero for now
         aCaptionMargin.left = 0;
@@ -904,17 +926,17 @@ nsTableOuterFrame::GetCaptionOrigin(PRUi
         break;
       case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
         aOrigin.y = PR_MAX(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
         break;
       default:
         break;
     }
   } break;
-  case NS_SIDE_RIGHT: {
+  case NS_STYLE_CAPTION_SIDE_RIGHT: {
     if (NS_AUTOMARGIN == aCaptionMargin.left) {
       if (NS_AUTOMARGIN != aInnerMargin.right) {
         aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
                                              aInnerMargin.right, aCaptionSize.width);
       }
       else {
        // zero for now
        aCaptionMargin.left = 0;
@@ -929,16 +951,19 @@ nsTableOuterFrame::GetCaptionOrigin(PRUi
       case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
         aOrigin.y += PR_MAX(0, aInnerSize.height - aCaptionSize.height);
         break;
       default:
         break;
     }
   } break;
   default: { // top
+    NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+                 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
+                 "unexpected caption side");
     if (NS_AUTOMARGIN == aCaptionMargin.left) {
       aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
                                            aContainBlockSize.width, aCaptionSize.width);
     }
     aOrigin.x = aCaptionMargin.left;
     if (NS_AUTOMARGIN == aCaptionMargin.bottom) {
       aCaptionMargin.bottom = 0;
     }
@@ -974,17 +999,18 @@ nsTableOuterFrame::GetInnerOrigin(PRUint
 
   nscoord minCapWidth = aCaptionSize.width;
   if (NS_AUTOMARGIN != aCaptionMargin.left)
     minCapWidth += aCaptionMargin.left;
   if (NS_AUTOMARGIN != aCaptionMargin.right)
     minCapWidth += aCaptionMargin.right;
 
   switch(aCaptionSide) {
-  case NS_SIDE_BOTTOM: {
+  case NS_STYLE_CAPTION_SIDE_BOTTOM:
+  case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
     if (NS_AUTOMARGIN == aInnerMargin.left) {
       aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
                                          aContainBlockSize.width, aInnerSize.width);
     }
     aOrigin.x = aInnerMargin.left;
     if (NS_AUTOMARGIN == aInnerMargin.bottom) {
       aInnerMargin.bottom = 0;
     }
@@ -994,17 +1020,17 @@ nsTableOuterFrame::GetInnerOrigin(PRUint
       marg.Include(aCaptionMargin.top);
       nscoord collapseMargin = marg.get();
       nscoord height = aInnerSize.height + collapseMargin + aCaptionSize.height;
       aInnerMargin.top = CalcAutoMargin(aInnerMargin.top, aCaptionMargin.bottom,
                                         aContainBlockSize.height, height);
     }
     aOrigin.y = aInnerMargin.top;
   } break;
-  case NS_SIDE_LEFT: {
+  case NS_STYLE_CAPTION_SIDE_LEFT: {
     
     if (NS_AUTOMARGIN == aInnerMargin.left) {
       aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
                                          aContainBlockSize.width, aInnerSize.width);
       
     }
     if (aInnerMargin.left < minCapWidth) {
       // shift the inner table to get some place for the caption
@@ -1023,17 +1049,17 @@ nsTableOuterFrame::GetInnerOrigin(PRUint
         break;
       case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
         aOrigin.y = PR_MAX(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
         break;
       default:
         break;
     }
   } break;
-  case NS_SIDE_RIGHT: {
+  case NS_STYLE_CAPTION_SIDE_RIGHT: {
     if (NS_AUTOMARGIN == aInnerMargin.right) {
       aInnerMargin.right = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
                                           aContainBlockSize.width, aInnerSize.width);
       if (aInnerMargin.right < minCapWidth) {
         // shift the inner table to get some place for the caption
         aInnerMargin.left -= aInnerMargin.right - minCapWidth;
         aInnerMargin.left  = PR_MAX(0, aInnerMargin.left);
         aInnerMargin.right = minCapWidth;
@@ -1051,16 +1077,20 @@ nsTableOuterFrame::GetInnerOrigin(PRUint
       case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
         aOrigin.y = PR_MAX(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
         break;
       default:
         break;
     }
   } break;
   default: { // top
+    NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+                 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
+                 aCaptionSide == NO_SIDE,
+                 "unexpected caption side");
     if (NS_AUTOMARGIN == aInnerMargin.left) {
       aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
                                          aContainBlockSize.width, aInnerSize.width);
     }
     aOrigin.x = aInnerMargin.left;
     if (NS_AUTOMARGIN == aInnerMargin.top) {
       aInnerMargin.top = 0;
     }
@@ -1125,18 +1155,22 @@ nsTableOuterFrame::OuterBeginReflowChild
   nsHTMLReflowState &childRS = * new (aChildRSSpace)
     nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
                       -1, -1, PR_FALSE);
   InitChildReflowState(*aPresContext, childRS);
 
   // see if we need to reset top of page due to a caption
   if (mCaptionFrame) {
     PRUint8 captionSide = GetCaptionSide();
-    if (((NS_SIDE_BOTTOM == captionSide) && (mCaptionFrame == aChildFrame)) || 
-        ((NS_SIDE_TOP == captionSide) && (mInnerTableFrame == aChildFrame))) {
+    if (((captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
+          captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) &&
+         mCaptionFrame == aChildFrame) || 
+        ((captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+          captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE) &&
+         mInnerTableFrame == aChildFrame)) {
       childRS.mFlags.mIsTopOfPage = PR_FALSE;
     }
   }
 }
 
 nsresult
 nsTableOuterFrame::OuterDoReflowChild(nsPresContext*             aPresContext,
                                       nsIFrame*                  aChildFrame,
@@ -1184,131 +1218,133 @@ NS_METHOD nsTableOuterFrame::Reflow(nsPr
   }
   nsresult rv = NS_OK;
   PRUint8 captionSide = GetCaptionSide();
 
   // Initialize out parameters
   aDesiredSize.width = aDesiredSize.height = 0;
   aStatus = NS_FRAME_COMPLETE;
 
-  PRBool reflowAllKids = aOuterRS.ShouldReflowAllKids();
-
-  if (captionSide == NS_SIDE_LEFT || captionSide == NS_SIDE_RIGHT)
-    reflowAllKids = PR_TRUE;
-
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // Set up our kids.  They're already present, on an overflow list, 
     // or there are none so we'll create them now
     MoveOverflowToChildList(aPresContext);
   }
 
-  PRBool reflowCaption =
-    mCaptionFrame && (reflowAllKids || NS_SUBTREE_DIRTY(mCaptionFrame));
-  PRBool reflowInner = reflowAllKids || NS_SUBTREE_DIRTY(mInnerTableFrame);
-
-  // First reflow the caption.  nsHTMLReflowState takes care of making
-  // side captions small.
-  nsHTMLReflowMetrics captionMet;
-  nsSize captionSize;
-  nsMargin captionMargin;
   // Use longs to get more-aligned space.
   #define LONGS_IN_HTMLRS \
     ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
   long captionRSSpace[LONGS_IN_HTMLRS];
   nsHTMLReflowState *captionRS =
     static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
-  if (reflowCaption) {
-    nsReflowStatus capStatus; // don't let the caption cause incomplete
+  long innerRSSpace[LONGS_IN_HTMLRS];
+  nsHTMLReflowState *innerRS =
+    static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
+
+  if (captionSide == NO_SIDE) {
+    // We don't have a caption.
+    OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
+                          innerRSSpace, aOuterRS.ComputedWidth());
+  } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+             captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
+    // nsTableCaptionFrame::ComputeAutoSize takes care of making side
+    // captions small.  Compute the caption's size first, and tell the
+    // table to fit in what's left.
     OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
                           captionRSSpace, aOuterRS.ComputedWidth());
+    nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
+      (captionRS->ComputedWidth() + captionRS->mComputedMargin.LeftRight() +
+       captionRS->mComputedBorderPadding.LeftRight());
+    OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
+                          innerRSSpace, innerAvailWidth);
+
+  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
+             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
+    // Compute the table's size first, and then prevent the caption from
+    // being wider unless it has to be.
+    //
+    // Note that CSS 2.1 (but not 2.0) says:
+    //   The width of the anonymous box is the border-edge width of the
+    //   table box inside it
+    // We don't actually make our anonymous box that width (if we did,
+    // it would break 'auto' margins), but this effectively does that.
+    OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
+                          innerRSSpace, aOuterRS.ComputedWidth());
+    // It's good that CSS 2.1 says not to include margins, since we
+    // can't, since they already been converted so they exactly
+    // fill the available width (ignoring the margin on one side if
+    // neither are auto).
+    nscoord innerBorderWidth = innerRS->ComputedWidth() +
+                               innerRS->mComputedBorderPadding.LeftRight();
+    OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
+                          captionRSSpace, innerBorderWidth);
+  } else {
+    NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
+                 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
+                 "unexpected caption-side");
+    // Size the table and the caption independently.
+    OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
+                          captionRSSpace, aOuterRS.ComputedWidth());
+    OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
+                          innerRSSpace, aOuterRS.ComputedWidth());
+  }
+
+  // First reflow the caption.
+  nsHTMLReflowMetrics captionMet;
+  nsSize captionSize;
+  nsMargin captionMargin;
+  if (mCaptionFrame) {
+    nsReflowStatus capStatus; // don't let the caption cause incomplete
     rv = OuterDoReflowChild(aPresContext, mCaptionFrame, *captionRS,
                             captionMet, capStatus);
     if (NS_FAILED(rv)) return rv;
     captionSize.width = captionMet.width;
     captionSize.height = captionMet.height;
     captionMargin = captionRS->mComputedMargin;
-  } else if (mCaptionFrame) {
-    captionSize = mCaptionFrame->GetSize();
-    GetMargin(aPresContext, aOuterRS, mCaptionFrame, aOuterRS.ComputedWidth(),
-              captionMargin);
   } else {
     captionSize.SizeTo(0,0);
     captionMargin.SizeTo(0,0,0,0);
   }
 
-  nscoord innerAvailWidth = aOuterRS.ComputedWidth();
-  if (captionSide == NS_SIDE_LEFT || captionSide == NS_SIDE_RIGHT)
-    // If side is left/right then we know we have a caption and we
-    // reflowed it.
-    innerAvailWidth -= captionMet.width + captionMargin.LeftRight();
-
   // Then, now that we know how much to reduce the width of the inner
   // table to account for side captions, reflow the inner table.
   nsHTMLReflowMetrics innerMet;
+  rv = OuterDoReflowChild(aPresContext, mInnerTableFrame, *innerRS,
+                          innerMet, aStatus);
+  if (NS_FAILED(rv)) return rv;
   nsSize innerSize;
-  nsMargin innerMargin;
-  long innerRSSpace[LONGS_IN_HTMLRS];
-  nsHTMLReflowState *innerRS =
-    static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
-  if (reflowInner) {
-    OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
-                          innerRSSpace, innerAvailWidth);
-    rv = OuterDoReflowChild(aPresContext, mInnerTableFrame, *innerRS,
-                            innerMet, aStatus);
-    if (NS_FAILED(rv)) return rv;
-    innerSize.width = innerMet.width;
-    innerSize.height = innerMet.height;
-    innerMargin = innerRS->mComputedMargin;
-  } else {
-    innerSize = mInnerTableFrame->GetSize();
-    GetMargin(aPresContext, aOuterRS, mInnerTableFrame,
-              aOuterRS.ComputedWidth(), innerMargin);
-  }
+  innerSize.width = innerMet.width;
+  innerSize.height = innerMet.height;
+  nsMargin innerMargin = innerRS->mComputedMargin;
 
   nsSize   containSize = GetContainingBlockSize(aOuterRS);
 
   // Now that we've reflowed both we can place them.
   // XXXldb Most of the input variables here are now uninitialized!
 
   // XXX Need to recompute inner table's auto margins for the case of side
   // captions.  (Caption's are broken too, but that should be fixed earlier.)
 
   if (mCaptionFrame) {
     nsPoint captionOrigin;
     GetCaptionOrigin(captionSide, containSize, innerSize, 
                      innerMargin, captionSize, captionMargin, captionOrigin);
-    if (reflowCaption) {
-      FinishReflowChild(mCaptionFrame, aPresContext, captionRS, captionMet,
-                        captionOrigin.x, captionOrigin.y, 0);
-      captionRS->~nsHTMLReflowState();
-    } else if (mCaptionFrame->GetPosition() != captionOrigin) {
-      // Invalidate both the old and new rects
-      mCaptionFrame->Invalidate(mCaptionFrame->GetOverflowRect());
-      mCaptionFrame->SetPosition(captionOrigin);
-      nsTableFrame::RePositionViews(mCaptionFrame);
-      mCaptionFrame->Invalidate(mCaptionFrame->GetOverflowRect());
-    }
+    FinishReflowChild(mCaptionFrame, aPresContext, captionRS, captionMet,
+                      captionOrigin.x, captionOrigin.y, 0);
+    captionRS->~nsHTMLReflowState();
   }
   // XXX If the height is constrained then we need to check whether
   // everything still fits...
 
   nsPoint innerOrigin;
   GetInnerOrigin(captionSide, containSize, captionSize, 
                  captionMargin, innerSize, innerMargin, innerOrigin);
-  if (reflowInner) {
-    FinishReflowChild(mInnerTableFrame, aPresContext, innerRS, innerMet,
-                      innerOrigin.x, innerOrigin.y, 0);
-    innerRS->~nsHTMLReflowState();
-  } else if (mInnerTableFrame->GetPosition() != innerOrigin) {
-    // Invalidate both the old and new rects
-    mInnerTableFrame->Invalidate(mInnerTableFrame->GetOverflowRect());
-    mInnerTableFrame->SetPosition(innerOrigin);
-    nsTableFrame::RePositionViews(mInnerTableFrame);
-    mInnerTableFrame->Invalidate(mInnerTableFrame->GetOverflowRect());
-  }
+  FinishReflowChild(mInnerTableFrame, aPresContext, innerRS, innerMet,
+                    innerOrigin.x, innerOrigin.y, 0);
+  innerRS->~nsHTMLReflowState();
 
   UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
   
   // Return our desired rect
 
   NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
   return rv;
 }
--- a/layout/tables/nsTableOuterFrame.h
+++ b/layout/tables/nsTableOuterFrame.h
@@ -213,17 +213,23 @@ protected:
 
 #ifdef NS_DEBUG
   /** overridden here to handle special caption-table relationship
     * @see nsContainerFrame::VerifyTree
     */
   NS_IMETHOD VerifyTree() const;
 #endif
 
-  PRUint8 GetCaptionSide();
+  PRUint8 GetCaptionSide(); // NS_STYLE_CAPTION_SIDE_* or NO_SIDE
+
+  PRBool HasSideCaption() {
+    PRUint8 captionSide = GetCaptionSide();
+    return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
+           captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
+  }
   
   PRUint8 GetCaptionVerticalAlign();
 
   void SetDesiredSize(PRUint8         aCaptionSide,
                       const nsMargin& aInnerMargin,
                       const nsMargin& aCaptionMargin,
                       nscoord&        aWidth,
                       nscoord&        aHeight);