Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / tables / nsTableOuterFrame.cpp
blob1292acfa53076551a647a9a90fbd7f28ad88d49c
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 void
1051 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
1052 nsIFrame* aChildFrame,
1053 const nsHTMLReflowState& aOuterRS,
1054 void* aChildRSSpace,
1055 nscoord aAvailWidth)
1057 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
1058 nscoord availHeight = aOuterRS.availableHeight;
1059 if (NS_UNCONSTRAINEDSIZE != availHeight) {
1060 if (mCaptionFrame == aChildFrame) {
1061 availHeight = NS_UNCONSTRAINEDSIZE;
1062 } else {
1063 nsMargin margin;
1064 GetMargin(aPresContext, aOuterRS, aChildFrame, aOuterRS.availableWidth,
1065 margin);
1067 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please");
1068 availHeight -= margin.top;
1070 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please");
1071 availHeight -= margin.bottom;
1074 nsSize availSize(aAvailWidth, availHeight);
1075 // create and init the child reflow state, using placement new on
1076 // stack space allocated by the caller, so that the caller can destroy
1077 // it
1078 nsHTMLReflowState &childRS = * new (aChildRSSpace)
1079 nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
1080 -1, -1, PR_FALSE);
1081 InitChildReflowState(*aPresContext, childRS);
1083 // see if we need to reset top of page due to a caption
1084 if (mCaptionFrame) {
1085 PRUint8 captionSide = GetCaptionSide();
1086 if (((captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
1087 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) &&
1088 mCaptionFrame == aChildFrame) ||
1089 ((captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
1090 captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE) &&
1091 mInnerTableFrame == aChildFrame)) {
1092 childRS.mFlags.mIsTopOfPage = PR_FALSE;
1097 nsresult
1098 nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext,
1099 nsIFrame* aChildFrame,
1100 const nsHTMLReflowState& aChildRS,
1101 nsHTMLReflowMetrics& aMetrics,
1102 nsReflowStatus& aStatus)
1105 // use the current position as a best guess for placement
1106 nsPoint childPt = aChildFrame->GetPosition();
1107 return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
1108 childPt.x, childPt.y, NS_FRAME_NO_MOVE_FRAME, aStatus);
1111 void
1112 nsTableOuterFrame::UpdateReflowMetrics(PRUint8 aCaptionSide,
1113 nsHTMLReflowMetrics& aMet,
1114 const nsMargin& aInnerMargin,
1115 const nsMargin& aCaptionMargin)
1117 SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
1118 aMet.width, aMet.height);
1120 aMet.mOverflowArea = nsRect(0, 0, aMet.width, aMet.height);
1121 ConsiderChildOverflow(aMet.mOverflowArea, mInnerTableFrame);
1122 if (mCaptionFrame) {
1123 ConsiderChildOverflow(aMet.mOverflowArea, mCaptionFrame);
1125 FinishAndStoreOverflow(&aMet);
1128 NS_METHOD nsTableOuterFrame::Reflow(nsPresContext* aPresContext,
1129 nsHTMLReflowMetrics& aDesiredSize,
1130 const nsHTMLReflowState& aOuterRS,
1131 nsReflowStatus& aStatus)
1133 DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
1134 DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
1136 // We desperately need an inner table frame,
1137 // if this fails fix the frame constructor
1138 if (mFrames.IsEmpty() || !mInnerTableFrame) {
1139 NS_ERROR("incomplete children");
1140 return NS_ERROR_FAILURE;
1142 nsresult rv = NS_OK;
1143 PRUint8 captionSide = GetCaptionSide();
1145 // Initialize out parameters
1146 aDesiredSize.width = aDesiredSize.height = 0;
1147 aStatus = NS_FRAME_COMPLETE;
1149 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1150 // Set up our kids. They're already present, on an overflow list,
1151 // or there are none so we'll create them now
1152 MoveOverflowToChildList(aPresContext);
1155 // Use longs to get more-aligned space.
1156 #define LONGS_IN_HTMLRS \
1157 ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
1158 long captionRSSpace[LONGS_IN_HTMLRS];
1159 nsHTMLReflowState *captionRS =
1160 static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
1161 long innerRSSpace[LONGS_IN_HTMLRS];
1162 nsHTMLReflowState *innerRS =
1163 static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
1165 nsRect origInnerRect = mInnerTableFrame->GetRect();
1166 nsRect origInnerOverflowRect = mInnerTableFrame->GetOverflowRect();
1167 PRBool innerFirstReflow =
1168 (mInnerTableFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
1169 nsRect origCaptionRect;
1170 nsRect origCaptionOverflowRect;
1171 PRBool captionFirstReflow;
1172 if (mCaptionFrame) {
1173 origCaptionRect = mCaptionFrame->GetRect();
1174 origCaptionOverflowRect = mCaptionFrame->GetOverflowRect();
1175 captionFirstReflow =
1176 (mCaptionFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
1179 // ComputeAutoSize has to match this logic.
1180 if (captionSide == NO_SIDE) {
1181 // We don't have a caption.
1182 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1183 innerRSSpace, aOuterRS.ComputedWidth());
1184 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
1185 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
1186 // nsTableCaptionFrame::ComputeAutoSize takes care of making side
1187 // captions small. Compute the caption's size first, and tell the
1188 // table to fit in what's left.
1189 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1190 captionRSSpace, aOuterRS.ComputedWidth());
1191 nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
1192 (captionRS->ComputedWidth() + captionRS->mComputedMargin.LeftRight() +
1193 captionRS->mComputedBorderPadding.LeftRight());
1194 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1195 innerRSSpace, innerAvailWidth);
1197 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
1198 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
1199 // Compute the table's size first, and then prevent the caption from
1200 // being wider unless it has to be.
1202 // Note that CSS 2.1 (but not 2.0) says:
1203 // The width of the anonymous box is the border-edge width of the
1204 // table box inside it
1205 // We don't actually make our anonymous box that width (if we did,
1206 // it would break 'auto' margins), but this effectively does that.
1207 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1208 innerRSSpace, aOuterRS.ComputedWidth());
1209 // It's good that CSS 2.1 says not to include margins, since we
1210 // can't, since they already been converted so they exactly
1211 // fill the available width (ignoring the margin on one side if
1212 // neither are auto). (We take advantage of that later when we call
1213 // GetCaptionOrigin, though.)
1214 nscoord innerBorderWidth = innerRS->ComputedWidth() +
1215 innerRS->mComputedBorderPadding.LeftRight();
1216 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1217 captionRSSpace, innerBorderWidth);
1218 } else {
1219 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
1220 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
1221 "unexpected caption-side");
1222 // Size the table and the caption independently.
1223 OuterBeginReflowChild(aPresContext, mCaptionFrame, aOuterRS,
1224 captionRSSpace, aOuterRS.ComputedWidth());
1225 OuterBeginReflowChild(aPresContext, mInnerTableFrame, aOuterRS,
1226 innerRSSpace, aOuterRS.ComputedWidth());
1229 // First reflow the caption.
1230 nsHTMLReflowMetrics captionMet;
1231 nsSize captionSize;
1232 nsMargin captionMargin;
1233 if (mCaptionFrame) {
1234 nsReflowStatus capStatus; // don't let the caption cause incomplete
1235 rv = OuterDoReflowChild(aPresContext, mCaptionFrame, *captionRS,
1236 captionMet, capStatus);
1237 if (NS_FAILED(rv)) return rv;
1238 captionSize.width = captionMet.width;
1239 captionSize.height = captionMet.height;
1240 captionMargin = captionRS->mComputedMargin;
1241 } else {
1242 captionSize.SizeTo(0,0);
1243 captionMargin.SizeTo(0,0,0,0);
1246 // Then, now that we know how much to reduce the width of the inner
1247 // table to account for side captions, reflow the inner table.
1248 nsHTMLReflowMetrics innerMet;
1249 rv = OuterDoReflowChild(aPresContext, mInnerTableFrame, *innerRS,
1250 innerMet, aStatus);
1251 if (NS_FAILED(rv)) return rv;
1252 nsSize innerSize;
1253 innerSize.width = innerMet.width;
1254 innerSize.height = innerMet.height;
1255 nsMargin innerMargin = innerRS->mComputedMargin;
1257 nsSize containSize = GetContainingBlockSize(aOuterRS);
1259 // Now that we've reflowed both we can place them.
1260 // XXXldb Most of the input variables here are now uninitialized!
1262 // XXX Need to recompute inner table's auto margins for the case of side
1263 // captions. (Caption's are broken too, but that should be fixed earlier.)
1265 if (mCaptionFrame) {
1266 nsPoint captionOrigin;
1267 GetCaptionOrigin(captionSide, containSize, innerSize,
1268 innerMargin, captionSize, captionMargin, captionOrigin);
1269 FinishReflowChild(mCaptionFrame, aPresContext, captionRS, captionMet,
1270 captionOrigin.x, captionOrigin.y, 0);
1271 captionRS->~nsHTMLReflowState();
1273 // XXX If the height is constrained then we need to check whether
1274 // everything still fits...
1276 nsPoint innerOrigin;
1277 GetInnerOrigin(captionSide, containSize, captionSize,
1278 captionMargin, innerSize, innerMargin, innerOrigin);
1279 FinishReflowChild(mInnerTableFrame, aPresContext, innerRS, innerMet,
1280 innerOrigin.x, innerOrigin.y, 0);
1281 innerRS->~nsHTMLReflowState();
1283 nsTableFrame::InvalidateFrame(mInnerTableFrame, origInnerRect,
1284 origInnerOverflowRect, innerFirstReflow);
1285 if (mCaptionFrame) {
1286 nsTableFrame::InvalidateFrame(mCaptionFrame, origCaptionRect,
1287 origCaptionOverflowRect, captionFirstReflow);
1290 UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
1292 // Return our desired rect
1294 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
1295 return rv;
1298 #ifdef NS_DEBUG
1299 NS_METHOD nsTableOuterFrame::VerifyTree() const
1301 return NS_OK;
1303 #endif
1305 nsIAtom*
1306 nsTableOuterFrame::GetType() const
1308 return nsGkAtoms::tableOuterFrame;
1311 /* ----- global methods ----- */
1313 /*------------------ nsITableLayout methods ------------------------------*/
1314 NS_IMETHODIMP
1315 nsTableOuterFrame::GetCellDataAt(PRInt32 aRowIndex, PRInt32 aColIndex,
1316 nsIDOMElement* &aCell, //out params
1317 PRInt32& aStartRowIndex, PRInt32& aStartColIndex,
1318 PRInt32& aRowSpan, PRInt32& aColSpan,
1319 PRInt32& aActualRowSpan, PRInt32& aActualColSpan,
1320 PRBool& aIsSelected)
1322 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1324 return mInnerTableFrame->GetCellDataAt(aRowIndex, aColIndex, aCell,
1325 aStartRowIndex, aStartColIndex,
1326 aRowSpan, aColSpan, aActualRowSpan,
1327 aActualColSpan, aIsSelected);
1330 NS_IMETHODIMP
1331 nsTableOuterFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount)
1333 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1335 return mInnerTableFrame->GetTableSize(aRowCount, aColCount);
1338 NS_IMETHODIMP
1339 nsTableOuterFrame::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn,
1340 PRInt32 *aIndex)
1342 NS_ENSURE_ARG_POINTER(aIndex);
1344 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1345 return mInnerTableFrame->GetIndexByRowAndColumn(aRow, aColumn, aIndex);
1348 NS_IMETHODIMP
1349 nsTableOuterFrame::GetRowAndColumnByIndex(PRInt32 aIndex,
1350 PRInt32 *aRow, PRInt32 *aColumn)
1352 NS_ENSURE_ARG_POINTER(aRow);
1353 NS_ENSURE_ARG_POINTER(aColumn);
1355 NS_ASSERTION(mInnerTableFrame, "no inner table frame yet?");
1356 return mInnerTableFrame->GetRowAndColumnByIndex(aIndex, aRow, aColumn);
1359 /*---------------- end of nsITableLayout implementation ------------------*/
1362 nsIFrame*
1363 NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1365 return new (aPresShell) nsTableOuterFrame(aContext);
1368 #ifdef DEBUG
1369 NS_IMETHODIMP
1370 nsTableOuterFrame::GetFrameName(nsAString& aResult) const
1372 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
1374 #endif