Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at */
#ifndef nsTableFrame_h__
#define nsTableFrame_h__
#include "mozilla/Attributes.h"
#include "celldata.h"
#include "nscore.h"
#include "nsContainerFrame.h"
#include "nsStyleConsts.h"
#include "nsCellMap.h"
#include "nsGkAtoms.h"
#include "nsDisplayList.h"
#include "TableArea.h"
struct BCPaintBorderAction;
struct BCPropertyData;
class nsTableCellFrame;
class nsTableCellMap;
class nsTableColFrame;
class nsTableRowGroupFrame;
class nsTableRowFrame;
class nsTableColGroupFrame;
class nsITableLayoutStrategy;
namespace mozilla {
class LogicalMargin;
class PresShell;
class WritingMode;
struct TableReflowInput;
namespace layers {
class StackingContextHelper;
class nsDisplayTableItem : public nsPaintedDisplayItem {
nsDisplayTableItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: nsPaintedDisplayItem(aBuilder, aFrame) {}
// With collapsed borders, parts of the collapsed border can extend outside
// the table part frames, so allow this display element to blow out to our
// overflow rect. This is also useful for row frames that have spanning
// cells extending outside them.
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) const override;
class nsDisplayTableBackgroundSet {
nsDisplayList* ColGroupBackgrounds() { return &mColGroupBackgrounds; }
nsDisplayList* ColBackgrounds() { return &mColBackgrounds; }
nsDisplayTableBackgroundSet(nsDisplayListBuilder* aBuilder, nsIFrame* aTable);
~nsDisplayTableBackgroundSet() {
mozilla::DebugOnly<nsDisplayTableBackgroundSet*> result =
MOZ_ASSERT(result == this);
* Move all display items in our lists to top of the corresponding lists in
* the destination.
void MoveTo(const nsDisplayListSet& aDestination) {
void AddColumn(nsTableColFrame* aFrame) { mColumns.AppendElement(aFrame); }
nsTableColFrame* GetColForIndex(int32_t aIndex) { return mColumns[aIndex]; }
const nsPoint& TableToReferenceFrame() { return mToReferenceFrame; }
const nsRect& GetDirtyRect() { return mDirtyRect; }
const DisplayItemClipChain* GetTableClipChain() {
return mCombinedTableClipChain;
const ActiveScrolledRoot* GetTableASR() { return mTableASR; }
// This class is only used on stack, so we don't have to worry about leaking
// it. Don't let us be heap-allocated!
void* operator new(size_t sz) noexcept(true);
nsDisplayListBuilder* mBuilder;
nsDisplayTableBackgroundSet* mPrevTableBackgroundSet;
nsDisplayList mColGroupBackgrounds;
nsDisplayList mColBackgrounds;
nsTArray<nsTableColFrame*> mColumns;
nsPoint mToReferenceFrame;
nsRect mDirtyRect;
const DisplayItemClipChain* mCombinedTableClipChain;
const ActiveScrolledRoot* mTableASR;
} // namespace mozilla
/* ========================================================================== */
enum nsTableColType {
eColContent = 0, // there is real col content associated
eColAnonymousCol = 1, // the result of a span on a col
eColAnonymousColGroup = 2, // the result of a span on a col group
eColAnonymousCell = 3 // the result of a cell alone
* nsTableFrame maps the inner portion of a table (everything except captions.)
* Used as a pseudo-frame within nsTableWrapperFrame, it may also be used
* stand-alone as the top-level frame.
* The principal child list contains row group frames. There is also an
* additional child list, kColGroupList, which contains the col group frames.
class nsTableFrame : public nsContainerFrame {
typedef mozilla::image::ImgDrawResult ImgDrawResult;
typedef mozilla::WritingMode WritingMode;
typedef mozilla::LogicalMargin LogicalMargin;
typedef mozilla::TableReflowInput TableReflowInput;
typedef nsTArray<nsIFrame*> FrameTArray;
/** nsTableWrapperFrame has intimate knowledge of the inner table frame */
friend class nsTableWrapperFrame;
* instantiate a new instance of nsTableRowFrame.
* @param aPresShell the pres shell for this frame
* @return the frame that was created
friend nsTableFrame* NS_NewTableFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle);
/** sets defaults for table-specific style.
* @see nsIFrame::Init
virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) override;
// Return true if aParentReflowInput.frame or any of its ancestors within
// the containing table have non-auto bsize. (e.g. pct or fixed bsize)
static bool AncestorsHaveStyleBSize(const ReflowInput& aParentReflowInput);
// See if a special bsize reflow will occur due to having a pct bsize when
// the pct bsize basis may not yet be valid.
static void CheckRequestSpecialBSizeReflow(const ReflowInput& aReflowInput);
// Notify the frame and its ancestors (up to the containing table) that a
// special height reflow will occur.
static void RequestSpecialBSizeReflow(const ReflowInput& aReflowInput);
static void RePositionViews(nsIFrame* aFrame);
static bool PageBreakAfter(nsIFrame* aSourceFrame, nsIFrame* aNextFrame);
// Register a positioned table part with its nsTableFrame. These objects will
// be visited by FixupPositionedTableParts after reflow is complete. (See that
// function for more explanation.) Should be called during frame construction.
static void RegisterPositionedTablePart(nsIFrame* aFrame);
// Unregister a positioned table part with its nsTableFrame.
static void UnregisterPositionedTablePart(nsIFrame* aFrame,
nsIFrame* aDestructRoot);
* Notification that rowspan or colspan has changed for content inside a
* table cell
void RowOrColSpanChanged(nsTableCellFrame* aCellFrame);
/** @see nsIFrame::DestroyFrom */
virtual void DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) override;
/** @see nsIFrame::DidSetComputedStyle */
virtual void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override;
virtual void SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) override;
virtual void AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) override;
virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
const nsLineList::iterator* aPrevFrameLine,
nsFrameList& aFrameList) override;
virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
virtual nsMargin GetUsedBorder() const override;
virtual nsMargin GetUsedPadding() const override;
virtual nsMargin GetUsedMargin() const override;
// Get the offset from the border box to the area where the row groups fit
LogicalMargin GetChildAreaOffset(const WritingMode aWM,
const ReflowInput* aReflowInput) const;
/** helper method to find the table parent of any table frame object */
static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame);
/* Like GetTableFrame, but will set *aDidPassThrough to false if we don't
* pass through aMustPassThrough on the way to the table.
static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
nsIFrame* aSourceFrame,
bool* aDidPassThrough);
// Return the closest sibling of aPriorChildFrame (including aPriroChildFrame)
// of type aChildType.
static nsIFrame* GetFrameAtOrBefore(nsIFrame* aParentFrame,
nsIFrame* aPriorChildFrame,
mozilla::LayoutFrameType aChildType);
bool IsAutoBSize(mozilla::WritingMode aWM);
/** @return true if aDisplayType represents a rowgroup of any sort
* (header, footer, or body)
bool IsRowGroup(mozilla::StyleDisplay aDisplayType) const;
virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override;
/** Get the outer half (i.e., the part outside the height and width of
* the table) of the largest segment (?) of border-collapsed border on
* the table on each side, or 0 for non border-collapsed tables.
LogicalMargin GetOuterBCBorder(const WritingMode aWM) const;
/** Same as above, but only if it's included from the border-box width
* of the table.
LogicalMargin GetIncludedOuterBCBorder(const WritingMode aWM) const;
/** Same as above, but only if it's excluded from the border-box width
* of the table. This is the area that leaks out into the margin
* (or potentially past it, if there is no margin).
LogicalMargin GetExcludedOuterBCBorder(const WritingMode aWM) const;
* Emplace our border and padding in aBorder and aPadding if we are
* border-collapsed. Otherwise, do nothing.
void GetCollapsedBorderPadding(
mozilla::Maybe<mozilla::LogicalMargin>& aBorder,
mozilla::Maybe<mozilla::LogicalMargin>& aPadding) const;
* In quirks mode, the size of the table background is reduced
* by the outer BC border. Compute the reduction needed.
nsMargin GetDeflationForBackground(nsPresContext* aPresContext) const;
/** Get width of table + colgroup + col collapse: elements that
* continue along the length of the whole iStart side.
* see nsTablePainter about continuous borders
nscoord GetContinuousIStartBCBorderWidth() const;
void SetContinuousIStartBCBorderWidth(nscoord aValue);
friend class nsDelayedCalcBCBorders;
void AddBCDamageArea(const mozilla::TableArea& aValue);
bool BCRecalcNeeded(ComputedStyle* aOldComputedStyle,
ComputedStyle* aNewComputedStyle);
void PaintBCBorders(DrawTarget& aDrawTarget, const nsRect& aDirtyRect);
void CreateWebRenderCommandsForBCBorders(
mozilla::wr::DisplayListBuilder& aBuilder,
const mozilla::layers::StackingContextHelper& aSc,
const nsRect& aVisibleRect, const nsPoint& aOffsetToReferenceFrame);
virtual void MarkIntrinsicISizesDirty() override;
// For border-collapse tables, the caller must not add padding and
// border to the results of these functions.
virtual nscoord GetMinISize(gfxContext* aRenderingContext) override;
virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override;
IntrinsicSizeOffsetData IntrinsicISizeOffsets(
nscoord aPercentageBasis = NS_UNCONSTRAINEDSIZE) override;
SizeComputationResult ComputeSize(
gfxContext* aRenderingContext, mozilla::WritingMode aWM,
const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
const mozilla::LogicalSize& aMargin,
const mozilla::LogicalSize& aBorderPadding,
const mozilla::StyleSizeOverrides& aSizeOverrides,
mozilla::ComputeSizeFlags aFlags) override;
mozilla::LogicalSize ComputeAutoSize(
gfxContext* aRenderingContext, mozilla::WritingMode aWM,
const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
const mozilla::LogicalSize& aMargin,
const mozilla::LogicalSize& aBorderPadding,
const mozilla::StyleSizeOverrides& aSizeOverrides,
mozilla::ComputeSizeFlags aFlags) override;
* A copy of nsIFrame::ShrinkWidthToFit that calls a different
* GetPrefISize, since tables have two different ones.
nscoord TableShrinkISizeToFit(gfxContext* aRenderingContext,
nscoord aWidthInCB);
// clang-format off
* Inner tables are reflowed in two steps.
* <pre>
* if mFirstPassValid is false, this is our first time through since content was last changed
* set pass to 1
* do pass 1
* get min/max info for all cells in an infinite space
* do column balancing
* set mFirstPassValid to true
* do pass 2
* use column widths to Reflow cells
* </pre>
* @see nsIFrame::Reflow
// clang-format on
virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
void ReflowTable(ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput,
nscoord aAvailBSize, nsIFrame*& aLastChildReflowed,
nsReflowStatus& aStatus);
nsFrameList& GetColGroups();
virtual ComputedStyle* GetParentComputedStyle(
nsIFrame** aProviderFrame) const override;
virtual bool IsFrameOfType(uint32_t aFlags) const override {
if (aFlags & eSupportsCSSTransforms) {
return false;
return nsContainerFrame::IsFrameOfType(aFlags);
/** @see nsIFrame::GetFrameName */
virtual nsresult GetFrameName(nsAString& aResult) const override;
/** Return the isize of the column at aColIndex.
* This may only be called on the table's first-in-flow.
nscoord GetColumnISizeFromFirstInFlow(int32_t aColIndex);
/** Helper to get the column spacing style value.
* The argument refers to the space between column aColIndex and column
* aColIndex + 1. An index of -1 indicates the padding between the table
* and the left border, an index equal to the number of columns indicates
* the padding between the table and the right border.
* Although in this class cell spacing does not depend on the index, it
* may be important for overriding classes.
virtual nscoord GetColSpacing(int32_t aColIndex);
/** Helper to find the sum of the cell spacing between arbitrary columns.
* The argument refers to the space between column aColIndex and column
* aColIndex + 1. An index of -1 indicates the padding between the table
* and the left border, an index equal to the number of columns indicates
* the padding between the table and the right border.
* This method is equivalent to
* nscoord result = 0;
* for (i = aStartColIndex; i < aEndColIndex; i++) {
* result += GetColSpacing(i);
* }
* return result;
virtual nscoord GetColSpacing(int32_t aStartColIndex, int32_t aEndColIndex);
/** Helper to get the row spacing style value.
* The argument refers to the space between row aRowIndex and row
* aRowIndex + 1. An index of -1 indicates the padding between the table
* and the top border, an index equal to the number of rows indicates
* the padding between the table and the bottom border.
* Although in this class cell spacing does not depend on the index, it
* may be important for overriding classes.
virtual nscoord GetRowSpacing(int32_t aRowIndex);
/** Helper to find the sum of the cell spacing between arbitrary rows.
* The argument refers to the space between row aRowIndex and row
* aRowIndex + 1. An index of -1 indicates the padding between the table
* and the top border, an index equal to the number of rows indicates
* the padding between the table and the bottom border.
* This method is equivalent to
* nscoord result = 0;
* for (i = aStartRowIndex; i < aEndRowIndex; i++) {
* result += GetRowSpacing(i);
* }
* return result;
virtual nscoord GetRowSpacing(int32_t aStartRowIndex, int32_t aEndRowIndex);
/* For the base implementation of nsTableFrame, cell spacing does not depend
* on row/column indexing.
nscoord GetColSpacing();
nscoord GetRowSpacing();
virtual nscoord GetLogicalBaseline(
mozilla::WritingMode aWritingMode) const override;
bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup,
nscoord* aBaseline) const override;
/** return the row span of a cell, taking into account row span magic at the
* bottom of a table. The row span equals the number of rows spanned by aCell
* starting at aStartRowIndex, and can be smaller if aStartRowIndex is greater
* than the row index in which aCell originates.
* @param aStartRowIndex the cell
* @param aCell the cell
* @return the row span, correcting for row spans that extend beyond the
* bottom of the table.
int32_t GetEffectiveRowSpan(int32_t aStartRowIndex,
const nsTableCellFrame& aCell) const;
int32_t GetEffectiveRowSpan(const nsTableCellFrame& aCell,
nsCellMap* aCellMap = nullptr);
/** return the col span of a cell, taking into account col span magic at the
* edge of a table.
* @param aCell the cell
* @return the col span, correcting for col spans that extend beyond the edge
* of the table.
int32_t GetEffectiveColSpan(const nsTableCellFrame& aCell,
nsCellMap* aCellMap = nullptr) const;
/** indicate whether the row has more than one cell that either originates
* or is spanned from the rows above
bool HasMoreThanOneCell(int32_t aRowIndex) const;
/** return the column frame associated with aColIndex
* returns nullptr if the col frame has not yet been allocated, or if
* aColIndex is out of range
nsTableColFrame* GetColFrame(int32_t aColIndex) const;
/** Insert a col frame reference into the colframe cache and adapt the cellmap
* @param aColFrame - the column frame
* @param aColIndex - index where the column should be inserted into the
* colframe cache
void InsertCol(nsTableColFrame& aColFrame, int32_t aColIndex);
nsTableColGroupFrame* CreateSyntheticColGroupFrame();
int32_t DestroyAnonymousColFrames(int32_t aNumFrames);
// Append aNumColsToAdd anonymous col frames of type eColAnonymousCell to our
// last synthetic colgroup. If we have no such colgroup, then create one.
void AppendAnonymousColFrames(int32_t aNumColsToAdd);
// Append aNumColsToAdd anonymous col frames of type aColType to
// aColGroupFrame. If aAddToTable is true, also call AddColsToTable on the
// new cols.
void AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
int32_t aNumColsToAdd, nsTableColType aColType,
bool aAddToTable);
void MatchCellMapToColCache(nsTableCellMap* aCellMap);
/** empty the column frame cache */
void ClearColCache();
void DidResizeColumns();
void AppendCell(nsTableCellFrame& aCellFrame, int32_t aRowIndex);
void InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, int32_t aRowIndex,
int32_t aColIndexBefore);
void RemoveCell(nsTableCellFrame* aCellFrame, int32_t aRowIndex);
void AppendRows(nsTableRowGroupFrame* aRowGroupFrame, int32_t aRowIndex,
nsTArray<nsTableRowFrame*>& aRowFrames);
int32_t InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
nsTArray<nsTableRowFrame*>& aFrames, int32_t aRowIndex,
bool aConsiderSpans);
void RemoveRows(nsTableRowFrame& aFirstRowFrame, int32_t aNumRowsToRemove,
bool aConsiderSpans);
/** Insert multiple rowgroups into the table cellmap handling
* @param aRowGroups - iterator that iterates over the rowgroups to insert
void InsertRowGroups(const nsFrameList::Slice& aRowGroups);
void InsertColGroups(int32_t aStartColIndex,
const nsFrameList::Slice& aColgroups);
void RemoveCol(nsTableColGroupFrame* aColGroupFrame, int32_t aColIndex,
bool aRemoveFromCache, bool aRemoveFromCellMap);
bool ColumnHasCellSpacingBefore(int32_t aColIndex) const;
bool HasPctCol() const;
void SetHasPctCol(bool aValue);
bool HasCellSpanningPctCol() const;
void SetHasCellSpanningPctCol(bool aValue);
* To be called on a frame by its parent after setting its size/position and
* calling DidReflow (possibly via FinishReflowChild()). This can also be
* used for child frames which are not being reflowed but did have their size
* or position changed.
* @param aFrame The frame to invalidate
* @param aOrigRect The original rect of aFrame (before the change).
* @param aOrigInkOverflow The original overflow rect of aFrame.
* @param aIsFirstReflow True if the size/position change is due to the
* first reflow of aFrame.
static void InvalidateTableFrame(nsIFrame* aFrame, const nsRect& aOrigRect,
const nsRect& aOrigInkOverflow,
bool aIsFirstReflow);
bool ComputeCustomOverflow(mozilla::OverflowAreas& aOverflowAreas) override;
// Return our wrapper frame.
void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
static void UpdateStyleOfOwnedAnonBoxesForTableWrapper(
nsIFrame* aOwningFrame, nsIFrame* aWrapperFrame,
mozilla::ServoRestyleState& aRestyleState);
/** protected constructor.
* @see NewFrame
explicit nsTableFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
ClassID aID = kClassID);
virtual ~nsTableFrame();
void InitChildReflowInput(ReflowInput& aReflowInput);
LogicalSides GetLogicalSkipSides() const override;
void IterateBCBorders(BCPaintBorderAction& aAction, const nsRect& aDirtyRect);
bool IsRowInserted() const;
void SetRowInserted(bool aValue);
// A helper function to reflow a header or footer with unconstrained height
// to see if it should be made repeatable and also to determine its desired
// height.
nsresult SetupHeaderFooterChild(const TableReflowInput& aReflowInput,
nsTableRowGroupFrame* aFrame,
nscoord* aDesiredHeight);
void ReflowChildren(TableReflowInput& aReflowInput, nsReflowStatus& aStatus,
nsIFrame*& aLastChildReflowed,
mozilla::OverflowAreas& aOverflowAreas);
// This calls the col group and column reflow methods, which do two things:
// (1) set all the dimensions to 0
// (2) notify the table about colgroups or columns with hidden visibility
void ReflowColGroups(gfxContext* aRenderingContext);
/** return the isize of the table taking into account visibility collapse
* on columns and colgroups
* @param aBorderPadding the border and padding of the table
nscoord GetCollapsedISize(const WritingMode aWM,
const LogicalMargin& aBorderPadding);
/** Adjust the table for visibility.collapse set on rowgroups, rows,
* colgroups and cols
* @param aDesiredSize the metrics of the table
* @param aBorderPadding the border and padding of the table
void AdjustForCollapsingRowsCols(ReflowOutput& aDesiredSize,
const WritingMode aWM,
const LogicalMargin& aBorderPadding);
/** FixupPositionedTableParts is called at the end of table reflow to reflow
* the absolutely positioned descendants of positioned table parts. This is
* necessary because the dimensions of table parts may change after they've
* been reflowed (e.g. in AdjustForCollapsingRowsCols).
void FixupPositionedTableParts(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput);
// Clears the list of positioned table parts.
void ClearAllPositionedTableParts();
nsITableLayoutStrategy* LayoutStrategy() const {
return static_cast<nsTableFrame*>(FirstInFlow())
// Helper for InsertFrames.
void HomogenousInsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
nsFrameList& aFrameList);
/* Handle a row that got inserted during reflow. aNewHeight is the
new height of the table after reflow. */
void ProcessRowInserted(nscoord aNewHeight);
// calculate the computed block-size of aFrame including its border and
// padding given its reflow input.
nscoord CalcBorderBoxBSize(const ReflowInput& aReflowInput,
const LogicalMargin& aBorderPadding,
nscoord aIntrinsicBorderBoxBSize);
// update the desired block-size of this table taking into account the
// current reflow input, the table attributes and the content driven rowgroup
// bsizes this function can change the overflow area
void CalcDesiredBSize(const ReflowInput& aReflowInput,
ReflowOutput& aDesiredSize);
// The following is a helper for CalcDesiredBSize
void DistributeBSizeToRows(const ReflowInput& aReflowInput, nscoord aAmount);
void PlaceChild(TableReflowInput& aReflowInput, nsIFrame* aKidFrame,
const ReflowInput& aKidReflowInput,
const mozilla::LogicalPoint& aKidPosition,
const nsSize& aContainerSize, ReflowOutput& aKidDesiredSize,
const nsRect& aOriginalKidRect,
const nsRect& aOriginalKidInkOverflow);
void PlaceRepeatedFooter(TableReflowInput& aReflowInput,
nsTableRowGroupFrame* aTfoot, nscoord aFooterHeight);
nsIFrame* GetFirstBodyRowGroupFrame();
typedef AutoTArray<nsTableRowGroupFrame*, 8> RowGroupArray;
* Push all our child frames from the aRowGroups array, in order, starting
* from the frame at aPushFrom to the end of the array. The frames are put on
* our overflow list or moved directly to our next-in-flow if one exists.
void PushChildren(const RowGroupArray& aRowGroups, int32_t aPushFrom);
// put the children frames in the display order (e.g. thead before tbodies
// before tfoot). This will handle calling GetRowGroupFrame() on the
// children, and not append nulls, so the array is guaranteed to contain
// nsTableRowGroupFrames. If there are multiple theads or tfoots, all but
// the first one are treated as tbodies instead.
void OrderRowGroups(RowGroupArray& aChildren,
nsTableRowGroupFrame** aHead = nullptr,
nsTableRowGroupFrame** aFoot = nullptr) const;
// Returns true if there are any cells above the row at
// aRowIndex and spanning into the row at aRowIndex, the number of
// effective columns limits the search up to that column
bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols);
// Returns true if there is a cell originating in aRowIndex
// which spans into the next row, the number of effective
// columns limits the search up to that column
bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols);
bool HaveReflowedColGroups() const;
void SetHaveReflowedColGroups(bool aValue);
bool IsBorderCollapse() const;
bool NeedToCalcBCBorders() const;
void SetNeedToCalcBCBorders(bool aValue);
bool NeedToCollapse() const;
void SetNeedToCollapse(bool aValue);
bool NeedToCalcHasBCBorders() const;
void SetNeedToCalcHasBCBorders(bool aValue);
void CalcHasBCBorders();
bool HasBCBorders();
void SetHasBCBorders(bool aValue);
/** The GeometryDirty bit is similar to the NS_FRAME_IS_DIRTY frame
* state bit, which implies that all descendants are dirty. The
* GeometryDirty still implies that all the parts of the table are
* dirty, but resizing optimizations should still apply to the
* contents of the individual cells.
void SetGeometryDirty() { mBits.mGeometryDirty = true; }
void ClearGeometryDirty() { mBits.mGeometryDirty = false; }
bool IsGeometryDirty() const { return mBits.mGeometryDirty; }
/** Get the cell map for this table frame. It is not always mCellMap.
* Only the firstInFlow has a legit cell map
nsTableCellMap* GetCellMap() const;
/** Iterate over the row groups and adjust the row indices of all rows
* whose index is >= aRowIndex.
* @param aRowIndex - start adjusting with this index
* @param aAdjustment - shift the row index by this amount
void AdjustRowIndices(int32_t aRowIndex, int32_t aAdjustment);
/** Reset the rowindices of all rows as they might have changed due to
* rowgroup reordering, exclude new row group frames that show in the
* reordering but are not yet inserted into the cellmap
* @param aRowGroupsToExclude - an iterator that will produce the row groups
* to exclude.
void ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude);
nsTArray<nsTableColFrame*>& GetColCache();
void SetBorderCollapse(bool aValue);
BCPropertyData* GetBCProperty() const;
BCPropertyData* GetOrCreateBCProperty();
void SetFullBCDamageArea();
void CalcBCBorders();
void ExpandBCDamageArea(mozilla::TableArea& aRect) const;
void SetColumnDimensions(nscoord aHeight, WritingMode aWM,
const LogicalMargin& aBorderPadding,
const nsSize& aContainerSize);
int32_t CollectRows(nsIFrame* aFrame,
nsTArray<nsTableRowFrame*>& aCollection);
public: /* ----- Cell Map public methods ----- */
int32_t GetStartRowIndex(const nsTableRowGroupFrame* aRowGroupFrame) const;
/** returns the number of rows in this table.
int32_t GetRowCount() const { return GetCellMap()->GetRowCount(); }
/** returns the number of columns in this table after redundant columns have
* been removed
int32_t GetEffectiveColCount() const;
/* return the col count including dead cols */
int32_t GetColCount() const { return GetCellMap()->GetColCount(); }
// return the last col index which isn't of type eColAnonymousCell
int32_t GetIndexOfLastRealCol();
/** returns true if table-layout:auto */
bool IsAutoLayout();
/* ---------- Row index management methods ------------ */
/** Add the given index to the existing ranges of
* deleted row indices and merge ranges if, with the addition of the new
* index, they become consecutive.
* @param aDeletedRowStoredIndex - index of the row that was deleted
* Note - 'stored' index here refers to the index that was assigned to
* the row before any remove row operations were performed i.e. the
* value of mRowIndex and not the value returned by GetRowIndex()
void AddDeletedRowIndex(int32_t aDeletedRowStoredIndex);
/** Calculate the change that aStoredIndex must be increased/decreased by
* to get new index.
* Note that aStoredIndex is always the index of an undeleted row (since
* rows that have already been deleted can never call this method).
* @param aStoredIndex - The stored index value that must be adjusted
* Note - 'stored' index here refers to the index that was assigned to
* the row before any remove row operations were performed i.e. the
* value of mRowIndex and not the value returned by GetRowIndex()
int32_t GetAdjustmentForStoredIndex(int32_t aStoredIndex);
/** Returns whether mDeletedRowIndexRanges is empty
bool IsDeletedRowIndexRangesEmpty() const {
return mDeletedRowIndexRanges.empty();
#ifdef DEBUG
void Dump(bool aDumpRows, bool aDumpCols, bool aDumpCellMap);
* Helper method for RemoveFrame.
void DoRemoveFrame(ChildListID aListID, nsIFrame* aOldFrame);
#ifdef DEBUG
void DumpRowGroup(nsIFrame* aChildFrame);
AutoTArray<nsTableColFrame*, 8> mColFrames;
struct TableBits {
uint32_t mHaveReflowedColGroups : 1; // have the col groups gotten their
// initial reflow
uint32_t mHasPctCol : 1; // does any cell or col have a pct width
uint32_t mCellSpansPctCol : 1; // does any cell span a col with a pct width
// (or containing a cell with a pct width)
uint32_t mIsBorderCollapse : 1; // border collapsing model vs. separate
// model
uint32_t mRowInserted : 1;
uint32_t mNeedToCalcBCBorders : 1;
uint32_t mGeometryDirty : 1;
uint32_t mIStartContBCBorder : 8;
uint32_t mNeedToCollapse : 1; // rows, cols that have visibility:collapse
// need to be collapsed
uint32_t mResizedColumns : 1; // have we resized columns since last reflow?
uint32_t mNeedToCalcHasBCBorders : 1;
uint32_t mHasBCBorders : 1;
} mBits;
std::map<int32_t, int32_t> mDeletedRowIndexRanges; // maintains ranges of row
// indices of deleted rows
mozilla::UniquePtr<nsTableCellMap> mCellMap; // maintains the relationships
// between rows, cols, and cells
// the layout strategy for this frame
mozilla::UniquePtr<nsITableLayoutStrategy> mTableLayoutStrategy;
nsFrameList mColGroups; // the list of colgroup frames
inline bool nsTableFrame::IsRowGroup(mozilla::StyleDisplay aDisplayType) const {
return mozilla::StyleDisplay::TableHeaderGroup == aDisplayType ||
mozilla::StyleDisplay::TableFooterGroup == aDisplayType ||
mozilla::StyleDisplay::TableRowGroup == aDisplayType;
inline void nsTableFrame::SetHaveReflowedColGroups(bool aValue) {
mBits.mHaveReflowedColGroups = aValue;
inline bool nsTableFrame::HaveReflowedColGroups() const {
return (bool)mBits.mHaveReflowedColGroups;
inline bool nsTableFrame::HasPctCol() const { return (bool)mBits.mHasPctCol; }
inline void nsTableFrame::SetHasPctCol(bool aValue) {
mBits.mHasPctCol = (unsigned)aValue;
inline bool nsTableFrame::HasCellSpanningPctCol() const {
return (bool)mBits.mCellSpansPctCol;
inline void nsTableFrame::SetHasCellSpanningPctCol(bool aValue) {
mBits.mCellSpansPctCol = (unsigned)aValue;
inline bool nsTableFrame::IsRowInserted() const {
return (bool)mBits.mRowInserted;
inline void nsTableFrame::SetRowInserted(bool aValue) {
mBits.mRowInserted = (unsigned)aValue;
inline void nsTableFrame::SetNeedToCollapse(bool aValue) {
static_cast<nsTableFrame*>(FirstInFlow())->mBits.mNeedToCollapse =
inline bool nsTableFrame::NeedToCollapse() const {
return (bool)static_cast<nsTableFrame*>(FirstInFlow())->mBits.mNeedToCollapse;
inline nsFrameList& nsTableFrame::GetColGroups() {
return static_cast<nsTableFrame*>(FirstInFlow())->mColGroups;
inline nsTArray<nsTableColFrame*>& nsTableFrame::GetColCache() {
return mColFrames;
inline bool nsTableFrame::IsBorderCollapse() const {
return (bool)mBits.mIsBorderCollapse;
inline void nsTableFrame::SetBorderCollapse(bool aValue) {
mBits.mIsBorderCollapse = aValue;
inline bool nsTableFrame::NeedToCalcBCBorders() const {
return (bool)mBits.mNeedToCalcBCBorders;
inline void nsTableFrame::SetNeedToCalcBCBorders(bool aValue) {
mBits.mNeedToCalcBCBorders = (unsigned)aValue;
inline bool nsTableFrame::NeedToCalcHasBCBorders() const {
return (bool)mBits.mNeedToCalcHasBCBorders;
inline void nsTableFrame::SetNeedToCalcHasBCBorders(bool aValue) {
mBits.mNeedToCalcHasBCBorders = (unsigned)aValue;
inline bool nsTableFrame::HasBCBorders() {
if (NeedToCalcHasBCBorders()) {
return (bool)mBits.mHasBCBorders;
inline void nsTableFrame::SetHasBCBorders(bool aValue) {
mBits.mHasBCBorders = (unsigned)aValue;
inline nscoord nsTableFrame::GetContinuousIStartBCBorderWidth() const {
int32_t d2a = PresContext()->AppUnitsPerDevPixel();
return BC_BORDER_END_HALF_COORD(d2a, mBits.mIStartContBCBorder);
inline void nsTableFrame::SetContinuousIStartBCBorderWidth(nscoord aValue) {
mBits.mIStartContBCBorder = (unsigned)aValue;
#define ABORT0() \
{ \
NS_ASSERTION(false, "CellIterator program error"); \
return; \
#define ABORT1(aReturn) \
{ \
NS_ASSERTION(false, "CellIterator program error"); \
return aReturn; \