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