Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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)
34 {
35 // shrink wrap
36 SetFlags(NS_BLOCK_FLOAT_MGR);
37 }
39 nsTableCaptionFrame::~nsTableCaptionFrame()
40 {
41 }
43 nsIAtom*
44 nsTableCaptionFrame::GetType() const
45 {
46 return nsGkAtoms::tableCaptionFrame;
47 }
49 /* virtual */ nscoord
50 nsTableOuterFrame::GetBaseline() const
51 {
52 nsIFrame* kid = mFrames.FirstChild();
53 if (!kid) {
54 NS_NOTREACHED("no inner table");
55 return nsContainerFrame::GetBaseline();
56 }
58 return kid->GetBaseline() + kid->GetPosition().y;
59 }
61 /* virtual */ nsSize
62 nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
63 nsSize aCBSize, nscoord aAvailableWidth,
64 nsSize aMargin, nsSize aBorder,
65 nsSize aPadding, bool aShrinkWrap)
66 {
67 nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize,
68 aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
70 // If we're a container for font size inflation, then shrink
71 // wrapping inside of us should not apply font size inflation.
72 AutoMaybeDisableFontInflation an(this);
74 uint8_t captionSide = StyleTableBorder()->mCaptionSide;
75 if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
76 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
77 result.width = GetMinWidth(aRenderingContext);
78 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
79 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
80 // The outer frame constrains our available width to the width of
81 // the table. Grow if our min-width is bigger than that, but not
82 // larger than the containing block width. (It would really be nice
83 // to transmit that information another way, so we could grow up to
84 // the table's available width, but that's harder.)
85 nscoord min = GetMinWidth(aRenderingContext);
86 if (min > aCBSize.width)
87 min = aCBSize.width;
88 if (min > result.width)
89 result.width = min;
90 }
91 return result;
92 }
94 nsIFrame*
95 nsTableCaptionFrame::GetParentStyleContextFrame() const
96 {
97 NS_PRECONDITION(mContent->GetParent(),
98 "How could we not have a parent here?");
100 // The caption's style context parent is the inner frame, unless
101 // it's anonymous.
102 nsIFrame* outerFrame = GetParent();
103 if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) {
104 nsIFrame* innerFrame = outerFrame->GetFirstPrincipalChild();
105 if (innerFrame) {
106 return nsFrame::CorrectStyleParentFrame(innerFrame,
107 StyleContext()->GetPseudo());
108 }
109 }
111 NS_NOTREACHED("Where is our inner table frame?");
112 return nsBlockFrame::GetParentStyleContextFrame();
113 }
115 #ifdef ACCESSIBILITY
116 a11y::AccType
117 nsTableCaptionFrame::AccessibleType()
118 {
119 if (!GetRect().IsEmpty()) {
120 return a11y::eHTMLCaptionType;
121 }
123 return a11y::eNoType;
124 }
125 #endif
127 #ifdef DEBUG_FRAME_DUMP
128 nsresult
129 nsTableCaptionFrame::GetFrameName(nsAString& aResult) const
130 {
131 return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult);
132 }
133 #endif
135 nsIFrame*
136 NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
137 {
138 return new (aPresShell) nsTableCaptionFrame(aContext);
139 }
141 NS_IMPL_FRAMEARENA_HELPERS(nsTableCaptionFrame)
143 /* ----------- nsTableOuterFrame ---------- */
145 nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext):
146 nsContainerFrame(aContext)
147 {
148 }
150 nsTableOuterFrame::~nsTableOuterFrame()
151 {
152 }
154 NS_QUERYFRAME_HEAD(nsTableOuterFrame)
155 NS_QUERYFRAME_ENTRY(nsTableOuterFrame)
156 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
158 #ifdef ACCESSIBILITY
159 a11y::AccType
160 nsTableOuterFrame::AccessibleType()
161 {
162 return a11y::eHTMLTableType;
163 }
164 #endif
166 void
167 nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot)
168 {
169 DestroyAbsoluteFrames(aDestructRoot);
170 mCaptionFrames.DestroyFramesFrom(aDestructRoot);
171 nsContainerFrame::DestroyFrom(aDestructRoot);
172 }
174 const nsFrameList&
175 nsTableOuterFrame::GetChildList(ChildListID aListID) const
176 {
177 if (aListID == kCaptionList) {
178 return mCaptionFrames;
179 }
181 return nsContainerFrame::GetChildList(aListID);
182 }
184 void
185 nsTableOuterFrame::GetChildLists(nsTArray<ChildList>* aLists) const
186 {
187 nsContainerFrame::GetChildLists(aLists);
188 mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
189 }
191 nsresult
192 nsTableOuterFrame::SetInitialChildList(ChildListID aListID,
193 nsFrameList& aChildList)
194 {
195 if (kCaptionList == aListID) {
196 // the frame constructor already checked for table-caption display type
197 mCaptionFrames.SetFrames(aChildList);
198 }
199 else {
200 NS_ASSERTION(aListID == kPrincipalList, "wrong childlist");
201 NS_ASSERTION(mFrames.IsEmpty(), "Frame leak!");
202 NS_ASSERTION(aChildList.FirstChild() &&
203 nsGkAtoms::tableFrame == aChildList.FirstChild()->GetType(),
204 "expected a table frame");
205 mFrames.SetFrames(aChildList);
206 }
208 return NS_OK;
209 }
211 nsresult
212 nsTableOuterFrame::AppendFrames(ChildListID aListID,
213 nsFrameList& aFrameList)
214 {
215 nsresult rv;
217 // We only have two child frames: the inner table and a caption frame.
218 // The inner frame is provided when we're initialized, and it cannot change
219 if (kCaptionList == aListID) {
220 NS_ASSERTION(aFrameList.IsEmpty() ||
221 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
222 "appending non-caption frame to captionList");
223 mCaptionFrames.AppendFrames(this, aFrameList);
224 rv = NS_OK;
226 // Reflow the new caption frame. It's already marked dirty, so
227 // just tell the pres shell.
228 PresContext()->PresShell()->
229 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
230 NS_FRAME_HAS_DIRTY_CHILDREN);
231 }
232 else {
233 NS_PRECONDITION(false, "unexpected child list");
234 rv = NS_ERROR_UNEXPECTED;
235 }
237 return rv;
238 }
240 nsresult
241 nsTableOuterFrame::InsertFrames(ChildListID aListID,
242 nsIFrame* aPrevFrame,
243 nsFrameList& aFrameList)
244 {
245 if (kCaptionList == aListID) {
246 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
247 "inserting after sibling frame with different parent");
248 NS_ASSERTION(aFrameList.IsEmpty() ||
249 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
250 "inserting non-caption frame into captionList");
251 mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
253 // Reflow the new caption frame. It's already marked dirty, so
254 // just tell the pres shell.
255 PresContext()->PresShell()->
256 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
257 NS_FRAME_HAS_DIRTY_CHILDREN);
258 return NS_OK;
259 }
260 else {
261 NS_PRECONDITION(!aPrevFrame, "invalid previous frame");
262 return AppendFrames(aListID, aFrameList);
263 }
264 }
266 nsresult
267 nsTableOuterFrame::RemoveFrame(ChildListID aListID,
268 nsIFrame* aOldFrame)
269 {
270 // We only have two child frames: the inner table and one caption frame.
271 // The inner frame can't be removed so this should be the caption
272 NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame");
274 if (HasSideCaption()) {
275 // The old caption width had an effect on the inner table width so
276 // we're going to need to reflow it. Mark it dirty
277 InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY);
278 }
280 // Remove the frame and destroy it
281 mCaptionFrames.DestroyFrame(aOldFrame);
283 PresContext()->PresShell()->
284 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
285 NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed
287 return NS_OK;
288 }
290 void
291 nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
292 const nsRect& aDirtyRect,
293 const nsDisplayListSet& aLists)
294 {
295 // No border, background or outline are painted because they all belong
296 // to the inner table.
298 // If there's no caption, take a short cut to avoid having to create
299 // the special display list set and then sort it.
300 if (mCaptionFrames.IsEmpty()) {
301 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists);
302 return;
303 }
305 nsDisplayListCollection set;
306 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set);
308 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
309 BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(),
310 aDirtyRect, captionSet);
312 // Now we have to sort everything by content order, since the caption
313 // may be somewhere inside the table
314 set.SortAllByContentOrder(aBuilder, GetContent());
315 set.MoveTo(aLists);
316 }
318 void
319 nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder,
320 const nsRect& aDirtyRect,
321 const nsDisplayListSet& aLists)
322 {
323 // Just paint the regular children, but the children's background is our
324 // true background (there should only be one, the real table)
325 nsIFrame* kid = mFrames.FirstChild();
326 // The children should be in content order
327 while (kid) {
328 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
329 kid = kid->GetNextSibling();
330 }
331 }
333 nsIFrame*
334 nsTableOuterFrame::GetParentStyleContextFrame() const
335 {
336 // The table outer frame and the (inner) table frame split the style
337 // data by giving the table frame the style context associated with
338 // the table content node and creating a style context for the outer
339 // frame that is a *child* of the table frame's style context,
340 // matching the ::-moz-table-outer pseudo-element. html.css has a
341 // rule that causes that pseudo-element (and thus the outer table)
342 // to inherit *some* style properties from the table frame. The
343 // children of the table inherit directly from the inner table, and
344 // the outer table's style context is a leaf.
346 return InnerTableFrame();
347 }
349 // INCREMENTAL REFLOW HELPER FUNCTIONS
351 void
352 nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext,
353 nsHTMLReflowState& aReflowState)
355 {
356 nsMargin collapseBorder;
357 nsMargin collapsePadding(0,0,0,0);
358 nsMargin* pCollapseBorder = nullptr;
359 nsMargin* pCollapsePadding = nullptr;
360 if (aReflowState.frame == InnerTableFrame() &&
361 InnerTableFrame()->IsBorderCollapse()) {
362 collapseBorder = InnerTableFrame()->GetIncludedOuterBCBorder();
363 pCollapseBorder = &collapseBorder;
364 pCollapsePadding = &collapsePadding;
365 }
366 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding);
367 }
369 // get the margin and padding data. nsHTMLReflowState doesn't handle the
370 // case of auto margins
371 void
372 nsTableOuterFrame::GetChildMargin(nsPresContext* aPresContext,
373 const nsHTMLReflowState& aOuterRS,
374 nsIFrame* aChildFrame,
375 nscoord aAvailWidth,
376 nsMargin& aMargin)
377 {
378 // construct a reflow state to compute margin and padding. Auto margins
379 // will not be computed at this time.
381 // create and init the child reflow state
382 // XXX We really shouldn't construct a reflow state to do this.
383 nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame,
384 nsSize(aAvailWidth, aOuterRS.AvailableHeight()),
385 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
386 InitChildReflowState(*aPresContext, childRS);
388 aMargin = childRS.ComputedPhysicalMargin();
389 }
391 static nsSize
392 GetContainingBlockSize(const nsHTMLReflowState& aOuterRS)
393 {
394 nsSize size(0,0);
395 const nsHTMLReflowState* containRS =
396 aOuterRS.mCBReflowState;
398 if (containRS) {
399 size.width = containRS->ComputedWidth();
400 if (NS_UNCONSTRAINEDSIZE == size.width) {
401 size.width = 0;
402 }
403 size.height = containRS->ComputedHeight();
404 if (NS_UNCONSTRAINEDSIZE == size.height) {
405 size.height = 0;
406 }
407 }
408 return size;
409 }
411 /* virtual */ nscoord
412 nsTableOuterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
413 {
414 nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
415 InnerTableFrame(), nsLayoutUtils::MIN_WIDTH);
416 DISPLAY_MIN_WIDTH(this, width);
417 if (mCaptionFrames.NotEmpty()) {
418 nscoord capWidth =
419 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
420 mCaptionFrames.FirstChild(),
421 nsLayoutUtils::MIN_WIDTH);
422 if (HasSideCaption()) {
423 width += capWidth;
424 } else {
425 if (capWidth > width) {
426 width = capWidth;
427 }
428 }
429 }
430 return width;
431 }
433 /* virtual */ nscoord
434 nsTableOuterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
435 {
436 nscoord maxWidth;
437 DISPLAY_PREF_WIDTH(this, maxWidth);
439 maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
440 InnerTableFrame(), nsLayoutUtils::PREF_WIDTH);
441 if (mCaptionFrames.NotEmpty()) {
442 uint8_t captionSide = GetCaptionSide();
443 switch(captionSide) {
444 case NS_STYLE_CAPTION_SIDE_LEFT:
445 case NS_STYLE_CAPTION_SIDE_RIGHT:
446 {
447 nscoord capMin =
448 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
449 mCaptionFrames.FirstChild(),
450 nsLayoutUtils::MIN_WIDTH);
451 maxWidth += capMin;
452 }
453 break;
454 default:
455 {
456 nsLayoutUtils::IntrinsicWidthType iwt;
457 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
458 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
459 // Don't let the caption's pref width expand the table's pref
460 // width.
461 iwt = nsLayoutUtils::MIN_WIDTH;
462 } else {
463 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
464 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
465 "unexpected caption side");
466 iwt = nsLayoutUtils::PREF_WIDTH;
467 }
468 nscoord capPref =
469 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
470 mCaptionFrames.FirstChild(),
471 iwt);
472 maxWidth = std::max(maxWidth, capPref);
473 }
474 break;
475 }
476 }
477 return maxWidth;
478 }
480 // Compute the margin-box width of aChildFrame given the inputs. If
481 // aMarginResult is non-null, fill it with the part of the margin-width
482 // that was contributed by the margin.
483 static nscoord
484 ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext,
485 nsIFrame *aChildFrame,
486 nsSize aCBSize, nscoord aAvailableWidth,
487 nscoord *aMarginResult = nullptr)
488 {
489 AutoMaybeDisableFontInflation an(aChildFrame);
491 nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width);
492 nsSize size = aChildFrame->ComputeSize(aRenderingContext, aCBSize,
493 aAvailableWidth,
494 nsSize(offsets.ComputedPhysicalMargin().LeftRight(),
495 offsets.ComputedPhysicalMargin().TopBottom()),
496 nsSize(offsets.ComputedPhysicalBorderPadding().LeftRight() -
497 offsets.ComputedPhysicalPadding().LeftRight(),
498 offsets.ComputedPhysicalBorderPadding().TopBottom() -
499 offsets.ComputedPhysicalPadding().TopBottom()),
500 nsSize(offsets.ComputedPhysicalPadding().LeftRight(),
501 offsets.ComputedPhysicalPadding().TopBottom()),
502 true);
503 if (aMarginResult)
504 *aMarginResult = offsets.ComputedPhysicalMargin().LeftRight();
505 return size.width + offsets.ComputedPhysicalMargin().LeftRight() +
506 offsets.ComputedPhysicalBorderPadding().LeftRight();
507 }
509 /* virtual */ nsSize
510 nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
511 nsSize aCBSize, nscoord aAvailableWidth,
512 nsSize aMargin, nsSize aBorder,
513 nsSize aPadding, bool aShrinkWrap)
514 {
515 nscoord kidAvailableWidth = aAvailableWidth - aMargin.width;
516 NS_ASSERTION(aBorder == nsSize(0, 0) &&
517 aPadding == nsSize(0, 0),
518 "Table outer frames cannot hae borders or paddings");
520 // When we're shrink-wrapping, our auto size needs to wrap around the
521 // actual size of the table, which (if it is specified as a percent)
522 // could be something that is not reflected in our GetMinWidth and
523 // GetPrefWidth. See bug 349457 for an example.
525 // Match the availableWidth logic in Reflow.
526 uint8_t captionSide = GetCaptionSide();
527 nscoord width;
528 if (captionSide == NO_SIDE) {
529 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
530 aCBSize, kidAvailableWidth);
531 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
532 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
533 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
534 mCaptionFrames.FirstChild(),
535 aCBSize, kidAvailableWidth);
536 width = capWidth + ChildShrinkWrapWidth(aRenderingContext,
537 InnerTableFrame(), aCBSize,
538 kidAvailableWidth - capWidth);
539 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
540 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
541 nscoord margin;
542 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
543 aCBSize, kidAvailableWidth, &margin);
544 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
545 mCaptionFrames.FirstChild(), aCBSize,
546 width - margin);
547 if (capWidth > width)
548 width = capWidth;
549 } else {
550 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
551 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
552 "unexpected caption-side");
553 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
554 aCBSize, kidAvailableWidth);
555 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
556 mCaptionFrames.FirstChild(),
557 aCBSize, kidAvailableWidth);
558 if (capWidth > width)
559 width = capWidth;
560 }
562 return nsSize(width, NS_UNCONSTRAINEDSIZE);
563 }
565 uint8_t
566 nsTableOuterFrame::GetCaptionSide()
567 {
568 if (mCaptionFrames.NotEmpty()) {
569 return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
570 }
571 else {
572 return NO_SIDE; // no caption
573 }
574 }
576 uint8_t
577 nsTableOuterFrame::GetCaptionVerticalAlign()
578 {
579 const nsStyleCoord& va =
580 mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign;
581 return (va.GetUnit() == eStyleUnit_Enumerated)
582 ? va.GetIntValue()
583 : NS_STYLE_VERTICAL_ALIGN_TOP;
584 }
586 void
587 nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide,
588 const nsMargin& aInnerMargin,
589 const nsMargin& aCaptionMargin,
590 nscoord& aWidth,
591 nscoord& aHeight)
592 {
593 aWidth = aHeight = 0;
595 nsRect innerRect = InnerTableFrame()->GetRect();
596 nscoord innerWidth = innerRect.width;
598 nsRect captionRect(0,0,0,0);
599 nscoord captionWidth = 0;
600 if (mCaptionFrames.NotEmpty()) {
601 captionRect = mCaptionFrames.FirstChild()->GetRect();
602 captionWidth = captionRect.width;
603 }
604 switch(aCaptionSide) {
605 case NS_STYLE_CAPTION_SIDE_LEFT:
606 aWidth = std::max(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
607 innerWidth + aInnerMargin.right;
608 break;
609 case NS_STYLE_CAPTION_SIDE_RIGHT:
610 aWidth = std::max(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
611 innerWidth + aInnerMargin.left;
612 break;
613 default:
614 aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
615 aWidth = std::max(aWidth, captionRect.XMost() + aCaptionMargin.right);
616 }
617 aHeight = innerRect.YMost() + aInnerMargin.bottom;
618 if (NS_STYLE_CAPTION_SIDE_BOTTOM != aCaptionSide) {
619 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom);
620 }
621 else {
622 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom +
623 aInnerMargin.bottom);
624 }
626 }
628 nsresult
629 nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide,
630 const nsSize& aContainBlockSize,
631 const nsSize& aInnerSize,
632 const nsMargin& aInnerMargin,
633 const nsSize& aCaptionSize,
634 nsMargin& aCaptionMargin,
635 nsPoint& aOrigin)
636 {
637 aOrigin.x = aOrigin.y = 0;
638 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
639 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
640 return NS_OK;
641 }
642 if (mCaptionFrames.IsEmpty()) return NS_OK;
644 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
645 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.top, "The computed caption margin is auto?");
646 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.bottom, "The computed caption margin is auto?");
648 // horizontal computation
649 switch(aCaptionSide) {
650 case NS_STYLE_CAPTION_SIDE_BOTTOM:
651 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
652 // FIXME: Position relative to right edge for RTL. (Based on table
653 // direction or table parent direction?)
654 aOrigin.x = aCaptionMargin.left;
655 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
656 // We placed the caption using only the table's width as available
657 // width, and we should position it this way as well.
658 aOrigin.x += aInnerMargin.left;
659 }
660 } break;
661 case NS_STYLE_CAPTION_SIDE_LEFT: {
662 aOrigin.x = aCaptionMargin.left;
663 } break;
664 case NS_STYLE_CAPTION_SIDE_RIGHT: {
665 aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left;
666 } break;
667 default: { // top
668 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
669 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
670 "unexpected caption side");
671 // FIXME: Position relative to right edge for RTL. (Based on table
672 // direction or table parent direction?)
673 aOrigin.x = aCaptionMargin.left;
674 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
675 // We placed the caption using only the table's width as available
676 // width, and we should position it this way as well.
677 aOrigin.x += aInnerMargin.left;
678 }
680 } break;
681 }
682 // vertical computation
683 switch (aCaptionSide) {
684 case NS_STYLE_CAPTION_SIDE_RIGHT:
685 case NS_STYLE_CAPTION_SIDE_LEFT:
686 aOrigin.y = aInnerMargin.top;
687 switch (GetCaptionVerticalAlign()) {
688 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
689 aOrigin.y = std::max(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2));
690 break;
691 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
692 aOrigin.y = std::max(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
693 break;
694 default:
695 break;
696 }
697 break;
698 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
699 case NS_STYLE_CAPTION_SIDE_BOTTOM: {
700 aOrigin.y = aInnerMargin.top + aInnerSize.height + aCaptionMargin.top;
701 } break;
702 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
703 case NS_STYLE_CAPTION_SIDE_TOP: {
704 aOrigin.y = aInnerMargin.top + aCaptionMargin.top;
705 } break;
706 default:
707 NS_NOTREACHED("Unknown caption alignment type");
708 break;
709 }
710 return NS_OK;
711 }
713 nsresult
714 nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide,
715 const nsSize& aContainBlockSize,
716 const nsSize& aCaptionSize,
717 const nsMargin& aCaptionMargin,
718 const nsSize& aInnerSize,
719 nsMargin& aInnerMargin,
720 nsPoint& aOrigin)
721 {
723 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
724 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.right, "The computed caption margin is auto?");
725 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.left, "The computed inner margin is auto?");
726 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.right, "The computed inner margin is auto?");
727 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.top, "The computed inner margin is auto?");
728 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.bottom, "The computed inner margin is auto?");
730 aOrigin.x = aOrigin.y = 0;
731 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
732 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
733 return NS_OK;
734 }
736 nscoord minCapWidth = aCaptionSize.width;
738 minCapWidth += aCaptionMargin.left;
739 minCapWidth += aCaptionMargin.right;
741 // horizontal computation
742 switch (aCaptionSide) {
743 case NS_STYLE_CAPTION_SIDE_LEFT: {
744 if (aInnerMargin.left < minCapWidth) {
745 // shift the inner table to get some place for the caption
746 aInnerMargin.right += aInnerMargin.left - minCapWidth;
747 aInnerMargin.right = std::max(0, aInnerMargin.right);
748 aInnerMargin.left = minCapWidth;
749 }
750 aOrigin.x = aInnerMargin.left;
751 } break;
752 default: {
753 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
754 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
755 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
756 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
757 aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT ||
758 aCaptionSide == NO_SIDE,
759 "unexpected caption side");
760 aOrigin.x = aInnerMargin.left;
761 } break;
762 }
764 // vertical computation
765 switch (aCaptionSide) {
766 case NS_STYLE_CAPTION_SIDE_BOTTOM:
767 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
768 aOrigin.y = aInnerMargin.top;
769 } break;
770 case NS_STYLE_CAPTION_SIDE_LEFT:
771 case NS_STYLE_CAPTION_SIDE_RIGHT: {
772 aOrigin.y = aInnerMargin.top;
773 switch (GetCaptionVerticalAlign()) {
774 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
775 aOrigin.y = std::max(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
776 break;
777 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
778 aOrigin.y = std::max(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
779 break;
780 default:
781 break;
782 }
783 } break;
784 case NO_SIDE:
785 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
786 case NS_STYLE_CAPTION_SIDE_TOP: {
787 aOrigin.y = aInnerMargin.top + aCaptionMargin.top + aCaptionSize.height +
788 aCaptionMargin.bottom;
789 } break;
790 default:
791 NS_NOTREACHED("Unknown caption alignment type");
792 break;
793 }
794 return NS_OK;
795 }
797 void
798 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
799 nsIFrame* aChildFrame,
800 const nsHTMLReflowState& aOuterRS,
801 void* aChildRSSpace,
802 nscoord aAvailWidth)
803 {
804 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
805 nscoord availHeight = aOuterRS.AvailableHeight();
806 if (NS_UNCONSTRAINEDSIZE != availHeight) {
807 if (mCaptionFrames.FirstChild() == aChildFrame) {
808 availHeight = NS_UNCONSTRAINEDSIZE;
809 } else {
810 nsMargin margin;
811 GetChildMargin(aPresContext, aOuterRS, aChildFrame,
812 aOuterRS.AvailableWidth(), margin);
814 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please");
815 availHeight -= margin.top;
817 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please");
818 availHeight -= margin.bottom;
819 }
820 }
821 nsSize availSize(aAvailWidth, availHeight);
822 // create and init the child reflow state, using placement new on
823 // stack space allocated by the caller, so that the caller can destroy
824 // it
825 nsHTMLReflowState &childRS = * new (aChildRSSpace)
826 nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
827 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
828 InitChildReflowState(*aPresContext, childRS);
830 // see if we need to reset top-of-page due to a caption
831 if (childRS.mFlags.mIsTopOfPage &&
832 mCaptionFrames.FirstChild() == aChildFrame) {
833 uint8_t captionSide = GetCaptionSide();
834 if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
835 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
836 childRS.mFlags.mIsTopOfPage = false;
837 }
838 }
839 }
841 nsresult
842 nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext,
843 nsIFrame* aChildFrame,
844 const nsHTMLReflowState& aChildRS,
845 nsHTMLReflowMetrics& aMetrics,
846 nsReflowStatus& aStatus)
847 {
849 // use the current position as a best guess for placement
850 nsPoint childPt = aChildFrame->GetPosition();
851 uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
853 // We don't want to delete our next-in-flow's child if it's an inner table
854 // frame, because outer table frames always assume that their inner table
855 // frames don't go away. If an outer table frame is removed because it is
856 // a next-in-flow of an already complete outer table frame, then it will
857 // take care of removing it's inner table frame.
858 if (aChildFrame == InnerTableFrame()) {
859 flags |= NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD;
860 }
862 return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
863 childPt.x, childPt.y, flags, aStatus);
864 }
866 void
867 nsTableOuterFrame::UpdateReflowMetrics(uint8_t aCaptionSide,
868 nsHTMLReflowMetrics& aMet,
869 const nsMargin& aInnerMargin,
870 const nsMargin& aCaptionMargin)
871 {
872 SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
873 aMet.Width(), aMet.Height());
875 aMet.SetOverflowAreasToDesiredBounds();
876 ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
877 if (mCaptionFrames.NotEmpty()) {
878 ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
879 }
880 }
882 nsresult nsTableOuterFrame::Reflow(nsPresContext* aPresContext,
883 nsHTMLReflowMetrics& aDesiredSize,
884 const nsHTMLReflowState& aOuterRS,
885 nsReflowStatus& aStatus)
886 {
887 DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
888 DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
890 nsresult rv = NS_OK;
891 uint8_t captionSide = GetCaptionSide();
893 // Initialize out parameters
894 aDesiredSize.Width() = aDesiredSize.Height() = 0;
895 aStatus = NS_FRAME_COMPLETE;
897 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
898 // Set up our kids. They're already present, on an overflow list,
899 // or there are none so we'll create them now
900 MoveOverflowToChildList();
901 }
903 // Use longs to get more-aligned space.
904 #define LONGS_IN_HTMLRS \
905 ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
906 long captionRSSpace[LONGS_IN_HTMLRS];
907 nsHTMLReflowState *captionRS =
908 static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
909 long innerRSSpace[LONGS_IN_HTMLRS];
910 nsHTMLReflowState *innerRS =
911 static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
913 nsRect origInnerRect = InnerTableFrame()->GetRect();
914 nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect();
915 bool innerFirstReflow =
916 (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
917 nsRect origCaptionRect;
918 nsRect origCaptionVisualOverflow;
919 bool captionFirstReflow;
920 if (mCaptionFrames.NotEmpty()) {
921 origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
922 origCaptionVisualOverflow =
923 mCaptionFrames.FirstChild()->GetVisualOverflowRect();
924 captionFirstReflow =
925 (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
926 }
928 // ComputeAutoSize has to match this logic.
929 if (captionSide == NO_SIDE) {
930 // We don't have a caption.
931 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
932 innerRSSpace, aOuterRS.ComputedWidth());
933 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
934 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
935 // nsTableCaptionFrame::ComputeAutoSize takes care of making side
936 // captions small. Compute the caption's size first, and tell the
937 // table to fit in what's left.
938 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
939 captionRSSpace, aOuterRS.ComputedWidth());
940 nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
941 (captionRS->ComputedWidth() + captionRS->ComputedPhysicalMargin().LeftRight() +
942 captionRS->ComputedPhysicalBorderPadding().LeftRight());
943 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
944 innerRSSpace, innerAvailWidth);
946 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
947 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
948 // Compute the table's size first, and then prevent the caption from
949 // being wider unless it has to be.
950 //
951 // Note that CSS 2.1 (but not 2.0) says:
952 // The width of the anonymous box is the border-edge width of the
953 // table box inside it
954 // We don't actually make our anonymous box that width (if we did,
955 // it would break 'auto' margins), but this effectively does that.
956 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
957 innerRSSpace, aOuterRS.ComputedWidth());
958 // It's good that CSS 2.1 says not to include margins, since we
959 // can't, since they already been converted so they exactly
960 // fill the available width (ignoring the margin on one side if
961 // neither are auto). (We take advantage of that later when we call
962 // GetCaptionOrigin, though.)
963 nscoord innerBorderWidth = innerRS->ComputedWidth() +
964 innerRS->ComputedPhysicalBorderPadding().LeftRight();
965 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
966 captionRSSpace, innerBorderWidth);
967 } else {
968 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
969 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
970 "unexpected caption-side");
971 // Size the table and the caption independently.
972 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
973 captionRSSpace, aOuterRS.ComputedWidth());
974 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
975 innerRSSpace, aOuterRS.ComputedWidth());
976 }
978 // First reflow the caption.
979 nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode());
980 nsSize captionSize;
981 nsMargin captionMargin;
982 if (mCaptionFrames.NotEmpty()) {
983 nsReflowStatus capStatus; // don't let the caption cause incomplete
984 rv = OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(),
985 *captionRS, captionMet, capStatus);
986 if (NS_FAILED(rv)) return rv;
987 captionSize.width = captionMet.Width();
988 captionSize.height = captionMet.Height();
989 captionMargin = captionRS->ComputedPhysicalMargin();
990 // Now that we know the height of the caption, reduce the available height
991 // for the table frame if we are height constrained and the caption is above
992 // or below the inner table.
993 if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableHeight()) {
994 nscoord captionHeight = 0;
995 switch (captionSide) {
996 case NS_STYLE_CAPTION_SIDE_TOP:
997 case NS_STYLE_CAPTION_SIDE_BOTTOM:
998 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
999 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
1000 captionHeight = captionSize.height + captionMargin.TopBottom();
1001 break;
1002 }
1003 }
1004 innerRS->AvailableHeight() =
1005 std::max(0, innerRS->AvailableHeight() - captionHeight);
1006 }
1007 } else {
1008 captionSize.SizeTo(0,0);
1009 captionMargin.SizeTo(0,0,0,0);
1010 }
1012 // Then, now that we know how much to reduce the width of the inner
1013 // table to account for side captions, reflow the inner table.
1014 nsHTMLReflowMetrics innerMet(innerRS->GetWritingMode());
1015 rv = OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS,
1016 innerMet, aStatus);
1017 if (NS_FAILED(rv)) return rv;
1018 nsSize innerSize;
1019 innerSize.width = innerMet.Width();
1020 innerSize.height = innerMet.Height();
1021 nsMargin innerMargin = innerRS->ComputedPhysicalMargin();
1023 nsSize containSize = GetContainingBlockSize(aOuterRS);
1025 // Now that we've reflowed both we can place them.
1026 // XXXldb Most of the input variables here are now uninitialized!
1028 // XXX Need to recompute inner table's auto margins for the case of side
1029 // captions. (Caption's are broken too, but that should be fixed earlier.)
1031 if (mCaptionFrames.NotEmpty()) {
1032 nsPoint captionOrigin;
1033 GetCaptionOrigin(captionSide, containSize, innerSize,
1034 innerMargin, captionSize, captionMargin, captionOrigin);
1035 FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet,
1036 captionRS, captionOrigin.x, captionOrigin.y, 0);
1037 captionRS->~nsHTMLReflowState();
1038 }
1039 // XXX If the height is constrained then we need to check whether
1040 // everything still fits...
1042 nsPoint innerOrigin;
1043 GetInnerOrigin(captionSide, containSize, captionSize,
1044 captionMargin, innerSize, innerMargin, innerOrigin);
1045 FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS,
1046 innerOrigin.x, innerOrigin.y, 0);
1047 innerRS->~nsHTMLReflowState();
1049 nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect,
1050 origInnerVisualOverflow, innerFirstReflow);
1051 if (mCaptionFrames.NotEmpty()) {
1052 nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect,
1053 origCaptionVisualOverflow,
1054 captionFirstReflow);
1055 }
1057 UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
1059 if (GetPrevInFlow()) {
1060 ReflowOverflowContainerChildren(aPresContext, aOuterRS,
1061 aDesiredSize.mOverflowAreas, 0,
1062 aStatus);
1063 }
1065 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRS, aStatus);
1067 // Return our desired rect
1069 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
1070 return rv;
1071 }
1073 nsIAtom*
1074 nsTableOuterFrame::GetType() const
1075 {
1076 return nsGkAtoms::tableOuterFrame;
1077 }
1079 /* ----- global methods ----- */
1081 nsIContent*
1082 nsTableOuterFrame::GetCellAt(uint32_t aRowIdx, uint32_t aColIdx) const
1083 {
1084 nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
1085 if (!cellMap) {
1086 return nullptr;
1087 }
1089 nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
1090 if (!cell) {
1091 return nullptr;
1092 }
1094 return cell->GetContent();
1095 }
1098 nsIFrame*
1099 NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1100 {
1101 return new (aPresShell) nsTableOuterFrame(aContext);
1102 }
1104 NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame)
1106 #ifdef DEBUG_FRAME_DUMP
1107 nsresult
1108 nsTableOuterFrame::GetFrameName(nsAString& aResult) const
1109 {
1110 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
1111 }
1112 #endif