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