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
6 #include "nsTableWrapperFrame.h"
7
8 #include "mozilla/ComputedStyle.h"
9 #include "mozilla/PresShell.h"
10 #include "nsFrameManager.h"
11 #include "nsTableFrame.h"
12 #include "nsTableCellFrame.h"
13 #include "nsStyleConsts.h"
14 #include "nsPresContext.h"
15 #include "nsCSSRendering.h"
16 #include "nsIContent.h"
17 #include "prinrval.h"
18 #include "nsGkAtoms.h"
19 #include "nsHTMLParts.h"
20 #include "nsDisplayList.h"
21 #include "nsLayoutUtils.h"
22 #include "nsIFrameInlines.h"
23 #include <algorithm>
24
25 using namespace mozilla;
26 using namespace mozilla::layout;
27
28 /* virtual */
GetLogicalBaseline(WritingMode aWritingMode) const29 nscoord nsTableWrapperFrame::GetLogicalBaseline(
30 WritingMode aWritingMode) const {
31 if (StyleDisplay()->IsContainLayout()) {
32 // We have no baseline. Fall back to the inherited impl which is
33 // appropriate for this situation.
34 return nsContainerFrame::GetLogicalBaseline(aWritingMode);
35 }
36
37 nsIFrame* kid = mFrames.FirstChild();
38 if (!kid) {
39 MOZ_ASSERT_UNREACHABLE("no inner table");
40 return nsContainerFrame::GetLogicalBaseline(aWritingMode);
41 }
42
43 return kid->GetLogicalBaseline(aWritingMode) +
44 kid->BStart(aWritingMode, mRect.Size());
45 }
46
nsTableWrapperFrame(ComputedStyle * aStyle,nsPresContext * aPresContext,ClassID aID)47 nsTableWrapperFrame::nsTableWrapperFrame(ComputedStyle* aStyle,
48 nsPresContext* aPresContext,
49 ClassID aID)
50 : nsContainerFrame(aStyle, aPresContext, aID) {}
51
52 nsTableWrapperFrame::~nsTableWrapperFrame() = default;
53
54 NS_QUERYFRAME_HEAD(nsTableWrapperFrame)
NS_QUERYFRAME_ENTRY(nsTableWrapperFrame)55 NS_QUERYFRAME_ENTRY(nsTableWrapperFrame)
56 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
57
58 #ifdef ACCESSIBILITY
59 a11y::AccType nsTableWrapperFrame::AccessibleType() {
60 return a11y::eHTMLTableType;
61 }
62 #endif
63
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)64 void nsTableWrapperFrame::DestroyFrom(nsIFrame* aDestructRoot,
65 PostDestroyData& aPostDestroyData) {
66 DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
67 mCaptionFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
68 nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
69 }
70
GetChildList(ChildListID aListID) const71 const nsFrameList& nsTableWrapperFrame::GetChildList(
72 ChildListID aListID) const {
73 if (aListID == kCaptionList) {
74 return mCaptionFrames;
75 }
76
77 return nsContainerFrame::GetChildList(aListID);
78 }
79
GetChildLists(nsTArray<ChildList> * aLists) const80 void nsTableWrapperFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
81 nsContainerFrame::GetChildLists(aLists);
82 mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
83 }
84
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)85 void nsTableWrapperFrame::SetInitialChildList(ChildListID aListID,
86 nsFrameList& aChildList) {
87 if (kCaptionList == aListID) {
88 #ifdef DEBUG
89 nsIFrame::VerifyDirtyBitSet(aChildList);
90 for (nsIFrame* f : aChildList) {
91 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
92 }
93 #endif
94 // the frame constructor already checked for table-caption display type
95 MOZ_ASSERT(mCaptionFrames.IsEmpty(),
96 "already have child frames in CaptionList");
97 mCaptionFrames.SetFrames(aChildList);
98 } else {
99 MOZ_ASSERT(kPrincipalList != aListID ||
100 (aChildList.FirstChild() &&
101 aChildList.FirstChild() == aChildList.LastChild() &&
102 aChildList.FirstChild()->IsTableFrame()),
103 "expected a single table frame in principal child list");
104 nsContainerFrame::SetInitialChildList(aListID, aChildList);
105 }
106 }
107
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)108 void nsTableWrapperFrame::AppendFrames(ChildListID aListID,
109 nsFrameList& aFrameList) {
110 // We only have two child frames: the inner table and a caption frame.
111 // The inner frame is provided when we're initialized, and it cannot change
112 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
113 MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
114 "appending non-caption frame to captionList");
115 mCaptionFrames.AppendFrames(nullptr, aFrameList);
116
117 // Reflow the new caption frame. It's already marked dirty, so
118 // just tell the pres shell.
119 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
120 NS_FRAME_HAS_DIRTY_CHILDREN);
121 // The presence of caption frames makes us sort our display
122 // list differently, so mark us as changed for the new
123 // ordering.
124 MarkNeedsDisplayItemRebuild();
125 }
126
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)127 void nsTableWrapperFrame::InsertFrames(
128 ChildListID aListID, nsIFrame* aPrevFrame,
129 const nsLineList::iterator* aPrevFrameLine, nsFrameList& aFrameList) {
130 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
131 MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
132 "inserting non-caption frame into captionList");
133 MOZ_ASSERT(!aPrevFrame || aPrevFrame->GetParent() == this,
134 "inserting after sibling frame with different parent");
135 mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
136
137 // Reflow the new caption frame. It's already marked dirty, so
138 // just tell the pres shell.
139 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
140 NS_FRAME_HAS_DIRTY_CHILDREN);
141 MarkNeedsDisplayItemRebuild();
142 }
143
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)144 void nsTableWrapperFrame::RemoveFrame(ChildListID aListID,
145 nsIFrame* aOldFrame) {
146 // We only have two child frames: the inner table and one caption frame.
147 // The inner frame can't be removed so this should be the caption
148 MOZ_ASSERT(kCaptionList == aListID, "can't remove inner frame");
149
150 if (HasSideCaption()) {
151 // The old caption isize had an effect on the inner table isize, so
152 // we're going to need to reflow it. Mark it dirty
153 InnerTableFrame()->MarkSubtreeDirty();
154 }
155
156 // Remove the frame and destroy it
157 mCaptionFrames.DestroyFrame(aOldFrame);
158
159 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
160 NS_FRAME_HAS_DIRTY_CHILDREN);
161 MarkNeedsDisplayItemRebuild();
162 }
163
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)164 void nsTableWrapperFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
165 const nsDisplayListSet& aLists) {
166 // No border or background is painted because they belong to the inner table.
167 // The outline belongs to the wrapper frame so it can contain the caption.
168
169 // If there's no caption, take a short cut to avoid having to create
170 // the special display list set and then sort it.
171 if (mCaptionFrames.IsEmpty()) {
172 BuildDisplayListForInnerTable(aBuilder, aLists);
173 DisplayOutline(aBuilder, aLists);
174 return;
175 }
176
177 nsDisplayListCollection set(aBuilder);
178 BuildDisplayListForInnerTable(aBuilder, set);
179
180 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
181 BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(), captionSet);
182
183 // Now we have to sort everything by content order, since the caption
184 // may be somewhere inside the table.
185 // We don't sort BlockBorderBackgrounds and BorderBackgrounds because the
186 // display items in those lists should stay out of content order in order to
187 // follow the rules in https://www.w3.org/TR/CSS21/zindex.html#painting-order
188 // and paint the caption background after all of the rest.
189 set.Floats()->SortByContentOrder(GetContent());
190 set.Content()->SortByContentOrder(GetContent());
191 set.PositionedDescendants()->SortByContentOrder(GetContent());
192 set.Outlines()->SortByContentOrder(GetContent());
193 set.MoveTo(aLists);
194
195 DisplayOutline(aBuilder, aLists);
196 }
197
BuildDisplayListForInnerTable(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)198 void nsTableWrapperFrame::BuildDisplayListForInnerTable(
199 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
200 // Just paint the regular children, but the children's background is our
201 // true background (there should only be one, the real table)
202 nsIFrame* kid = mFrames.FirstChild();
203 // The children should be in content order
204 while (kid) {
205 BuildDisplayListForChild(aBuilder, kid, aLists);
206 kid = kid->GetNextSibling();
207 }
208 }
209
GetParentComputedStyle(nsIFrame ** aProviderFrame) const210 ComputedStyle* nsTableWrapperFrame::GetParentComputedStyle(
211 nsIFrame** aProviderFrame) const {
212 // The table wrapper frame and the (inner) table frame split the style
213 // data by giving the table frame the ComputedStyle associated with
214 // the table content node and creating a ComputedStyle for the wrapper
215 // frame that is a *child* of the table frame's ComputedStyle,
216 // matching the ::-moz-table-wrapper pseudo-element. html.css has a
217 // rule that causes that pseudo-element (and thus the wrapper table)
218 // to inherit *some* style properties from the table frame. The
219 // children of the table inherit directly from the inner table, and
220 // the table wrapper's ComputedStyle is a leaf.
221
222 return (*aProviderFrame = InnerTableFrame())->Style();
223 }
224
GetContainingBlockSize(const ReflowInput & aOuterRI)225 static nsSize GetContainingBlockSize(const ReflowInput& aOuterRI) {
226 nsSize size(0, 0);
227 const ReflowInput* containRS = aOuterRI.mCBReflowInput;
228
229 if (containRS) {
230 size.width = containRS->ComputedWidth();
231 if (NS_UNCONSTRAINEDSIZE == size.width) {
232 size.width = 0;
233 }
234 size.height = containRS->ComputedHeight();
235 if (NS_UNCONSTRAINEDSIZE == size.height) {
236 size.height = 0;
237 }
238 }
239 return size;
240 }
241
242 /* virtual */
GetMinISize(gfxContext * aRenderingContext)243 nscoord nsTableWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
244 nscoord iSize = nsLayoutUtils::IntrinsicForContainer(
245 aRenderingContext, InnerTableFrame(), IntrinsicISizeType::MinISize);
246 DISPLAY_MIN_INLINE_SIZE(this, iSize);
247 if (mCaptionFrames.NotEmpty()) {
248 nscoord capISize = nsLayoutUtils::IntrinsicForContainer(
249 aRenderingContext, mCaptionFrames.FirstChild(),
250 IntrinsicISizeType::MinISize);
251 if (HasSideCaption()) {
252 iSize += capISize;
253 } else {
254 if (capISize > iSize) {
255 iSize = capISize;
256 }
257 }
258 }
259 return iSize;
260 }
261
262 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)263 nscoord nsTableWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
264 nscoord maxISize;
265 DISPLAY_PREF_INLINE_SIZE(this, maxISize);
266
267 maxISize = nsLayoutUtils::IntrinsicForContainer(
268 aRenderingContext, InnerTableFrame(), IntrinsicISizeType::PrefISize);
269 if (Maybe<StyleCaptionSide> captionSide = GetCaptionSide()) {
270 switch (*captionSide) {
271 case StyleCaptionSide::Left:
272 case StyleCaptionSide::Right: {
273 nscoord capMin = nsLayoutUtils::IntrinsicForContainer(
274 aRenderingContext, mCaptionFrames.FirstChild(),
275 IntrinsicISizeType::MinISize);
276 maxISize += capMin;
277 } break;
278 default: {
279 IntrinsicISizeType iwt;
280 if (*captionSide == StyleCaptionSide::Top ||
281 *captionSide == StyleCaptionSide::Bottom) {
282 // Don't let the caption's pref isize expand the table's pref
283 // isize.
284 iwt = IntrinsicISizeType::MinISize;
285 } else {
286 MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
287 *captionSide == StyleCaptionSide::BottomOutside,
288 "unexpected caption side");
289 iwt = IntrinsicISizeType::PrefISize;
290 }
291 nscoord capPref = nsLayoutUtils::IntrinsicForContainer(
292 aRenderingContext, mCaptionFrames.FirstChild(), iwt);
293 maxISize = std::max(maxISize, capPref);
294 } break;
295 }
296 }
297 return maxISize;
298 }
299
InnerTableShrinkWrapSize(gfxContext * aRenderingContext,nsTableFrame * aTableFrame,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags) const300 LogicalSize nsTableWrapperFrame::InnerTableShrinkWrapSize(
301 gfxContext* aRenderingContext, nsTableFrame* aTableFrame, WritingMode aWM,
302 const LogicalSize& aCBSize, nscoord aAvailableISize,
303 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) const {
304 MOZ_ASSERT(InnerTableFrame() == aTableFrame);
305
306 AutoMaybeDisableFontInflation an(aTableFrame);
307
308 Maybe<LogicalMargin> collapseBorder;
309 Maybe<LogicalMargin> collapsePadding;
310 aTableFrame->GetCollapsedBorderPadding(collapseBorder, collapsePadding);
311
312 SizeComputationInput input(aTableFrame, aRenderingContext, aWM,
313 aCBSize.ISize(aWM), collapseBorder,
314 collapsePadding);
315 LogicalSize marginSize(aWM); // Inner table doesn't have any margin
316 LogicalSize bpSize = input.ComputedLogicalBorderPadding(aWM).Size(aWM);
317
318 // Note that we pass an empty caption-area here (rather than the caption's
319 // actual size). This is fine because:
320 //
321 // 1) nsTableWrapperFrame::ComputeSize() only uses the size returned by this
322 // method (indirectly via calling nsTableWrapperFrame::ComputeAutoSize())
323 // if it get a aSizeOverrides arg containing any size overrides with
324 // mApplyOverridesVerbatim=true. The aSizeOverrides arg is passed to this
325 // method without any modifications.
326 //
327 // 2) With 1), that means the aSizeOverrides passing into this method should
328 // be applied to the inner table directly, so we don't need to subtract
329 // caption-area when preparing innerOverrides for
330 // nsTableFrame::ComputeSize().
331 const LogicalSize areaOccupiedByCaption(aWM);
332 StyleSizeOverrides innerOverrides = ComputeSizeOverridesForInnerTable(
333 aTableFrame, aSizeOverrides, bpSize, areaOccupiedByCaption);
334 auto size =
335 aTableFrame
336 ->ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize,
337 marginSize, bpSize, innerOverrides, aFlags)
338 .mLogicalSize;
339 size.ISize(aWM) += bpSize.ISize(aWM);
340 if (size.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
341 size.BSize(aWM) += bpSize.BSize(aWM);
342 }
343 return size;
344 }
345
CaptionShrinkWrapSize(gfxContext * aRenderingContext,nsIFrame * aCaptionFrame,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,ComputeSizeFlags aFlags) const346 LogicalSize nsTableWrapperFrame::CaptionShrinkWrapSize(
347 gfxContext* aRenderingContext, nsIFrame* aCaptionFrame, WritingMode aWM,
348 const LogicalSize& aCBSize, nscoord aAvailableISize,
349 ComputeSizeFlags aFlags) const {
350 MOZ_ASSERT(aCaptionFrame == mCaptionFrames.FirstChild());
351
352 AutoMaybeDisableFontInflation an(aCaptionFrame);
353
354 SizeComputationInput input(aCaptionFrame, aRenderingContext, aWM,
355 aCBSize.ISize(aWM));
356 LogicalSize marginSize = input.ComputedLogicalMargin(aWM).Size(aWM);
357 LogicalSize bpSize = input.ComputedLogicalBorderPadding(aWM).Size(aWM);
358
359 auto size = aCaptionFrame
360 ->ComputeSize(aRenderingContext, aWM, aCBSize,
361 aAvailableISize, marginSize, bpSize, {}, aFlags)
362 .mLogicalSize;
363 size.ISize(aWM) += (marginSize.ISize(aWM) + bpSize.ISize(aWM));
364 if (size.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
365 size.BSize(aWM) += (marginSize.BSize(aWM) + bpSize.BSize(aWM));
366 }
367 return size;
368 }
369
ReduceStyleSizeBy(const StyleSize & aStyleSize,const nscoord aAmountToReduce) const370 StyleSize nsTableWrapperFrame::ReduceStyleSizeBy(
371 const StyleSize& aStyleSize, const nscoord aAmountToReduce) const {
372 MOZ_ASSERT(aStyleSize.ConvertsToLength(), "Only handles 'Length' StyleSize!");
373 const nscoord size = std::max(0, aStyleSize.ToLength() - aAmountToReduce);
374 return StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(size));
375 }
376
ComputeSizeOverridesForInnerTable(const nsTableFrame * aTableFrame,const StyleSizeOverrides & aWrapperSizeOverrides,const LogicalSize & aBorderPadding,const LogicalSize & aAreaOccupiedByCaption) const377 StyleSizeOverrides nsTableWrapperFrame::ComputeSizeOverridesForInnerTable(
378 const nsTableFrame* aTableFrame,
379 const StyleSizeOverrides& aWrapperSizeOverrides,
380 const LogicalSize& aBorderPadding,
381 const LogicalSize& aAreaOccupiedByCaption) const {
382 if (aWrapperSizeOverrides.mApplyOverridesVerbatim ||
383 !aWrapperSizeOverrides.HasAnyLengthOverrides()) {
384 // We are asked to apply the size overrides directly to the inner table, or
385 // there's no 'Length' size overrides. No need to tweak the size overrides.
386 return aWrapperSizeOverrides;
387 }
388
389 const auto wm = aTableFrame->GetWritingMode();
390 LogicalSize areaOccupied = aAreaOccupiedByCaption;
391 if (aTableFrame->StylePosition()->mBoxSizing == StyleBoxSizing::Content) {
392 // If the inner table frame has 'box-sizing: content', enlarge the occupied
393 // area by adding border & padding because they should also be subtracted
394 // from the size overrides.
395 areaOccupied += aBorderPadding;
396 }
397
398 StyleSizeOverrides innerSizeOverrides;
399 const auto& wrapperISize = aWrapperSizeOverrides.mStyleISize;
400 if (wrapperISize) {
401 MOZ_ASSERT(!wrapperISize->HasPercent(),
402 "Table doesn't support size overrides containing percentages!");
403 innerSizeOverrides.mStyleISize.emplace(
404 wrapperISize->ConvertsToLength()
405 ? ReduceStyleSizeBy(*wrapperISize, areaOccupied.ISize(wm))
406 : *wrapperISize);
407 }
408
409 const auto& wrapperBSize = aWrapperSizeOverrides.mStyleBSize;
410 if (wrapperBSize) {
411 MOZ_ASSERT(!wrapperBSize->HasPercent(),
412 "Table doesn't support size overrides containing percentages!");
413 innerSizeOverrides.mStyleBSize.emplace(
414 wrapperBSize->ConvertsToLength()
415 ? ReduceStyleSizeBy(*wrapperBSize, areaOccupied.BSize(wm))
416 : *wrapperBSize);
417 }
418
419 return innerSizeOverrides;
420 }
421
422 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)423 nsIFrame::SizeComputationResult nsTableWrapperFrame::ComputeSize(
424 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
425 nscoord aAvailableISize, const LogicalSize& aMargin,
426 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
427 ComputeSizeFlags aFlags) {
428 auto result = nsContainerFrame::ComputeSize(
429 aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorderPadding,
430 aSizeOverrides, aFlags);
431
432 if (aSizeOverrides.mApplyOverridesVerbatim &&
433 aSizeOverrides.HasAnyOverrides()) {
434 // We are asked to apply the size overrides directly to the inner table, but
435 // we still want ourselves to remain 'auto'-sized and shrink-wrapping our
436 // children's sizes. (Table wrapper frames always have 'auto' inline-size
437 // and block-size, since we don't inherit those properties from inner table,
438 // and authors can't target them with styling.)
439 auto size =
440 ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize,
441 aMargin, aBorderPadding, aSizeOverrides, aFlags);
442 result.mLogicalSize = size;
443 }
444
445 return result;
446 }
447
448 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)449 LogicalSize nsTableWrapperFrame::ComputeAutoSize(
450 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
451 nscoord aAvailableISize, const LogicalSize& aMargin,
452 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
453 ComputeSizeFlags aFlags) {
454 nscoord kidAvailableISize = aAvailableISize - aMargin.ISize(aWM);
455 NS_ASSERTION(aBorderPadding.IsAllZero(),
456 "Table wrapper frames cannot have borders or paddings");
457
458 // When we're shrink-wrapping, our auto size needs to wrap around the
459 // actual size of the table, which (if it is specified as a percent)
460 // could be something that is not reflected in our GetMinISize and
461 // GetPrefISize. See bug 349457 for an example.
462
463 // Shrink-wrap aChildFrame by default, except if we're a stretched grid item.
464 ComputeSizeFlags flags(ComputeSizeFlag::ShrinkWrap);
465 if (MOZ_UNLIKELY(IsGridItem()) && !StyleMargin()->HasInlineAxisAuto(aWM)) {
466 const auto* parent = GetParent();
467 auto inlineAxisAlignment =
468 aWM.IsOrthogonalTo(parent->GetWritingMode())
469 ? StylePosition()->UsedAlignSelf(parent->Style())._0
470 : StylePosition()->UsedJustifySelf(parent->Style())._0;
471 if (inlineAxisAlignment == StyleAlignFlags::NORMAL ||
472 inlineAxisAlignment == StyleAlignFlags::STRETCH) {
473 flags -= ComputeSizeFlag::ShrinkWrap;
474 }
475 }
476
477 // Match the availableISize logic in Reflow.
478 Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
479
480 // The result.ISize() is unconditionally set to a meaningful value in the
481 // logic that follows, but the unconstrained BSize() value might be used for
482 // cases where either BSize is indeed unconstrained).
483 LogicalSize result(aWM, 0, NS_UNCONSTRAINEDSIZE);
484 if (!captionSide) {
485 result = InnerTableShrinkWrapSize(aRenderingContext, InnerTableFrame(), aWM,
486 aCBSize, kidAvailableISize,
487 aSizeOverrides, flags);
488 } else if (*captionSide == StyleCaptionSide::Left ||
489 *captionSide == StyleCaptionSide::Right) {
490 const LogicalSize captionSize =
491 CaptionShrinkWrapSize(aRenderingContext, mCaptionFrames.FirstChild(),
492 aWM, aCBSize, kidAvailableISize, flags);
493 const LogicalSize innerTableSize = InnerTableShrinkWrapSize(
494 aRenderingContext, InnerTableFrame(), aWM, aCBSize,
495 kidAvailableISize - captionSize.ISize(aWM), aSizeOverrides, flags);
496
497 result.ISize(aWM) = captionSize.ISize(aWM) + innerTableSize.ISize(aWM);
498 if (captionSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
499 innerTableSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
500 result.BSize(aWM) =
501 std::max(captionSize.BSize(aWM), innerTableSize.BSize(aWM));
502 }
503 } else if (*captionSide == StyleCaptionSide::Top ||
504 *captionSide == StyleCaptionSide::Bottom) {
505 const LogicalSize innerTableSize = InnerTableShrinkWrapSize(
506 aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize,
507 aSizeOverrides, flags);
508 const LogicalSize captionSize =
509 CaptionShrinkWrapSize(aRenderingContext, mCaptionFrames.FirstChild(),
510 aWM, aCBSize, innerTableSize.ISize(aWM), flags);
511 result.ISize(aWM) =
512 std::max(innerTableSize.ISize(aWM), captionSize.ISize(aWM));
513 if (innerTableSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
514 captionSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
515 result.BSize(aWM) = innerTableSize.BSize(aWM) + captionSize.BSize(aWM);
516 }
517 } else {
518 MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
519 *captionSide == StyleCaptionSide::BottomOutside,
520 "unexpected caption-side");
521 const LogicalSize innerTableSize = InnerTableShrinkWrapSize(
522 aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize,
523 aSizeOverrides, flags);
524 const LogicalSize captionSize =
525 CaptionShrinkWrapSize(aRenderingContext, mCaptionFrames.FirstChild(),
526 aWM, aCBSize, kidAvailableISize, flags);
527 result.ISize(aWM) =
528 std::max(innerTableSize.ISize(aWM), captionSize.ISize(aWM));
529 if (innerTableSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
530 captionSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
531 result.BSize(aWM) = innerTableSize.BSize(aWM) + captionSize.BSize(aWM);
532 }
533 }
534
535 return result;
536 }
537
GetCaptionSide() const538 Maybe<StyleCaptionSide> nsTableWrapperFrame::GetCaptionSide() const {
539 if (mCaptionFrames.IsEmpty()) {
540 return Nothing();
541 }
542 return Some(mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide);
543 }
544
GetCaptionVerticalAlign() const545 StyleVerticalAlignKeyword nsTableWrapperFrame::GetCaptionVerticalAlign() const {
546 const auto& va = mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign;
547 return va.IsKeyword() ? va.AsKeyword() : StyleVerticalAlignKeyword::Top;
548 }
549
ComputeFinalBSize(const MaybeCaptionSide & aCaptionSide,const LogicalSize & aInnerSize,const LogicalSize & aCaptionSize,const LogicalMargin & aCaptionMargin,const WritingMode aWM) const550 nscoord nsTableWrapperFrame::ComputeFinalBSize(
551 const MaybeCaptionSide& aCaptionSide, const LogicalSize& aInnerSize,
552 const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin,
553 const WritingMode aWM) const {
554 nscoord bSize = aInnerSize.BSize(aWM);
555
556 // compute the overall block-size
557 if (aCaptionSide) {
558 switch (*aCaptionSide) {
559 case StyleCaptionSide::Top:
560 case StyleCaptionSide::TopOutside:
561 case StyleCaptionSide::Bottom:
562 case StyleCaptionSide::BottomOutside:
563 bSize = aInnerSize.BSize(aWM) +
564 std::max(
565 0, aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM));
566 break;
567 case StyleCaptionSide::Left:
568 case StyleCaptionSide::Right:
569 bSize = std::max(aInnerSize.BSize(aWM),
570 aCaptionSize.BSize(aWM) + aCaptionMargin.BEnd(aWM));
571 break;
572 }
573 }
574
575 // negative sizes can upset overflow-area code
576 return std::max(bSize, 0);
577 }
578
GetCaptionOrigin(StyleCaptionSide aCaptionSide,const LogicalSize & aContainBlockSize,const LogicalSize & aInnerSize,const LogicalSize & aCaptionSize,LogicalMargin & aCaptionMargin,LogicalPoint & aOrigin,WritingMode aWM)579 nsresult nsTableWrapperFrame::GetCaptionOrigin(
580 StyleCaptionSide aCaptionSide, const LogicalSize& aContainBlockSize,
581 const LogicalSize& aInnerSize, const LogicalSize& aCaptionSize,
582 LogicalMargin& aCaptionMargin, LogicalPoint& aOrigin, WritingMode aWM) {
583 aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
584 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
585 (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
586 (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
587 (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
588 return NS_OK;
589 }
590 if (mCaptionFrames.IsEmpty()) {
591 return NS_OK;
592 }
593
594 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
595 NS_AUTOMARGIN != aCaptionMargin.BStart(aWM) &&
596 NS_AUTOMARGIN != aCaptionMargin.BEnd(aWM),
597 "The computed caption margin is auto?");
598
599 // inline-dir computation
600 switch (aCaptionSide) {
601 case StyleCaptionSide::Top:
602 case StyleCaptionSide::TopOutside:
603 case StyleCaptionSide::Bottom:
604 case StyleCaptionSide::BottomOutside:
605 aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
606 break;
607 case StyleCaptionSide::Left:
608 case StyleCaptionSide::Right:
609 aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
610 if (aWM.IsBidiLTR() == (aCaptionSide == StyleCaptionSide::Right)) {
611 aOrigin.I(aWM) += aInnerSize.ISize(aWM);
612 }
613 break;
614 }
615 // block-dir computation
616 switch (aCaptionSide) {
617 case StyleCaptionSide::Right:
618 case StyleCaptionSide::Left:
619 aOrigin.B(aWM) = 0;
620 switch (GetCaptionVerticalAlign()) {
621 case StyleVerticalAlignKeyword::Middle:
622 aOrigin.B(aWM) = std::max(
623 0, (aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)) / 2);
624 break;
625 case StyleVerticalAlignKeyword::Bottom:
626 aOrigin.B(aWM) =
627 std::max(0, aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM));
628 break;
629 default:
630 break;
631 }
632 break;
633 case StyleCaptionSide::BottomOutside:
634 case StyleCaptionSide::Bottom:
635 aOrigin.B(aWM) = aInnerSize.BSize(aWM) + aCaptionMargin.BStart(aWM);
636 break;
637 case StyleCaptionSide::TopOutside:
638 case StyleCaptionSide::Top:
639 aOrigin.B(aWM) = aCaptionMargin.BStart(aWM);
640 break;
641 }
642 return NS_OK;
643 }
644
GetInnerOrigin(const MaybeCaptionSide & aCaptionSide,const LogicalSize & aContainBlockSize,const LogicalSize & aCaptionSize,const LogicalMargin & aCaptionMargin,const LogicalSize & aInnerSize,LogicalPoint & aOrigin,WritingMode aWM)645 nsresult nsTableWrapperFrame::GetInnerOrigin(
646 const MaybeCaptionSide& aCaptionSide, const LogicalSize& aContainBlockSize,
647 const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin,
648 const LogicalSize& aInnerSize, LogicalPoint& aOrigin, WritingMode aWM) {
649 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
650 NS_AUTOMARGIN != aCaptionMargin.IEnd(aWM),
651 "The computed caption margin is auto?");
652
653 aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
654 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
655 (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
656 (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
657 (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
658 return NS_OK;
659 }
660
661 nscoord minCapISize = aCaptionSize.ISize(aWM) + aCaptionMargin.IStartEnd(aWM);
662
663 // inline-dir computation
664 if (aCaptionSide && IsSideCaption(*aCaptionSide)) {
665 aOrigin.I(aWM) =
666 (aWM.IsBidiLTR() == (*aCaptionSide == StyleCaptionSide::Left))
667 ? minCapISize
668 : 0;
669 }
670
671 // block-dir computation
672 if (aCaptionSide) {
673 switch (*aCaptionSide) {
674 case StyleCaptionSide::Bottom:
675 case StyleCaptionSide::BottomOutside:
676 // Leave at zero.
677 break;
678 case StyleCaptionSide::Left:
679 case StyleCaptionSide::Right:
680 switch (GetCaptionVerticalAlign()) {
681 case StyleVerticalAlignKeyword::Middle:
682 aOrigin.B(aWM) = std::max(
683 0, (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2);
684 break;
685 case StyleVerticalAlignKeyword::Bottom:
686 aOrigin.B(aWM) =
687 std::max(0, aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM));
688 break;
689 default:
690 // Leave at zero.
691 break;
692 }
693 break;
694 case StyleCaptionSide::TopOutside:
695 case StyleCaptionSide::Top:
696 aOrigin.B(aWM) =
697 aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM);
698 break;
699 }
700 }
701 return NS_OK;
702 }
703
GetAreaOccupiedByCaption(StyleCaptionSide aCaptionSide,const mozilla::LogicalSize & aCaptionMarginBoxSize) const704 LogicalSize nsTableWrapperFrame::GetAreaOccupiedByCaption(
705 StyleCaptionSide aCaptionSide,
706 const mozilla::LogicalSize& aCaptionMarginBoxSize) const {
707 const WritingMode wm = GetWritingMode();
708 LogicalSize area(wm);
709
710 switch (aCaptionSide) {
711 case StyleCaptionSide::Top:
712 case StyleCaptionSide::Bottom:
713 case StyleCaptionSide::TopOutside:
714 case StyleCaptionSide::BottomOutside:
715 area.BSize(wm) = aCaptionMarginBoxSize.BSize(wm);
716 break;
717 case StyleCaptionSide::Left:
718 case StyleCaptionSide::Right:
719 area.ISize(wm) = aCaptionMarginBoxSize.ISize(wm);
720 break;
721 }
722
723 return area;
724 }
725
CreateReflowInputForInnerTable(nsPresContext * aPresContext,nsTableFrame * aTableFrame,const ReflowInput & aOuterRI,Maybe<ReflowInput> & aChildRI,const nscoord aAvailISize,const Maybe<LogicalSize> & aAreaOccupiedByCaption) const726 void nsTableWrapperFrame::CreateReflowInputForInnerTable(
727 nsPresContext* aPresContext, nsTableFrame* aTableFrame,
728 const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI,
729 const nscoord aAvailISize,
730 const Maybe<LogicalSize>& aAreaOccupiedByCaption) const {
731 MOZ_ASSERT(InnerTableFrame() == aTableFrame);
732
733 const WritingMode wm = aTableFrame->GetWritingMode();
734 const LogicalSize areaOccupiedByCaption =
735 aAreaOccupiedByCaption ? *aAreaOccupiedByCaption : LogicalSize(wm);
736
737 // If we have a caption occupied our content-box area, reduce the available
738 // block-size by the amount.
739 nscoord availBSize = aOuterRI.AvailableBSize();
740 if (availBSize != NS_UNCONSTRAINEDSIZE) {
741 availBSize = std::max(0, availBSize - areaOccupiedByCaption.BSize(wm));
742 }
743 const LogicalSize availSize(wm, aAvailISize, availBSize);
744
745 // For inner table frames, the containing block is the same as for the outer
746 // table frame.
747 Maybe<LogicalSize> cbSize = Some(aOuterRI.mContainingBlockSize);
748
749 // However, if we are a grid item, the CB size needs to subtract our margins
750 // and the area occupied by the caption.
751 //
752 // Note that inner table computed margins are always zero, they're inherited
753 // by the table wrapper, so we need to get our margin from aOuterRI.
754 if (IsGridItem()) {
755 const LogicalMargin margin = aOuterRI.ComputedLogicalMargin(wm);
756 cbSize->ISize(wm) = std::max(0, cbSize->ISize(wm) - margin.IStartEnd(wm) -
757 areaOccupiedByCaption.ISize(wm));
758 if (cbSize->BSize(wm) != NS_UNCONSTRAINEDSIZE) {
759 cbSize->BSize(wm) = std::max(0, cbSize->BSize(wm) - margin.BStartEnd(wm) -
760 areaOccupiedByCaption.BSize(wm));
761 }
762 }
763
764 if (!aTableFrame->IsBorderCollapse() &&
765 !aOuterRI.mStyleSizeOverrides.HasAnyOverrides()) {
766 // We are not border-collapsed and not given any size overrides. It's
767 // sufficient to call the standard ReflowInput constructor.
768 aChildRI.emplace(aPresContext, aOuterRI, aTableFrame, availSize, cbSize);
769 return;
770 }
771
772 Maybe<LogicalMargin> borderPadding;
773 Maybe<LogicalMargin> padding;
774 {
775 // Compute inner table frame's border & padding because we may need to
776 // reduce the size for inner table's size overrides. We won't waste time if
777 // they are not used, because we can use them directly by passing them into
778 // ReflowInput::Init().
779 Maybe<LogicalMargin> collapseBorder;
780 Maybe<LogicalMargin> collapsePadding;
781 aTableFrame->GetCollapsedBorderPadding(collapseBorder, collapsePadding);
782 SizeComputationInput input(aTableFrame, aOuterRI.mRenderingContext, wm,
783 cbSize->ISize(wm), collapseBorder,
784 collapsePadding);
785 borderPadding.emplace(input.ComputedLogicalBorderPadding(wm));
786 padding.emplace(input.ComputedLogicalPadding(wm));
787 }
788
789 StyleSizeOverrides innerOverrides = ComputeSizeOverridesForInnerTable(
790 aTableFrame, aOuterRI.mStyleSizeOverrides, borderPadding->Size(wm),
791 areaOccupiedByCaption);
792
793 aChildRI.emplace(aPresContext, aOuterRI, aTableFrame, availSize, Nothing(),
794 ReflowInput::InitFlag::CallerWillInit, innerOverrides);
795 aChildRI->Init(aPresContext, cbSize, Some(*borderPadding - *padding),
796 padding);
797 }
798
CreateReflowInputForCaption(nsPresContext * aPresContext,nsIFrame * aCaptionFrame,const ReflowInput & aOuterRI,Maybe<ReflowInput> & aChildRI,const nscoord aAvailISize) const799 void nsTableWrapperFrame::CreateReflowInputForCaption(
800 nsPresContext* aPresContext, nsIFrame* aCaptionFrame,
801 const ReflowInput& aOuterRI, Maybe<ReflowInput>& aChildRI,
802 const nscoord aAvailISize) const {
803 MOZ_ASSERT(aCaptionFrame == mCaptionFrames.FirstChild());
804
805 const WritingMode wm = aCaptionFrame->GetWritingMode();
806
807 // Use unconstrained available block-size so that the caption is always
808 // fully-complete.
809 const LogicalSize availSize(wm, aAvailISize, NS_UNCONSTRAINEDSIZE);
810 aChildRI.emplace(aPresContext, aOuterRI, aCaptionFrame, availSize);
811
812 // See if we need to reset mIsTopOfPage flag.
813 if (aChildRI->mFlags.mIsTopOfPage) {
814 if (auto captionSide = GetCaptionSide()) {
815 if (*captionSide == StyleCaptionSide::Bottom ||
816 *captionSide == StyleCaptionSide::BottomOutside) {
817 aChildRI->mFlags.mIsTopOfPage = false;
818 }
819 }
820 }
821 }
822
ReflowChild(nsPresContext * aPresContext,nsIFrame * aChildFrame,const ReflowInput & aChildRI,ReflowOutput & aMetrics,nsReflowStatus & aStatus)823 void nsTableWrapperFrame::ReflowChild(nsPresContext* aPresContext,
824 nsIFrame* aChildFrame,
825 const ReflowInput& aChildRI,
826 ReflowOutput& aMetrics,
827 nsReflowStatus& aStatus) {
828 // Using zero as containerSize here because we want consistency between
829 // the GetLogicalPosition and ReflowChild calls, to avoid unnecessarily
830 // changing the frame's coordinates; but we don't yet know its final
831 // position anyway so the actual value is unimportant.
832 const nsSize zeroCSize;
833 WritingMode wm = aChildRI.GetWritingMode();
834
835 // Use the current position as a best guess for placement.
836 LogicalPoint childPt = aChildFrame->GetLogicalPosition(wm, zeroCSize);
837 ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame;
838
839 // We don't want to delete our next-in-flow's child if it's an inner table
840 // frame, because table wrapper frames always assume that their inner table
841 // frames don't go away. If a table wrapper frame is removed because it is
842 // a next-in-flow of an already complete table wrapper frame, then it will
843 // take care of removing it's inner table frame.
844 if (aChildFrame == InnerTableFrame()) {
845 flags |= ReflowChildFlags::NoDeleteNextInFlowChild;
846 }
847
848 nsContainerFrame::ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRI,
849 wm, childPt, zeroCSize, flags, aStatus);
850 }
851
UpdateOverflowAreas(ReflowOutput & aMet)852 void nsTableWrapperFrame::UpdateOverflowAreas(ReflowOutput& aMet) {
853 aMet.SetOverflowAreasToDesiredBounds();
854 ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
855 if (mCaptionFrames.NotEmpty()) {
856 ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
857 }
858 }
859
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aOuterRI,nsReflowStatus & aStatus)860 void nsTableWrapperFrame::Reflow(nsPresContext* aPresContext,
861 ReflowOutput& aDesiredSize,
862 const ReflowInput& aOuterRI,
863 nsReflowStatus& aStatus) {
864 MarkInReflow();
865 DO_GLOBAL_REFLOW_COUNT("nsTableWrapperFrame");
866 DISPLAY_REFLOW(aPresContext, this, aOuterRI, aDesiredSize, aStatus);
867 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
868
869 // Initialize out parameters
870 aDesiredSize.ClearSize();
871
872 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
873 // Set up our kids. They're already present, on an overflow list,
874 // or there are none so we'll create them now
875 MoveOverflowToChildList();
876 }
877
878 Maybe<ReflowInput> captionRI;
879 Maybe<ReflowInput> innerRI;
880
881 nsRect origCaptionRect;
882 nsRect origCaptionInkOverflow;
883 bool captionFirstReflow = false;
884 if (mCaptionFrames.NotEmpty()) {
885 origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
886 origCaptionInkOverflow = mCaptionFrames.FirstChild()->InkOverflowRect();
887 captionFirstReflow =
888 mCaptionFrames.FirstChild()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
889 }
890
891 // ComputeAutoSize has to match this logic.
892 WritingMode wm = aOuterRI.GetWritingMode();
893 Maybe<StyleCaptionSide> captionSide = GetCaptionSide();
894 WritingMode captionWM = wm; // will be changed below if necessary
895 const nscoord contentBoxISize = aOuterRI.ComputedSize(wm).ISize(wm);
896
897 MOZ_ASSERT(mCaptionFrames.NotEmpty() == captionSide.isSome());
898
899 if (!captionSide) {
900 // We don't have a caption.
901 CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
902 innerRI, contentBoxISize);
903 } else if (*captionSide == StyleCaptionSide::Left ||
904 *captionSide == StyleCaptionSide::Right) {
905 // ComputeAutoSize takes care of making side captions small. Compute
906 // the caption's size first, and tell the table to fit in what's left.
907 CreateReflowInputForCaption(aPresContext, mCaptionFrames.FirstChild(),
908 aOuterRI, captionRI, contentBoxISize);
909 captionWM = captionRI->GetWritingMode();
910 nscoord innerAvailISize =
911 contentBoxISize -
912 captionRI->ComputedSizeWithMarginBorderPadding(wm).ISize(wm);
913 CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
914 innerRI, innerAvailISize);
915 } else if (*captionSide == StyleCaptionSide::Top ||
916 *captionSide == StyleCaptionSide::Bottom) {
917 // Compute the table's size first, and then prevent the caption from
918 // being larger in the inline dir unless it has to be.
919 //
920 // Note that CSS 2.1 (but not 2.0) says:
921 // The width of the anonymous box is the border-edge width of the
922 // table box inside it
923 // We don't actually make our anonymous box that isize (if we did,
924 // it would break 'auto' margins), but this effectively does that.
925 CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
926 innerRI, contentBoxISize);
927 // It's good that CSS 2.1 says not to include margins, since we
928 // can't, since they already been converted so they exactly
929 // fill the available isize (ignoring the margin on one side if
930 // neither are auto). (We take advantage of that later when we call
931 // GetCaptionOrigin, though.)
932 nscoord innerBorderISize =
933 innerRI->ComputedSizeWithBorderPadding(wm).ISize(wm);
934 CreateReflowInputForCaption(aPresContext, mCaptionFrames.FirstChild(),
935 aOuterRI, captionRI, innerBorderISize);
936 captionWM = captionRI->GetWritingMode();
937 } else {
938 MOZ_ASSERT(*captionSide == StyleCaptionSide::TopOutside ||
939 *captionSide == StyleCaptionSide::BottomOutside,
940 "unexpected caption-side");
941 // Size the table and the caption independently.
942 captionWM = mCaptionFrames.FirstChild()->GetWritingMode();
943 CreateReflowInputForCaption(
944 aPresContext, mCaptionFrames.FirstChild(), aOuterRI, captionRI,
945 aOuterRI.ComputedSize(captionWM).ISize(captionWM));
946 CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
947 innerRI, contentBoxISize);
948 }
949
950 // First reflow the caption.
951 Maybe<ReflowOutput> captionMet;
952 LogicalSize captionSize(wm);
953 LogicalMargin captionMargin(wm);
954 Maybe<LogicalSize> areaOccupiedByCaption;
955 if (captionSide) {
956 captionMet.emplace(wm);
957 // We intentionally don't merge capStatus into aStatus, since we currently
958 // can't handle caption continuations, but we probably should.
959 nsReflowStatus capStatus;
960 ReflowChild(aPresContext, mCaptionFrames.FirstChild(), *captionRI,
961 *captionMet, capStatus);
962 captionSize = captionMet->Size(wm);
963 captionMargin = captionRI->ComputedLogicalMargin(wm);
964 areaOccupiedByCaption.emplace(GetAreaOccupiedByCaption(
965 *captionSide, captionSize + captionMargin.Size(wm)));
966
967 if (!areaOccupiedByCaption->IsAllZero()) {
968 // Reset the inner table's ReflowInput to reduce various sizes because of
969 // the area occupied by caption.
970 innerRI.reset();
971 CreateReflowInputForInnerTable(aPresContext, InnerTableFrame(), aOuterRI,
972 innerRI, contentBoxISize,
973 areaOccupiedByCaption);
974 }
975 }
976
977 // Then, now that we know how much to reduce the isize of the inner
978 // table to account for side captions, reflow the inner table.
979 ReflowOutput innerMet(innerRI->GetWritingMode());
980 ReflowChild(aPresContext, InnerTableFrame(), *innerRI, innerMet, aStatus);
981 LogicalSize innerSize(wm, innerMet.ISize(wm), innerMet.BSize(wm));
982
983 LogicalSize containSize(wm, GetContainingBlockSize(aOuterRI));
984
985 // Now that we've reflowed both we can place them.
986 // XXXldb Most of the input variables here are now uninitialized!
987
988 // XXX Need to recompute inner table's auto margins for the case of side
989 // captions. (Caption's are broken too, but that should be fixed earlier.)
990
991 // Compute the desiredSize so that we can use it as the containerSize
992 // for the FinishReflowChild calls below.
993 LogicalSize desiredSize(wm);
994
995 // We have zero border and padding, so content-box inline-size is our desired
996 // border-box inline-size.
997 desiredSize.ISize(wm) = contentBoxISize;
998 desiredSize.BSize(wm) =
999 ComputeFinalBSize(captionSide, innerSize, captionSize, captionMargin, wm);
1000
1001 aDesiredSize.SetSize(wm, desiredSize);
1002 nsSize containerSize = aDesiredSize.PhysicalSize();
1003 // XXX It's possible for this to be NS_UNCONSTRAINEDSIZE, which will result
1004 // in assertions from FinishReflowChild.
1005
1006 MOZ_ASSERT(mCaptionFrames.NotEmpty() == captionSide.isSome());
1007 if (mCaptionFrames.NotEmpty()) {
1008 LogicalPoint captionOrigin(wm);
1009 GetCaptionOrigin(*captionSide, containSize, innerSize, captionSize,
1010 captionMargin, captionOrigin, wm);
1011 FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, *captionMet,
1012 captionRI.ptr(), wm, captionOrigin, containerSize,
1013 ReflowChildFlags::Default);
1014 captionRI.reset();
1015 }
1016 // XXX If the bsize is constrained then we need to check whether
1017 // everything still fits...
1018
1019 LogicalPoint innerOrigin(wm);
1020 GetInnerOrigin(captionSide, containSize, captionSize, captionMargin,
1021 innerSize, innerOrigin, wm);
1022 FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRI.ptr(),
1023 wm, innerOrigin, containerSize, ReflowChildFlags::Default);
1024 innerRI.reset();
1025
1026 if (mCaptionFrames.NotEmpty()) {
1027 nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(),
1028 origCaptionRect, origCaptionInkOverflow,
1029 captionFirstReflow);
1030 }
1031
1032 UpdateOverflowAreas(aDesiredSize);
1033
1034 if (GetPrevInFlow()) {
1035 ReflowOverflowContainerChildren(aPresContext, aOuterRI,
1036 aDesiredSize.mOverflowAreas,
1037 ReflowChildFlags::Default, aStatus);
1038 }
1039
1040 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRI, aStatus);
1041
1042 // Return our desired rect
1043
1044 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRI, aDesiredSize);
1045 }
1046
1047 /* ----- global methods ----- */
1048
GetCellAt(uint32_t aRowIdx,uint32_t aColIdx) const1049 nsIContent* nsTableWrapperFrame::GetCellAt(uint32_t aRowIdx,
1050 uint32_t aColIdx) const {
1051 nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
1052 if (!cellMap) {
1053 return nullptr;
1054 }
1055
1056 nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
1057 if (!cell) {
1058 return nullptr;
1059 }
1060
1061 return cell->GetContent();
1062 }
1063
NS_NewTableWrapperFrame(PresShell * aPresShell,ComputedStyle * aStyle)1064 nsTableWrapperFrame* NS_NewTableWrapperFrame(PresShell* aPresShell,
1065 ComputedStyle* aStyle) {
1066 return new (aPresShell)
1067 nsTableWrapperFrame(aStyle, aPresShell->GetPresContext());
1068 }
1069
NS_IMPL_FRAMEARENA_HELPERS(nsTableWrapperFrame)1070 NS_IMPL_FRAMEARENA_HELPERS(nsTableWrapperFrame)
1071
1072 #ifdef DEBUG_FRAME_DUMP
1073 nsresult nsTableWrapperFrame::GetFrameName(nsAString& aResult) const {
1074 return MakeFrameName(u"TableWrapper"_ns, aResult);
1075 }
1076 #endif
1077