Bug 454376 imgLoader.cpp does not compile with Sun Studio 12 on Solaris r=joedraw...
[wine-gecko.git] / layout / tables / nsTableOuterFrame.cpp
blobd60d39f8038255c31994c7a1c5a4a939d3dd23be
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
38 #include "nsTableOuterFrame.h"
39 #include "nsTableFrame.h"
40 #include "nsStyleContext.h"
41 #include "nsStyleConsts.h"
42 #include "nsPresContext.h"
43 #include "nsIRenderingContext.h"
44 #include "nsCSSRendering.h"
45 #include "nsIContent.h"
46 #include "prinrval.h"
47 #include "nsGkAtoms.h"
48 #include "nsHTMLParts.h"
49 #include "nsIPresShell.h"
50 #ifdef ACCESSIBILITY
51 #include "nsIAccessibilityService.h"
52 #endif
53 #include "nsIServiceManager.h"
54 #include "nsIDOMNode.h"
55 #include "nsDisplayList.h"
56 #include "nsLayoutUtils.h"
58 /* ----------- nsTableCaptionFrame ---------- */
60 #define NS_TABLE_FRAME_CAPTION_LIST_INDEX 0
61 #define NO_SIDE 100
63 // caption frame
64 nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext* aContext):
65 nsBlockFrame(aContext)
67 // shrink wrap
68 SetFlags(NS_BLOCK_SPACE_MGR);
71 nsTableCaptionFrame::~nsTableCaptionFrame()
75 nsIAtom*
76 nsTableCaptionFrame::GetType() const
78 return nsGkAtoms::tableCaptionFrame;
81 /* virtual */ nscoord
82 nsTableOuterFrame::GetBaseline() const
84 nsIFrame* kid = mFrames.FirstChild();
85 if (!kid) {
86 NS_NOTREACHED("no inner table");
87 return nsHTMLContainerFrame::GetBaseline();
90 return kid->GetBaseline() + kid->GetPosition().y;
93 /* virtual */ nsSize
94 nsTableCaptionFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
95 nsSize aCBSize, nscoord aAvailableWidth,
96 nsSize aMargin, nsSize aBorder,
97 nsSize aPadding, PRBool aShrinkWrap)
99 nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize,
100 aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
101 PRUint8 captionSide = GetStyleTableBorder()->mCaptionSide;
102 if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
103 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
104 result.width = GetMinWidth(aRenderingContext);
105 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
106 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
107 // The outer frame constrains our available width to the width of
108 // the table. Grow if our min-width is bigger than that, but not
109 // larger than the containing block width. (It would really be nice
110 // to transmit that information another way, so we could grow up to
111 // the table's available width, but that's harder.)
112 nscoord min = GetMinWidth(aRenderingContext);
113 if (min > aCBSize.width)
114 min = aCBSize.width;
115 if (min > result.width)
116 result.width = min;
118 return result;
121 NS_IMETHODIMP
122 nsTableCaptionFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
123 nsIFrame** aProviderFrame,
124 PRBool* aIsChild)
126 NS_PRECONDITION(mContent->GetParent(),
127 "How could we not have a parent here?");
129 // The caption's style context parent is the inner frame, unless
130 // it's anonymous.
131 nsIFrame* outerFrame = GetParent();
132 if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) {
133 nsIFrame* innerFrame = outerFrame->GetFirstChild(nsnull);
134 if (innerFrame) {
135 *aProviderFrame =
136 nsFrame::CorrectStyleParentFrame(innerFrame,
137 GetStyleContext()->GetPseudoType());
138 *aIsChild = PR_FALSE;
139 return NS_OK;
143 NS_NOTREACHED("Where is our inner table frame?");
144 return nsBlockFrame::GetParentStyleContextFrame(aPresContext, aProviderFrame,
145 aIsChild);
148 #ifdef ACCESSIBILITY
149 NS_IMETHODIMP nsTableCaptionFrame::GetAccessible(nsIAccessible** aAccessible)
151 *aAccessible = nsnull;
152 if (!GetRect().IsEmpty()) {
153 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
154 if (accService) {
155 return accService->CreateHTMLCaptionAccessible(static_cast<nsIFrame*>(this), aAccessible);
159 return NS_ERROR_FAILURE;
161 #endif
163 #ifdef NS_DEBUG
164 NS_IMETHODIMP
165 nsTableCaptionFrame::GetFrameName(nsAString& aResult) const
167 return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult);
169 #endif
171 nsIFrame*
172 NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
174 return new (aPresShell) nsTableCaptionFrame(aContext);
177 /* ----------- nsTableOuterFrame ---------- */
179 NS_IMPL_ADDREF_INHERITED(nsTableOuterFrame, nsHTMLContainerFrame)
180 NS_IMPL_RELEASE_INHERITED(nsTableOuterFrame, nsHTMLContainerFrame)
182 nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext):
183 nsHTMLContainerFrame(aContext)
187 nsTableOuterFrame::~nsTableOuterFrame()
191 NS_IMETHODIMP
192 nsTableOuterFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
194 NS_PRECONDITION(aInstancePtr, "null out param");
196 if (aIID.Equals(NS_GET_IID(nsITableLayout))) {
197 *aInstancePtr = static_cast<nsITableLayout*>(this);
198 return NS_OK;
201 return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
204 #ifdef ACCESSIBILITY
205 NS_IMETHODIMP nsTableOuterFrame::GetAccessible(nsIAccessible** aAccessible)
207 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
209 if (accService) {
210 return accService->CreateHTMLTableAccessible(static_cast<nsIFrame*>(this), aAccessible);
213 return NS_ERROR_FAILURE;
215 #endif
217 /* virtual */ PRBool
218 nsTableOuterFrame::IsContainingBlock() const
220 return PR_FALSE;
223 void
224 nsTableOuterFrame::Destroy()
226 mCaptionFrames.DestroyFrames();
227 nsHTMLContainerFrame::Destroy();
230 nsIFrame*
231 nsTableOuterFrame::GetFirstChild(nsIAtom* aListName) const
233 if (nsGkAtoms::captionList == aListName) {
234 return mCaptionFrames.FirstChild();
236 if (!aListName) {
237 return mFrames.FirstChild();
239 return nsnull;
242 nsIAtom*
243 nsTableOuterFrame::GetAdditionalChildListName(PRInt32 aIndex) const
245 if (aIndex == NS_TABLE_FRAME_CAPTION_LIST_INDEX) {
246 return nsGkAtoms::captionList;
248 return nsnull;
251 NS_IMETHODIMP
252 nsTableOuterFrame::SetInitialChildList(nsIAtom* aListName,
253 nsIFrame* aChildList)
255 if (nsGkAtoms::captionList == aListName) {
256 // the frame constructor already checked for table-caption display type
257 mCaptionFrames.SetFrames(aChildList);
258 mCaptionFrame = mCaptionFrames.FirstChild();
260 else {
261 NS_ASSERTION(!aListName, "wrong childlist");
262 NS_ASSERTION(mFrames.IsEmpty(), "Frame leak!");
263 mFrames.SetFrames(aChildList);
264 mInnerTableFrame = nsnull;
265 if (aChildList) {
266 if (nsGkAtoms::tableFrame == aChildList->GetType()) {
267 mInnerTableFrame = (nsTableFrame*)aChildList;
269 else {
270 NS_ERROR("expected a table frame");
271 return NS_ERROR_INVALID_ARG;
276 return NS_OK;
279 NS_IMETHODIMP
280 nsTableOuterFrame::AppendFrames(nsIAtom* aListName,
281 nsIFrame* aFrameList)
283 nsresult rv;
285 // We only have two child frames: the inner table and a caption frame.
286 // The inner frame is provided when we're initialized, and it cannot change
287 if (nsGkAtoms::captionList == aListName) {
288 NS_ASSERTION(!aFrameList ||
289 aFrameList->GetType() == nsGkAtoms::tableCaptionFrame,
290 "appending non-caption frame to captionList");
291 mCaptionFrames.AppendFrames(this, aFrameList);
292 mCaptionFrame = mCaptionFrames.FirstChild();
293 rv = NS_OK;
295 // Reflow the new caption frame. It's already marked dirty, so
296 // just tell the pres shell.
297 PresContext()->PresShell()->
298 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
299 NS_FRAME_HAS_DIRTY_CHILDREN);
301 else {
302 NS_PRECONDITION(PR_FALSE, "unexpected child list");
303 rv = NS_ERROR_UNEXPECTED;
306 return rv;
309 NS_IMETHODIMP
310 nsTableOuterFrame::InsertFrames(nsIAtom* aListName,
311 nsIFrame* aPrevFrame,
312 nsIFrame* aFrameList)
314 if (nsGkAtoms::captionList == aListName) {
315 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
316 "inserting after sibling frame with different parent");
317 NS_ASSERTION(!aFrameList ||
318 aFrameList->GetType() == nsGkAtoms::tableCaptionFrame,
319 "inserting non-caption frame into captionList");
320 mCaptionFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
321 mCaptionFrame = mCaptionFrames.FirstChild();
323 // Reflow the new caption frame. It's already marked dirty, so
324 // just tell the pres shell.
325 PresContext()->PresShell()->
326 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
327 NS_FRAME_HAS_DIRTY_CHILDREN);
328 return NS_OK;
330 else {
331 NS_PRECONDITION(!aPrevFrame, "invalid previous frame");
332 return AppendFrames(aListName, aFrameList);
336 NS_IMETHODIMP
337 nsTableOuterFrame::RemoveFrame(nsIAtom* aListName,
338 nsIFrame* aOldFrame)
340 // We only have two child frames: the inner table and one caption frame.
341 // The inner frame can't be removed so this should be the caption
342 NS_PRECONDITION(nsGkAtoms::captionList == aListName, "can't remove inner frame");
344 if (HasSideCaption()) {
345 // The old caption width had an effect on the inner table width so
346 // we're going to need to reflow it. Mark it dirty
347 mInnerTableFrame->AddStateBits(NS_FRAME_IS_DIRTY);
350 // Remove the frame and destroy it
351 mCaptionFrames.DestroyFrame(aOldFrame);
352 mCaptionFrame = mCaptionFrames.FirstChild();
354 PresContext()->PresShell()->
355 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
356 NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed
358 return NS_OK;
361 NS_METHOD
362 nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
363 const nsRect& aDirtyRect,
364 const nsDisplayListSet& aLists)
366 // No border, background or outline are painted because they all belong
367 // to the inner table.
368 if (!IsVisibleInSelection(aBuilder))
369 return NS_OK;
371 // If there's no caption, take a short cut to avoid having to create
372 // the special display list set and then sort it.
373 if (!mCaptionFrame)
374 return BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists);
376 nsDisplayListCollection set;
377 nsresult rv = BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set);
378 NS_ENSURE_SUCCESS(rv, rv);
380 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
381 rv = BuildDisplayListForChild(aBuilder, mCaptionFrame, aDirtyRect, captionSet);
382 NS_ENSURE_SUCCESS(rv, rv);
384 // Now we have to sort everything by content order, since the caption
385 // may be somewhere inside the table
386 set.SortAllByContentOrder(aBuilder, GetContent());
387 set.MoveTo(aLists);
388 return NS_OK;
391 nsresult
392 nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder,
393 const nsRect& aDirtyRect,
394 const nsDisplayListSet& aLists)
396 // Just paint the regular children, but the children's background is our
397 // true background (there should only be one, the real table)
398 nsIFrame* kid = mFrames.FirstChild();
399 // The children should be in content order
400 while (kid) {
401 nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
402 NS_ENSURE_SUCCESS(rv, rv);
403 kid = kid->GetNextSibling();
405 return NS_OK;
408 NS_IMETHODIMP nsTableOuterFrame::SetSelected(nsPresContext* aPresContext,
409 nsIDOMRange *aRange,
410 PRBool aSelected,
411 nsSpread aSpread,
412 SelectionType aType)
414 nsresult result = nsFrame::SetSelected(aPresContext, aRange,aSelected, aSpread, aType);
415 if (NS_SUCCEEDED(result) && mInnerTableFrame)
416 return mInnerTableFrame->SetSelected(aPresContext, aRange,aSelected, aSpread, aType);
417 return result;
420 NS_IMETHODIMP
421 nsTableOuterFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
422 nsIFrame** aProviderFrame,
423 PRBool* aIsChild)
425 // The table outer frame and the (inner) table frame split the style
426 // data by giving the table frame the style context associated with
427 // the table content node and creating a style context for the outer
428 // frame that is a *child* of the table frame's style context,
429 // matching the ::-moz-table-outer pseudo-element. html.css has a
430 // rule that causes that pseudo-element (and thus the outer table)
431 // to inherit *some* style properties from the table frame. The
432 // children of the table inherit directly from the inner table, and
433 // the outer table's style context is a leaf.
435 if (!mInnerTableFrame) {
436 *aProviderFrame = this;
437 *aIsChild = PR_FALSE;
438 return NS_ERROR_FAILURE;
440 *aProviderFrame = mInnerTableFrame;
441 *aIsChild = PR_TRUE;
442 return NS_OK;
445 // INCREMENTAL REFLOW HELPER FUNCTIONS
447 void
448 nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext,
449 nsHTMLReflowState& aReflowState)
452 nsMargin collapseBorder;
453 nsMargin collapsePadding(0,0,0,0);
454 nsMargin* pCollapseBorder = nsnull;
455 nsMargin* pCollapsePadding = nsnull;
456 if ((aReflowState.frame == mInnerTableFrame) && (mInnerTableFrame->IsBorderCollapse())) {
457 collapseBorder = mInnerTableFrame->GetIncludedOuterBCBorder();
458 pCollapseBorder = &collapseBorder;
459 pCollapsePadding = &collapsePadding;
461 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding);
464 // get the margin and padding data. nsHTMLReflowState doesn't handle the
465 // case of auto margins
466 void
467 nsTableOuterFrame::GetMargin(nsPresContext* aPresContext,
468 const nsHTMLReflowState& aOuterRS,
469 nsIFrame* aChildFrame,
470 nscoord aAvailWidth,
471 nsMargin& aMargin)
473 // construct a reflow state to compute margin and padding. Auto margins
474 // will not be computed at this time.
476 // create and init the child reflow state
477 // XXX We really shouldn't construct a reflow state to do this.
478 nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame,
479 nsSize(aAvailWidth, aOuterRS.availableHeight),
480 -1, -1, PR_FALSE);
481 InitChildReflowState(*aPresContext, childRS);
483 aMargin = childRS.mComputedMargin;
486 static
487 nscoord CalcAutoMargin(nscoord aAutoMargin,
488 nscoord aOppositeMargin,
489 nscoord aContainBlockSize,
490 nscoord aFrameSize)
492 nscoord margin;
493 if (NS_AUTOMARGIN == aOppositeMargin)
494 margin = (aContainBlockSize - aFrameSize) / 2;
495 else {
496 margin = aContainBlockSize - aFrameSize - aOppositeMargin;
498 return PR_MAX(0, margin);
501 static nsSize
502 GetContainingBlockSize(const nsHTMLReflowState& aOuterRS)
504 nsSize size(0,0);
505 const nsHTMLReflowState* containRS =
506 aOuterRS.mCBReflowState;
508 if (containRS) {
509 size.width = containRS->ComputedWidth();
510 if (NS_UNCONSTRAINEDSIZE == size.width) {
511 size.width = 0;
513 size.height = containRS->ComputedHeight();
514 if (NS_UNCONSTRAINEDSIZE == size.height) {
515 size.height = 0;
518 return size;
521 /* virtual */ nscoord
522 nsTableOuterFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
524 nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
525 mInnerTableFrame, nsLayoutUtils::MIN_WIDTH);
526 DISPLAY_MIN_WIDTH(this, width);
527 if (mCaptionFrame) {
528 nscoord capWidth =
529 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
530 nsLayoutUtils::MIN_WIDTH);
531 if (HasSideCaption()) {
532 width += capWidth;
533 } else {
534 if (capWidth > width) {
535 width = capWidth;
539 return width;
542 /* virtual */ nscoord
543 nsTableOuterFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
545 nscoord maxWidth;
546 DISPLAY_PREF_WIDTH(this, maxWidth);
548 maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
549 mInnerTableFrame, nsLayoutUtils::PREF_WIDTH);
550 if (mCaptionFrame) {
551 PRUint8 captionSide = GetCaptionSide();
552 switch(captionSide) {
553 case NS_STYLE_CAPTION_SIDE_LEFT:
554 case NS_STYLE_CAPTION_SIDE_RIGHT:
556 nscoord capMin =
557 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
558 nsLayoutUtils::MIN_WIDTH);
559 maxWidth += capMin;
561 break;
562 default:
564 nsLayoutUtils::IntrinsicWidthType iwt;
565 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
566 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
567 // Don't let the caption's pref width expand the table's pref
568 // width.
569 iwt = nsLayoutUtils::MIN_WIDTH;
570 } else {
571 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
572 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
573 "unexpected caption side");
574 iwt = nsLayoutUtils::PREF_WIDTH;
576 nscoord capPref =
577 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrame,
578 iwt);
579 maxWidth = PR_MAX(maxWidth, capPref);
581 break;
584 return maxWidth;
587 // Compute the margin-box width of aChildFrame given the inputs. If
588 // aMarginResult is non-null, fill it with the part of the margin-width
589 // that was contributed by the margin.
590 static nscoord
591 ChildShrinkWrapWidth(nsIRenderingContext *aRenderingContext,
592 nsIFrame *aChildFrame,
593 nsSize aCBSize, nscoord aAvailableWidth,
594 nscoord *aMarginResult = nsnull)
596 // The outer table's children do not use it as a containing block.
597 nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width);
598 nsSize size = aChildFrame->ComputeSize(aRenderingContext, aCBSize,
599 aAvailableWidth,
600 nsSize(offsets.mComputedMargin.LeftRight(),
601 offsets.mComputedMargin.TopBottom()),
602 nsSize(offsets.mComputedBorderPadding.LeftRight() -
603 offsets.mComputedPadding.LeftRight(),
604 offsets.mComputedBorderPadding.TopBottom() -
605 offsets.mComputedPadding.TopBottom()),
606 nsSize(offsets.mComputedPadding.LeftRight(),
607 offsets.mComputedPadding.TopBottom()),
608 PR_TRUE);
609 if (aMarginResult)
610 *aMarginResult = offsets.mComputedMargin.LeftRight();
611 return size.width + offsets.mComputedMargin.LeftRight() +
612 offsets.mComputedBorderPadding.LeftRight();
615 /* virtual */ nsSize
616 nsTableOuterFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
617 nsSize aCBSize, nscoord aAvailableWidth,
618 nsSize aMargin, nsSize aBorder,
619 nsSize aPadding, PRBool aShrinkWrap)
621 if (!aShrinkWrap)
622 return nsHTMLContainerFrame::ComputeAutoSize(aRenderingContext, aCBSize,
623 aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
625 // When we're shrink-wrapping, our auto size needs to wrap around the
626 // actual size of the table, which (if it is specified as a percent)
627 // could be something that is not reflected in our GetMinWidth and
628 // GetPrefWidth. See bug 349457 for an example.
630 // Match the availableWidth logic in Reflow.
631 PRUint8 captionSide = GetCaptionSide();
632 nscoord width;
633 if (captionSide == NO_SIDE) {
634 width = ChildShrinkWrapWidth(aRenderingContext, mInnerTableFrame,
635 aCBSize, aAvailableWidth);
636 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
637 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
638 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext, mCaptionFrame,
639 aCBSize, aAvailableWidth);
640 width = capWidth + ChildShrinkWrapWidth(aRenderingContext,
641 mInnerTableFrame, aCBSize,
642 aAvailableWidth - capWidth);
643 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
644 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
645 nscoord margin;
646 width = ChildShrinkWrapWidth(aRenderingContext, mInnerTableFrame,
647 aCBSize, aAvailableWidth, &margin);
648 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
649 mCaptionFrame, aCBSize,
650 width - margin);
651 if (capWidth > width)
652 width = capWidth;
653 } else {
654 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
655 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
656 "unexpected caption-side");
657 width = ChildShrinkWrapWidth(aRenderingContext, mInnerTableFrame,
658 aCBSize, aAvailableWidth);
659 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
660 mCaptionFrame, aCBSize,
661 aAvailableWidth);
662 if (capWidth > width)
663 width = capWidth;
666 return nsSize(width, NS_UNCONSTRAINEDSIZE);
669 PRUint8
670 nsTableOuterFrame::GetCaptionSide()
672 if (mCaptionFrame) {
673 return mCaptionFrame->GetStyleTableBorder()->mCaptionSide;
675 else {
676 return NO_SIDE; // no caption
680 PRUint8
681 nsTableOuterFrame::GetCaptionVerticalAlign()
683 const nsStyleCoord& va = mCaptionFrame->GetStyleTextReset()->mVerticalAlign;
684 return (va.GetUnit() == eStyleUnit_Enumerated)
685 ? va.GetIntValue()
686 : NS_STYLE_VERTICAL_ALIGN_TOP;
689 void
690 nsTableOuterFrame::SetDesiredSize(PRUint8 aCaptionSide,
691 const nsMargin& aInnerMargin,
692 const nsMargin& aCaptionMargin,
693 nscoord& aWidth,
694 nscoord& aHeight)
696 aWidth = aHeight = 0;
698 nsRect innerRect = mInnerTableFrame->GetRect();
699 nscoord innerWidth = innerRect.width;
701 nsRect captionRect(0,0,0,0);
702 nscoord captionWidth = 0;
703 if (mCaptionFrame) {
704 captionRect = mCaptionFrame->GetRect();
705 captionWidth = captionRect.width;
707 switch(aCaptionSide) {
708 case NS_STYLE_CAPTION_SIDE_LEFT:
709 aWidth = PR_MAX(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
710 innerWidth + aInnerMargin.right;
711 break;
712 case NS_STYLE_CAPTION_SIDE_RIGHT:
713 aWidth = PR_MAX(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
714 innerWidth + aInnerMargin.left;
715 break;
716 default:
717 aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
718 aWidth = PR_MAX(aWidth, captionRect.XMost() + aCaptionMargin.right);
720 aHeight = innerRect.YMost() + aInnerMargin.bottom;
721 aHeight = PR_MAX(aHeight, captionRect.YMost() + aCaptionMargin.bottom);
725 // XXX This is now unused, but it probably should be used!
726 void
727 nsTableOuterFrame::BalanceLeftRightCaption(PRUint8 aCaptionSide,
728 const nsMargin& aInnerMargin,
729 const nsMargin& aCaptionMargin,
730 nscoord& aInnerWidth,
731 nscoord& aCaptionWidth)
734 /* balance the caption and inner table widths to ensure space for percent widths
735 * Percent widths for captions or the inner table frame can determine how much of the
736 * available width is used and how the available width is distributed between those frames
737 * The inner table frame has already a quite sophisticated treatment of percentage widths
738 * (see BasicTableLayoutStrategy.cpp). So it acts as master in the below computations.
739 * There are four possible scenarios
740 * a) None of the frames have a percentage width - then the aInnerWidth and aCaptionwidth will not change
741 * b) Only the inner frame has a percentage width - this is handled in BasicTableLayoutStrategy.cpp,
742 * both widths will not change
743 * c) Only the caption has a percentage width - then the overall width (ow) will be different depending on
744 * the caption side. For the left side
745 * ow = aCaptionMargin.left + aCaptionWidth + aCaptionMargin.right + aInnerwidth + aInnerMargin.right
746 * aCaptionWidth = capPercent * ow
747 * solving this equation for aCaptionWidth gives:
748 * aCaptionWidth = capPercent/(1-capPercent) *
749 * (aCaptionMargin.left + aCaptionMargin.right + aInnerwidth + aInnerMargin.right)
750 * this result will cause problems for capPercent >= 1, in these cases the algorithm will now bail out
751 * a similar expression can be found for the right case
752 * d) both frames have percent widths in this case the caption width will be the inner width multiplied
753 * by the weight capPercent/innerPercent
757 float capPercent = -1.0;
758 float innerPercent = -1.0;
759 const nsStylePosition* position = mCaptionFrame->GetStylePosition();
760 if (eStyleUnit_Percent == position->mWidth.GetUnit()) {
761 capPercent = position->mWidth.GetPercentValue();
762 if (capPercent >= 1.0)
763 return;
766 position = mInnerTableFrame->GetStylePosition();
767 if (eStyleUnit_Percent == position->mWidth.GetUnit()) {
768 innerPercent = position->mWidth.GetPercentValue();
769 if (innerPercent >= 1.0)
770 return;
773 if ((capPercent <= 0.0) && (innerPercent <= 0.0))
774 return;
777 if (innerPercent <= 0.0) {
778 if (NS_STYLE_CAPTION_SIDE_LEFT == aCaptionSide)
779 aCaptionWidth= (nscoord) ((capPercent / (1.0 - capPercent)) * (aCaptionMargin.left + aCaptionMargin.right +
780 aInnerWidth + aInnerMargin.right));
781 else
782 aCaptionWidth= (nscoord) ((capPercent / (1.0 - capPercent)) * (aCaptionMargin.left + aCaptionMargin.right +
783 aInnerWidth + aInnerMargin.left));
785 else {
786 aCaptionWidth = (nscoord) ((capPercent / innerPercent) * aInnerWidth);
790 nsresult
791 nsTableOuterFrame::GetCaptionOrigin(PRUint32 aCaptionSide,
792 const nsSize& aContainBlockSize,
793 const nsSize& aInnerSize,
794 const nsMargin& aInnerMargin,
795 const nsSize& aCaptionSize,
796 nsMargin& aCaptionMargin,
797 nsPoint& aOrigin)
799 // FIXME: This function expects computed margin values to be
800 // NS_AUTOMARGIN, but this is no longer the case.
801 aOrigin.x = aOrigin.y = 0;
802 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
803 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
804 return NS_OK;
806 if (!mCaptionFrame) return NS_OK;
808 // FIXME: Have two separate switch statements so we can coalesce the
809 // horizontal computation for top and bottom.
810 switch(aCaptionSide) {
811 case NS_STYLE_CAPTION_SIDE_BOTTOM:
812 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
813 if (NS_AUTOMARGIN == aCaptionMargin.left) {
814 aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
815 aContainBlockSize.width, aCaptionSize.width);
817 aOrigin.x = aCaptionMargin.left;
818 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
819 // We placed the caption using only the table's width as available
820 // width, and we should position it this way as well.
821 aOrigin.x += aInnerMargin.left;
823 if (NS_AUTOMARGIN == aCaptionMargin.top) {
824 aCaptionMargin.top = 0;
826 // FIXME: Position relative to right edge for RTL. (Based on table
827 // direction or table parent direction?)
828 nsCollapsingMargin marg;
829 marg.Include(aCaptionMargin.top);
830 marg.Include(aInnerMargin.bottom);
831 nscoord collapseMargin = marg.get();
832 if (NS_AUTOMARGIN == aCaptionMargin.bottom) {
833 nscoord height = aInnerSize.height + collapseMargin + aCaptionSize.height;
834 aCaptionMargin.bottom = CalcAutoMargin(aCaptionMargin.bottom, aInnerMargin.top,
835 aContainBlockSize.height, height);
837 aOrigin.y = aInnerMargin.top + aInnerSize.height + collapseMargin;
838 } break;
839 case NS_STYLE_CAPTION_SIDE_LEFT: {
840 if (NS_AUTOMARGIN == aCaptionMargin.left) {
841 if (NS_AUTOMARGIN != aInnerMargin.left) {
842 aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
843 aInnerMargin.left, aCaptionSize.width);
845 else {
846 // zero for now
847 aCaptionMargin.left = 0;
850 aOrigin.x = aCaptionMargin.left;
851 aOrigin.y = aInnerMargin.top;
852 switch(GetCaptionVerticalAlign()) {
853 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
854 aOrigin.y = PR_MAX(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2));
855 break;
856 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
857 aOrigin.y = PR_MAX(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
858 break;
859 default:
860 break;
862 } break;
863 case NS_STYLE_CAPTION_SIDE_RIGHT: {
864 if (NS_AUTOMARGIN == aCaptionMargin.left) {
865 if (NS_AUTOMARGIN != aInnerMargin.right) {
866 aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
867 aInnerMargin.right, aCaptionSize.width);
869 else {
870 // zero for now
871 aCaptionMargin.left = 0;
874 aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left;
875 aOrigin.y = aInnerMargin.top;
876 switch(GetCaptionVerticalAlign()) {
877 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
878 aOrigin.y += PR_MAX(0, (aInnerSize.height - aCaptionSize.height) / 2);
879 break;
880 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
881 aOrigin.y += PR_MAX(0, aInnerSize.height - aCaptionSize.height);
882 break;
883 default:
884 break;
886 } break;
887 default: { // top
888 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
889 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
890 "unexpected caption side");
891 if (NS_AUTOMARGIN == aCaptionMargin.left) {
892 aCaptionMargin.left = CalcAutoMargin(aCaptionMargin.left, aCaptionMargin.right,
893 aContainBlockSize.width, aCaptionSize.width);
895 aOrigin.x = aCaptionMargin.left;
896 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
897 // We placed the caption using only the table's width as available
898 // width, and we should position it this way as well.
899 aOrigin.x += aInnerMargin.left;
901 // FIXME: Position relative to right edge for RTL. (Based on table
902 // direction or table parent direction?)
903 if (NS_AUTOMARGIN == aCaptionMargin.bottom) {
904 aCaptionMargin.bottom = 0;
906 if (NS_AUTOMARGIN == aCaptionMargin.top) {
907 nsCollapsingMargin marg;
908 marg.Include(aCaptionMargin.bottom);
909 marg.Include(aInnerMargin.top);
910 nscoord collapseMargin = marg.get();
911 nscoord height = aCaptionSize.height + collapseMargin + aInnerSize.height;
912 aCaptionMargin.top = CalcAutoMargin(aCaptionMargin.top, aInnerMargin.bottom,
913 aContainBlockSize.height, height);
915 aOrigin.y = aCaptionMargin.top;
916 } break;
918 return NS_OK;
921 nsresult
922 nsTableOuterFrame::GetInnerOrigin(PRUint32 aCaptionSide,
923 const nsSize& aContainBlockSize,
924 const nsSize& aCaptionSize,
925 const nsMargin& aCaptionMargin,
926 const nsSize& aInnerSize,
927 nsMargin& aInnerMargin,
928 nsPoint& aOrigin)
930 // FIXME: This function expects computed margin values to be
931 // NS_AUTOMARGIN, but this is no longer the case.
932 aOrigin.x = aOrigin.y = 0;
933 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
934 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
935 return NS_OK;
938 nscoord minCapWidth = aCaptionSize.width;
939 if (NS_AUTOMARGIN != aCaptionMargin.left)
940 minCapWidth += aCaptionMargin.left;
941 if (NS_AUTOMARGIN != aCaptionMargin.right)
942 minCapWidth += aCaptionMargin.right;
944 switch(aCaptionSide) {
945 case NS_STYLE_CAPTION_SIDE_BOTTOM:
946 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
947 if (NS_AUTOMARGIN == aInnerMargin.left) {
948 aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
949 aContainBlockSize.width, aInnerSize.width);
951 aOrigin.x = aInnerMargin.left;
952 if (NS_AUTOMARGIN == aInnerMargin.bottom) {
953 aInnerMargin.bottom = 0;
955 if (NS_AUTOMARGIN == aInnerMargin.top) {
956 nsCollapsingMargin marg;
957 marg.Include(aInnerMargin.bottom);
958 marg.Include(aCaptionMargin.top);
959 nscoord collapseMargin = marg.get();
960 nscoord height = aInnerSize.height + collapseMargin + aCaptionSize.height;
961 aInnerMargin.top = CalcAutoMargin(aInnerMargin.top, aCaptionMargin.bottom,
962 aContainBlockSize.height, height);
964 aOrigin.y = aInnerMargin.top;
965 } break;
966 case NS_STYLE_CAPTION_SIDE_LEFT: {
968 if (NS_AUTOMARGIN == aInnerMargin.left) {
969 aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
970 aContainBlockSize.width, aInnerSize.width);
973 if (aInnerMargin.left < minCapWidth) {
974 // shift the inner table to get some place for the caption
975 aInnerMargin.right += aInnerMargin.left - minCapWidth;
976 aInnerMargin.right = PR_MAX(0, aInnerMargin.right);
977 aInnerMargin.left = minCapWidth;
979 aOrigin.x = aInnerMargin.left;
980 if (NS_AUTOMARGIN == aInnerMargin.top) {
981 aInnerMargin.top = 0;
983 aOrigin.y = aInnerMargin.top;
984 switch(GetCaptionVerticalAlign()) {
985 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
986 aOrigin.y = PR_MAX(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
987 break;
988 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
989 aOrigin.y = PR_MAX(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
990 break;
991 default:
992 break;
994 } break;
995 case NS_STYLE_CAPTION_SIDE_RIGHT: {
996 if (NS_AUTOMARGIN == aInnerMargin.right) {
997 aInnerMargin.right = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
998 aContainBlockSize.width, aInnerSize.width);
999 if (aInnerMargin.right < minCapWidth) {
1000 // shift the inner table to get some place for the caption
1001 aInnerMargin.left -= aInnerMargin.right - minCapWidth;
1002 aInnerMargin.left = PR_MAX(0, aInnerMargin.left);
1003 aInnerMargin.right = minCapWidth;
1006 aOrigin.x = aInnerMargin.left;
1007 if (NS_AUTOMARGIN == aInnerMargin.top) {
1008 aInnerMargin.top = 0;
1010 aOrigin.y = aInnerMargin.top;
1011 switch(GetCaptionVerticalAlign()) {
1012 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
1013 aOrigin.y = PR_MAX(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
1014 break;
1015 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
1016 aOrigin.y = PR_MAX(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
1017 break;
1018 default:
1019 break;
1021 } break;
1022 default: { // top
1023 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
1024 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
1025 aCaptionSide == NO_SIDE,
1026 "unexpected caption side");
1027 if (NS_AUTOMARGIN == aInnerMargin.left) {
1028 aInnerMargin.left = CalcAutoMargin(aInnerMargin.left, aInnerMargin.right,
1029 aContainBlockSize.width, aInnerSize.width);
1031 aOrigin.x = aInnerMargin.left;
1032 if (NS_AUTOMARGIN == aInnerMargin.top) {
1033 aInnerMargin.top = 0;
1035 nsCollapsingMargin marg;
1036 marg.Include(aCaptionMargin.bottom);
1037 marg.Include(aInnerMargin.top);
1038 nscoord collapseMargin = marg.get();
1039 if (NS_AUTOMARGIN == aInnerMargin.bottom) {
1040 nscoord height = aCaptionSize.height + collapseMargin + aInnerSize.height;
1041 aInnerMargin.bottom = CalcAutoMargin(aCaptionMargin.bottom, aInnerMargin.top,
1042 aContainBlockSize.height, height);
1044 aOrigin.y = aCaptionMargin.top + aCaptionSize.height + collapseMargin;
1045 } break;
1047 return NS_OK;
1050 // helper method for determining if this is a nested table or not
1051 PRBool
1052 nsTableOuterFrame::IsNested(const nsHTMLReflowState& aReflowState) const
1054 // Walk up the reflow state chain until we find a cell or the root
1055 const nsHTMLReflowState* rs = aReflowState.parentReflowState;
1056 while (rs) {
1057 if (nsGkAtoms::tableFrame == rs->frame->GetType()) {
1058 return PR_TRUE;
1060 rs = rs->parentReflowState;
1062 return PR_FALSE;
1065 void
1066 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
1067 nsIFrame* aChildFrame,
1068 const nsHTMLReflowState& aOuterRS,
1069 void* aChildRSSpace,
1070 nscoord aAvailWidth)
1072 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
1073 nscoord availHeight = aOuterRS.availableHeight;
1074 if (NS_UNCONSTRAINEDSIZE != availHeight) {
1075 if (mCaptionFrame == aChildFrame) {
1076 availHeight = NS_UNCONSTRAINEDSIZE;
1077 } else {
1078 nsMargin margin;
1079 GetMargin(aPresContext, aOuterRS, aChildFrame, aOuterRS.availableWidth,
1080 margin);
1082 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please");
1083 availHeight -= margin.top;
1085 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please");
1086 availHeight -= margin.bottom;
1089 nsSize availSize(aAvailWidth, availHeight);
1090 // create and init the child reflow state, using placement new on
1091 // stack space allocated by the caller, so that the caller can destroy
1092 // it
1093 nsHTMLReflowState &childRS = * new (aChildRSSpace)
1094 nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
1095 -1, -1, PR_FALSE);
1096 InitChildReflowState(*aPresContext, childRS);
1098 // see if we need to reset top of page due to a caption
1099 if (mCaptionFrame) {
1100 PRUint8 captionSide = GetCaptionSide();
1101 if (((captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
1102 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) &&
1103 mCaptionFrame == aChildFrame) ||
1104 ((captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
1105 captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE) &&
1106 mInnerTableFrame == aChildFrame)) {
1107 childRS.mFlags.mIsTopOfPage = PR_FALSE;
1112 nsresult
1113 nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext,
1114 nsIFrame* aChildFrame,
1115 const nsHTMLReflowState& aChildRS,
1116 nsHTMLReflowMetrics& aMetrics,
1117 nsReflowStatus& aStatus)
1120 // use the current position as a best guess for placement
1121 nsPoint childPt = aChildFrame->GetPosition();
1122 return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
1123 childPt.x, childPt.y, NS_FRAME_NO_MOVE_FRAME, aStatus);
1126 void
1127 nsTableOuterFrame::UpdateReflowMetrics(PRUint8 aCaptionSide,
1128 nsHTMLReflowMetrics& aMet,
1129 const nsMargin& aInnerMargin,
1130 const nsMargin& aCaptionMargin)
1132 SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
1133 aMet.width, aMet.height);
1135 aMet.mOverflowArea = nsRect(0, 0, aMet.width, aMet.height);
1136 ConsiderChildOverflow(aMet.mOverflowArea, mInnerTableFrame);
1137 if (mCaptionFrame) {
1138 ConsiderChildOverflow(aMet.mOverflowArea, mCaptionFrame);
1140 FinishAndStoreOverflow(&aMet);
1143 NS_METHOD nsTableOuterFrame::Reflow(nsPresContext* aPresContext,
1144 nsHTMLReflowMetrics& aDesiredSize,
1145 const nsHTMLReflowState& aOuterRS,
1146 nsReflowStatus& aStatus)
1148 DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
1149 DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
1151 // We desperately need an inner table frame,
1152 // if this fails fix the frame constructor
1153 if (mFrames.IsEmpty() || !mInnerTableFrame) {
1154 NS_ERROR("incomplete children");
1155 return NS_ERROR_FAILURE;
1157 nsresult rv = NS_OK;
1158 PRUint8 captionSide = GetCaptionSide();
1160 // Initialize out parameters
1161 aDesiredSize.width = aDesiredSize.height = 0;
1162 aStatus = NS_FRAME_COMPLETE;
1164 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1165 // Set up our kids. They're already present, on an overflow list,
1166 // or there are none so we'll create them now
1167 MoveOverflowToChildList(aPresContext);
1170 // Use longs to get more-aligned space.
1171 #define LONGS_IN_HTMLRS \
1172 ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
1173 long captionRSSpace[LONGS_IN_HTMLRS];
1174 nsHTMLReflowState *captionRS =
1175 static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
1176 long innerRSSpace[LONGS_IN_HTMLRS];
1177 nsHTMLReflowState *innerRS =
1178 static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
1180 nsRect origInnerRect = mInnerTableFrame->GetRect();
1181 nsRect origInnerOverflowRect = mInnerTableFrame->GetOverflowRect();
1182 PRBool innerFirstReflow =
1183 (mInnerTableFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
1184 nsRect origCaptionRect;
1185 nsRect origCaptionOverflowRect;
1186 PRBool captionFirstReflow;
1187 if (mCaptionFrame) {
1188 origCaptionRect = mCaptionFrame->GetRect();
1189 origCaptionOverflowRect = mCaptionFrame->GetOverflowRect();
1190 captionFirstReflow =
1191 (mCaptionFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
1194 // ComputeAutoSize has to match this logic.
1195 if (captionSide == NO_SIDE) {
1196 // We don't have a caption.
1197 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1198 innerRSSpace, aOuterRS.ComputedWidth());
1199 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
1200 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
1201 // nsTableCaptionFrame::ComputeAutoSize takes care of making side
1202 // captions small. Compute the caption's size first, and tell the
1203 // table to fit in what's left.
1204 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1205 captionRSSpace, aOuterRS.ComputedWidth());
1206 nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
1207 (captionRS->ComputedWidth() + captionRS->mComputedMargin.LeftRight() +
1208 captionRS->mComputedBorderPadding.LeftRight());
1209 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1210 innerRSSpace, innerAvailWidth);
1212 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
1213 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
1214 // Compute the table's size first, and then prevent the caption from
1215 // being wider unless it has to be.
1217 // Note that CSS 2.1 (but not 2.0) says:
1218 // The width of the anonymous box is the border-edge width of the
1219 // table box inside it
1220 // We don't actually make our anonymous box that width (if we did,
1221 // it would break 'auto' margins), but this effectively does that.
1222 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1223 innerRSSpace, aOuterRS.ComputedWidth());
1224 // It's good that CSS 2.1 says not to include margins, since we
1225 // can't, since they already been converted so they exactly
1226 // fill the available width (ignoring the margin on one side if
1227 // neither are auto). (We take advantage of that later when we call
1228 // GetCaptionOrigin, though.)
1229 nscoord innerBorderWidth = innerRS->ComputedWidth() +
1230 innerRS->mComputedBorderPadding.LeftRight();
1231 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1232 captionRSSpace, innerBorderWidth);
1233 } else {
1234 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
1235 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
1236 "unexpected caption-side");
1237 // Size the table and the caption independently.
1238 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1239 captionRSSpace, aOuterRS.ComputedWidth());
1240 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1241 innerRSSpace, aOuterRS.ComputedWidth());
1244 // First reflow the caption.
1245 nsHTMLReflowMetrics captionMet;
1246 nsSize captionSize;
1247 nsMargin captionMargin;
1248 if (mCaptionFrame) {
1249 nsReflowStatus capStatus; // don't let the caption cause incomplete
1250 rv = OuterDoReflowChild(aPresContext, mCaptionFrame, *captionRS,
1251 captionMet, capStatus);
1252 if (NS_FAILED(rv)) return rv;
1253 captionSize.width = captionMet.width;
1254 captionSize.height = captionMet.height;
1255 captionMargin = captionRS->mComputedMargin;
1256 } else {
1257 captionSize.SizeTo(0,0);
1258 captionMargin.SizeTo(0,0,0,0);
1261 // Then, now that we know how much to reduce the width of the inner
1262 // table to account for side captions, reflow the inner table.
1263 nsHTMLReflowMetrics innerMet;
1264 rv = OuterDoReflowChild(aPresContext, mInnerTableFrame, *innerRS,
1265 innerMet, aStatus);
1266 if (NS_FAILED(rv)) return rv;
1267 nsSize innerSize;
1268 innerSize.width = innerMet.width;
1269 innerSize.height = innerMet.height;
1270 nsMargin innerMargin = innerRS->mComputedMargin;
1272 nsSize containSize = GetContainingBlockSize(aOuterRS);
1274 // Now that we've reflowed both we can place them.
1275 // XXXldb Most of the input variables here are now uninitialized!
1277 // XXX Need to recompute inner table's auto margins for the case of side
1278 // captions. (Caption's are broken too, but that should be fixed earlier.)
1280 if (mCaptionFrame) {
1281 nsPoint captionOrigin;
1282 GetCaptionOrigin(captionSide, containSize, innerSize,
1283 innerMargin, captionSize, captionMargin, captionOrigin);
1284 FinishReflowChild(mCaptionFrame, aPresContext, captionRS, captionMet,
1285 captionOrigin.x, captionOrigin.y, 0);
1286 captionRS->~nsHTMLReflowState();
1288 // XXX If the height is constrained then we need to check whether
1289 // everything still fits...
1291 nsPoint innerOrigin;
1292 GetInnerOrigin(captionSide, containSize, captionSize,
1293 captionMargin, innerSize, innerMargin, innerOrigin);
1294 FinishReflowChild(mInnerTableFrame, aPresContext, innerRS, innerMet,
1295 innerOrigin.x, innerOrigin.y, 0);
1296 innerRS->~nsHTMLReflowState();
1298 nsTableFrame::InvalidateFrame(mInnerTableFrame, origInnerRect,
1299 origInnerOverflowRect, innerFirstReflow);
1300 if (mCaptionFrame) {
1301 nsTableFrame::InvalidateFrame(mCaptionFrame, origCaptionRect,
1302 origCaptionOverflowRect, captionFirstReflow);
1305 UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
1307 // Return our desired rect
1309 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
1310 return rv;
1313 #ifdef NS_DEBUG
1314 NS_METHOD nsTableOuterFrame::VerifyTree() const
1316 return NS_OK;
1318 #endif
1320 nsIAtom*
1321 nsTableOuterFrame::GetType() const
1323 return nsGkAtoms::tableOuterFrame;
1326 /* ----- global methods ----- */
1328 /*------------------ nsITableLayout methods ------------------------------*/
1329 NS_IMETHODIMP
1330 nsTableOuterFrame::GetCellDataAt(PRInt32 aRowIndex, PRInt32 aColIndex,
1331 nsIDOMElement* &aCell, //out params
1332 PRInt32& aStartRowIndex, PRInt32& aStartColIndex,
1333 PRInt32& aRowSpan, PRInt32& aColSpan,
1334 PRInt32& aActualRowSpan, PRInt32& aActualColSpan,
1335 PRBool& aIsSelected)
1337 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1339 return mInnerTableFrame->GetCellDataAt(aRowIndex, aColIndex, aCell,
1340 aStartRowIndex, aStartColIndex,
1341 aRowSpan, aColSpan, aActualRowSpan,
1342 aActualColSpan, aIsSelected);
1345 NS_IMETHODIMP
1346 nsTableOuterFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount)
1348 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1350 return mInnerTableFrame->GetTableSize(aRowCount, aColCount);
1353 NS_IMETHODIMP
1354 nsTableOuterFrame::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn,
1355 PRInt32 *aIndex)
1357 NS_ENSURE_ARG_POINTER(aIndex);
1359 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1360 return mInnerTableFrame->GetIndexByRowAndColumn(aRow, aColumn, aIndex);
1363 NS_IMETHODIMP
1364 nsTableOuterFrame::GetRowAndColumnByIndex(PRInt32 aIndex,
1365 PRInt32 *aRow, PRInt32 *aColumn)
1367 NS_ENSURE_ARG_POINTER(aRow);
1368 NS_ENSURE_ARG_POINTER(aColumn);
1370 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1371 return mInnerTableFrame->GetRowAndColumnByIndex(aIndex, aRow, aColumn);
1374 /*---------------- end of nsITableLayout implementation ------------------*/
1377 nsIFrame*
1378 NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1380 return new (aPresShell) nsTableOuterFrame(aContext);
1383 #ifdef DEBUG
1384 NS_IMETHODIMP
1385 nsTableOuterFrame::GetFrameName(nsAString& aResult) const
1387 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
1389 #endif