layout/tables/nsTableOuterFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/tables/nsTableOuterFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1113 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +#include "nsTableOuterFrame.h"
     1.9 +#include "nsTableFrame.h"
    1.10 +#include "nsTableCellFrame.h"
    1.11 +#include "nsStyleContext.h"
    1.12 +#include "nsStyleConsts.h"
    1.13 +#include "nsPresContext.h"
    1.14 +#include "nsCSSRendering.h"
    1.15 +#include "nsIContent.h"
    1.16 +#include "prinrval.h"
    1.17 +#include "nsGkAtoms.h"
    1.18 +#include "nsHTMLParts.h"
    1.19 +#include "nsIPresShell.h"
    1.20 +#include "nsIServiceManager.h"
    1.21 +#include "nsIDOMNode.h"
    1.22 +#include "nsDisplayList.h"
    1.23 +#include "nsLayoutUtils.h"
    1.24 +#include <algorithm>
    1.25 +
    1.26 +using namespace mozilla;
    1.27 +using namespace mozilla::layout;
    1.28 +
    1.29 +/* ----------- nsTableCaptionFrame ---------- */
    1.30 +
    1.31 +#define NS_TABLE_FRAME_CAPTION_LIST_INDEX 1
    1.32 +#define NO_SIDE 100
    1.33 +
    1.34 +// caption frame
    1.35 +nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext* aContext):
    1.36 +  nsBlockFrame(aContext)
    1.37 +{
    1.38 +  // shrink wrap 
    1.39 +  SetFlags(NS_BLOCK_FLOAT_MGR);
    1.40 +}
    1.41 +
    1.42 +nsTableCaptionFrame::~nsTableCaptionFrame()
    1.43 +{
    1.44 +}
    1.45 +
    1.46 +nsIAtom*
    1.47 +nsTableCaptionFrame::GetType() const
    1.48 +{
    1.49 +  return nsGkAtoms::tableCaptionFrame;
    1.50 +}
    1.51 +
    1.52 +/* virtual */ nscoord
    1.53 +nsTableOuterFrame::GetBaseline() const
    1.54 +{
    1.55 +  nsIFrame* kid = mFrames.FirstChild();
    1.56 +  if (!kid) {
    1.57 +    NS_NOTREACHED("no inner table");
    1.58 +    return nsContainerFrame::GetBaseline();
    1.59 +  }
    1.60 +
    1.61 +  return kid->GetBaseline() + kid->GetPosition().y;
    1.62 +}
    1.63 +
    1.64 +/* virtual */ nsSize
    1.65 +nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
    1.66 +                                     nsSize aCBSize, nscoord aAvailableWidth,
    1.67 +                                     nsSize aMargin, nsSize aBorder,
    1.68 +                                     nsSize aPadding, bool aShrinkWrap)
    1.69 +{
    1.70 +  nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize,
    1.71 +                    aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
    1.72 +
    1.73 +  // If we're a container for font size inflation, then shrink
    1.74 +  // wrapping inside of us should not apply font size inflation.
    1.75 +  AutoMaybeDisableFontInflation an(this);
    1.76 +
    1.77 +  uint8_t captionSide = StyleTableBorder()->mCaptionSide;
    1.78 +  if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
    1.79 +      captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
    1.80 +    result.width = GetMinWidth(aRenderingContext);
    1.81 +  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
    1.82 +             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
    1.83 +    // The outer frame constrains our available width to the width of
    1.84 +    // the table.  Grow if our min-width is bigger than that, but not
    1.85 +    // larger than the containing block width.  (It would really be nice
    1.86 +    // to transmit that information another way, so we could grow up to
    1.87 +    // the table's available width, but that's harder.)
    1.88 +    nscoord min = GetMinWidth(aRenderingContext);
    1.89 +    if (min > aCBSize.width)
    1.90 +      min = aCBSize.width;
    1.91 +    if (min > result.width)
    1.92 +      result.width = min;
    1.93 +  }
    1.94 +  return result;
    1.95 +}
    1.96 +
    1.97 +nsIFrame*
    1.98 +nsTableCaptionFrame::GetParentStyleContextFrame() const
    1.99 +{
   1.100 +  NS_PRECONDITION(mContent->GetParent(),
   1.101 +                  "How could we not have a parent here?");
   1.102 +    
   1.103 +  // The caption's style context parent is the inner frame, unless
   1.104 +  // it's anonymous.
   1.105 +  nsIFrame* outerFrame = GetParent();
   1.106 +  if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) {
   1.107 +    nsIFrame* innerFrame = outerFrame->GetFirstPrincipalChild();
   1.108 +    if (innerFrame) {
   1.109 +      return nsFrame::CorrectStyleParentFrame(innerFrame,
   1.110 +                                              StyleContext()->GetPseudo());
   1.111 +    }
   1.112 +  }
   1.113 +
   1.114 +  NS_NOTREACHED("Where is our inner table frame?");
   1.115 +  return nsBlockFrame::GetParentStyleContextFrame();
   1.116 +}
   1.117 +
   1.118 +#ifdef ACCESSIBILITY
   1.119 +a11y::AccType
   1.120 +nsTableCaptionFrame::AccessibleType()
   1.121 +{
   1.122 +  if (!GetRect().IsEmpty()) {
   1.123 +    return a11y::eHTMLCaptionType;
   1.124 +  }
   1.125 +
   1.126 +  return a11y::eNoType;
   1.127 +}
   1.128 +#endif
   1.129 +
   1.130 +#ifdef DEBUG_FRAME_DUMP
   1.131 +nsresult
   1.132 +nsTableCaptionFrame::GetFrameName(nsAString& aResult) const
   1.133 +{
   1.134 +  return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult);
   1.135 +}
   1.136 +#endif
   1.137 +
   1.138 +nsIFrame* 
   1.139 +NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.140 +{
   1.141 +  return new (aPresShell) nsTableCaptionFrame(aContext);
   1.142 +}
   1.143 +
   1.144 +NS_IMPL_FRAMEARENA_HELPERS(nsTableCaptionFrame)
   1.145 +
   1.146 +/* ----------- nsTableOuterFrame ---------- */
   1.147 +
   1.148 +nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext):
   1.149 +  nsContainerFrame(aContext)
   1.150 +{
   1.151 +}
   1.152 +
   1.153 +nsTableOuterFrame::~nsTableOuterFrame()
   1.154 +{
   1.155 +}
   1.156 +
   1.157 +NS_QUERYFRAME_HEAD(nsTableOuterFrame)
   1.158 +  NS_QUERYFRAME_ENTRY(nsTableOuterFrame)
   1.159 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
   1.160 +
   1.161 +#ifdef ACCESSIBILITY
   1.162 +a11y::AccType
   1.163 +nsTableOuterFrame::AccessibleType()
   1.164 +{
   1.165 +  return a11y::eHTMLTableType;
   1.166 +}
   1.167 +#endif
   1.168 +
   1.169 +void
   1.170 +nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.171 +{
   1.172 +  DestroyAbsoluteFrames(aDestructRoot);
   1.173 +  mCaptionFrames.DestroyFramesFrom(aDestructRoot);
   1.174 +  nsContainerFrame::DestroyFrom(aDestructRoot);
   1.175 +}
   1.176 +
   1.177 +const nsFrameList&
   1.178 +nsTableOuterFrame::GetChildList(ChildListID aListID) const
   1.179 +{
   1.180 +  if (aListID == kCaptionList) {
   1.181 +    return mCaptionFrames;
   1.182 +  }
   1.183 +
   1.184 +  return nsContainerFrame::GetChildList(aListID);
   1.185 +}
   1.186 +
   1.187 +void
   1.188 +nsTableOuterFrame::GetChildLists(nsTArray<ChildList>* aLists) const
   1.189 +{
   1.190 +  nsContainerFrame::GetChildLists(aLists);
   1.191 +  mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
   1.192 +}
   1.193 +
   1.194 +nsresult 
   1.195 +nsTableOuterFrame::SetInitialChildList(ChildListID     aListID,
   1.196 +                                       nsFrameList&    aChildList)
   1.197 +{
   1.198 +  if (kCaptionList == aListID) {
   1.199 +    // the frame constructor already checked for table-caption display type
   1.200 +    mCaptionFrames.SetFrames(aChildList);
   1.201 +  }
   1.202 +  else {
   1.203 +    NS_ASSERTION(aListID == kPrincipalList, "wrong childlist");
   1.204 +    NS_ASSERTION(mFrames.IsEmpty(), "Frame leak!");
   1.205 +    NS_ASSERTION(aChildList.FirstChild() &&
   1.206 +                 nsGkAtoms::tableFrame == aChildList.FirstChild()->GetType(),
   1.207 +                 "expected a table frame");
   1.208 +    mFrames.SetFrames(aChildList);
   1.209 +  }
   1.210 +
   1.211 +  return NS_OK;
   1.212 +}
   1.213 +
   1.214 +nsresult
   1.215 +nsTableOuterFrame::AppendFrames(ChildListID     aListID,
   1.216 +                                nsFrameList&    aFrameList)
   1.217 +{
   1.218 +  nsresult rv;
   1.219 +
   1.220 +  // We only have two child frames: the inner table and a caption frame.
   1.221 +  // The inner frame is provided when we're initialized, and it cannot change
   1.222 +  if (kCaptionList == aListID) {
   1.223 +    NS_ASSERTION(aFrameList.IsEmpty() ||
   1.224 +                 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
   1.225 +                 "appending non-caption frame to captionList");
   1.226 +    mCaptionFrames.AppendFrames(this, aFrameList);
   1.227 +    rv = NS_OK;
   1.228 +
   1.229 +    // Reflow the new caption frame. It's already marked dirty, so
   1.230 +    // just tell the pres shell.
   1.231 +    PresContext()->PresShell()->
   1.232 +      FrameNeedsReflow(this, nsIPresShell::eTreeChange,
   1.233 +                       NS_FRAME_HAS_DIRTY_CHILDREN);
   1.234 +  }
   1.235 +  else {
   1.236 +    NS_PRECONDITION(false, "unexpected child list");
   1.237 +    rv = NS_ERROR_UNEXPECTED;
   1.238 +  }
   1.239 +
   1.240 +  return rv;
   1.241 +}
   1.242 +
   1.243 +nsresult
   1.244 +nsTableOuterFrame::InsertFrames(ChildListID     aListID,
   1.245 +                                nsIFrame*       aPrevFrame,
   1.246 +                                nsFrameList&    aFrameList)
   1.247 +{
   1.248 +  if (kCaptionList == aListID) {
   1.249 +    NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
   1.250 +                 "inserting after sibling frame with different parent");
   1.251 +    NS_ASSERTION(aFrameList.IsEmpty() ||
   1.252 +                 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
   1.253 +                 "inserting non-caption frame into captionList");
   1.254 +    mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
   1.255 +
   1.256 +    // Reflow the new caption frame. It's already marked dirty, so
   1.257 +    // just tell the pres shell.
   1.258 +    PresContext()->PresShell()->
   1.259 +      FrameNeedsReflow(this, nsIPresShell::eTreeChange,
   1.260 +                       NS_FRAME_HAS_DIRTY_CHILDREN);
   1.261 +    return NS_OK;
   1.262 +  }
   1.263 +  else {
   1.264 +    NS_PRECONDITION(!aPrevFrame, "invalid previous frame");
   1.265 +    return AppendFrames(aListID, aFrameList);
   1.266 +  }
   1.267 +}
   1.268 +
   1.269 +nsresult
   1.270 +nsTableOuterFrame::RemoveFrame(ChildListID     aListID,
   1.271 +                               nsIFrame*       aOldFrame)
   1.272 +{
   1.273 +  // We only have two child frames: the inner table and one caption frame.
   1.274 +  // The inner frame can't be removed so this should be the caption
   1.275 +  NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame");
   1.276 +
   1.277 +  if (HasSideCaption()) {
   1.278 +    // The old caption width had an effect on the inner table width so
   1.279 +    // we're going to need to reflow it. Mark it dirty
   1.280 +    InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY);
   1.281 +  }
   1.282 +
   1.283 +  // Remove the frame and destroy it
   1.284 +  mCaptionFrames.DestroyFrame(aOldFrame);
   1.285 +  
   1.286 +  PresContext()->PresShell()->
   1.287 +    FrameNeedsReflow(this, nsIPresShell::eTreeChange,
   1.288 +                     NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed
   1.289 +
   1.290 +  return NS_OK;
   1.291 +}
   1.292 +
   1.293 +void
   1.294 +nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   1.295 +                                    const nsRect&           aDirtyRect,
   1.296 +                                    const nsDisplayListSet& aLists)
   1.297 +{
   1.298 +  // No border, background or outline are painted because they all belong
   1.299 +  // to the inner table.
   1.300 +
   1.301 +  // If there's no caption, take a short cut to avoid having to create
   1.302 +  // the special display list set and then sort it.
   1.303 +  if (mCaptionFrames.IsEmpty()) {
   1.304 +    BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists);
   1.305 +    return;
   1.306 +  }
   1.307 +
   1.308 +  nsDisplayListCollection set;
   1.309 +  BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set);
   1.310 +  
   1.311 +  nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
   1.312 +  BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(),
   1.313 +                           aDirtyRect, captionSet);
   1.314 +  
   1.315 +  // Now we have to sort everything by content order, since the caption
   1.316 +  // may be somewhere inside the table
   1.317 +  set.SortAllByContentOrder(aBuilder, GetContent());
   1.318 +  set.MoveTo(aLists);
   1.319 +}
   1.320 +
   1.321 +void
   1.322 +nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder*   aBuilder,
   1.323 +                                                 const nsRect&           aDirtyRect,
   1.324 +                                                 const nsDisplayListSet& aLists)
   1.325 +{
   1.326 +  // Just paint the regular children, but the children's background is our
   1.327 +  // true background (there should only be one, the real table)
   1.328 +  nsIFrame* kid = mFrames.FirstChild();
   1.329 +  // The children should be in content order
   1.330 +  while (kid) {
   1.331 +    BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   1.332 +    kid = kid->GetNextSibling();
   1.333 +  }
   1.334 +}
   1.335 +
   1.336 +nsIFrame*
   1.337 +nsTableOuterFrame::GetParentStyleContextFrame() const
   1.338 +{
   1.339 +  // The table outer frame and the (inner) table frame split the style
   1.340 +  // data by giving the table frame the style context associated with
   1.341 +  // the table content node and creating a style context for the outer
   1.342 +  // frame that is a *child* of the table frame's style context,
   1.343 +  // matching the ::-moz-table-outer pseudo-element.  html.css has a
   1.344 +  // rule that causes that pseudo-element (and thus the outer table)
   1.345 +  // to inherit *some* style properties from the table frame.  The
   1.346 +  // children of the table inherit directly from the inner table, and
   1.347 +  // the outer table's style context is a leaf.
   1.348 +
   1.349 +  return InnerTableFrame();
   1.350 +}
   1.351 +
   1.352 +// INCREMENTAL REFLOW HELPER FUNCTIONS 
   1.353 +
   1.354 +void
   1.355 +nsTableOuterFrame::InitChildReflowState(nsPresContext&    aPresContext,                     
   1.356 +                                        nsHTMLReflowState& aReflowState)
   1.357 +                                    
   1.358 +{
   1.359 +  nsMargin collapseBorder;
   1.360 +  nsMargin collapsePadding(0,0,0,0);
   1.361 +  nsMargin* pCollapseBorder  = nullptr;
   1.362 +  nsMargin* pCollapsePadding = nullptr;
   1.363 +  if (aReflowState.frame == InnerTableFrame() &&
   1.364 +      InnerTableFrame()->IsBorderCollapse()) {
   1.365 +    collapseBorder  = InnerTableFrame()->GetIncludedOuterBCBorder();
   1.366 +    pCollapseBorder = &collapseBorder;
   1.367 +    pCollapsePadding = &collapsePadding;
   1.368 +  }
   1.369 +  aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding);
   1.370 +}
   1.371 +
   1.372 +// get the margin and padding data. nsHTMLReflowState doesn't handle the
   1.373 +// case of auto margins
   1.374 +void
   1.375 +nsTableOuterFrame::GetChildMargin(nsPresContext*           aPresContext,
   1.376 +                                  const nsHTMLReflowState& aOuterRS,
   1.377 +                                  nsIFrame*                aChildFrame,
   1.378 +                                  nscoord                  aAvailWidth,
   1.379 +                                  nsMargin&                aMargin)
   1.380 +{
   1.381 +  // construct a reflow state to compute margin and padding. Auto margins
   1.382 +  // will not be computed at this time.
   1.383 +
   1.384 +  // create and init the child reflow state
   1.385 +  // XXX We really shouldn't construct a reflow state to do this.
   1.386 +  nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame,
   1.387 +                            nsSize(aAvailWidth, aOuterRS.AvailableHeight()),
   1.388 +                            -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
   1.389 +  InitChildReflowState(*aPresContext, childRS);
   1.390 +
   1.391 +  aMargin = childRS.ComputedPhysicalMargin();
   1.392 +}
   1.393 +
   1.394 +static nsSize
   1.395 +GetContainingBlockSize(const nsHTMLReflowState& aOuterRS)
   1.396 +{
   1.397 +  nsSize size(0,0);
   1.398 +  const nsHTMLReflowState* containRS =
   1.399 +    aOuterRS.mCBReflowState;
   1.400 +
   1.401 +  if (containRS) {
   1.402 +    size.width = containRS->ComputedWidth();
   1.403 +    if (NS_UNCONSTRAINEDSIZE == size.width) {
   1.404 +      size.width = 0;
   1.405 +    }
   1.406 +    size.height = containRS->ComputedHeight();
   1.407 +    if (NS_UNCONSTRAINEDSIZE == size.height) {
   1.408 +      size.height = 0;
   1.409 +    }
   1.410 +  }
   1.411 +  return size;
   1.412 +}
   1.413 +
   1.414 +/* virtual */ nscoord
   1.415 +nsTableOuterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   1.416 +{
   1.417 +  nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   1.418 +                    InnerTableFrame(), nsLayoutUtils::MIN_WIDTH);
   1.419 +  DISPLAY_MIN_WIDTH(this, width);
   1.420 +  if (mCaptionFrames.NotEmpty()) {
   1.421 +    nscoord capWidth =
   1.422 +      nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   1.423 +                                           mCaptionFrames.FirstChild(),
   1.424 +                                           nsLayoutUtils::MIN_WIDTH);
   1.425 +    if (HasSideCaption()) {
   1.426 +      width += capWidth;
   1.427 +    } else {
   1.428 +      if (capWidth > width) {
   1.429 +        width = capWidth;
   1.430 +      }
   1.431 +    }
   1.432 +  }
   1.433 +  return width;
   1.434 +}
   1.435 +
   1.436 +/* virtual */ nscoord
   1.437 +nsTableOuterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   1.438 +{
   1.439 +  nscoord maxWidth;
   1.440 +  DISPLAY_PREF_WIDTH(this, maxWidth);
   1.441 +
   1.442 +  maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   1.443 +               InnerTableFrame(), nsLayoutUtils::PREF_WIDTH);
   1.444 +  if (mCaptionFrames.NotEmpty()) {
   1.445 +    uint8_t captionSide = GetCaptionSide();
   1.446 +    switch(captionSide) {
   1.447 +    case NS_STYLE_CAPTION_SIDE_LEFT:
   1.448 +    case NS_STYLE_CAPTION_SIDE_RIGHT:
   1.449 +      {
   1.450 +        nscoord capMin =
   1.451 +          nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   1.452 +                                               mCaptionFrames.FirstChild(),
   1.453 +                                               nsLayoutUtils::MIN_WIDTH);
   1.454 +        maxWidth += capMin;
   1.455 +      }
   1.456 +      break;
   1.457 +    default:
   1.458 +      {
   1.459 +        nsLayoutUtils::IntrinsicWidthType iwt;
   1.460 +        if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
   1.461 +            captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
   1.462 +          // Don't let the caption's pref width expand the table's pref
   1.463 +          // width.
   1.464 +          iwt = nsLayoutUtils::MIN_WIDTH;
   1.465 +        } else {
   1.466 +          NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
   1.467 +                       captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
   1.468 +                       "unexpected caption side");
   1.469 +          iwt = nsLayoutUtils::PREF_WIDTH;
   1.470 +        }
   1.471 +        nscoord capPref =
   1.472 +          nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   1.473 +                                               mCaptionFrames.FirstChild(),
   1.474 +                                               iwt);
   1.475 +        maxWidth = std::max(maxWidth, capPref);
   1.476 +      }
   1.477 +      break;
   1.478 +    }
   1.479 +  }
   1.480 +  return maxWidth;
   1.481 +}
   1.482 +
   1.483 +// Compute the margin-box width of aChildFrame given the inputs.  If
   1.484 +// aMarginResult is non-null, fill it with the part of the margin-width
   1.485 +// that was contributed by the margin.
   1.486 +static nscoord
   1.487 +ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext,
   1.488 +                     nsIFrame *aChildFrame,
   1.489 +                     nsSize aCBSize, nscoord aAvailableWidth,
   1.490 +                     nscoord *aMarginResult = nullptr)
   1.491 +{
   1.492 +  AutoMaybeDisableFontInflation an(aChildFrame);
   1.493 +
   1.494 +  nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width);
   1.495 +  nsSize size = aChildFrame->ComputeSize(aRenderingContext, aCBSize,
   1.496 +                  aAvailableWidth,
   1.497 +                  nsSize(offsets.ComputedPhysicalMargin().LeftRight(),
   1.498 +                         offsets.ComputedPhysicalMargin().TopBottom()),
   1.499 +                  nsSize(offsets.ComputedPhysicalBorderPadding().LeftRight() -
   1.500 +                           offsets.ComputedPhysicalPadding().LeftRight(),
   1.501 +                         offsets.ComputedPhysicalBorderPadding().TopBottom() -
   1.502 +                           offsets.ComputedPhysicalPadding().TopBottom()),
   1.503 +                  nsSize(offsets.ComputedPhysicalPadding().LeftRight(),
   1.504 +                         offsets.ComputedPhysicalPadding().TopBottom()),
   1.505 +                  true);
   1.506 +  if (aMarginResult)
   1.507 +    *aMarginResult = offsets.ComputedPhysicalMargin().LeftRight();
   1.508 +  return size.width + offsets.ComputedPhysicalMargin().LeftRight() +
   1.509 +                      offsets.ComputedPhysicalBorderPadding().LeftRight();
   1.510 +}
   1.511 +
   1.512 +/* virtual */ nsSize
   1.513 +nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
   1.514 +                                   nsSize aCBSize, nscoord aAvailableWidth,
   1.515 +                                   nsSize aMargin, nsSize aBorder,
   1.516 +                                   nsSize aPadding, bool aShrinkWrap)
   1.517 +{
   1.518 +  nscoord kidAvailableWidth = aAvailableWidth - aMargin.width;
   1.519 +  NS_ASSERTION(aBorder == nsSize(0, 0) &&
   1.520 +               aPadding == nsSize(0, 0),
   1.521 +               "Table outer frames cannot hae borders or paddings");
   1.522 +
   1.523 +  // When we're shrink-wrapping, our auto size needs to wrap around the
   1.524 +  // actual size of the table, which (if it is specified as a percent)
   1.525 +  // could be something that is not reflected in our GetMinWidth and
   1.526 +  // GetPrefWidth.  See bug 349457 for an example.
   1.527 +
   1.528 +  // Match the availableWidth logic in Reflow.
   1.529 +  uint8_t captionSide = GetCaptionSide();
   1.530 +  nscoord width;
   1.531 +  if (captionSide == NO_SIDE) {
   1.532 +    width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
   1.533 +                                 aCBSize, kidAvailableWidth);
   1.534 +  } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
   1.535 +             captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
   1.536 +    nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
   1.537 +                                            mCaptionFrames.FirstChild(),
   1.538 +                                            aCBSize, kidAvailableWidth);
   1.539 +    width = capWidth + ChildShrinkWrapWidth(aRenderingContext,
   1.540 +                                            InnerTableFrame(), aCBSize,
   1.541 +                                            kidAvailableWidth - capWidth);
   1.542 +  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
   1.543 +             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
   1.544 +    nscoord margin;
   1.545 +    width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
   1.546 +                                 aCBSize, kidAvailableWidth, &margin);
   1.547 +    nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
   1.548 +                                            mCaptionFrames.FirstChild(), aCBSize,
   1.549 +                                            width - margin);
   1.550 +    if (capWidth > width)
   1.551 +      width = capWidth;
   1.552 +  } else {
   1.553 +    NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
   1.554 +                 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
   1.555 +                 "unexpected caption-side");
   1.556 +    width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
   1.557 +                                 aCBSize, kidAvailableWidth);
   1.558 +    nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
   1.559 +                                            mCaptionFrames.FirstChild(),
   1.560 +                                            aCBSize, kidAvailableWidth);
   1.561 +    if (capWidth > width)
   1.562 +      width = capWidth;
   1.563 +  }
   1.564 +
   1.565 +  return nsSize(width, NS_UNCONSTRAINEDSIZE);
   1.566 +}
   1.567 +
   1.568 +uint8_t
   1.569 +nsTableOuterFrame::GetCaptionSide()
   1.570 +{
   1.571 +  if (mCaptionFrames.NotEmpty()) {
   1.572 +    return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
   1.573 +  }
   1.574 +  else {
   1.575 +    return NO_SIDE; // no caption
   1.576 +  }
   1.577 +}
   1.578 +
   1.579 +uint8_t
   1.580 +nsTableOuterFrame::GetCaptionVerticalAlign()
   1.581 +{
   1.582 +  const nsStyleCoord& va =
   1.583 +    mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign;
   1.584 +  return (va.GetUnit() == eStyleUnit_Enumerated)
   1.585 +           ? va.GetIntValue()
   1.586 +           : NS_STYLE_VERTICAL_ALIGN_TOP;
   1.587 +}
   1.588 +
   1.589 +void
   1.590 +nsTableOuterFrame::SetDesiredSize(uint8_t         aCaptionSide,
   1.591 +                                  const nsMargin& aInnerMargin,
   1.592 +                                  const nsMargin& aCaptionMargin,
   1.593 +                                  nscoord&        aWidth,
   1.594 +                                  nscoord&        aHeight)
   1.595 +{
   1.596 +  aWidth = aHeight = 0;
   1.597 +
   1.598 +  nsRect innerRect = InnerTableFrame()->GetRect();
   1.599 +  nscoord innerWidth = innerRect.width;
   1.600 +
   1.601 +  nsRect captionRect(0,0,0,0);
   1.602 +  nscoord captionWidth = 0;
   1.603 +  if (mCaptionFrames.NotEmpty()) {
   1.604 +    captionRect = mCaptionFrames.FirstChild()->GetRect();
   1.605 +    captionWidth = captionRect.width;
   1.606 +  }
   1.607 +  switch(aCaptionSide) {
   1.608 +    case NS_STYLE_CAPTION_SIDE_LEFT:
   1.609 +      aWidth = std::max(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
   1.610 +               innerWidth + aInnerMargin.right;
   1.611 +      break;
   1.612 +    case NS_STYLE_CAPTION_SIDE_RIGHT:
   1.613 +      aWidth = std::max(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
   1.614 +               innerWidth + aInnerMargin.left;
   1.615 +      break;
   1.616 +    default:
   1.617 +      aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
   1.618 +      aWidth = std::max(aWidth, captionRect.XMost() + aCaptionMargin.right);
   1.619 +  }
   1.620 +  aHeight = innerRect.YMost() + aInnerMargin.bottom;
   1.621 +  if (NS_STYLE_CAPTION_SIDE_BOTTOM != aCaptionSide) {
   1.622 +    aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom);
   1.623 +  }
   1.624 +  else {
   1.625 +    aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom +
   1.626 +                              aInnerMargin.bottom);
   1.627 +  }
   1.628 +
   1.629 +}
   1.630 +
   1.631 +nsresult 
   1.632 +nsTableOuterFrame::GetCaptionOrigin(uint32_t         aCaptionSide,
   1.633 +                                    const nsSize&    aContainBlockSize,
   1.634 +                                    const nsSize&    aInnerSize, 
   1.635 +                                    const nsMargin&  aInnerMargin,
   1.636 +                                    const nsSize&    aCaptionSize,
   1.637 +                                    nsMargin&        aCaptionMargin,
   1.638 +                                    nsPoint&         aOrigin)
   1.639 +{
   1.640 +  aOrigin.x = aOrigin.y = 0;
   1.641 +  if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||  
   1.642 +      (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
   1.643 +    return NS_OK;
   1.644 +  }
   1.645 +  if (mCaptionFrames.IsEmpty()) return NS_OK;
   1.646 +  
   1.647 +  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left,   "The computed caption margin is auto?");
   1.648 +  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.top,    "The computed caption margin is auto?");
   1.649 +  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.bottom, "The computed caption margin is auto?");
   1.650 +
   1.651 +  // horizontal computation
   1.652 +  switch(aCaptionSide) {
   1.653 +  case NS_STYLE_CAPTION_SIDE_BOTTOM:
   1.654 +  case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
   1.655 +    // FIXME: Position relative to right edge for RTL.  (Based on table
   1.656 +    // direction or table parent direction?)
   1.657 +    aOrigin.x = aCaptionMargin.left;
   1.658 +    if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
   1.659 +      // We placed the caption using only the table's width as available
   1.660 +      // width, and we should position it this way as well.
   1.661 +      aOrigin.x += aInnerMargin.left;
   1.662 +    }
   1.663 +  } break;
   1.664 +  case NS_STYLE_CAPTION_SIDE_LEFT: {
   1.665 +    aOrigin.x = aCaptionMargin.left;
   1.666 +  } break;
   1.667 +  case NS_STYLE_CAPTION_SIDE_RIGHT: {
   1.668 +    aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left;
   1.669 +  } break;
   1.670 +  default: { // top
   1.671 +    NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
   1.672 +                 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
   1.673 +                 "unexpected caption side");
   1.674 +    // FIXME: Position relative to right edge for RTL.  (Based on table
   1.675 +    // direction or table parent direction?)
   1.676 +    aOrigin.x = aCaptionMargin.left;
   1.677 +    if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
   1.678 +      // We placed the caption using only the table's width as available
   1.679 +      // width, and we should position it this way as well.
   1.680 +      aOrigin.x += aInnerMargin.left;
   1.681 +    }
   1.682 +    
   1.683 +  } break;
   1.684 +  }
   1.685 +  // vertical computation
   1.686 +  switch (aCaptionSide) {
   1.687 +    case NS_STYLE_CAPTION_SIDE_RIGHT:
   1.688 +    case NS_STYLE_CAPTION_SIDE_LEFT:
   1.689 +      aOrigin.y = aInnerMargin.top;
   1.690 +      switch (GetCaptionVerticalAlign()) {
   1.691 +        case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
   1.692 +          aOrigin.y = std::max(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2));
   1.693 +          break;
   1.694 +        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
   1.695 +          aOrigin.y = std::max(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
   1.696 +          break;
   1.697 +        default:
   1.698 +          break;
   1.699 +      }
   1.700 +      break;
   1.701 +    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
   1.702 +    case NS_STYLE_CAPTION_SIDE_BOTTOM: {
   1.703 +      aOrigin.y = aInnerMargin.top + aInnerSize.height + aCaptionMargin.top;
   1.704 +    } break;
   1.705 +    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
   1.706 +    case NS_STYLE_CAPTION_SIDE_TOP: {
   1.707 +      aOrigin.y = aInnerMargin.top + aCaptionMargin.top;
   1.708 +    } break;
   1.709 +    default:
   1.710 +      NS_NOTREACHED("Unknown caption alignment type");
   1.711 +      break;
   1.712 +  }
   1.713 +  return NS_OK;
   1.714 +}
   1.715 +
   1.716 +nsresult 
   1.717 +nsTableOuterFrame::GetInnerOrigin(uint32_t         aCaptionSide,
   1.718 +                                  const nsSize&    aContainBlockSize,
   1.719 +                                  const nsSize&    aCaptionSize, 
   1.720 +                                  const nsMargin&  aCaptionMargin,
   1.721 +                                  const nsSize&    aInnerSize,
   1.722 +                                  nsMargin&        aInnerMargin,
   1.723 +                                  nsPoint&         aOrigin)
   1.724 +{
   1.725 +  
   1.726 +  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left,  "The computed caption margin is auto?");
   1.727 +  NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.right, "The computed caption margin is auto?");
   1.728 +  NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.left,    "The computed inner margin is auto?");
   1.729 +  NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.right,   "The computed inner margin is auto?");
   1.730 +  NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.top,     "The computed inner margin is auto?");
   1.731 +  NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.bottom,  "The computed inner margin is auto?");
   1.732 +  
   1.733 +  aOrigin.x = aOrigin.y = 0;
   1.734 +  if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||  
   1.735 +      (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
   1.736 +    return NS_OK;
   1.737 +  }
   1.738 +
   1.739 +  nscoord minCapWidth = aCaptionSize.width;
   1.740 +  
   1.741 +  minCapWidth += aCaptionMargin.left;
   1.742 +  minCapWidth += aCaptionMargin.right;
   1.743 +
   1.744 +  // horizontal computation
   1.745 +  switch (aCaptionSide) {
   1.746 +  case NS_STYLE_CAPTION_SIDE_LEFT: {
   1.747 +    if (aInnerMargin.left < minCapWidth) {
   1.748 +      // shift the inner table to get some place for the caption
   1.749 +      aInnerMargin.right += aInnerMargin.left - minCapWidth;
   1.750 +      aInnerMargin.right  = std::max(0, aInnerMargin.right);
   1.751 +      aInnerMargin.left   = minCapWidth;
   1.752 +    }
   1.753 +    aOrigin.x = aInnerMargin.left;
   1.754 +  } break;
   1.755 +  default: {
   1.756 +    NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
   1.757 +                 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
   1.758 +                 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
   1.759 +                 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
   1.760 +                 aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT ||
   1.761 +                 aCaptionSide == NO_SIDE,
   1.762 +                 "unexpected caption side");
   1.763 +    aOrigin.x = aInnerMargin.left;
   1.764 +  } break;
   1.765 +  }
   1.766 +  
   1.767 +  // vertical computation
   1.768 +  switch (aCaptionSide) {
   1.769 +    case NS_STYLE_CAPTION_SIDE_BOTTOM:
   1.770 +    case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
   1.771 +      aOrigin.y = aInnerMargin.top;
   1.772 +    } break;
   1.773 +    case NS_STYLE_CAPTION_SIDE_LEFT:
   1.774 +    case NS_STYLE_CAPTION_SIDE_RIGHT: {
   1.775 +      aOrigin.y = aInnerMargin.top;
   1.776 +      switch (GetCaptionVerticalAlign()) {
   1.777 +        case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
   1.778 +          aOrigin.y = std::max(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
   1.779 +          break;
   1.780 +        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
   1.781 +          aOrigin.y = std::max(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
   1.782 +          break;
   1.783 +        default:
   1.784 +          break;
   1.785 +      }
   1.786 +    } break;
   1.787 +    case NO_SIDE:
   1.788 +    case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
   1.789 +    case NS_STYLE_CAPTION_SIDE_TOP: {
   1.790 +      aOrigin.y = aInnerMargin.top + aCaptionMargin.top + aCaptionSize.height +
   1.791 +                  aCaptionMargin.bottom;
   1.792 +    } break;
   1.793 +    default:
   1.794 +      NS_NOTREACHED("Unknown caption alignment type");
   1.795 +      break;
   1.796 +  }
   1.797 +  return NS_OK;
   1.798 +}
   1.799 +
   1.800 +void
   1.801 +nsTableOuterFrame::OuterBeginReflowChild(nsPresContext*           aPresContext,
   1.802 +                                         nsIFrame*                aChildFrame,
   1.803 +                                         const nsHTMLReflowState& aOuterRS,
   1.804 +                                         void*                    aChildRSSpace,
   1.805 +                                         nscoord                  aAvailWidth)
   1.806 +{ 
   1.807 +  // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
   1.808 +  nscoord availHeight = aOuterRS.AvailableHeight();
   1.809 +  if (NS_UNCONSTRAINEDSIZE != availHeight) {
   1.810 +    if (mCaptionFrames.FirstChild() == aChildFrame) {
   1.811 +      availHeight = NS_UNCONSTRAINEDSIZE;
   1.812 +    } else {
   1.813 +      nsMargin margin;
   1.814 +      GetChildMargin(aPresContext, aOuterRS, aChildFrame,
   1.815 +                     aOuterRS.AvailableWidth(), margin);
   1.816 +    
   1.817 +      NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please");
   1.818 +      availHeight -= margin.top;
   1.819 + 
   1.820 +      NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please");
   1.821 +      availHeight -= margin.bottom;
   1.822 +    }
   1.823 +  }
   1.824 +  nsSize availSize(aAvailWidth, availHeight);
   1.825 +  // create and init the child reflow state, using placement new on
   1.826 +  // stack space allocated by the caller, so that the caller can destroy
   1.827 +  // it
   1.828 +  nsHTMLReflowState &childRS = * new (aChildRSSpace)
   1.829 +    nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
   1.830 +                      -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
   1.831 +  InitChildReflowState(*aPresContext, childRS);
   1.832 +
   1.833 +  // see if we need to reset top-of-page due to a caption
   1.834 +  if (childRS.mFlags.mIsTopOfPage &&
   1.835 +      mCaptionFrames.FirstChild() == aChildFrame) {
   1.836 +    uint8_t captionSide = GetCaptionSide();
   1.837 +    if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
   1.838 +        captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
   1.839 +      childRS.mFlags.mIsTopOfPage = false;
   1.840 +    }
   1.841 +  }
   1.842 +}
   1.843 +
   1.844 +nsresult
   1.845 +nsTableOuterFrame::OuterDoReflowChild(nsPresContext*             aPresContext,
   1.846 +                                      nsIFrame*                  aChildFrame,
   1.847 +                                      const nsHTMLReflowState&   aChildRS,
   1.848 +                                      nsHTMLReflowMetrics&       aMetrics,
   1.849 +                                      nsReflowStatus&            aStatus)
   1.850 +{ 
   1.851 +
   1.852 +  // use the current position as a best guess for placement
   1.853 +  nsPoint childPt = aChildFrame->GetPosition();
   1.854 +  uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
   1.855 +
   1.856 +  // We don't want to delete our next-in-flow's child if it's an inner table
   1.857 +  // frame, because outer table frames always assume that their inner table
   1.858 +  // frames don't go away. If an outer table frame is removed because it is
   1.859 +  // a next-in-flow of an already complete outer table frame, then it will
   1.860 +  // take care of removing it's inner table frame.
   1.861 +  if (aChildFrame == InnerTableFrame()) {
   1.862 +    flags |= NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD;
   1.863 +  }
   1.864 +
   1.865 +  return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
   1.866 +                     childPt.x, childPt.y, flags, aStatus);
   1.867 +}
   1.868 +
   1.869 +void 
   1.870 +nsTableOuterFrame::UpdateReflowMetrics(uint8_t              aCaptionSide,
   1.871 +                                       nsHTMLReflowMetrics& aMet,
   1.872 +                                       const nsMargin&      aInnerMargin,
   1.873 +                                       const nsMargin&      aCaptionMargin)
   1.874 +{
   1.875 +  SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
   1.876 +                 aMet.Width(), aMet.Height());
   1.877 +
   1.878 +  aMet.SetOverflowAreasToDesiredBounds();
   1.879 +  ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
   1.880 +  if (mCaptionFrames.NotEmpty()) {
   1.881 +    ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
   1.882 +  }
   1.883 +}
   1.884 +
   1.885 +nsresult nsTableOuterFrame::Reflow(nsPresContext*           aPresContext,
   1.886 +                                    nsHTMLReflowMetrics&     aDesiredSize,
   1.887 +                                    const nsHTMLReflowState& aOuterRS,
   1.888 +                                    nsReflowStatus&          aStatus)
   1.889 +{
   1.890 +  DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
   1.891 +  DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
   1.892 +
   1.893 +  nsresult rv = NS_OK;
   1.894 +  uint8_t captionSide = GetCaptionSide();
   1.895 +
   1.896 +  // Initialize out parameters
   1.897 +  aDesiredSize.Width() = aDesiredSize.Height() = 0;
   1.898 +  aStatus = NS_FRAME_COMPLETE;
   1.899 +
   1.900 +  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
   1.901 +    // Set up our kids.  They're already present, on an overflow list, 
   1.902 +    // or there are none so we'll create them now
   1.903 +    MoveOverflowToChildList();
   1.904 +  }
   1.905 +
   1.906 +  // Use longs to get more-aligned space.
   1.907 +  #define LONGS_IN_HTMLRS \
   1.908 +    ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
   1.909 +  long captionRSSpace[LONGS_IN_HTMLRS];
   1.910 +  nsHTMLReflowState *captionRS =
   1.911 +    static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
   1.912 +  long innerRSSpace[LONGS_IN_HTMLRS];
   1.913 +  nsHTMLReflowState *innerRS =
   1.914 +    static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
   1.915 +
   1.916 +  nsRect origInnerRect = InnerTableFrame()->GetRect();
   1.917 +  nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect();
   1.918 +  bool innerFirstReflow =
   1.919 +    (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
   1.920 +  nsRect origCaptionRect;
   1.921 +  nsRect origCaptionVisualOverflow;
   1.922 +  bool captionFirstReflow;
   1.923 +  if (mCaptionFrames.NotEmpty()) {
   1.924 +    origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
   1.925 +    origCaptionVisualOverflow =
   1.926 +      mCaptionFrames.FirstChild()->GetVisualOverflowRect();
   1.927 +    captionFirstReflow =
   1.928 +      (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
   1.929 +  }
   1.930 +  
   1.931 +  // ComputeAutoSize has to match this logic.
   1.932 +  if (captionSide == NO_SIDE) {
   1.933 +    // We don't have a caption.
   1.934 +    OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
   1.935 +                          innerRSSpace, aOuterRS.ComputedWidth());
   1.936 +  } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
   1.937 +             captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
   1.938 +    // nsTableCaptionFrame::ComputeAutoSize takes care of making side
   1.939 +    // captions small.  Compute the caption's size first, and tell the
   1.940 +    // table to fit in what's left.
   1.941 +    OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
   1.942 +                          captionRSSpace, aOuterRS.ComputedWidth());
   1.943 +    nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
   1.944 +      (captionRS->ComputedWidth() + captionRS->ComputedPhysicalMargin().LeftRight() +
   1.945 +       captionRS->ComputedPhysicalBorderPadding().LeftRight());
   1.946 +    OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
   1.947 +                          innerRSSpace, innerAvailWidth);
   1.948 +
   1.949 +  } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
   1.950 +             captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
   1.951 +    // Compute the table's size first, and then prevent the caption from
   1.952 +    // being wider unless it has to be.
   1.953 +    //
   1.954 +    // Note that CSS 2.1 (but not 2.0) says:
   1.955 +    //   The width of the anonymous box is the border-edge width of the
   1.956 +    //   table box inside it
   1.957 +    // We don't actually make our anonymous box that width (if we did,
   1.958 +    // it would break 'auto' margins), but this effectively does that.
   1.959 +    OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
   1.960 +                          innerRSSpace, aOuterRS.ComputedWidth());
   1.961 +    // It's good that CSS 2.1 says not to include margins, since we
   1.962 +    // can't, since they already been converted so they exactly
   1.963 +    // fill the available width (ignoring the margin on one side if
   1.964 +    // neither are auto).  (We take advantage of that later when we call
   1.965 +    // GetCaptionOrigin, though.)
   1.966 +    nscoord innerBorderWidth = innerRS->ComputedWidth() +
   1.967 +                               innerRS->ComputedPhysicalBorderPadding().LeftRight();
   1.968 +    OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
   1.969 +                          captionRSSpace, innerBorderWidth);
   1.970 +  } else {
   1.971 +    NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
   1.972 +                 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
   1.973 +                 "unexpected caption-side");
   1.974 +    // Size the table and the caption independently.
   1.975 +    OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
   1.976 +                          captionRSSpace, aOuterRS.ComputedWidth());
   1.977 +    OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
   1.978 +                          innerRSSpace, aOuterRS.ComputedWidth());
   1.979 +  }
   1.980 +
   1.981 +  // First reflow the caption.
   1.982 +  nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode());
   1.983 +  nsSize captionSize;
   1.984 +  nsMargin captionMargin;
   1.985 +  if (mCaptionFrames.NotEmpty()) {
   1.986 +    nsReflowStatus capStatus; // don't let the caption cause incomplete
   1.987 +    rv = OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(),
   1.988 +                            *captionRS, captionMet, capStatus);
   1.989 +    if (NS_FAILED(rv)) return rv;
   1.990 +    captionSize.width = captionMet.Width();
   1.991 +    captionSize.height = captionMet.Height();
   1.992 +    captionMargin = captionRS->ComputedPhysicalMargin();
   1.993 +    // Now that we know the height of the caption, reduce the available height
   1.994 +    // for the table frame if we are height constrained and the caption is above
   1.995 +    // or below the inner table.
   1.996 +    if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableHeight()) {
   1.997 +      nscoord captionHeight = 0;
   1.998 +      switch (captionSide) {
   1.999 +        case NS_STYLE_CAPTION_SIDE_TOP:
  1.1000 +        case NS_STYLE_CAPTION_SIDE_BOTTOM:
  1.1001 +        case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
  1.1002 +        case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
  1.1003 +          captionHeight = captionSize.height + captionMargin.TopBottom();
  1.1004 +          break;
  1.1005 +        }
  1.1006 +      }
  1.1007 +      innerRS->AvailableHeight() =
  1.1008 +        std::max(0, innerRS->AvailableHeight() - captionHeight);
  1.1009 +    }
  1.1010 +  } else {
  1.1011 +    captionSize.SizeTo(0,0);
  1.1012 +    captionMargin.SizeTo(0,0,0,0);
  1.1013 +  }
  1.1014 +
  1.1015 +  // Then, now that we know how much to reduce the width of the inner
  1.1016 +  // table to account for side captions, reflow the inner table.
  1.1017 +  nsHTMLReflowMetrics innerMet(innerRS->GetWritingMode());
  1.1018 +  rv = OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS,
  1.1019 +                          innerMet, aStatus);
  1.1020 +  if (NS_FAILED(rv)) return rv;
  1.1021 +  nsSize innerSize;
  1.1022 +  innerSize.width = innerMet.Width();
  1.1023 +  innerSize.height = innerMet.Height();
  1.1024 +  nsMargin innerMargin = innerRS->ComputedPhysicalMargin();
  1.1025 +
  1.1026 +  nsSize   containSize = GetContainingBlockSize(aOuterRS);
  1.1027 +
  1.1028 +  // Now that we've reflowed both we can place them.
  1.1029 +  // XXXldb Most of the input variables here are now uninitialized!
  1.1030 +
  1.1031 +  // XXX Need to recompute inner table's auto margins for the case of side
  1.1032 +  // captions.  (Caption's are broken too, but that should be fixed earlier.)
  1.1033 +
  1.1034 +  if (mCaptionFrames.NotEmpty()) {
  1.1035 +    nsPoint captionOrigin;
  1.1036 +    GetCaptionOrigin(captionSide, containSize, innerSize, 
  1.1037 +                     innerMargin, captionSize, captionMargin, captionOrigin);
  1.1038 +    FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet,
  1.1039 +                      captionRS, captionOrigin.x, captionOrigin.y, 0);
  1.1040 +    captionRS->~nsHTMLReflowState();
  1.1041 +  }
  1.1042 +  // XXX If the height is constrained then we need to check whether
  1.1043 +  // everything still fits...
  1.1044 +
  1.1045 +  nsPoint innerOrigin;
  1.1046 +  GetInnerOrigin(captionSide, containSize, captionSize, 
  1.1047 +                 captionMargin, innerSize, innerMargin, innerOrigin);
  1.1048 +  FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS,
  1.1049 +                    innerOrigin.x, innerOrigin.y, 0);
  1.1050 +  innerRS->~nsHTMLReflowState();
  1.1051 +
  1.1052 +  nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect,
  1.1053 +                                     origInnerVisualOverflow, innerFirstReflow);
  1.1054 +  if (mCaptionFrames.NotEmpty()) {
  1.1055 +    nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect,
  1.1056 +                                       origCaptionVisualOverflow,
  1.1057 +                                       captionFirstReflow);
  1.1058 +  }
  1.1059 +
  1.1060 +  UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
  1.1061 +
  1.1062 +  if (GetPrevInFlow()) {
  1.1063 +    ReflowOverflowContainerChildren(aPresContext, aOuterRS,
  1.1064 +                                    aDesiredSize.mOverflowAreas, 0,
  1.1065 +                                    aStatus);
  1.1066 +  }
  1.1067 +
  1.1068 +  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRS, aStatus);
  1.1069 +
  1.1070 +  // Return our desired rect
  1.1071 +
  1.1072 +  NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
  1.1073 +  return rv;
  1.1074 +}
  1.1075 +
  1.1076 +nsIAtom*
  1.1077 +nsTableOuterFrame::GetType() const
  1.1078 +{
  1.1079 +  return nsGkAtoms::tableOuterFrame;
  1.1080 +}
  1.1081 +
  1.1082 +/* ----- global methods ----- */
  1.1083 +
  1.1084 +nsIContent*
  1.1085 +nsTableOuterFrame::GetCellAt(uint32_t aRowIdx, uint32_t aColIdx) const
  1.1086 +{
  1.1087 +  nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
  1.1088 +  if (!cellMap) {
  1.1089 +    return nullptr;
  1.1090 +  }
  1.1091 +
  1.1092 +  nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
  1.1093 +  if (!cell) {
  1.1094 +    return nullptr;
  1.1095 +  }
  1.1096 +
  1.1097 +  return cell->GetContent();
  1.1098 +}
  1.1099 +
  1.1100 +
  1.1101 +nsIFrame*
  1.1102 +NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1.1103 +{
  1.1104 +  return new (aPresShell) nsTableOuterFrame(aContext);
  1.1105 +}
  1.1106 +
  1.1107 +NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame)
  1.1108 +
  1.1109 +#ifdef DEBUG_FRAME_DUMP
  1.1110 +nsresult
  1.1111 +nsTableOuterFrame::GetFrameName(nsAString& aResult) const
  1.1112 +{
  1.1113 +  return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
  1.1114 +}
  1.1115 +#endif
  1.1116 +

mercurial