Bumping manifests a=b2g-bump
[gecko.git] / layout / tables / nsTableOuterFrame.cpp
blob935bd5b40426521ef59ec2313e8376fe01a972d1
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsTableOuterFrame.h"
6 #include "nsTableFrame.h"
7 #include "nsTableCellFrame.h"
8 #include "nsStyleContext.h"
9 #include "nsStyleConsts.h"
10 #include "nsPresContext.h"
11 #include "nsCSSRendering.h"
12 #include "nsIContent.h"
13 #include "prinrval.h"
14 #include "nsGkAtoms.h"
15 #include "nsHTMLParts.h"
16 #include "nsIPresShell.h"
17 #include "nsIServiceManager.h"
18 #include "nsIDOMNode.h"
19 #include "nsDisplayList.h"
20 #include "nsLayoutUtils.h"
21 #include <algorithm>
23 using namespace mozilla;
24 using namespace mozilla::layout;
26 /* ----------- nsTableCaptionFrame ---------- */
28 #define NS_TABLE_FRAME_CAPTION_LIST_INDEX 1
29 #define NO_SIDE 100
31 // caption frame
32 nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext* aContext):
33 nsBlockFrame(aContext)
35 // shrink wrap
36 SetFlags(NS_BLOCK_FLOAT_MGR);
39 nsTableCaptionFrame::~nsTableCaptionFrame()
43 nsIAtom*
44 nsTableCaptionFrame::GetType() const
46 return nsGkAtoms::tableCaptionFrame;
49 /* virtual */ nscoord
50 nsTableOuterFrame::GetLogicalBaseline(WritingMode aWritingMode) const
52 nsIFrame* kid = mFrames.FirstChild();
53 if (!kid) {
54 NS_NOTREACHED("no inner table");
55 return nsContainerFrame::GetLogicalBaseline(aWritingMode);
58 return kid->GetLogicalBaseline(aWritingMode) +
59 kid->BStart(aWritingMode, mRect.width);
62 /* virtual */
63 LogicalSize
64 nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
65 WritingMode aWM,
66 const LogicalSize& aCBSize,
67 nscoord aAvailableISize,
68 const LogicalSize& aMargin,
69 const LogicalSize& aBorder,
70 const LogicalSize& aPadding,
71 bool aShrinkWrap)
73 LogicalSize result =
74 nsBlockFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize,
75 aAvailableISize, aMargin, aBorder,
76 aPadding, aShrinkWrap);
78 // If we're a container for font size inflation, then shrink
79 // wrapping inside of us should not apply font size inflation.
80 AutoMaybeDisableFontInflation an(this);
82 // XXX todo: make this aware of vertical writing modes
83 uint8_t captionSide = StyleTableBorder()->mCaptionSide;
84 if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
85 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
86 result.ISize(aWM) = GetMinISize(aRenderingContext);
87 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
88 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
89 // The outer frame constrains our available width to the width of
90 // the table. Grow if our min-width is bigger than that, but not
91 // larger than the containing block width. (It would really be nice
92 // to transmit that information another way, so we could grow up to
93 // the table's available width, but that's harder.)
94 nscoord min = GetMinISize(aRenderingContext);
95 if (min > aCBSize.ISize(aWM)) {
96 min = aCBSize.ISize(aWM);
98 if (min > result.ISize(aWM)) {
99 result.ISize(aWM) = min;
102 return result;
105 nsIFrame*
106 nsTableCaptionFrame::GetParentStyleContextFrame() const
108 NS_PRECONDITION(mContent->GetParent(),
109 "How could we not have a parent here?");
111 // The caption's style context parent is the inner frame, unless
112 // it's anonymous.
113 nsIFrame* outerFrame = GetParent();
114 if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) {
115 nsIFrame* innerFrame = outerFrame->GetFirstPrincipalChild();
116 if (innerFrame) {
117 return nsFrame::CorrectStyleParentFrame(innerFrame,
118 StyleContext()->GetPseudo());
122 NS_NOTREACHED("Where is our inner table frame?");
123 return nsBlockFrame::GetParentStyleContextFrame();
126 #ifdef ACCESSIBILITY
127 a11y::AccType
128 nsTableCaptionFrame::AccessibleType()
130 if (!GetRect().IsEmpty()) {
131 return a11y::eHTMLCaptionType;
134 return a11y::eNoType;
136 #endif
138 #ifdef DEBUG_FRAME_DUMP
139 nsresult
140 nsTableCaptionFrame::GetFrameName(nsAString& aResult) const
142 return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult);
144 #endif
146 nsTableCaptionFrame*
147 NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
149 return new (aPresShell) nsTableCaptionFrame(aContext);
152 NS_IMPL_FRAMEARENA_HELPERS(nsTableCaptionFrame)
154 /* ----------- nsTableOuterFrame ---------- */
156 nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext):
157 nsContainerFrame(aContext)
161 nsTableOuterFrame::~nsTableOuterFrame()
165 NS_QUERYFRAME_HEAD(nsTableOuterFrame)
166 NS_QUERYFRAME_ENTRY(nsTableOuterFrame)
167 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
169 #ifdef ACCESSIBILITY
170 a11y::AccType
171 nsTableOuterFrame::AccessibleType()
173 return a11y::eHTMLTableType;
175 #endif
177 void
178 nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot)
180 DestroyAbsoluteFrames(aDestructRoot);
181 mCaptionFrames.DestroyFramesFrom(aDestructRoot);
182 nsContainerFrame::DestroyFrom(aDestructRoot);
185 const nsFrameList&
186 nsTableOuterFrame::GetChildList(ChildListID aListID) const
188 if (aListID == kCaptionList) {
189 return mCaptionFrames;
192 return nsContainerFrame::GetChildList(aListID);
195 void
196 nsTableOuterFrame::GetChildLists(nsTArray<ChildList>* aLists) const
198 nsContainerFrame::GetChildLists(aLists);
199 mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
202 void
203 nsTableOuterFrame::SetInitialChildList(ChildListID aListID,
204 nsFrameList& aChildList)
206 MOZ_ASSERT(kCaptionList == aListID || aListID == kPrincipalList,
207 "unexpected child list");
208 MOZ_ASSERT(GetChildList(aListID).IsEmpty(),
209 "already have child frames in SetInitialChildList");
211 if (kCaptionList == aListID) {
212 // the frame constructor already checked for table-caption display type
213 mCaptionFrames.SetFrames(aChildList);
214 } else {
215 MOZ_ASSERT(aChildList.FirstChild() &&
216 aChildList.FirstChild() == aChildList.LastChild() &&
217 nsGkAtoms::tableFrame == aChildList.FirstChild()->GetType(),
218 "expected a single table frame");
219 mFrames.SetFrames(aChildList);
223 void
224 nsTableOuterFrame::AppendFrames(ChildListID aListID,
225 nsFrameList& aFrameList)
227 // We only have two child frames: the inner table and a caption frame.
228 // The inner frame is provided when we're initialized, and it cannot change
229 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
230 MOZ_ASSERT(aFrameList.IsEmpty() ||
231 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
232 "appending non-caption frame to captionList");
233 mCaptionFrames.AppendFrames(this, aFrameList);
235 // Reflow the new caption frame. It's already marked dirty, so
236 // just tell the pres shell.
237 PresContext()->PresShell()->
238 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
239 NS_FRAME_HAS_DIRTY_CHILDREN);
242 void
243 nsTableOuterFrame::InsertFrames(ChildListID aListID,
244 nsIFrame* aPrevFrame,
245 nsFrameList& aFrameList)
247 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
248 MOZ_ASSERT(aFrameList.IsEmpty() ||
249 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
250 "inserting non-caption frame into captionList");
251 MOZ_ASSERT(!aPrevFrame || aPrevFrame->GetParent() == this,
252 "inserting after sibling frame with different parent");
253 mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
255 // Reflow the new caption frame. It's already marked dirty, so
256 // just tell the pres shell.
257 PresContext()->PresShell()->
258 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
259 NS_FRAME_HAS_DIRTY_CHILDREN);
262 void
263 nsTableOuterFrame::RemoveFrame(ChildListID aListID,
264 nsIFrame* aOldFrame)
266 // We only have two child frames: the inner table and one caption frame.
267 // The inner frame can't be removed so this should be the caption
268 NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame");
270 if (HasSideCaption()) {
271 // The old caption width had an effect on the inner table width so
272 // we're going to need to reflow it. Mark it dirty
273 InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY);
276 // Remove the frame and destroy it
277 mCaptionFrames.DestroyFrame(aOldFrame);
279 PresContext()->PresShell()->
280 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
281 NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed
284 void
285 nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
286 const nsRect& aDirtyRect,
287 const nsDisplayListSet& aLists)
289 // No border, background or outline are painted because they all belong
290 // to the inner table.
292 // If there's no caption, take a short cut to avoid having to create
293 // the special display list set and then sort it.
294 if (mCaptionFrames.IsEmpty()) {
295 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists);
296 return;
299 nsDisplayListCollection set;
300 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set);
302 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
303 BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(),
304 aDirtyRect, captionSet);
306 // Now we have to sort everything by content order, since the caption
307 // may be somewhere inside the table
308 set.SortAllByContentOrder(aBuilder, GetContent());
309 set.MoveTo(aLists);
312 void
313 nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder,
314 const nsRect& aDirtyRect,
315 const nsDisplayListSet& aLists)
317 // Just paint the regular children, but the children's background is our
318 // true background (there should only be one, the real table)
319 nsIFrame* kid = mFrames.FirstChild();
320 // The children should be in content order
321 while (kid) {
322 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
323 kid = kid->GetNextSibling();
327 nsIFrame*
328 nsTableOuterFrame::GetParentStyleContextFrame() const
330 // The table outer frame and the (inner) table frame split the style
331 // data by giving the table frame the style context associated with
332 // the table content node and creating a style context for the outer
333 // frame that is a *child* of the table frame's style context,
334 // matching the ::-moz-table-outer pseudo-element. html.css has a
335 // rule that causes that pseudo-element (and thus the outer table)
336 // to inherit *some* style properties from the table frame. The
337 // children of the table inherit directly from the inner table, and
338 // the outer table's style context is a leaf.
340 return InnerTableFrame();
343 // INCREMENTAL REFLOW HELPER FUNCTIONS
345 void
346 nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext,
347 nsHTMLReflowState& aReflowState)
350 nsMargin collapseBorder;
351 nsMargin collapsePadding(0,0,0,0);
352 nsMargin* pCollapseBorder = nullptr;
353 nsMargin* pCollapsePadding = nullptr;
354 if (aReflowState.frame == InnerTableFrame() &&
355 InnerTableFrame()->IsBorderCollapse()) {
356 collapseBorder = InnerTableFrame()->GetIncludedOuterBCBorder();
357 pCollapseBorder = &collapseBorder;
358 pCollapsePadding = &collapsePadding;
360 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding);
363 // get the margin and padding data. nsHTMLReflowState doesn't handle the
364 // case of auto margins
365 void
366 nsTableOuterFrame::GetChildMargin(nsPresContext* aPresContext,
367 const nsHTMLReflowState& aOuterRS,
368 nsIFrame* aChildFrame,
369 nscoord aAvailISize,
370 LogicalMargin& aMargin)
372 // construct a reflow state to compute margin and padding. Auto margins
373 // will not be computed at this time.
375 // create and init the child reflow state
376 // XXX We really shouldn't construct a reflow state to do this.
377 WritingMode wm = aChildFrame->GetWritingMode();
378 LogicalSize availSize(wm, aAvailISize, aOuterRS.AvailableSize(wm).BSize(wm));
379 nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame, availSize,
380 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
381 InitChildReflowState(*aPresContext, childRS);
383 aMargin = childRS.ComputedLogicalMargin();
386 static nsSize
387 GetContainingBlockSize(const nsHTMLReflowState& aOuterRS)
389 nsSize size(0,0);
390 const nsHTMLReflowState* containRS =
391 aOuterRS.mCBReflowState;
393 if (containRS) {
394 size.width = containRS->ComputedWidth();
395 if (NS_UNCONSTRAINEDSIZE == size.width) {
396 size.width = 0;
398 size.height = containRS->ComputedHeight();
399 if (NS_UNCONSTRAINEDSIZE == size.height) {
400 size.height = 0;
403 return size;
406 /* virtual */ nscoord
407 nsTableOuterFrame::GetMinISize(nsRenderingContext *aRenderingContext)
409 nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
410 InnerTableFrame(), nsLayoutUtils::MIN_ISIZE);
411 DISPLAY_MIN_WIDTH(this, width);
412 if (mCaptionFrames.NotEmpty()) {
413 nscoord capWidth =
414 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
415 mCaptionFrames.FirstChild(),
416 nsLayoutUtils::MIN_ISIZE);
417 if (HasSideCaption()) {
418 width += capWidth;
419 } else {
420 if (capWidth > width) {
421 width = capWidth;
425 return width;
428 /* virtual */ nscoord
429 nsTableOuterFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
431 nscoord maxWidth;
432 DISPLAY_PREF_WIDTH(this, maxWidth);
434 maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
435 InnerTableFrame(), nsLayoutUtils::PREF_ISIZE);
436 if (mCaptionFrames.NotEmpty()) {
437 uint8_t captionSide = GetCaptionSide();
438 switch(captionSide) {
439 case NS_STYLE_CAPTION_SIDE_LEFT:
440 case NS_STYLE_CAPTION_SIDE_RIGHT:
442 nscoord capMin =
443 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
444 mCaptionFrames.FirstChild(),
445 nsLayoutUtils::MIN_ISIZE);
446 maxWidth += capMin;
448 break;
449 default:
451 nsLayoutUtils::IntrinsicISizeType iwt;
452 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
453 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
454 // Don't let the caption's pref width expand the table's pref
455 // width.
456 iwt = nsLayoutUtils::MIN_ISIZE;
457 } else {
458 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
459 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
460 "unexpected caption side");
461 iwt = nsLayoutUtils::PREF_ISIZE;
463 nscoord capPref =
464 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
465 mCaptionFrames.FirstChild(),
466 iwt);
467 maxWidth = std::max(maxWidth, capPref);
469 break;
472 return maxWidth;
475 // Compute the margin-box width of aChildFrame given the inputs. If
476 // aMarginResult is non-null, fill it with the part of the margin-width
477 // that was contributed by the margin.
478 static nscoord
479 ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext,
480 nsIFrame *aChildFrame,
481 nsSize aCBSize, nscoord aAvailableWidth,
482 nscoord *aMarginResult = nullptr)
484 AutoMaybeDisableFontInflation an(aChildFrame);
486 nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width);
487 WritingMode wm = offsets.GetWritingMode();
488 LogicalSize size =
489 aChildFrame->ComputeSize(aRenderingContext,
490 wm, LogicalSize(wm, aCBSize),
491 aAvailableWidth,
492 offsets.ComputedLogicalMargin().Size(wm),
493 offsets.ComputedLogicalBorderPadding().Size(wm) -
494 offsets.ComputedLogicalPadding().Size(wm),
495 offsets.ComputedLogicalPadding().Size(wm),
496 true);
497 if (aMarginResult)
498 *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(wm);
499 return size.ISize(wm) + offsets.ComputedLogicalMargin().IStartEnd(wm) +
500 offsets.ComputedLogicalBorderPadding().IStartEnd(wm);
503 /* virtual */
504 LogicalSize
505 nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
506 WritingMode aWM,
507 const LogicalSize& aCBSize,
508 nscoord aAvailableISize,
509 const LogicalSize& aMargin,
510 const LogicalSize& aBorder,
511 const LogicalSize& aPadding,
512 bool aShrinkWrap)
514 nscoord kidAvailableWidth = aAvailableISize - aMargin.ISize(aWM);
515 NS_ASSERTION(aBorder == LogicalSize(aWM) &&
516 aPadding == LogicalSize(aWM),
517 "Table outer frames cannot have borders or paddings");
519 // When we're shrink-wrapping, our auto size needs to wrap around the
520 // actual size of the table, which (if it is specified as a percent)
521 // could be something that is not reflected in our GetMinISize and
522 // GetPrefISize. See bug 349457 for an example.
524 // XXX The use of aCBSize.GetPhysicalSize(aWM) below is temporary,
525 // until ChildShrinkWrapWidth is updated and width becomes inlineSize.
527 // Match the availableWidth logic in Reflow.
528 uint8_t captionSide = GetCaptionSide();
529 nscoord width;
530 if (captionSide == NO_SIDE) {
531 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
532 aCBSize.GetPhysicalSize(aWM),
533 kidAvailableWidth);
534 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
535 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
536 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
537 mCaptionFrames.FirstChild(),
538 aCBSize.GetPhysicalSize(aWM),
539 kidAvailableWidth);
540 width = capWidth + ChildShrinkWrapWidth(aRenderingContext,
541 InnerTableFrame(),
542 aCBSize.GetPhysicalSize(aWM),
543 kidAvailableWidth - capWidth);
544 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
545 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
546 nscoord margin;
547 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
548 aCBSize.GetPhysicalSize(aWM),
549 kidAvailableWidth, &margin);
550 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
551 mCaptionFrames.FirstChild(),
552 aCBSize.GetPhysicalSize(aWM),
553 width - margin);
554 if (capWidth > width)
555 width = capWidth;
556 } else {
557 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
558 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
559 "unexpected caption-side");
560 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
561 aCBSize.GetPhysicalSize(aWM),
562 kidAvailableWidth);
563 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
564 mCaptionFrames.FirstChild(),
565 aCBSize.GetPhysicalSize(aWM),
566 kidAvailableWidth);
567 if (capWidth > width)
568 width = capWidth;
571 return LogicalSize(aWM, width, NS_UNCONSTRAINEDSIZE);
574 uint8_t
575 nsTableOuterFrame::GetCaptionSide()
577 if (mCaptionFrames.NotEmpty()) {
578 return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
580 else {
581 return NO_SIDE; // no caption
585 uint8_t
586 nsTableOuterFrame::GetCaptionVerticalAlign()
588 const nsStyleCoord& va =
589 mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign;
590 return (va.GetUnit() == eStyleUnit_Enumerated)
591 ? va.GetIntValue()
592 : NS_STYLE_VERTICAL_ALIGN_TOP;
595 void
596 nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide,
597 const nsMargin& aInnerMargin,
598 const nsMargin& aCaptionMargin,
599 nscoord& aWidth,
600 nscoord& aHeight)
602 aWidth = aHeight = 0;
604 nsRect innerRect = InnerTableFrame()->GetRect();
605 nscoord innerWidth = innerRect.width;
607 nsRect captionRect(0,0,0,0);
608 nscoord captionWidth = 0;
609 if (mCaptionFrames.NotEmpty()) {
610 captionRect = mCaptionFrames.FirstChild()->GetRect();
611 captionWidth = captionRect.width;
613 switch(aCaptionSide) {
614 case NS_STYLE_CAPTION_SIDE_LEFT:
615 aWidth = std::max(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
616 innerWidth + aInnerMargin.right;
617 break;
618 case NS_STYLE_CAPTION_SIDE_RIGHT:
619 aWidth = std::max(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
620 innerWidth + aInnerMargin.left;
621 break;
622 default:
623 aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
624 aWidth = std::max(aWidth, captionRect.XMost() + aCaptionMargin.right);
626 aHeight = innerRect.YMost() + aInnerMargin.bottom;
627 if (NS_STYLE_CAPTION_SIDE_BOTTOM != aCaptionSide) {
628 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom);
630 else {
631 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom +
632 aInnerMargin.bottom);
637 nsresult
638 nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide,
639 const nsSize& aContainBlockSize,
640 const nsSize& aInnerSize,
641 const nsMargin& aInnerMargin,
642 const nsSize& aCaptionSize,
643 nsMargin& aCaptionMargin,
644 nsPoint& aOrigin)
646 aOrigin.x = aOrigin.y = 0;
647 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
648 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
649 return NS_OK;
651 if (mCaptionFrames.IsEmpty()) return NS_OK;
653 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
654 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.top, "The computed caption margin is auto?");
655 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.bottom, "The computed caption margin is auto?");
657 // horizontal computation
658 switch(aCaptionSide) {
659 case NS_STYLE_CAPTION_SIDE_BOTTOM:
660 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
661 // FIXME: Position relative to right edge for RTL. (Based on table
662 // direction or table parent direction?)
663 aOrigin.x = aCaptionMargin.left;
664 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
665 // We placed the caption using only the table's width as available
666 // width, and we should position it this way as well.
667 aOrigin.x += aInnerMargin.left;
669 } break;
670 case NS_STYLE_CAPTION_SIDE_LEFT: {
671 aOrigin.x = aCaptionMargin.left;
672 } break;
673 case NS_STYLE_CAPTION_SIDE_RIGHT: {
674 aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left;
675 } break;
676 default: { // top
677 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
678 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
679 "unexpected caption side");
680 // FIXME: Position relative to right edge for RTL. (Based on table
681 // direction or table parent direction?)
682 aOrigin.x = aCaptionMargin.left;
683 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
684 // We placed the caption using only the table's width as available
685 // width, and we should position it this way as well.
686 aOrigin.x += aInnerMargin.left;
689 } break;
691 // vertical computation
692 switch (aCaptionSide) {
693 case NS_STYLE_CAPTION_SIDE_RIGHT:
694 case NS_STYLE_CAPTION_SIDE_LEFT:
695 aOrigin.y = aInnerMargin.top;
696 switch (GetCaptionVerticalAlign()) {
697 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
698 aOrigin.y = std::max(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2));
699 break;
700 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
701 aOrigin.y = std::max(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
702 break;
703 default:
704 break;
706 break;
707 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
708 case NS_STYLE_CAPTION_SIDE_BOTTOM: {
709 aOrigin.y = aInnerMargin.top + aInnerSize.height + aCaptionMargin.top;
710 } break;
711 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
712 case NS_STYLE_CAPTION_SIDE_TOP: {
713 aOrigin.y = aInnerMargin.top + aCaptionMargin.top;
714 } break;
715 default:
716 NS_NOTREACHED("Unknown caption alignment type");
717 break;
719 return NS_OK;
722 nsresult
723 nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide,
724 const nsSize& aContainBlockSize,
725 const nsSize& aCaptionSize,
726 const nsMargin& aCaptionMargin,
727 const nsSize& aInnerSize,
728 nsMargin& aInnerMargin,
729 nsPoint& aOrigin)
732 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
733 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.right, "The computed caption margin is auto?");
734 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.left, "The computed inner margin is auto?");
735 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.right, "The computed inner margin is auto?");
736 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.top, "The computed inner margin is auto?");
737 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.bottom, "The computed inner margin is auto?");
739 aOrigin.x = aOrigin.y = 0;
740 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
741 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
742 return NS_OK;
745 nscoord minCapWidth = aCaptionSize.width;
747 minCapWidth += aCaptionMargin.left;
748 minCapWidth += aCaptionMargin.right;
750 // horizontal computation
751 switch (aCaptionSide) {
752 case NS_STYLE_CAPTION_SIDE_LEFT: {
753 if (aInnerMargin.left < minCapWidth) {
754 // shift the inner table to get some place for the caption
755 aInnerMargin.right += aInnerMargin.left - minCapWidth;
756 aInnerMargin.right = std::max(0, aInnerMargin.right);
757 aInnerMargin.left = minCapWidth;
759 aOrigin.x = aInnerMargin.left;
760 } break;
761 default: {
762 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
763 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
764 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
765 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
766 aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT ||
767 aCaptionSide == NO_SIDE,
768 "unexpected caption side");
769 aOrigin.x = aInnerMargin.left;
770 } break;
773 // vertical computation
774 switch (aCaptionSide) {
775 case NS_STYLE_CAPTION_SIDE_BOTTOM:
776 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
777 aOrigin.y = aInnerMargin.top;
778 } break;
779 case NS_STYLE_CAPTION_SIDE_LEFT:
780 case NS_STYLE_CAPTION_SIDE_RIGHT: {
781 aOrigin.y = aInnerMargin.top;
782 switch (GetCaptionVerticalAlign()) {
783 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
784 aOrigin.y = std::max(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
785 break;
786 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
787 aOrigin.y = std::max(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
788 break;
789 default:
790 break;
792 } break;
793 case NO_SIDE:
794 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
795 case NS_STYLE_CAPTION_SIDE_TOP: {
796 aOrigin.y = aInnerMargin.top + aCaptionMargin.top + aCaptionSize.height +
797 aCaptionMargin.bottom;
798 } break;
799 default:
800 NS_NOTREACHED("Unknown caption alignment type");
801 break;
803 return NS_OK;
806 void
807 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
808 nsIFrame* aChildFrame,
809 const nsHTMLReflowState& aOuterRS,
810 void* aChildRSSpace,
811 nscoord aAvailISize)
813 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
814 WritingMode wm = aChildFrame->GetWritingMode();
815 LogicalSize outerSize = aOuterRS.AvailableSize(wm);
816 nscoord availBSize = outerSize.BSize(wm);
817 if (NS_UNCONSTRAINEDSIZE != availBSize) {
818 if (mCaptionFrames.FirstChild() == aChildFrame) {
819 availBSize = NS_UNCONSTRAINEDSIZE;
820 } else {
821 LogicalMargin margin(wm);
822 GetChildMargin(aPresContext, aOuterRS, aChildFrame,
823 outerSize.ISize(wm), margin);
825 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BStart(wm),
826 "No unconstrainedsize arithmetic, please");
827 availBSize -= margin.BStart(wm);
829 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BEnd(wm),
830 "No unconstrainedsize arithmetic, please");
831 availBSize -= margin.BEnd(wm);
834 LogicalSize availSize(wm, aAvailISize, availBSize);
835 // create and init the child reflow state, using placement new on
836 // stack space allocated by the caller, so that the caller can destroy
837 // it
838 nsHTMLReflowState &childRS = * new (aChildRSSpace)
839 nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
840 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
841 InitChildReflowState(*aPresContext, childRS);
843 // see if we need to reset top-of-page due to a caption
844 if (childRS.mFlags.mIsTopOfPage &&
845 mCaptionFrames.FirstChild() == aChildFrame) {
846 uint8_t captionSide = GetCaptionSide();
847 if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
848 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
849 childRS.mFlags.mIsTopOfPage = false;
854 void
855 nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext,
856 nsIFrame* aChildFrame,
857 const nsHTMLReflowState& aChildRS,
858 nsHTMLReflowMetrics& aMetrics,
859 nsReflowStatus& aStatus)
862 // use the current position as a best guess for placement
863 nsPoint childPt = aChildFrame->GetPosition();
864 uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
866 // We don't want to delete our next-in-flow's child if it's an inner table
867 // frame, because outer table frames always assume that their inner table
868 // frames don't go away. If an outer table frame is removed because it is
869 // a next-in-flow of an already complete outer table frame, then it will
870 // take care of removing it's inner table frame.
871 if (aChildFrame == InnerTableFrame()) {
872 flags |= NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD;
875 ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
876 childPt.x, childPt.y, flags, aStatus);
879 void
880 nsTableOuterFrame::UpdateReflowMetrics(uint8_t aCaptionSide,
881 nsHTMLReflowMetrics& aMet,
882 const nsMargin& aInnerMargin,
883 const nsMargin& aCaptionMargin)
885 SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
886 aMet.Width(), aMet.Height());
888 aMet.SetOverflowAreasToDesiredBounds();
889 ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
890 if (mCaptionFrames.NotEmpty()) {
891 ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
895 void
896 nsTableOuterFrame::Reflow(nsPresContext* aPresContext,
897 nsHTMLReflowMetrics& aDesiredSize,
898 const nsHTMLReflowState& aOuterRS,
899 nsReflowStatus& aStatus)
901 DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
902 DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
904 uint8_t captionSide = GetCaptionSide();
906 // Initialize out parameters
907 aDesiredSize.ClearSize();
908 aStatus = NS_FRAME_COMPLETE;
910 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
911 // Set up our kids. They're already present, on an overflow list,
912 // or there are none so we'll create them now
913 MoveOverflowToChildList();
916 // Use longs to get more-aligned space.
917 #define LONGS_IN_HTMLRS \
918 ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
919 long captionRSSpace[LONGS_IN_HTMLRS];
920 nsHTMLReflowState *captionRS =
921 static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
922 long innerRSSpace[LONGS_IN_HTMLRS];
923 nsHTMLReflowState *innerRS =
924 static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
926 nsRect origInnerRect = InnerTableFrame()->GetRect();
927 nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect();
928 bool innerFirstReflow =
929 (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
930 nsRect origCaptionRect;
931 nsRect origCaptionVisualOverflow;
932 bool captionFirstReflow;
933 if (mCaptionFrames.NotEmpty()) {
934 origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
935 origCaptionVisualOverflow =
936 mCaptionFrames.FirstChild()->GetVisualOverflowRect();
937 captionFirstReflow =
938 (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
941 // ComputeAutoSize has to match this logic.
942 WritingMode wm;
943 if (captionSide == NO_SIDE) {
944 // We don't have a caption.
945 wm = InnerTableFrame()->GetWritingMode();
946 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
947 innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm));
948 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
949 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
950 // nsTableCaptionFrame::ComputeAutoSize takes care of making side
951 // captions small. Compute the caption's size first, and tell the
952 // table to fit in what's left.
953 wm = mCaptionFrames.FirstChild()->GetWritingMode();
954 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
955 captionRSSpace, aOuterRS.ComputedSize(wm).ISize(wm));
956 nscoord innerAvailISize = aOuterRS.ComputedSize(wm).ISize(wm) -
957 captionRS->ComputedSizeWithMarginBorderPadding(wm).ISize(wm);
958 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
959 innerRSSpace, innerAvailISize);
961 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
962 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
963 // Compute the table's size first, and then prevent the caption from
964 // being wider unless it has to be.
966 // Note that CSS 2.1 (but not 2.0) says:
967 // The width of the anonymous box is the border-edge width of the
968 // table box inside it
969 // We don't actually make our anonymous box that width (if we did,
970 // it would break 'auto' margins), but this effectively does that.
971 wm = InnerTableFrame()->GetWritingMode();
972 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
973 innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm));
974 // It's good that CSS 2.1 says not to include margins, since we
975 // can't, since they already been converted so they exactly
976 // fill the available width (ignoring the margin on one side if
977 // neither are auto). (We take advantage of that later when we call
978 // GetCaptionOrigin, though.)
979 nscoord innerBorderWidth =
980 innerRS->ComputedSizeWithBorderPadding(wm).ISize(wm);
981 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
982 captionRSSpace, innerBorderWidth);
983 } else {
984 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
985 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
986 "unexpected caption-side");
987 // Size the table and the caption independently.
988 wm = mCaptionFrames.FirstChild()->GetWritingMode();
989 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
990 captionRSSpace, aOuterRS.ComputedSize(wm).ISize(wm));
991 wm = InnerTableFrame()->GetWritingMode();
992 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
993 innerRSSpace, aOuterRS.ComputedSize(wm).ISize(wm));
996 // First reflow the caption.
997 nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode());
998 nsSize captionSize;
999 nsMargin captionMargin;
1000 if (mCaptionFrames.NotEmpty()) {
1001 nsReflowStatus capStatus; // don't let the caption cause incomplete
1002 OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(),
1003 *captionRS, captionMet, capStatus);
1004 captionSize.width = captionMet.Width();
1005 captionSize.height = captionMet.Height();
1006 captionMargin = captionRS->ComputedPhysicalMargin();
1007 // Now that we know the height of the caption, reduce the available height
1008 // for the table frame if we are height constrained and the caption is above
1009 // or below the inner table.
1010 if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableHeight()) {
1011 nscoord captionHeight = 0;
1012 switch (captionSide) {
1013 case NS_STYLE_CAPTION_SIDE_TOP:
1014 case NS_STYLE_CAPTION_SIDE_BOTTOM:
1015 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
1016 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
1017 captionHeight = captionSize.height + captionMargin.TopBottom();
1018 break;
1021 innerRS->AvailableHeight() =
1022 std::max(0, innerRS->AvailableHeight() - captionHeight);
1024 } else {
1025 captionSize.SizeTo(0,0);
1026 captionMargin.SizeTo(0,0,0,0);
1029 // Then, now that we know how much to reduce the width of the inner
1030 // table to account for side captions, reflow the inner table.
1031 nsHTMLReflowMetrics innerMet(innerRS->GetWritingMode());
1032 OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS,
1033 innerMet, aStatus);
1034 nsSize innerSize;
1035 innerSize.width = innerMet.Width();
1036 innerSize.height = innerMet.Height();
1037 nsMargin innerMargin = innerRS->ComputedPhysicalMargin();
1039 nsSize containSize = GetContainingBlockSize(aOuterRS);
1041 // Now that we've reflowed both we can place them.
1042 // XXXldb Most of the input variables here are now uninitialized!
1044 // XXX Need to recompute inner table's auto margins for the case of side
1045 // captions. (Caption's are broken too, but that should be fixed earlier.)
1047 if (mCaptionFrames.NotEmpty()) {
1048 nsPoint captionOrigin;
1049 GetCaptionOrigin(captionSide, containSize, innerSize,
1050 innerMargin, captionSize, captionMargin, captionOrigin);
1051 FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet,
1052 captionRS, captionOrigin.x, captionOrigin.y, 0);
1053 captionRS->~nsHTMLReflowState();
1055 // XXX If the height is constrained then we need to check whether
1056 // everything still fits...
1058 nsPoint innerOrigin;
1059 GetInnerOrigin(captionSide, containSize, captionSize,
1060 captionMargin, innerSize, innerMargin, innerOrigin);
1061 FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS,
1062 innerOrigin.x, innerOrigin.y, 0);
1063 innerRS->~nsHTMLReflowState();
1065 nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect,
1066 origInnerVisualOverflow, innerFirstReflow);
1067 if (mCaptionFrames.NotEmpty()) {
1068 nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect,
1069 origCaptionVisualOverflow,
1070 captionFirstReflow);
1073 UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
1075 if (GetPrevInFlow()) {
1076 ReflowOverflowContainerChildren(aPresContext, aOuterRS,
1077 aDesiredSize.mOverflowAreas, 0,
1078 aStatus);
1081 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRS, aStatus);
1083 // Return our desired rect
1085 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
1088 nsIAtom*
1089 nsTableOuterFrame::GetType() const
1091 return nsGkAtoms::tableOuterFrame;
1094 /* ----- global methods ----- */
1096 nsIContent*
1097 nsTableOuterFrame::GetCellAt(uint32_t aRowIdx, uint32_t aColIdx) const
1099 nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
1100 if (!cellMap) {
1101 return nullptr;
1104 nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
1105 if (!cell) {
1106 return nullptr;
1109 return cell->GetContent();
1113 nsTableOuterFrame*
1114 NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1116 return new (aPresShell) nsTableOuterFrame(aContext);
1119 NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame)
1121 #ifdef DEBUG_FRAME_DUMP
1122 nsresult
1123 nsTableOuterFrame::GetFrameName(nsAString& aResult) const
1125 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
1127 #endif