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"
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"
23 using namespace mozilla
;
24 using namespace mozilla::layout
;
26 /* ----------- nsTableCaptionFrame ---------- */
28 #define NS_TABLE_FRAME_CAPTION_LIST_INDEX 1
32 nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext
* aContext
):
33 nsBlockFrame(aContext
)
36 SetFlags(NS_BLOCK_FLOAT_MGR
);
39 nsTableCaptionFrame::~nsTableCaptionFrame()
44 nsTableCaptionFrame::GetType() const
46 return nsGkAtoms::tableCaptionFrame
;
50 nsTableOuterFrame::GetLogicalBaseline(WritingMode aWritingMode
) const
52 nsIFrame
* kid
= mFrames
.FirstChild();
54 NS_NOTREACHED("no inner table");
55 return nsContainerFrame::GetLogicalBaseline(aWritingMode
);
58 return kid
->GetLogicalBaseline(aWritingMode
) +
59 kid
->BStart(aWritingMode
, mRect
.width
);
64 nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext
*aRenderingContext
,
66 const LogicalSize
& aCBSize
,
67 nscoord aAvailableISize
,
68 const LogicalSize
& aMargin
,
69 const LogicalSize
& aBorder
,
70 const LogicalSize
& aPadding
,
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
;
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
113 nsIFrame
* outerFrame
= GetParent();
114 if (outerFrame
&& outerFrame
->GetType() == nsGkAtoms::tableOuterFrame
) {
115 nsIFrame
* innerFrame
= outerFrame
->GetFirstPrincipalChild();
117 return nsFrame::CorrectStyleParentFrame(innerFrame
,
118 StyleContext()->GetPseudo());
122 NS_NOTREACHED("Where is our inner table frame?");
123 return nsBlockFrame::GetParentStyleContextFrame();
128 nsTableCaptionFrame::AccessibleType()
130 if (!GetRect().IsEmpty()) {
131 return a11y::eHTMLCaptionType
;
134 return a11y::eNoType
;
138 #ifdef DEBUG_FRAME_DUMP
140 nsTableCaptionFrame::GetFrameName(nsAString
& aResult
) const
142 return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult
);
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
)
171 nsTableOuterFrame::AccessibleType()
173 return a11y::eHTMLTableType
;
178 nsTableOuterFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
180 DestroyAbsoluteFrames(aDestructRoot
);
181 mCaptionFrames
.DestroyFramesFrom(aDestructRoot
);
182 nsContainerFrame::DestroyFrom(aDestructRoot
);
186 nsTableOuterFrame::GetChildList(ChildListID aListID
) const
188 if (aListID
== kCaptionList
) {
189 return mCaptionFrames
;
192 return nsContainerFrame::GetChildList(aListID
);
196 nsTableOuterFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const
198 nsContainerFrame::GetChildLists(aLists
);
199 mCaptionFrames
.AppendIfNonempty(aLists
, kCaptionList
);
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
);
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
);
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
);
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
);
263 nsTableOuterFrame::RemoveFrame(ChildListID aListID
,
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
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
);
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());
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
322 BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
323 kid
= kid
->GetNextSibling();
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
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
366 nsTableOuterFrame::GetChildMargin(nsPresContext
* aPresContext
,
367 const nsHTMLReflowState
& aOuterRS
,
368 nsIFrame
* aChildFrame
,
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();
387 GetContainingBlockSize(const nsHTMLReflowState
& aOuterRS
)
390 const nsHTMLReflowState
* containRS
=
391 aOuterRS
.mCBReflowState
;
394 size
.width
= containRS
->ComputedWidth();
395 if (NS_UNCONSTRAINEDSIZE
== size
.width
) {
398 size
.height
= containRS
->ComputedHeight();
399 if (NS_UNCONSTRAINEDSIZE
== size
.height
) {
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()) {
414 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
415 mCaptionFrames
.FirstChild(),
416 nsLayoutUtils::MIN_ISIZE
);
417 if (HasSideCaption()) {
420 if (capWidth
> width
) {
428 /* virtual */ nscoord
429 nsTableOuterFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
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
:
443 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
444 mCaptionFrames
.FirstChild(),
445 nsLayoutUtils::MIN_ISIZE
);
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
456 iwt
= nsLayoutUtils::MIN_ISIZE
;
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
;
464 nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
465 mCaptionFrames
.FirstChild(),
467 maxWidth
= std::max(maxWidth
, capPref
);
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.
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();
489 aChildFrame
->ComputeSize(aRenderingContext
,
490 wm
, LogicalSize(wm
, aCBSize
),
492 offsets
.ComputedLogicalMargin().Size(wm
),
493 offsets
.ComputedLogicalBorderPadding().Size(wm
) -
494 offsets
.ComputedLogicalPadding().Size(wm
),
495 offsets
.ComputedLogicalPadding().Size(wm
),
498 *aMarginResult
= offsets
.ComputedLogicalMargin().IStartEnd(wm
);
499 return size
.ISize(wm
) + offsets
.ComputedLogicalMargin().IStartEnd(wm
) +
500 offsets
.ComputedLogicalBorderPadding().IStartEnd(wm
);
505 nsTableOuterFrame::ComputeAutoSize(nsRenderingContext
*aRenderingContext
,
507 const LogicalSize
& aCBSize
,
508 nscoord aAvailableISize
,
509 const LogicalSize
& aMargin
,
510 const LogicalSize
& aBorder
,
511 const LogicalSize
& aPadding
,
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();
530 if (captionSide
== NO_SIDE
) {
531 width
= ChildShrinkWrapWidth(aRenderingContext
, InnerTableFrame(),
532 aCBSize
.GetPhysicalSize(aWM
),
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
),
540 width
= capWidth
+ ChildShrinkWrapWidth(aRenderingContext
,
542 aCBSize
.GetPhysicalSize(aWM
),
543 kidAvailableWidth
- capWidth
);
544 } else if (captionSide
== NS_STYLE_CAPTION_SIDE_TOP
||
545 captionSide
== NS_STYLE_CAPTION_SIDE_BOTTOM
) {
547 width
= ChildShrinkWrapWidth(aRenderingContext
, InnerTableFrame(),
548 aCBSize
.GetPhysicalSize(aWM
),
549 kidAvailableWidth
, &margin
);
550 nscoord capWidth
= ChildShrinkWrapWidth(aRenderingContext
,
551 mCaptionFrames
.FirstChild(),
552 aCBSize
.GetPhysicalSize(aWM
),
554 if (capWidth
> width
)
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
),
563 nscoord capWidth
= ChildShrinkWrapWidth(aRenderingContext
,
564 mCaptionFrames
.FirstChild(),
565 aCBSize
.GetPhysicalSize(aWM
),
567 if (capWidth
> width
)
571 return LogicalSize(aWM
, width
, NS_UNCONSTRAINEDSIZE
);
575 nsTableOuterFrame::GetCaptionSide()
577 if (mCaptionFrames
.NotEmpty()) {
578 return mCaptionFrames
.FirstChild()->StyleTableBorder()->mCaptionSide
;
581 return NO_SIDE
; // no caption
586 nsTableOuterFrame::GetCaptionVerticalAlign()
588 const nsStyleCoord
& va
=
589 mCaptionFrames
.FirstChild()->StyleTextReset()->mVerticalAlign
;
590 return (va
.GetUnit() == eStyleUnit_Enumerated
)
592 : NS_STYLE_VERTICAL_ALIGN_TOP
;
596 nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide
,
597 const nsMargin
& aInnerMargin
,
598 const nsMargin
& aCaptionMargin
,
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
;
618 case NS_STYLE_CAPTION_SIDE_RIGHT
:
619 aWidth
= std::max(aInnerMargin
.right
, aCaptionMargin
.left
+ captionWidth
+ aCaptionMargin
.right
) +
620 innerWidth
+ aInnerMargin
.left
;
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
);
631 aHeight
= std::max(aHeight
, captionRect
.YMost() + aCaptionMargin
.bottom
+
632 aInnerMargin
.bottom
);
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
,
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
)) {
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
;
670 case NS_STYLE_CAPTION_SIDE_LEFT
: {
671 aOrigin
.x
= aCaptionMargin
.left
;
673 case NS_STYLE_CAPTION_SIDE_RIGHT
: {
674 aOrigin
.x
= aInnerMargin
.left
+ aInnerSize
.width
+ aCaptionMargin
.left
;
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
;
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));
700 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
701 aOrigin
.y
= std::max(0, aInnerMargin
.top
+ aInnerSize
.height
- aCaptionSize
.height
);
707 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE
:
708 case NS_STYLE_CAPTION_SIDE_BOTTOM
: {
709 aOrigin
.y
= aInnerMargin
.top
+ aInnerSize
.height
+ aCaptionMargin
.top
;
711 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE
:
712 case NS_STYLE_CAPTION_SIDE_TOP
: {
713 aOrigin
.y
= aInnerMargin
.top
+ aCaptionMargin
.top
;
716 NS_NOTREACHED("Unknown caption alignment type");
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
,
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
)) {
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
;
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
;
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
;
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);
786 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
787 aOrigin
.y
= std::max(aInnerMargin
.top
, aCaptionSize
.height
- aInnerSize
.height
);
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
;
800 NS_NOTREACHED("Unknown caption alignment type");
807 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext
* aPresContext
,
808 nsIFrame
* aChildFrame
,
809 const nsHTMLReflowState
& aOuterRS
,
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
;
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
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;
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
);
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());
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();
938 (mCaptionFrames
.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW
) != 0;
941 // ComputeAutoSize has to match this logic.
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
);
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());
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();
1021 innerRS
->AvailableHeight() =
1022 std::max(0, innerRS
->AvailableHeight() - captionHeight
);
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
,
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,
1081 FinishReflowWithAbsoluteFrames(aPresContext
, aDesiredSize
, aOuterRS
, aStatus
);
1083 // Return our desired rect
1085 NS_FRAME_SET_TRUNCATION(aStatus
, aOuterRS
, aDesiredSize
);
1089 nsTableOuterFrame::GetType() const
1091 return nsGkAtoms::tableOuterFrame
;
1094 /* ----- global methods ----- */
1097 nsTableOuterFrame::GetCellAt(uint32_t aRowIdx
, uint32_t aColIdx
) const
1099 nsTableCellMap
* cellMap
= InnerTableFrame()->GetCellMap();
1104 nsTableCellFrame
* cell
= cellMap
->GetCellInfoAt(aRowIdx
, aColIdx
);
1109 return cell
->GetContent();
1114 NS_NewTableOuterFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
1116 return new (aPresShell
) nsTableOuterFrame(aContext
);
1119 NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame
)
1121 #ifdef DEBUG_FRAME_DUMP
1123 nsTableOuterFrame::GetFrameName(nsAString
& aResult
) const
1125 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult
);