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 +