michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsTableOuterFrame.h" michael@0: #include "nsTableFrame.h" michael@0: #include "nsTableCellFrame.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsIContent.h" michael@0: #include "prinrval.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::layout; michael@0: michael@0: /* ----------- nsTableCaptionFrame ---------- */ michael@0: michael@0: #define NS_TABLE_FRAME_CAPTION_LIST_INDEX 1 michael@0: #define NO_SIDE 100 michael@0: michael@0: // caption frame michael@0: nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext* aContext): michael@0: nsBlockFrame(aContext) michael@0: { michael@0: // shrink wrap michael@0: SetFlags(NS_BLOCK_FLOAT_MGR); michael@0: } michael@0: michael@0: nsTableCaptionFrame::~nsTableCaptionFrame() michael@0: { michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsTableCaptionFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::tableCaptionFrame; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsTableOuterFrame::GetBaseline() const michael@0: { michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: if (!kid) { michael@0: NS_NOTREACHED("no inner table"); michael@0: return nsContainerFrame::GetBaseline(); michael@0: } michael@0: michael@0: return kid->GetBaseline() + kid->GetPosition().y; michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) michael@0: { michael@0: nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize, michael@0: aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap); michael@0: michael@0: // If we're a container for font size inflation, then shrink michael@0: // wrapping inside of us should not apply font size inflation. michael@0: AutoMaybeDisableFontInflation an(this); michael@0: michael@0: uint8_t captionSide = StyleTableBorder()->mCaptionSide; michael@0: if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { michael@0: result.width = GetMinWidth(aRenderingContext); michael@0: } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { michael@0: // The outer frame constrains our available width to the width of michael@0: // the table. Grow if our min-width is bigger than that, but not michael@0: // larger than the containing block width. (It would really be nice michael@0: // to transmit that information another way, so we could grow up to michael@0: // the table's available width, but that's harder.) michael@0: nscoord min = GetMinWidth(aRenderingContext); michael@0: if (min > aCBSize.width) michael@0: min = aCBSize.width; michael@0: if (min > result.width) michael@0: result.width = min; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsTableCaptionFrame::GetParentStyleContextFrame() const michael@0: { michael@0: NS_PRECONDITION(mContent->GetParent(), michael@0: "How could we not have a parent here?"); michael@0: michael@0: // The caption's style context parent is the inner frame, unless michael@0: // it's anonymous. michael@0: nsIFrame* outerFrame = GetParent(); michael@0: if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) { michael@0: nsIFrame* innerFrame = outerFrame->GetFirstPrincipalChild(); michael@0: if (innerFrame) { michael@0: return nsFrame::CorrectStyleParentFrame(innerFrame, michael@0: StyleContext()->GetPseudo()); michael@0: } michael@0: } michael@0: michael@0: NS_NOTREACHED("Where is our inner table frame?"); michael@0: return nsBlockFrame::GetParentStyleContextFrame(); michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: a11y::AccType michael@0: nsTableCaptionFrame::AccessibleType() michael@0: { michael@0: if (!GetRect().IsEmpty()) { michael@0: return a11y::eHTMLCaptionType; michael@0: } michael@0: michael@0: return a11y::eNoType; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsTableCaptionFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: nsIFrame* michael@0: NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsTableCaptionFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsTableCaptionFrame) michael@0: michael@0: /* ----------- nsTableOuterFrame ---------- */ michael@0: michael@0: nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext): michael@0: nsContainerFrame(aContext) michael@0: { michael@0: } michael@0: michael@0: nsTableOuterFrame::~nsTableOuterFrame() michael@0: { michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsTableOuterFrame) michael@0: NS_QUERYFRAME_ENTRY(nsTableOuterFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: a11y::AccType michael@0: nsTableOuterFrame::AccessibleType() michael@0: { michael@0: return a11y::eHTMLTableType; michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: DestroyAbsoluteFrames(aDestructRoot); michael@0: mCaptionFrames.DestroyFramesFrom(aDestructRoot); michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: const nsFrameList& michael@0: nsTableOuterFrame::GetChildList(ChildListID aListID) const michael@0: { michael@0: if (aListID == kCaptionList) { michael@0: return mCaptionFrames; michael@0: } michael@0: michael@0: return nsContainerFrame::GetChildList(aListID); michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::GetChildLists(nsTArray* aLists) const michael@0: { michael@0: nsContainerFrame::GetChildLists(aLists); michael@0: mCaptionFrames.AppendIfNonempty(aLists, kCaptionList); michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: if (kCaptionList == aListID) { michael@0: // the frame constructor already checked for table-caption display type michael@0: mCaptionFrames.SetFrames(aChildList); michael@0: } michael@0: else { michael@0: NS_ASSERTION(aListID == kPrincipalList, "wrong childlist"); michael@0: NS_ASSERTION(mFrames.IsEmpty(), "Frame leak!"); michael@0: NS_ASSERTION(aChildList.FirstChild() && michael@0: nsGkAtoms::tableFrame == aChildList.FirstChild()->GetType(), michael@0: "expected a table frame"); michael@0: mFrames.SetFrames(aChildList); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // We only have two child frames: the inner table and a caption frame. michael@0: // The inner frame is provided when we're initialized, and it cannot change michael@0: if (kCaptionList == aListID) { michael@0: NS_ASSERTION(aFrameList.IsEmpty() || michael@0: aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame, michael@0: "appending non-caption frame to captionList"); michael@0: mCaptionFrames.AppendFrames(this, aFrameList); michael@0: rv = NS_OK; michael@0: michael@0: // Reflow the new caption frame. It's already marked dirty, so michael@0: // just tell the pres shell. michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: else { michael@0: NS_PRECONDITION(false, "unexpected child list"); michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: if (kCaptionList == aListID) { michael@0: NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, michael@0: "inserting after sibling frame with different parent"); michael@0: NS_ASSERTION(aFrameList.IsEmpty() || michael@0: aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame, michael@0: "inserting non-caption frame into captionList"); michael@0: mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); michael@0: michael@0: // Reflow the new caption frame. It's already marked dirty, so michael@0: // just tell the pres shell. michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: return NS_OK; michael@0: } michael@0: else { michael@0: NS_PRECONDITION(!aPrevFrame, "invalid previous frame"); michael@0: return AppendFrames(aListID, aFrameList); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: // We only have two child frames: the inner table and one caption frame. michael@0: // The inner frame can't be removed so this should be the caption michael@0: NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame"); michael@0: michael@0: if (HasSideCaption()) { michael@0: // The old caption width had an effect on the inner table width so michael@0: // we're going to need to reflow it. Mark it dirty michael@0: InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY); michael@0: } michael@0: michael@0: // Remove the frame and destroy it michael@0: mCaptionFrames.DestroyFrame(aOldFrame); michael@0: michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: // No border, background or outline are painted because they all belong michael@0: // to the inner table. michael@0: michael@0: // If there's no caption, take a short cut to avoid having to create michael@0: // the special display list set and then sort it. michael@0: if (mCaptionFrames.IsEmpty()) { michael@0: BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists); michael@0: return; michael@0: } michael@0: michael@0: nsDisplayListCollection set; michael@0: BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set); michael@0: michael@0: nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds()); michael@0: BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(), michael@0: aDirtyRect, captionSet); michael@0: michael@0: // Now we have to sort everything by content order, since the caption michael@0: // may be somewhere inside the table michael@0: set.SortAllByContentOrder(aBuilder, GetContent()); michael@0: set.MoveTo(aLists); michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: // Just paint the regular children, but the children's background is our michael@0: // true background (there should only be one, the real table) michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: // The children should be in content order michael@0: while (kid) { michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsTableOuterFrame::GetParentStyleContextFrame() const michael@0: { michael@0: // The table outer frame and the (inner) table frame split the style michael@0: // data by giving the table frame the style context associated with michael@0: // the table content node and creating a style context for the outer michael@0: // frame that is a *child* of the table frame's style context, michael@0: // matching the ::-moz-table-outer pseudo-element. html.css has a michael@0: // rule that causes that pseudo-element (and thus the outer table) michael@0: // to inherit *some* style properties from the table frame. The michael@0: // children of the table inherit directly from the inner table, and michael@0: // the outer table's style context is a leaf. michael@0: michael@0: return InnerTableFrame(); michael@0: } michael@0: michael@0: // INCREMENTAL REFLOW HELPER FUNCTIONS michael@0: michael@0: void michael@0: nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext, michael@0: nsHTMLReflowState& aReflowState) michael@0: michael@0: { michael@0: nsMargin collapseBorder; michael@0: nsMargin collapsePadding(0,0,0,0); michael@0: nsMargin* pCollapseBorder = nullptr; michael@0: nsMargin* pCollapsePadding = nullptr; michael@0: if (aReflowState.frame == InnerTableFrame() && michael@0: InnerTableFrame()->IsBorderCollapse()) { michael@0: collapseBorder = InnerTableFrame()->GetIncludedOuterBCBorder(); michael@0: pCollapseBorder = &collapseBorder; michael@0: pCollapsePadding = &collapsePadding; michael@0: } michael@0: aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding); michael@0: } michael@0: michael@0: // get the margin and padding data. nsHTMLReflowState doesn't handle the michael@0: // case of auto margins michael@0: void michael@0: nsTableOuterFrame::GetChildMargin(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aOuterRS, michael@0: nsIFrame* aChildFrame, michael@0: nscoord aAvailWidth, michael@0: nsMargin& aMargin) michael@0: { michael@0: // construct a reflow state to compute margin and padding. Auto margins michael@0: // will not be computed at this time. michael@0: michael@0: // create and init the child reflow state michael@0: // XXX We really shouldn't construct a reflow state to do this. michael@0: nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, michael@0: nsSize(aAvailWidth, aOuterRS.AvailableHeight()), michael@0: -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); michael@0: InitChildReflowState(*aPresContext, childRS); michael@0: michael@0: aMargin = childRS.ComputedPhysicalMargin(); michael@0: } michael@0: michael@0: static nsSize michael@0: GetContainingBlockSize(const nsHTMLReflowState& aOuterRS) michael@0: { michael@0: nsSize size(0,0); michael@0: const nsHTMLReflowState* containRS = michael@0: aOuterRS.mCBReflowState; michael@0: michael@0: if (containRS) { michael@0: size.width = containRS->ComputedWidth(); michael@0: if (NS_UNCONSTRAINEDSIZE == size.width) { michael@0: size.width = 0; michael@0: } michael@0: size.height = containRS->ComputedHeight(); michael@0: if (NS_UNCONSTRAINEDSIZE == size.height) { michael@0: size.height = 0; michael@0: } michael@0: } michael@0: return size; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsTableOuterFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: InnerTableFrame(), nsLayoutUtils::MIN_WIDTH); michael@0: DISPLAY_MIN_WIDTH(this, width); michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: nscoord capWidth = michael@0: nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), michael@0: nsLayoutUtils::MIN_WIDTH); michael@0: if (HasSideCaption()) { michael@0: width += capWidth; michael@0: } else { michael@0: if (capWidth > width) { michael@0: width = capWidth; michael@0: } michael@0: } michael@0: } michael@0: return width; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsTableOuterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord maxWidth; michael@0: DISPLAY_PREF_WIDTH(this, maxWidth); michael@0: michael@0: maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: InnerTableFrame(), nsLayoutUtils::PREF_WIDTH); michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: uint8_t captionSide = GetCaptionSide(); michael@0: switch(captionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: michael@0: case NS_STYLE_CAPTION_SIDE_RIGHT: michael@0: { michael@0: nscoord capMin = michael@0: nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), michael@0: nsLayoutUtils::MIN_WIDTH); michael@0: maxWidth += capMin; michael@0: } michael@0: break; michael@0: default: michael@0: { michael@0: nsLayoutUtils::IntrinsicWidthType iwt; michael@0: if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { michael@0: // Don't let the caption's pref width expand the table's pref michael@0: // width. michael@0: iwt = nsLayoutUtils::MIN_WIDTH; michael@0: } else { michael@0: NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, michael@0: "unexpected caption side"); michael@0: iwt = nsLayoutUtils::PREF_WIDTH; michael@0: } michael@0: nscoord capPref = michael@0: nsLayoutUtils::IntrinsicForContainer(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), michael@0: iwt); michael@0: maxWidth = std::max(maxWidth, capPref); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: return maxWidth; michael@0: } michael@0: michael@0: // Compute the margin-box width of aChildFrame given the inputs. If michael@0: // aMarginResult is non-null, fill it with the part of the margin-width michael@0: // that was contributed by the margin. michael@0: static nscoord michael@0: ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext, michael@0: nsIFrame *aChildFrame, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nscoord *aMarginResult = nullptr) michael@0: { michael@0: AutoMaybeDisableFontInflation an(aChildFrame); michael@0: michael@0: nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width); michael@0: nsSize size = aChildFrame->ComputeSize(aRenderingContext, aCBSize, michael@0: aAvailableWidth, michael@0: nsSize(offsets.ComputedPhysicalMargin().LeftRight(), michael@0: offsets.ComputedPhysicalMargin().TopBottom()), michael@0: nsSize(offsets.ComputedPhysicalBorderPadding().LeftRight() - michael@0: offsets.ComputedPhysicalPadding().LeftRight(), michael@0: offsets.ComputedPhysicalBorderPadding().TopBottom() - michael@0: offsets.ComputedPhysicalPadding().TopBottom()), michael@0: nsSize(offsets.ComputedPhysicalPadding().LeftRight(), michael@0: offsets.ComputedPhysicalPadding().TopBottom()), michael@0: true); michael@0: if (aMarginResult) michael@0: *aMarginResult = offsets.ComputedPhysicalMargin().LeftRight(); michael@0: return size.width + offsets.ComputedPhysicalMargin().LeftRight() + michael@0: offsets.ComputedPhysicalBorderPadding().LeftRight(); michael@0: } michael@0: michael@0: /* virtual */ nsSize michael@0: nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) michael@0: { michael@0: nscoord kidAvailableWidth = aAvailableWidth - aMargin.width; michael@0: NS_ASSERTION(aBorder == nsSize(0, 0) && michael@0: aPadding == nsSize(0, 0), michael@0: "Table outer frames cannot hae borders or paddings"); michael@0: michael@0: // When we're shrink-wrapping, our auto size needs to wrap around the michael@0: // actual size of the table, which (if it is specified as a percent) michael@0: // could be something that is not reflected in our GetMinWidth and michael@0: // GetPrefWidth. See bug 349457 for an example. michael@0: michael@0: // Match the availableWidth logic in Reflow. michael@0: uint8_t captionSide = GetCaptionSide(); michael@0: nscoord width; michael@0: if (captionSide == NO_SIDE) { michael@0: width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), michael@0: aCBSize, kidAvailableWidth); michael@0: } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { michael@0: nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), michael@0: aCBSize, kidAvailableWidth); michael@0: width = capWidth + ChildShrinkWrapWidth(aRenderingContext, michael@0: InnerTableFrame(), aCBSize, michael@0: kidAvailableWidth - capWidth); michael@0: } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { michael@0: nscoord margin; michael@0: width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), michael@0: aCBSize, kidAvailableWidth, &margin); michael@0: nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), aCBSize, michael@0: width - margin); michael@0: if (capWidth > width) michael@0: width = capWidth; michael@0: } else { michael@0: NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, michael@0: "unexpected caption-side"); michael@0: width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(), michael@0: aCBSize, kidAvailableWidth); michael@0: nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, michael@0: mCaptionFrames.FirstChild(), michael@0: aCBSize, kidAvailableWidth); michael@0: if (capWidth > width) michael@0: width = capWidth; michael@0: } michael@0: michael@0: return nsSize(width, NS_UNCONSTRAINEDSIZE); michael@0: } michael@0: michael@0: uint8_t michael@0: nsTableOuterFrame::GetCaptionSide() michael@0: { michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide; michael@0: } michael@0: else { michael@0: return NO_SIDE; // no caption michael@0: } michael@0: } michael@0: michael@0: uint8_t michael@0: nsTableOuterFrame::GetCaptionVerticalAlign() michael@0: { michael@0: const nsStyleCoord& va = michael@0: mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign; michael@0: return (va.GetUnit() == eStyleUnit_Enumerated) michael@0: ? va.GetIntValue() michael@0: : NS_STYLE_VERTICAL_ALIGN_TOP; michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide, michael@0: const nsMargin& aInnerMargin, michael@0: const nsMargin& aCaptionMargin, michael@0: nscoord& aWidth, michael@0: nscoord& aHeight) michael@0: { michael@0: aWidth = aHeight = 0; michael@0: michael@0: nsRect innerRect = InnerTableFrame()->GetRect(); michael@0: nscoord innerWidth = innerRect.width; michael@0: michael@0: nsRect captionRect(0,0,0,0); michael@0: nscoord captionWidth = 0; michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: captionRect = mCaptionFrames.FirstChild()->GetRect(); michael@0: captionWidth = captionRect.width; michael@0: } michael@0: switch(aCaptionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: michael@0: aWidth = std::max(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) + michael@0: innerWidth + aInnerMargin.right; michael@0: break; michael@0: case NS_STYLE_CAPTION_SIDE_RIGHT: michael@0: aWidth = std::max(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) + michael@0: innerWidth + aInnerMargin.left; michael@0: break; michael@0: default: michael@0: aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right; michael@0: aWidth = std::max(aWidth, captionRect.XMost() + aCaptionMargin.right); michael@0: } michael@0: aHeight = innerRect.YMost() + aInnerMargin.bottom; michael@0: if (NS_STYLE_CAPTION_SIDE_BOTTOM != aCaptionSide) { michael@0: aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom); michael@0: } michael@0: else { michael@0: aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom + michael@0: aInnerMargin.bottom); michael@0: } michael@0: michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide, michael@0: const nsSize& aContainBlockSize, michael@0: const nsSize& aInnerSize, michael@0: const nsMargin& aInnerMargin, michael@0: const nsSize& aCaptionSize, michael@0: nsMargin& aCaptionMargin, michael@0: nsPoint& aOrigin) michael@0: { michael@0: aOrigin.x = aOrigin.y = 0; michael@0: if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) || michael@0: (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) { michael@0: return NS_OK; michael@0: } michael@0: if (mCaptionFrames.IsEmpty()) return NS_OK; michael@0: michael@0: NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.top, "The computed caption margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.bottom, "The computed caption margin is auto?"); michael@0: michael@0: // horizontal computation michael@0: switch(aCaptionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM: michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: { michael@0: // FIXME: Position relative to right edge for RTL. (Based on table michael@0: // direction or table parent direction?) michael@0: aOrigin.x = aCaptionMargin.left; michael@0: if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { michael@0: // We placed the caption using only the table's width as available michael@0: // width, and we should position it this way as well. michael@0: aOrigin.x += aInnerMargin.left; michael@0: } michael@0: } break; michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: { michael@0: aOrigin.x = aCaptionMargin.left; michael@0: } break; michael@0: case NS_STYLE_CAPTION_SIDE_RIGHT: { michael@0: aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left; michael@0: } break; michael@0: default: { // top michael@0: NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE, michael@0: "unexpected caption side"); michael@0: // FIXME: Position relative to right edge for RTL. (Based on table michael@0: // direction or table parent direction?) michael@0: aOrigin.x = aCaptionMargin.left; michael@0: if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) { michael@0: // We placed the caption using only the table's width as available michael@0: // width, and we should position it this way as well. michael@0: aOrigin.x += aInnerMargin.left; michael@0: } michael@0: michael@0: } break; michael@0: } michael@0: // vertical computation michael@0: switch (aCaptionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_RIGHT: michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: michael@0: aOrigin.y = aInnerMargin.top; michael@0: switch (GetCaptionVerticalAlign()) { michael@0: case NS_STYLE_VERTICAL_ALIGN_MIDDLE: michael@0: aOrigin.y = std::max(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2)); michael@0: break; michael@0: case NS_STYLE_VERTICAL_ALIGN_BOTTOM: michael@0: aOrigin.y = std::max(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: break; michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM: { michael@0: aOrigin.y = aInnerMargin.top + aInnerSize.height + aCaptionMargin.top; michael@0: } break; michael@0: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: michael@0: case NS_STYLE_CAPTION_SIDE_TOP: { michael@0: aOrigin.y = aInnerMargin.top + aCaptionMargin.top; michael@0: } break; michael@0: default: michael@0: NS_NOTREACHED("Unknown caption alignment type"); michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide, michael@0: const nsSize& aContainBlockSize, michael@0: const nsSize& aCaptionSize, michael@0: const nsMargin& aCaptionMargin, michael@0: const nsSize& aInnerSize, michael@0: nsMargin& aInnerMargin, michael@0: nsPoint& aOrigin) michael@0: { michael@0: michael@0: NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.right, "The computed caption margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.left, "The computed inner margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.right, "The computed inner margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.top, "The computed inner margin is auto?"); michael@0: NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.bottom, "The computed inner margin is auto?"); michael@0: michael@0: aOrigin.x = aOrigin.y = 0; michael@0: if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) || michael@0: (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nscoord minCapWidth = aCaptionSize.width; michael@0: michael@0: minCapWidth += aCaptionMargin.left; michael@0: minCapWidth += aCaptionMargin.right; michael@0: michael@0: // horizontal computation michael@0: switch (aCaptionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: { michael@0: if (aInnerMargin.left < minCapWidth) { michael@0: // shift the inner table to get some place for the caption michael@0: aInnerMargin.right += aInnerMargin.left - minCapWidth; michael@0: aInnerMargin.right = std::max(0, aInnerMargin.right); michael@0: aInnerMargin.left = minCapWidth; michael@0: } michael@0: aOrigin.x = aInnerMargin.left; michael@0: } break; michael@0: default: { michael@0: NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || michael@0: aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || michael@0: aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE || michael@0: aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT || michael@0: aCaptionSide == NO_SIDE, michael@0: "unexpected caption side"); michael@0: aOrigin.x = aInnerMargin.left; michael@0: } break; michael@0: } michael@0: michael@0: // vertical computation michael@0: switch (aCaptionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM: michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: { michael@0: aOrigin.y = aInnerMargin.top; michael@0: } break; michael@0: case NS_STYLE_CAPTION_SIDE_LEFT: michael@0: case NS_STYLE_CAPTION_SIDE_RIGHT: { michael@0: aOrigin.y = aInnerMargin.top; michael@0: switch (GetCaptionVerticalAlign()) { michael@0: case NS_STYLE_VERTICAL_ALIGN_MIDDLE: michael@0: aOrigin.y = std::max(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2); michael@0: break; michael@0: case NS_STYLE_VERTICAL_ALIGN_BOTTOM: michael@0: aOrigin.y = std::max(aInnerMargin.top, aCaptionSize.height - aInnerSize.height); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } break; michael@0: case NO_SIDE: michael@0: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: michael@0: case NS_STYLE_CAPTION_SIDE_TOP: { michael@0: aOrigin.y = aInnerMargin.top + aCaptionMargin.top + aCaptionSize.height + michael@0: aCaptionMargin.bottom; michael@0: } break; michael@0: default: michael@0: NS_NOTREACHED("Unknown caption alignment type"); michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, michael@0: nsIFrame* aChildFrame, michael@0: const nsHTMLReflowState& aOuterRS, michael@0: void* aChildRSSpace, michael@0: nscoord aAvailWidth) michael@0: { michael@0: // work around pixel rounding errors, round down to ensure we don't exceed the avail height in michael@0: nscoord availHeight = aOuterRS.AvailableHeight(); michael@0: if (NS_UNCONSTRAINEDSIZE != availHeight) { michael@0: if (mCaptionFrames.FirstChild() == aChildFrame) { michael@0: availHeight = NS_UNCONSTRAINEDSIZE; michael@0: } else { michael@0: nsMargin margin; michael@0: GetChildMargin(aPresContext, aOuterRS, aChildFrame, michael@0: aOuterRS.AvailableWidth(), margin); michael@0: michael@0: NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please"); michael@0: availHeight -= margin.top; michael@0: michael@0: NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please"); michael@0: availHeight -= margin.bottom; michael@0: } michael@0: } michael@0: nsSize availSize(aAvailWidth, availHeight); michael@0: // create and init the child reflow state, using placement new on michael@0: // stack space allocated by the caller, so that the caller can destroy michael@0: // it michael@0: nsHTMLReflowState &childRS = * new (aChildRSSpace) michael@0: nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize, michael@0: -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); michael@0: InitChildReflowState(*aPresContext, childRS); michael@0: michael@0: // see if we need to reset top-of-page due to a caption michael@0: if (childRS.mFlags.mIsTopOfPage && michael@0: mCaptionFrames.FirstChild() == aChildFrame) { michael@0: uint8_t captionSide = GetCaptionSide(); michael@0: if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { michael@0: childRS.mFlags.mIsTopOfPage = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext, michael@0: nsIFrame* aChildFrame, michael@0: const nsHTMLReflowState& aChildRS, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: michael@0: // use the current position as a best guess for placement michael@0: nsPoint childPt = aChildFrame->GetPosition(); michael@0: uint32_t flags = NS_FRAME_NO_MOVE_FRAME; michael@0: michael@0: // We don't want to delete our next-in-flow's child if it's an inner table michael@0: // frame, because outer table frames always assume that their inner table michael@0: // frames don't go away. If an outer table frame is removed because it is michael@0: // a next-in-flow of an already complete outer table frame, then it will michael@0: // take care of removing it's inner table frame. michael@0: if (aChildFrame == InnerTableFrame()) { michael@0: flags |= NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD; michael@0: } michael@0: michael@0: return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS, michael@0: childPt.x, childPt.y, flags, aStatus); michael@0: } michael@0: michael@0: void michael@0: nsTableOuterFrame::UpdateReflowMetrics(uint8_t aCaptionSide, michael@0: nsHTMLReflowMetrics& aMet, michael@0: const nsMargin& aInnerMargin, michael@0: const nsMargin& aCaptionMargin) michael@0: { michael@0: SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin, michael@0: aMet.Width(), aMet.Height()); michael@0: michael@0: aMet.SetOverflowAreasToDesiredBounds(); michael@0: ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame()); michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild()); michael@0: } michael@0: } michael@0: michael@0: nsresult nsTableOuterFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aOuterRS, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus); michael@0: michael@0: nsresult rv = NS_OK; michael@0: uint8_t captionSide = GetCaptionSide(); michael@0: michael@0: // Initialize out parameters michael@0: aDesiredSize.Width() = aDesiredSize.Height() = 0; michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: michael@0: if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: // Set up our kids. They're already present, on an overflow list, michael@0: // or there are none so we'll create them now michael@0: MoveOverflowToChildList(); michael@0: } michael@0: michael@0: // Use longs to get more-aligned space. michael@0: #define LONGS_IN_HTMLRS \ michael@0: ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long)) michael@0: long captionRSSpace[LONGS_IN_HTMLRS]; michael@0: nsHTMLReflowState *captionRS = michael@0: static_cast((void*)captionRSSpace); michael@0: long innerRSSpace[LONGS_IN_HTMLRS]; michael@0: nsHTMLReflowState *innerRS = michael@0: static_cast((void*) innerRSSpace); michael@0: michael@0: nsRect origInnerRect = InnerTableFrame()->GetRect(); michael@0: nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect(); michael@0: bool innerFirstReflow = michael@0: (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; michael@0: nsRect origCaptionRect; michael@0: nsRect origCaptionVisualOverflow; michael@0: bool captionFirstReflow; michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: origCaptionRect = mCaptionFrames.FirstChild()->GetRect(); michael@0: origCaptionVisualOverflow = michael@0: mCaptionFrames.FirstChild()->GetVisualOverflowRect(); michael@0: captionFirstReflow = michael@0: (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; michael@0: } michael@0: michael@0: // ComputeAutoSize has to match this logic. michael@0: if (captionSide == NO_SIDE) { michael@0: // We don't have a caption. michael@0: OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, michael@0: innerRSSpace, aOuterRS.ComputedWidth()); michael@0: } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { michael@0: // nsTableCaptionFrame::ComputeAutoSize takes care of making side michael@0: // captions small. Compute the caption's size first, and tell the michael@0: // table to fit in what's left. michael@0: OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, michael@0: captionRSSpace, aOuterRS.ComputedWidth()); michael@0: nscoord innerAvailWidth = aOuterRS.ComputedWidth() - michael@0: (captionRS->ComputedWidth() + captionRS->ComputedPhysicalMargin().LeftRight() + michael@0: captionRS->ComputedPhysicalBorderPadding().LeftRight()); michael@0: OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, michael@0: innerRSSpace, innerAvailWidth); michael@0: michael@0: } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { michael@0: // Compute the table's size first, and then prevent the caption from michael@0: // being wider unless it has to be. michael@0: // michael@0: // Note that CSS 2.1 (but not 2.0) says: michael@0: // The width of the anonymous box is the border-edge width of the michael@0: // table box inside it michael@0: // We don't actually make our anonymous box that width (if we did, michael@0: // it would break 'auto' margins), but this effectively does that. michael@0: OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, michael@0: innerRSSpace, aOuterRS.ComputedWidth()); michael@0: // It's good that CSS 2.1 says not to include margins, since we michael@0: // can't, since they already been converted so they exactly michael@0: // fill the available width (ignoring the margin on one side if michael@0: // neither are auto). (We take advantage of that later when we call michael@0: // GetCaptionOrigin, though.) michael@0: nscoord innerBorderWidth = innerRS->ComputedWidth() + michael@0: innerRS->ComputedPhysicalBorderPadding().LeftRight(); michael@0: OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, michael@0: captionRSSpace, innerBorderWidth); michael@0: } else { michael@0: NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || michael@0: captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, michael@0: "unexpected caption-side"); michael@0: // Size the table and the caption independently. michael@0: OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, michael@0: captionRSSpace, aOuterRS.ComputedWidth()); michael@0: OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, michael@0: innerRSSpace, aOuterRS.ComputedWidth()); michael@0: } michael@0: michael@0: // First reflow the caption. michael@0: nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode()); michael@0: nsSize captionSize; michael@0: nsMargin captionMargin; michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: nsReflowStatus capStatus; // don't let the caption cause incomplete michael@0: rv = OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(), michael@0: *captionRS, captionMet, capStatus); michael@0: if (NS_FAILED(rv)) return rv; michael@0: captionSize.width = captionMet.Width(); michael@0: captionSize.height = captionMet.Height(); michael@0: captionMargin = captionRS->ComputedPhysicalMargin(); michael@0: // Now that we know the height of the caption, reduce the available height michael@0: // for the table frame if we are height constrained and the caption is above michael@0: // or below the inner table. michael@0: if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableHeight()) { michael@0: nscoord captionHeight = 0; michael@0: switch (captionSide) { michael@0: case NS_STYLE_CAPTION_SIDE_TOP: michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM: michael@0: case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: michael@0: case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: { michael@0: captionHeight = captionSize.height + captionMargin.TopBottom(); michael@0: break; michael@0: } michael@0: } michael@0: innerRS->AvailableHeight() = michael@0: std::max(0, innerRS->AvailableHeight() - captionHeight); michael@0: } michael@0: } else { michael@0: captionSize.SizeTo(0,0); michael@0: captionMargin.SizeTo(0,0,0,0); michael@0: } michael@0: michael@0: // Then, now that we know how much to reduce the width of the inner michael@0: // table to account for side captions, reflow the inner table. michael@0: nsHTMLReflowMetrics innerMet(innerRS->GetWritingMode()); michael@0: rv = OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS, michael@0: innerMet, aStatus); michael@0: if (NS_FAILED(rv)) return rv; michael@0: nsSize innerSize; michael@0: innerSize.width = innerMet.Width(); michael@0: innerSize.height = innerMet.Height(); michael@0: nsMargin innerMargin = innerRS->ComputedPhysicalMargin(); michael@0: michael@0: nsSize containSize = GetContainingBlockSize(aOuterRS); michael@0: michael@0: // Now that we've reflowed both we can place them. michael@0: // XXXldb Most of the input variables here are now uninitialized! michael@0: michael@0: // XXX Need to recompute inner table's auto margins for the case of side michael@0: // captions. (Caption's are broken too, but that should be fixed earlier.) michael@0: michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: nsPoint captionOrigin; michael@0: GetCaptionOrigin(captionSide, containSize, innerSize, michael@0: innerMargin, captionSize, captionMargin, captionOrigin); michael@0: FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet, michael@0: captionRS, captionOrigin.x, captionOrigin.y, 0); michael@0: captionRS->~nsHTMLReflowState(); michael@0: } michael@0: // XXX If the height is constrained then we need to check whether michael@0: // everything still fits... michael@0: michael@0: nsPoint innerOrigin; michael@0: GetInnerOrigin(captionSide, containSize, captionSize, michael@0: captionMargin, innerSize, innerMargin, innerOrigin); michael@0: FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS, michael@0: innerOrigin.x, innerOrigin.y, 0); michael@0: innerRS->~nsHTMLReflowState(); michael@0: michael@0: nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect, michael@0: origInnerVisualOverflow, innerFirstReflow); michael@0: if (mCaptionFrames.NotEmpty()) { michael@0: nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect, michael@0: origCaptionVisualOverflow, michael@0: captionFirstReflow); michael@0: } michael@0: michael@0: UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin); michael@0: michael@0: if (GetPrevInFlow()) { michael@0: ReflowOverflowContainerChildren(aPresContext, aOuterRS, michael@0: aDesiredSize.mOverflowAreas, 0, michael@0: aStatus); michael@0: } michael@0: michael@0: FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRS, aStatus); michael@0: michael@0: // Return our desired rect michael@0: michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize); michael@0: return rv; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsTableOuterFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::tableOuterFrame; michael@0: } michael@0: michael@0: /* ----- global methods ----- */ michael@0: michael@0: nsIContent* michael@0: nsTableOuterFrame::GetCellAt(uint32_t aRowIdx, uint32_t aColIdx) const michael@0: { michael@0: nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap(); michael@0: if (!cellMap) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx); michael@0: if (!cell) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return cell->GetContent(); michael@0: } michael@0: michael@0: michael@0: nsIFrame* michael@0: NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsTableOuterFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame) michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsTableOuterFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult); michael@0: } michael@0: #endif michael@0: