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