Bug 1637697 [wpt PR 23571] - De-flaky some pointerevents wpt tests - Part 1, a=testonly
[gecko.git] / layout / tables / nsTableWrapperFrame.cpp
blob5ed0200a1265864abcfd809289a17a1ace466486
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/. */
6 #include "nsTableWrapperFrame.h"
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>
25 using namespace mozilla;
26 using namespace mozilla::layout;
28 #define NO_SIDE 100
30 /* virtual */
31 nscoord nsTableWrapperFrame::GetLogicalBaseline(
32 WritingMode aWritingMode) const {
33 if (StyleDisplay()->IsContainLayout()) {
34 // We have no baseline. Fall back to the inherited impl which is
35 // appropriate for this situation.
36 return nsContainerFrame::GetLogicalBaseline(aWritingMode);
39 nsIFrame* kid = mFrames.FirstChild();
40 if (!kid) {
41 MOZ_ASSERT_UNREACHABLE("no inner table");
42 return nsContainerFrame::GetLogicalBaseline(aWritingMode);
45 return kid->GetLogicalBaseline(aWritingMode) +
46 kid->BStart(aWritingMode, mRect.Size());
49 nsTableWrapperFrame::nsTableWrapperFrame(ComputedStyle* aStyle,
50 nsPresContext* aPresContext,
51 ClassID aID)
52 : nsContainerFrame(aStyle, aPresContext, aID) {}
54 nsTableWrapperFrame::~nsTableWrapperFrame() = default;
56 NS_QUERYFRAME_HEAD(nsTableWrapperFrame)
57 NS_QUERYFRAME_ENTRY(nsTableWrapperFrame)
58 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
60 #ifdef ACCESSIBILITY
61 a11y::AccType nsTableWrapperFrame::AccessibleType() {
62 return a11y::eHTMLTableType;
64 #endif
66 void nsTableWrapperFrame::DestroyFrom(nsIFrame* aDestructRoot,
67 PostDestroyData& aPostDestroyData) {
68 DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
69 mCaptionFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
70 nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
73 const nsFrameList& nsTableWrapperFrame::GetChildList(
74 ChildListID aListID) const {
75 if (aListID == kCaptionList) {
76 return mCaptionFrames;
79 return nsContainerFrame::GetChildList(aListID);
82 void nsTableWrapperFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
83 nsContainerFrame::GetChildLists(aLists);
84 mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
87 void nsTableWrapperFrame::SetInitialChildList(ChildListID aListID,
88 nsFrameList& aChildList) {
89 if (kCaptionList == aListID) {
90 #ifdef DEBUG
91 nsFrame::VerifyDirtyBitSet(aChildList);
92 for (nsIFrame* f : aChildList) {
93 MOZ_ASSERT(f->GetParent() == this, "Unexpected parent");
95 #endif
96 // the frame constructor already checked for table-caption display type
97 MOZ_ASSERT(mCaptionFrames.IsEmpty(),
98 "already have child frames in CaptionList");
99 mCaptionFrames.SetFrames(aChildList);
100 } else {
101 MOZ_ASSERT(kPrincipalList != aListID ||
102 (aChildList.FirstChild() &&
103 aChildList.FirstChild() == aChildList.LastChild() &&
104 aChildList.FirstChild()->IsTableFrame()),
105 "expected a single table frame in principal child list");
106 nsContainerFrame::SetInitialChildList(aListID, aChildList);
110 void nsTableWrapperFrame::AppendFrames(ChildListID aListID,
111 nsFrameList& aFrameList) {
112 // We only have two child frames: the inner table and a caption frame.
113 // The inner frame is provided when we're initialized, and it cannot change
114 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
115 MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
116 "appending non-caption frame to captionList");
117 mCaptionFrames.AppendFrames(nullptr, aFrameList);
119 // Reflow the new caption frame. It's already marked dirty, so
120 // just tell the pres shell.
121 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
122 NS_FRAME_HAS_DIRTY_CHILDREN);
123 // The presence of caption frames makes us sort our display
124 // list differently, so mark us as changed for the new
125 // ordering.
126 MarkNeedsDisplayItemRebuild();
129 void nsTableWrapperFrame::InsertFrames(
130 ChildListID aListID, nsIFrame* aPrevFrame,
131 const nsLineList::iterator* aPrevFrameLine, nsFrameList& aFrameList) {
132 MOZ_ASSERT(kCaptionList == aListID, "unexpected child list");
133 MOZ_ASSERT(aFrameList.IsEmpty() || aFrameList.FirstChild()->IsTableCaption(),
134 "inserting non-caption frame into captionList");
135 MOZ_ASSERT(!aPrevFrame || aPrevFrame->GetParent() == this,
136 "inserting after sibling frame with different parent");
137 mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
139 // Reflow the new caption frame. It's already marked dirty, so
140 // just tell the pres shell.
141 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
142 NS_FRAME_HAS_DIRTY_CHILDREN);
143 MarkNeedsDisplayItemRebuild();
146 void nsTableWrapperFrame::RemoveFrame(ChildListID aListID,
147 nsIFrame* aOldFrame) {
148 // We only have two child frames: the inner table and one caption frame.
149 // The inner frame can't be removed so this should be the caption
150 MOZ_ASSERT(kCaptionList == aListID, "can't remove inner frame");
152 if (HasSideCaption()) {
153 // The old caption isize had an effect on the inner table isize, so
154 // we're going to need to reflow it. Mark it dirty
155 InnerTableFrame()->MarkSubtreeDirty();
158 // Remove the frame and destroy it
159 mCaptionFrames.DestroyFrame(aOldFrame);
161 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
162 NS_FRAME_HAS_DIRTY_CHILDREN);
163 MarkNeedsDisplayItemRebuild();
166 void nsTableWrapperFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
167 const nsDisplayListSet& aLists) {
168 // No border or background is painted because they belong to the inner table.
169 // The outline belongs to the wrapper frame so it can contain the caption.
171 // If there's no caption, take a short cut to avoid having to create
172 // the special display list set and then sort it.
173 if (mCaptionFrames.IsEmpty()) {
174 BuildDisplayListForInnerTable(aBuilder, aLists);
175 DisplayOutline(aBuilder, aLists);
176 return;
179 nsDisplayListCollection set(aBuilder);
180 BuildDisplayListForInnerTable(aBuilder, set);
182 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
183 BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(), captionSet);
185 // Now we have to sort everything by content order, since the caption
186 // may be somewhere inside the table.
187 // We don't sort BlockBorderBackgrounds and BorderBackgrounds because the
188 // display items in those lists should stay out of content order in order to
189 // follow the rules in https://www.w3.org/TR/CSS21/zindex.html#painting-order
190 // and paint the caption background after all of the rest.
191 set.Floats()->SortByContentOrder(GetContent());
192 set.Content()->SortByContentOrder(GetContent());
193 set.PositionedDescendants()->SortByContentOrder(GetContent());
194 set.Outlines()->SortByContentOrder(GetContent());
195 set.MoveTo(aLists);
197 DisplayOutline(aBuilder, aLists);
200 void nsTableWrapperFrame::BuildDisplayListForInnerTable(
201 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
202 // Just paint the regular children, but the children's background is our
203 // true background (there should only be one, the real table)
204 nsIFrame* kid = mFrames.FirstChild();
205 // The children should be in content order
206 while (kid) {
207 BuildDisplayListForChild(aBuilder, kid, aLists);
208 kid = kid->GetNextSibling();
212 ComputedStyle* nsTableWrapperFrame::GetParentComputedStyle(
213 nsIFrame** aProviderFrame) const {
214 // The table wrapper frame and the (inner) table frame split the style
215 // data by giving the table frame the ComputedStyle associated with
216 // the table content node and creating a ComputedStyle for the wrapper
217 // frame that is a *child* of the table frame's ComputedStyle,
218 // matching the ::-moz-table-wrapper pseudo-element. html.css has a
219 // rule that causes that pseudo-element (and thus the wrapper table)
220 // to inherit *some* style properties from the table frame. The
221 // children of the table inherit directly from the inner table, and
222 // the table wrapper's ComputedStyle is a leaf.
224 return (*aProviderFrame = InnerTableFrame())->Style();
227 // INCREMENTAL REFLOW HELPER FUNCTIONS
229 void nsTableWrapperFrame::InitChildReflowInput(nsPresContext& aPresContext,
230 const ReflowInput& aOuterRI,
231 ReflowInput& aReflowInput) {
232 nsMargin collapseBorder;
233 nsMargin collapsePadding(0, 0, 0, 0);
234 nsMargin* pCollapseBorder = nullptr;
235 nsMargin* pCollapsePadding = nullptr;
236 Maybe<LogicalSize> cbSize;
237 if (aReflowInput.mFrame == InnerTableFrame()) {
238 WritingMode wm = aReflowInput.GetWritingMode();
239 if (InnerTableFrame()->IsBorderCollapse()) {
240 LogicalMargin border = InnerTableFrame()->GetIncludedOuterBCBorder(wm);
241 collapseBorder = border.GetPhysicalMargin(wm);
242 pCollapseBorder = &collapseBorder;
243 pCollapsePadding = &collapsePadding;
245 // Propagate our stored CB size if present, minus any margins.
247 // Note that inner table computed margins are always zero, they're inherited
248 // by the table wrapper, so we need to get our margin from aOuterRI.
249 if (!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
250 if (LogicalSize* cb = GetProperty(GridItemCBSizeProperty())) {
251 cbSize.emplace(*cb);
252 *cbSize -= aOuterRI.ComputedLogicalMargin().Size(wm);
255 if (!cbSize) {
256 // For inner table frames, the containing block is the same as for
257 // the outer table frame.
258 cbSize.emplace(aOuterRI.mContainingBlockSize);
261 aReflowInput.Init(&aPresContext, cbSize, pCollapseBorder, pCollapsePadding);
264 // get the margin and padding data. ReflowInput doesn't handle the
265 // case of auto margins
266 void nsTableWrapperFrame::GetChildMargin(nsPresContext* aPresContext,
267 const ReflowInput& aOuterRI,
268 nsIFrame* aChildFrame,
269 nscoord aAvailISize,
270 LogicalMargin& aMargin) {
271 NS_ASSERTION(!aChildFrame->IsTableCaption(),
272 "didn't expect caption frame; writing-mode may be wrong!");
274 // construct a reflow input to compute margin and padding. Auto margins
275 // will not be computed at this time.
277 // create and init the child reflow input
278 // XXX We really shouldn't construct a reflow input to do this.
279 WritingMode wm = aOuterRI.GetWritingMode();
280 LogicalSize availSize(wm, aAvailISize, aOuterRI.AvailableSize(wm).BSize(wm));
281 ReflowInput childRI(aPresContext, aOuterRI, aChildFrame, availSize, Nothing(),
282 ReflowInput::CALLER_WILL_INIT);
283 InitChildReflowInput(*aPresContext, aOuterRI, childRI);
285 aMargin = childRI.ComputedLogicalMargin();
288 static nsSize GetContainingBlockSize(const ReflowInput& aOuterRI) {
289 nsSize size(0, 0);
290 const ReflowInput* containRS = aOuterRI.mCBReflowInput;
292 if (containRS) {
293 size.width = containRS->ComputedWidth();
294 if (NS_UNCONSTRAINEDSIZE == size.width) {
295 size.width = 0;
297 size.height = containRS->ComputedHeight();
298 if (NS_UNCONSTRAINEDSIZE == size.height) {
299 size.height = 0;
302 return size;
305 /* virtual */
306 nscoord nsTableWrapperFrame::GetMinISize(gfxContext* aRenderingContext) {
307 nscoord iSize = nsLayoutUtils::IntrinsicForContainer(
308 aRenderingContext, InnerTableFrame(), nsLayoutUtils::MIN_ISIZE);
309 DISPLAY_MIN_INLINE_SIZE(this, iSize);
310 if (mCaptionFrames.NotEmpty()) {
311 nscoord capISize = nsLayoutUtils::IntrinsicForContainer(
312 aRenderingContext, mCaptionFrames.FirstChild(),
313 nsLayoutUtils::MIN_ISIZE);
314 if (HasSideCaption()) {
315 iSize += capISize;
316 } else {
317 if (capISize > iSize) {
318 iSize = capISize;
322 return iSize;
325 /* virtual */
326 nscoord nsTableWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
327 nscoord maxISize;
328 DISPLAY_PREF_INLINE_SIZE(this, maxISize);
330 maxISize = nsLayoutUtils::IntrinsicForContainer(
331 aRenderingContext, InnerTableFrame(), nsLayoutUtils::PREF_ISIZE);
332 if (mCaptionFrames.NotEmpty()) {
333 uint8_t captionSide = GetCaptionSide();
334 switch (captionSide) {
335 case NS_STYLE_CAPTION_SIDE_LEFT:
336 case NS_STYLE_CAPTION_SIDE_RIGHT: {
337 nscoord capMin = nsLayoutUtils::IntrinsicForContainer(
338 aRenderingContext, mCaptionFrames.FirstChild(),
339 nsLayoutUtils::MIN_ISIZE);
340 maxISize += capMin;
341 } break;
342 default: {
343 nsLayoutUtils::IntrinsicISizeType iwt;
344 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
345 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
346 // Don't let the caption's pref isize expand the table's pref
347 // isize.
348 iwt = nsLayoutUtils::MIN_ISIZE;
349 } else {
350 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
351 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
352 "unexpected caption side");
353 iwt = nsLayoutUtils::PREF_ISIZE;
355 nscoord capPref = nsLayoutUtils::IntrinsicForContainer(
356 aRenderingContext, mCaptionFrames.FirstChild(), iwt);
357 maxISize = std::max(maxISize, capPref);
358 } break;
361 return maxISize;
364 nscoord nsTableWrapperFrame::ChildShrinkWrapISize(
365 gfxContext* aRenderingContext, nsIFrame* aChildFrame, WritingMode aWM,
366 LogicalSize aCBSize, nscoord aAvailableISize,
367 nscoord* aMarginResult) const {
368 AutoMaybeDisableFontInflation an(aChildFrame);
370 // For the caption frame, child's WM may differ from the table's main WM.
371 WritingMode childWM = aChildFrame->GetWritingMode();
373 SizeComputationInput offsets(aChildFrame, aRenderingContext, aWM,
374 aCBSize.ISize(aWM));
375 LogicalSize marginSize =
376 offsets.ComputedLogicalMargin().Size(childWM).ConvertTo(aWM, childWM);
377 LogicalSize paddingSize =
378 offsets.ComputedLogicalPadding().Size(childWM).ConvertTo(aWM, childWM);
379 LogicalSize bpSize =
380 offsets.ComputedLogicalBorderPadding().Size(childWM).ConvertTo(aWM,
381 childWM);
383 // Shrink-wrap aChildFrame by default, except if we're a stretched grid item.
384 auto flags = ComputeSizeFlags::eShrinkWrap;
385 auto parent = GetParent();
386 bool isGridItem = parent && parent->IsGridContainerFrame() &&
387 !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
388 if (MOZ_UNLIKELY(isGridItem) && !StyleMargin()->HasInlineAxisAuto(aWM)) {
389 auto inlineAxisAlignment =
390 aWM.IsOrthogonalTo(parent->GetWritingMode())
391 ? StylePosition()->UsedAlignSelf(parent->Style())._0
392 : StylePosition()->UsedJustifySelf(parent->Style())._0;
393 if (inlineAxisAlignment == StyleAlignFlags::NORMAL ||
394 inlineAxisAlignment == StyleAlignFlags::STRETCH) {
395 flags = nsIFrame::ComputeSizeFlags::eDefault;
399 LogicalSize size = aChildFrame->ComputeSize(
400 aRenderingContext, aWM, aCBSize, aAvailableISize, marginSize,
401 bpSize - paddingSize, paddingSize, flags);
402 if (aMarginResult) {
403 *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(aWM);
405 return size.ISize(aWM) + marginSize.ISize(aWM) + bpSize.ISize(aWM);
408 /* virtual */
409 LogicalSize nsTableWrapperFrame::ComputeAutoSize(
410 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
411 nscoord aAvailableISize, const LogicalSize& aMargin,
412 const LogicalSize& aBorder, const LogicalSize& aPadding,
413 ComputeSizeFlags aFlags) {
414 nscoord kidAvailableISize = aAvailableISize - aMargin.ISize(aWM);
415 NS_ASSERTION(aBorder.IsAllZero() && aPadding.IsAllZero(),
416 "Table wrapper frames cannot have borders or paddings");
418 // When we're shrink-wrapping, our auto size needs to wrap around the
419 // actual size of the table, which (if it is specified as a percent)
420 // could be something that is not reflected in our GetMinISize and
421 // GetPrefISize. See bug 349457 for an example.
423 // Match the availableISize logic in Reflow.
424 uint8_t captionSide = GetCaptionSide();
425 nscoord inlineSize;
426 if (captionSide == NO_SIDE) {
427 inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM,
428 aCBSize, kidAvailableISize);
429 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
430 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
431 nscoord capISize =
432 ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
433 aWM, aCBSize, kidAvailableISize);
434 inlineSize = capISize +
435 ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM,
436 aCBSize, kidAvailableISize - capISize);
437 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
438 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
439 nscoord margin;
440 inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM,
441 aCBSize, kidAvailableISize, &margin);
442 nscoord capISize =
443 ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
444 aWM, aCBSize, inlineSize - margin);
445 if (capISize > inlineSize) {
446 inlineSize = capISize;
448 } else {
449 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
450 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
451 "unexpected caption-side");
452 inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM,
453 aCBSize, kidAvailableISize);
454 nscoord capISize =
455 ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(),
456 aWM, aCBSize, kidAvailableISize);
457 if (capISize > inlineSize) {
458 inlineSize = capISize;
462 return LogicalSize(aWM, inlineSize, NS_UNCONSTRAINEDSIZE);
465 uint8_t nsTableWrapperFrame::GetCaptionSide() {
466 if (mCaptionFrames.NotEmpty()) {
467 return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
468 } else {
469 return NO_SIDE; // no caption
473 StyleVerticalAlignKeyword nsTableWrapperFrame::GetCaptionVerticalAlign() const {
474 const auto& va = mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign;
475 return va.IsKeyword() ? va.AsKeyword() : StyleVerticalAlignKeyword::Top;
478 void nsTableWrapperFrame::SetDesiredSize(uint8_t aCaptionSide,
479 const LogicalSize& aInnerSize,
480 const LogicalSize& aCaptionSize,
481 const LogicalMargin& aInnerMargin,
482 const LogicalMargin& aCaptionMargin,
483 nscoord& aISize, nscoord& aBSize,
484 WritingMode aWM) {
485 aISize = aBSize = 0;
487 // compute the overall inline-size
488 switch (aCaptionSide) {
489 case NS_STYLE_CAPTION_SIDE_LEFT:
490 aISize =
491 std::max(aInnerMargin.LineLeft(aWM),
492 aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) +
493 aInnerSize.ISize(aWM) + aInnerMargin.LineRight(aWM);
494 break;
495 case NS_STYLE_CAPTION_SIDE_RIGHT:
496 aISize =
497 std::max(aInnerMargin.LineRight(aWM),
498 aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) +
499 aInnerSize.ISize(aWM) + aInnerMargin.LineLeft(aWM);
500 break;
501 default:
502 aISize =
503 std::max(aInnerMargin.IStartEnd(aWM) + aInnerSize.ISize(aWM),
504 aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM));
505 break;
508 // compute the overall block-size
509 switch (aCaptionSide) {
510 case NS_STYLE_CAPTION_SIDE_TOP:
511 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
512 aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM);
513 aBSize +=
514 std::max(aInnerMargin.BStart(aWM),
515 aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM));
516 break;
517 case NS_STYLE_CAPTION_SIDE_BOTTOM:
518 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
519 aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BStart(aWM);
520 aBSize +=
521 std::max(aInnerMargin.BEnd(aWM),
522 aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM));
523 break;
524 case NS_STYLE_CAPTION_SIDE_LEFT:
525 case NS_STYLE_CAPTION_SIDE_RIGHT:
526 aBSize = aInnerMargin.BStart(aWM);
527 aBSize += std::max(aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM),
528 aCaptionSize.BSize(aWM) + aCaptionMargin.BEnd(aWM));
529 break;
530 default:
531 NS_ASSERTION(aCaptionSide == NO_SIDE, "unexpected caption side");
532 aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BStartEnd(aWM);
533 break;
536 // negative sizes can upset overflow-area code
537 aISize = std::max(aISize, 0);
538 aBSize = std::max(aBSize, 0);
541 nsresult nsTableWrapperFrame::GetCaptionOrigin(
542 uint32_t aCaptionSide, const LogicalSize& aContainBlockSize,
543 const LogicalSize& aInnerSize, const LogicalMargin& aInnerMargin,
544 const LogicalSize& aCaptionSize, LogicalMargin& aCaptionMargin,
545 LogicalPoint& aOrigin, WritingMode aWM) {
546 aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
547 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
548 (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
549 (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
550 (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
551 return NS_OK;
553 if (mCaptionFrames.IsEmpty()) {
554 return NS_OK;
557 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
558 NS_AUTOMARGIN != aCaptionMargin.BStart(aWM) &&
559 NS_AUTOMARGIN != aCaptionMargin.BEnd(aWM),
560 "The computed caption margin is auto?");
562 // inline-dir computation
563 switch (aCaptionSide) {
564 case NS_STYLE_CAPTION_SIDE_BOTTOM:
565 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
566 aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
567 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
568 // We placed the caption using only the table's isize as available
569 // isize, and we should position it this way as well.
570 aOrigin.I(aWM) += aInnerMargin.IStart(aWM);
572 break;
573 case NS_STYLE_CAPTION_SIDE_LEFT:
574 case NS_STYLE_CAPTION_SIDE_RIGHT:
575 aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
576 if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT)) {
577 aOrigin.I(aWM) += aInnerMargin.IStart(aWM) + aInnerSize.ISize(aWM);
579 break;
580 default: // block-start
581 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
582 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
583 "unexpected caption side");
584 aOrigin.I(aWM) = aCaptionMargin.IStart(aWM);
585 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
586 // We placed the caption using only the table's isize as available
587 // isize, and we should position it this way as well.
588 aOrigin.I(aWM) += aInnerMargin.IStart(aWM);
590 break;
592 // block-dir computation
593 switch (aCaptionSide) {
594 case NS_STYLE_CAPTION_SIDE_RIGHT:
595 case NS_STYLE_CAPTION_SIDE_LEFT:
596 aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
597 switch (GetCaptionVerticalAlign()) {
598 case StyleVerticalAlignKeyword::Middle:
599 aOrigin.B(aWM) = std::max(
600 0, aInnerMargin.BStart(aWM) +
601 ((aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)) / 2));
602 break;
603 case StyleVerticalAlignKeyword::Bottom:
604 aOrigin.B(aWM) =
605 std::max(0, aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) -
606 aCaptionSize.BSize(aWM));
607 break;
608 default:
609 break;
611 break;
612 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
613 case NS_STYLE_CAPTION_SIDE_BOTTOM:
614 aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) +
615 aCaptionMargin.BStart(aWM);
616 break;
617 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
618 case NS_STYLE_CAPTION_SIDE_TOP:
619 aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionMargin.BStart(aWM);
620 break;
621 default:
622 MOZ_ASSERT_UNREACHABLE("Unknown caption alignment type");
623 break;
625 return NS_OK;
628 nsresult nsTableWrapperFrame::GetInnerOrigin(
629 uint32_t aCaptionSide, const LogicalSize& aContainBlockSize,
630 const LogicalSize& aCaptionSize, const LogicalMargin& aCaptionMargin,
631 const LogicalSize& aInnerSize, LogicalMargin& aInnerMargin,
632 LogicalPoint& aOrigin, WritingMode aWM) {
633 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.IStart(aWM) &&
634 NS_AUTOMARGIN != aCaptionMargin.IEnd(aWM),
635 "The computed caption margin is auto?");
636 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.IStart(aWM) &&
637 NS_AUTOMARGIN != aInnerMargin.IEnd(aWM) &&
638 NS_AUTOMARGIN != aInnerMargin.BStart(aWM) &&
639 NS_AUTOMARGIN != aInnerMargin.BEnd(aWM),
640 "The computed inner margin is auto?");
642 aOrigin.I(aWM) = aOrigin.B(aWM) = 0;
643 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.ISize(aWM)) ||
644 (NS_UNCONSTRAINEDSIZE == aInnerSize.BSize(aWM)) ||
645 (NS_UNCONSTRAINEDSIZE == aCaptionSize.ISize(aWM)) ||
646 (NS_UNCONSTRAINEDSIZE == aCaptionSize.BSize(aWM))) {
647 return NS_OK;
650 nscoord minCapISize = aCaptionSize.ISize(aWM) + aCaptionMargin.IStartEnd(aWM);
652 // inline-dir computation
653 switch (aCaptionSide) {
654 case NS_STYLE_CAPTION_SIDE_LEFT:
655 case NS_STYLE_CAPTION_SIDE_RIGHT:
656 if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_LEFT)) {
657 if (aInnerMargin.IStart(aWM) < minCapISize) {
658 // shift the inner table to get some place for the caption
659 aInnerMargin.IEnd(aWM) += aInnerMargin.IStart(aWM) - minCapISize;
660 aInnerMargin.IEnd(aWM) = std::max(0, aInnerMargin.IEnd(aWM));
661 aInnerMargin.IStart(aWM) = minCapISize;
664 aOrigin.I(aWM) = aInnerMargin.IStart(aWM);
665 break;
666 default:
667 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
668 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
669 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
670 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
671 aCaptionSide == NO_SIDE,
672 "unexpected caption side");
673 aOrigin.I(aWM) = aInnerMargin.IStart(aWM);
674 break;
677 // block-dir computation
678 switch (aCaptionSide) {
679 case NS_STYLE_CAPTION_SIDE_BOTTOM:
680 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
681 aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
682 break;
683 case NS_STYLE_CAPTION_SIDE_LEFT:
684 case NS_STYLE_CAPTION_SIDE_RIGHT:
685 aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
686 switch (GetCaptionVerticalAlign()) {
687 case StyleVerticalAlignKeyword::Middle:
688 aOrigin.B(aWM) =
689 std::max(aInnerMargin.BStart(aWM),
690 (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2);
691 break;
692 case StyleVerticalAlignKeyword::Bottom:
693 aOrigin.B(aWM) =
694 std::max(aInnerMargin.BStart(aWM),
695 aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM));
696 break;
697 default:
698 break;
700 break;
701 case NO_SIDE:
702 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
703 case NS_STYLE_CAPTION_SIDE_TOP:
704 aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionSize.BSize(aWM) +
705 aCaptionMargin.BStartEnd(aWM);
706 break;
707 default:
708 MOZ_ASSERT_UNREACHABLE("Unknown caption alignment type");
709 break;
711 return NS_OK;
714 void nsTableWrapperFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
715 nsIFrame* aChildFrame,
716 const ReflowInput& aOuterRI,
717 Maybe<ReflowInput>& aChildRI,
718 nscoord aAvailISize) {
719 // work around pixel rounding errors, round down to ensure we don't exceed the
720 // avail height in
721 WritingMode wm = aChildFrame->GetWritingMode();
722 LogicalSize outerSize = aOuterRI.AvailableSize(wm);
723 nscoord availBSize = outerSize.BSize(wm);
724 if (NS_UNCONSTRAINEDSIZE != availBSize) {
725 if (mCaptionFrames.FirstChild() == aChildFrame) {
726 availBSize = NS_UNCONSTRAINEDSIZE;
727 } else {
728 LogicalMargin margin(wm);
729 GetChildMargin(aPresContext, aOuterRI, aChildFrame, outerSize.ISize(wm),
730 margin);
732 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BStart(wm),
733 "No unconstrainedsize arithmetic, please");
734 availBSize -= margin.BStart(wm);
736 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.BEnd(wm),
737 "No unconstrainedsize arithmetic, please");
738 availBSize -= margin.BEnd(wm);
741 LogicalSize availSize(wm, aAvailISize, availBSize);
742 // create and init the child reflow input, using passed-in Maybe<>,
743 // so that caller can use it after we return.
744 aChildRI.emplace(aPresContext, aOuterRI, aChildFrame, availSize, Nothing(),
745 ReflowInput::CALLER_WILL_INIT);
746 InitChildReflowInput(*aPresContext, aOuterRI, *aChildRI);
748 // see if we need to reset top-of-page due to a caption
749 if (aChildRI->mFlags.mIsTopOfPage &&
750 mCaptionFrames.FirstChild() == aChildFrame) {
751 uint8_t captionSide = GetCaptionSide();
752 if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
753 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
754 aChildRI->mFlags.mIsTopOfPage = false;
759 void nsTableWrapperFrame::OuterDoReflowChild(nsPresContext* aPresContext,
760 nsIFrame* aChildFrame,
761 const ReflowInput& aChildRI,
762 ReflowOutput& aMetrics,
763 nsReflowStatus& aStatus) {
764 // Using zero as containerSize here because we want consistency between
765 // the GetLogicalPosition and ReflowChild calls, to avoid unnecessarily
766 // changing the frame's coordinates; but we don't yet know its final
767 // position anyway so the actual value is unimportant.
768 const nsSize zeroCSize;
769 WritingMode wm = aChildRI.GetWritingMode();
771 // Use the current position as a best guess for placement.
772 LogicalPoint childPt = aChildFrame->GetLogicalPosition(wm, zeroCSize);
773 ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame;
775 // We don't want to delete our next-in-flow's child if it's an inner table
776 // frame, because table wrapper frames always assume that their inner table
777 // frames don't go away. If a table wrapper frame is removed because it is
778 // a next-in-flow of an already complete table wrapper frame, then it will
779 // take care of removing it's inner table frame.
780 if (aChildFrame == InnerTableFrame()) {
781 flags |= ReflowChildFlags::NoDeleteNextInFlowChild;
784 ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRI, wm, childPt,
785 zeroCSize, flags, aStatus);
788 void nsTableWrapperFrame::UpdateOverflowAreas(ReflowOutput& aMet) {
789 aMet.SetOverflowAreasToDesiredBounds();
790 ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
791 if (mCaptionFrames.NotEmpty()) {
792 ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
796 void nsTableWrapperFrame::Reflow(nsPresContext* aPresContext,
797 ReflowOutput& aDesiredSize,
798 const ReflowInput& aOuterRI,
799 nsReflowStatus& aStatus) {
800 MarkInReflow();
801 DO_GLOBAL_REFLOW_COUNT("nsTableWrapperFrame");
802 DISPLAY_REFLOW(aPresContext, this, aOuterRI, aDesiredSize, aStatus);
803 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
805 // Initialize out parameters
806 aDesiredSize.ClearSize();
808 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
809 // Set up our kids. They're already present, on an overflow list,
810 // or there are none so we'll create them now
811 MoveOverflowToChildList();
814 Maybe<ReflowInput> captionRI;
815 Maybe<ReflowInput> innerRI;
817 nsRect origCaptionRect;
818 nsRect origCaptionVisualOverflow;
819 bool captionFirstReflow = false;
820 if (mCaptionFrames.NotEmpty()) {
821 origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
822 origCaptionVisualOverflow =
823 mCaptionFrames.FirstChild()->GetVisualOverflowRect();
824 captionFirstReflow =
825 mCaptionFrames.FirstChild()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
828 // ComputeAutoSize has to match this logic.
829 WritingMode wm = aOuterRI.GetWritingMode();
830 uint8_t captionSide = GetCaptionSide();
831 WritingMode captionWM = wm; // will be changed below if necessary
833 if (captionSide == NO_SIDE) {
834 // We don't have a caption.
835 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI,
836 aOuterRI.ComputedSize(wm).ISize(wm));
837 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
838 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
839 // ComputeAutoSize takes care of making side captions small. Compute
840 // the caption's size first, and tell the table to fit in what's left.
841 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI,
842 captionRI, aOuterRI.ComputedSize(wm).ISize(wm));
843 captionWM = captionRI->GetWritingMode();
844 nscoord innerAvailISize =
845 aOuterRI.ComputedSize(wm).ISize(wm) -
846 captionRI->ComputedSizeWithMarginBorderPadding(wm).ISize(wm);
847 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI,
848 innerAvailISize);
849 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
850 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
851 // Compute the table's size first, and then prevent the caption from
852 // being larger in the inline dir unless it has to be.
854 // Note that CSS 2.1 (but not 2.0) says:
855 // The width of the anonymous box is the border-edge width of the
856 // table box inside it
857 // We don't actually make our anonymous box that isize (if we did,
858 // it would break 'auto' margins), but this effectively does that.
859 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI,
860 aOuterRI.ComputedSize(wm).ISize(wm));
861 // It's good that CSS 2.1 says not to include margins, since we
862 // can't, since they already been converted so they exactly
863 // fill the available isize (ignoring the margin on one side if
864 // neither are auto). (We take advantage of that later when we call
865 // GetCaptionOrigin, though.)
866 nscoord innerBorderISize =
867 innerRI->ComputedSizeWithBorderPadding(wm).ISize(wm);
868 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI,
869 captionRI, innerBorderISize);
870 captionWM = captionRI->GetWritingMode();
871 } else {
872 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
873 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
874 "unexpected caption-side");
875 // Size the table and the caption independently.
876 captionWM = mCaptionFrames.FirstChild()->GetWritingMode();
877 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRI,
878 captionRI,
879 aOuterRI.ComputedSize(captionWM).ISize(captionWM));
880 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI, innerRI,
881 aOuterRI.ComputedSize(wm).ISize(wm));
884 // First reflow the caption.
885 Maybe<ReflowOutput> captionMet;
886 LogicalSize captionSize(wm);
887 LogicalMargin captionMargin(wm);
888 if (mCaptionFrames.NotEmpty()) {
889 captionMet.emplace(wm);
890 nsReflowStatus capStatus; // don't let the caption cause incomplete
891 OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(), *captionRI,
892 *captionMet, capStatus);
893 captionSize.ISize(wm) = captionMet->ISize(wm);
894 captionSize.BSize(wm) = captionMet->BSize(wm);
895 captionMargin = captionRI->ComputedLogicalMargin().ConvertTo(wm, captionWM);
896 // Now that we know the bsize of the caption, reduce the available bsize
897 // for the table frame if we are bsize constrained and the caption is above
898 // or below the inner table. Also reduce the CB size that we store for
899 // our children in case we're a grid item, by the same amount.
900 LogicalSize* cbSize = GetProperty(GridItemCBSizeProperty());
901 if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize() || cbSize) {
902 nscoord captionBSize = 0;
903 nscoord captionISize = 0;
904 switch (captionSide) {
905 case NS_STYLE_CAPTION_SIDE_TOP:
906 case NS_STYLE_CAPTION_SIDE_BOTTOM:
907 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
908 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
909 captionBSize = captionSize.BSize(wm) + captionMargin.BStartEnd(wm);
910 break;
911 case NS_STYLE_CAPTION_SIDE_LEFT:
912 case NS_STYLE_CAPTION_SIDE_RIGHT:
913 captionISize = captionSize.ISize(wm) + captionMargin.IStartEnd(wm);
914 break;
916 if (NS_UNCONSTRAINEDSIZE != aOuterRI.AvailableBSize()) {
917 innerRI->AvailableBSize() =
918 std::max(0, innerRI->AvailableBSize() - captionBSize);
920 if (cbSize) {
921 // Shrink the CB size by the size reserved for the caption.
922 LogicalSize oldCBSize = *cbSize;
923 cbSize->ISize(wm) = std::max(0, cbSize->ISize(wm) - captionISize);
924 cbSize->BSize(wm) = std::max(0, cbSize->BSize(wm) - captionBSize);
925 if (oldCBSize != *cbSize) {
926 // Reset the inner table's ReflowInput to stretch it to the new size.
927 innerRI.reset();
928 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRI,
929 innerRI, aOuterRI.ComputedSize(wm).ISize(wm));
935 // Then, now that we know how much to reduce the isize of the inner
936 // table to account for side captions, reflow the inner table.
937 ReflowOutput innerMet(innerRI->GetWritingMode());
938 OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRI, innerMet,
939 aStatus);
940 LogicalSize innerSize(wm, innerMet.ISize(wm), innerMet.BSize(wm));
941 LogicalMargin innerMargin = innerRI->ComputedLogicalMargin();
943 LogicalSize containSize(wm, GetContainingBlockSize(aOuterRI));
945 // Now that we've reflowed both we can place them.
946 // XXXldb Most of the input variables here are now uninitialized!
948 // XXX Need to recompute inner table's auto margins for the case of side
949 // captions. (Caption's are broken too, but that should be fixed earlier.)
951 // Compute the desiredSize so that we can use it as the containerSize
952 // for the FinishReflowChild calls below.
953 LogicalSize desiredSize(wm);
954 SetDesiredSize(captionSide, innerSize, captionSize, innerMargin,
955 captionMargin, desiredSize.ISize(wm), desiredSize.BSize(wm),
956 wm);
957 aDesiredSize.SetSize(wm, desiredSize);
958 nsSize containerSize = aDesiredSize.PhysicalSize();
959 // XXX It's possible for this to be NS_UNCONSTRAINEDSIZE, which will result
960 // in assertions from FinishReflowChild.
962 if (mCaptionFrames.NotEmpty()) {
963 LogicalPoint captionOrigin(wm);
964 GetCaptionOrigin(captionSide, containSize, innerSize, innerMargin,
965 captionSize, captionMargin, captionOrigin, wm);
966 FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, *captionMet,
967 captionRI.ptr(), wm, captionOrigin, containerSize,
968 ReflowChildFlags::Default);
969 captionRI.reset();
971 // XXX If the bsize is constrained then we need to check whether
972 // everything still fits...
974 LogicalPoint innerOrigin(wm);
975 GetInnerOrigin(captionSide, containSize, captionSize, captionMargin,
976 innerSize, innerMargin, innerOrigin, wm);
977 FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRI.ptr(),
978 wm, innerOrigin, containerSize, ReflowChildFlags::Default);
979 innerRI.reset();
981 if (mCaptionFrames.NotEmpty()) {
982 nsTableFrame::InvalidateTableFrame(
983 mCaptionFrames.FirstChild(), origCaptionRect, origCaptionVisualOverflow,
984 captionFirstReflow);
987 UpdateOverflowAreas(aDesiredSize);
989 if (GetPrevInFlow()) {
990 ReflowOverflowContainerChildren(aPresContext, aOuterRI,
991 aDesiredSize.mOverflowAreas,
992 ReflowChildFlags::Default, aStatus);
995 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRI, aStatus);
997 // Return our desired rect
999 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRI, aDesiredSize);
1002 /* ----- global methods ----- */
1004 nsIContent* nsTableWrapperFrame::GetCellAt(uint32_t aRowIdx,
1005 uint32_t aColIdx) const {
1006 nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
1007 if (!cellMap) {
1008 return nullptr;
1011 nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
1012 if (!cell) {
1013 return nullptr;
1016 return cell->GetContent();
1019 nsTableWrapperFrame* NS_NewTableWrapperFrame(PresShell* aPresShell,
1020 ComputedStyle* aStyle) {
1021 return new (aPresShell)
1022 nsTableWrapperFrame(aStyle, aPresShell->GetPresContext());
1025 NS_IMPL_FRAMEARENA_HELPERS(nsTableWrapperFrame)
1027 #ifdef DEBUG_FRAME_DUMP
1028 nsresult nsTableWrapperFrame::GetFrameName(nsAString& aResult) const {
1029 return MakeFrameName(NS_LITERAL_STRING("TableWrapper"), aResult);
1031 #endif