michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #ifndef nsTableFrame_h__ michael@0: #define nsTableFrame_h__ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "celldata.h" michael@0: #include "nscore.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsStyleCoord.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsTableColFrame.h" michael@0: #include "nsTableColGroupFrame.h" michael@0: #include "nsCellMap.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsDisplayList.h" michael@0: michael@0: class nsTableCellFrame; michael@0: class nsTableCellMap; michael@0: class nsTableColFrame; michael@0: class nsColGroupFrame; michael@0: class nsTableRowGroupFrame; michael@0: class nsTableRowFrame; michael@0: class nsTableColGroupFrame; michael@0: class nsITableLayoutStrategy; michael@0: class nsStyleContext; michael@0: michael@0: struct nsTableReflowState; michael@0: struct nsStylePosition; michael@0: struct BCPropertyData; michael@0: michael@0: static inline bool IS_TABLE_CELL(nsIAtom* frameType) { michael@0: return nsGkAtoms::tableCellFrame == frameType || michael@0: nsGkAtoms::bcTableCellFrame == frameType; michael@0: } michael@0: michael@0: static inline bool FrameHasBorderOrBackground(nsIFrame* f) { michael@0: return (f->StyleVisibility()->IsVisible() && michael@0: (!f->StyleBackground()->IsTransparent() || michael@0: f->StyleDisplay()->mAppearance || michael@0: f->StyleBorder()->HasBorder())); michael@0: } michael@0: michael@0: class nsDisplayTableItem : public nsDisplayItem michael@0: { michael@0: public: michael@0: nsDisplayTableItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : michael@0: nsDisplayItem(aBuilder, aFrame), michael@0: mPartHasFixedBackground(false) {} michael@0: michael@0: virtual bool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame) MOZ_OVERRIDE; michael@0: // With collapsed borders, parts of the collapsed border can extend outside michael@0: // the table part frames, so allow this display element to blow out to our michael@0: // overflow rect. This is also useful for row frames that have spanning michael@0: // cells extending outside them. michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE; michael@0: michael@0: void UpdateForFrameBackground(nsIFrame* aFrame); michael@0: michael@0: private: michael@0: bool mPartHasFixedBackground; michael@0: }; michael@0: michael@0: class nsAutoPushCurrentTableItem michael@0: { michael@0: public: michael@0: nsAutoPushCurrentTableItem() : mBuilder(nullptr) {} michael@0: michael@0: void Push(nsDisplayListBuilder* aBuilder, nsDisplayTableItem* aPushItem) michael@0: { michael@0: mBuilder = aBuilder; michael@0: mOldCurrentItem = aBuilder->GetCurrentTableItem(); michael@0: aBuilder->SetCurrentTableItem(aPushItem); michael@0: #ifdef DEBUG michael@0: mPushedItem = aPushItem; michael@0: #endif michael@0: } michael@0: ~nsAutoPushCurrentTableItem() { michael@0: if (!mBuilder) michael@0: return; michael@0: #ifdef DEBUG michael@0: NS_ASSERTION(mBuilder->GetCurrentTableItem() == mPushedItem, michael@0: "Someone messed with the current table item behind our back!"); michael@0: #endif michael@0: mBuilder->SetCurrentTableItem(mOldCurrentItem); michael@0: } michael@0: michael@0: private: michael@0: nsDisplayListBuilder* mBuilder; michael@0: nsDisplayTableItem* mOldCurrentItem; michael@0: #ifdef DEBUG michael@0: nsDisplayTableItem* mPushedItem; michael@0: #endif michael@0: }; michael@0: michael@0: /* ============================================================================ */ michael@0: michael@0: /** michael@0: * nsTableFrame maps the inner portion of a table (everything except captions.) michael@0: * Used as a pseudo-frame within nsTableOuterFrame, it may also be used michael@0: * stand-alone as the top-level frame. michael@0: * michael@0: * The principal child list contains row group frames. There is also an michael@0: * additional child list, kColGroupList, which contains the col group frames. michael@0: */ michael@0: class nsTableFrame : public nsContainerFrame michael@0: { michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: static void DestroyPositionedTablePartArray(void* aPropertyValue); michael@0: NS_DECLARE_FRAME_PROPERTY(PositionedTablePartArray, DestroyPositionedTablePartArray) michael@0: michael@0: /** nsTableOuterFrame has intimate knowledge of the inner table frame */ michael@0: friend class nsTableOuterFrame; michael@0: michael@0: /** instantiate a new instance of nsTableRowFrame. michael@0: * @param aPresShell the pres shell for this frame michael@0: * michael@0: * @return the frame that was created michael@0: */ michael@0: friend nsIFrame* NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: michael@0: /** sets defaults for table-specific style. michael@0: * @see nsIFrame::Init michael@0: */ michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: michael@0: static float GetTwipsToPixels(nsPresContext* aPresContext); michael@0: michael@0: // Return true if aParentReflowState.frame or any of its ancestors within michael@0: // the containing table have non-auto height. (e.g. pct or fixed height) michael@0: static bool AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState); michael@0: michael@0: // See if a special height reflow will occur due to having a pct height when michael@0: // the pct height basis may not yet be valid. michael@0: static void CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState); michael@0: michael@0: // Notify the frame and its ancestors (up to the containing table) that a special michael@0: // height reflow will occur. michael@0: static void RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState); michael@0: michael@0: static void RePositionViews(nsIFrame* aFrame); michael@0: michael@0: static bool PageBreakAfter(nsIFrame* aSourceFrame, michael@0: nsIFrame* aNextFrame); michael@0: michael@0: // Register a positioned table part with its nsTableFrame. These objects will michael@0: // be visited by FixupPositionedTableParts after reflow is complete. (See that michael@0: // function for more explanation.) Should be called during frame construction. michael@0: static void RegisterPositionedTablePart(nsIFrame* aFrame); michael@0: michael@0: // Unregister a positioned table part with its nsTableFrame. michael@0: static void UnregisterPositionedTablePart(nsIFrame* aFrame, michael@0: nsIFrame* aDestructRoot); michael@0: michael@0: nsPoint GetFirstSectionOrigin(const nsHTMLReflowState& aReflowState) const; michael@0: /* michael@0: * Notification that aAttribute has changed for content inside a table (cell, row, etc) michael@0: */ michael@0: void AttributeChangedFor(nsIFrame* aFrame, michael@0: nsIContent* aContent, michael@0: nsIAtom* aAttribute); michael@0: michael@0: /** @see nsIFrame::DestroyFrom */ michael@0: virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; michael@0: michael@0: /** @see nsIFrame::DidSetStyleContext */ michael@0: virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: virtual nsresult RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) MOZ_OVERRIDE; michael@0: michael@0: virtual nsMargin GetUsedBorder() const MOZ_OVERRIDE; michael@0: virtual nsMargin GetUsedPadding() const MOZ_OVERRIDE; michael@0: virtual nsMargin GetUsedMargin() const MOZ_OVERRIDE; michael@0: michael@0: // Get the offset from the border box to the area where the row groups fit michael@0: nsMargin GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const; michael@0: michael@0: /** helper method to find the table parent of any table frame object */ michael@0: static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame); michael@0: michael@0: /* Like GetTableFrame, but will return nullptr if we don't pass through michael@0: * aMustPassThrough on the way to the table. michael@0: */ michael@0: static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough, michael@0: nsIFrame* aSourceFrame); michael@0: michael@0: typedef void (* DisplayGenericTablePartTraversal) michael@0: (nsDisplayListBuilder* aBuilder, nsFrame* aFrame, michael@0: const nsRect& aDirtyRect, const nsDisplayListSet& aLists); michael@0: static void GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, michael@0: const nsRect& aDirtyRect, const nsDisplayListSet& aLists); michael@0: michael@0: /** michael@0: * Helper method to handle display common to table frames, rowgroup frames michael@0: * and row frames. It creates a background display item for handling events michael@0: * if necessary, an outline display item if necessary, and displays michael@0: * all the the frame's children. michael@0: * @param aDisplayItem the display item created for this part, or null michael@0: * if this part's border/background painting is delegated to an ancestor michael@0: * @param aTraversal a function that gets called to traverse the table michael@0: * part's child frames and add their display list items to a michael@0: * display list set. michael@0: */ michael@0: static void DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, michael@0: nsFrame* aFrame, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists, michael@0: nsDisplayTableItem* aDisplayItem, michael@0: DisplayGenericTablePartTraversal aTraversal = GenericTraversal); michael@0: michael@0: // Return the closest sibling of aPriorChildFrame (including aPriroChildFrame) michael@0: // of type aChildType. michael@0: static nsIFrame* GetFrameAtOrBefore(nsIFrame* aParentFrame, michael@0: nsIFrame* aPriorChildFrame, michael@0: nsIAtom* aChildType); michael@0: bool IsAutoHeight(); michael@0: michael@0: /** @return true if aDisplayType represents a rowgroup of any sort michael@0: * (header, footer, or body) michael@0: */ michael@0: bool IsRowGroup(int32_t aDisplayType) const; michael@0: michael@0: /** Initialize the table frame with a set of children. michael@0: * @see nsIFrame::SetInitialChildList michael@0: */ michael@0: virtual nsresult SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) MOZ_OVERRIDE; michael@0: michael@0: virtual const nsFrameList& GetChildList(ChildListID aListID) const MOZ_OVERRIDE; michael@0: virtual void GetChildLists(nsTArray* aLists) const MOZ_OVERRIDE; michael@0: michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Paint the background of the table and its parts (column groups, michael@0: * columns, row groups, rows, and cells), and the table border, and all michael@0: * internal borders if border-collapse is on. michael@0: */ michael@0: void PaintTableBorderBackground(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: nsPoint aPt, uint32_t aBGPaintFlags); michael@0: michael@0: /** michael@0: * Determines if any table part has a background image that is currently not michael@0: * decoded. Does not look into cell contents (ie only table parts). michael@0: */ michael@0: static bool AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart, michael@0: nsIFrame* aEnd); michael@0: michael@0: /** Get the outer half (i.e., the part outside the height and width of michael@0: * the table) of the largest segment (?) of border-collapsed border on michael@0: * the table on each side, or 0 for non border-collapsed tables. michael@0: */ michael@0: nsMargin GetOuterBCBorder() const; michael@0: michael@0: /** Same as above, but only if it's included from the border-box width michael@0: * of the table. michael@0: */ michael@0: nsMargin GetIncludedOuterBCBorder() const; michael@0: michael@0: /** Same as above, but only if it's excluded from the border-box width michael@0: * of the table. This is the area that leaks out into the margin michael@0: * (or potentially past it, if there is no margin). michael@0: */ michael@0: nsMargin GetExcludedOuterBCBorder() const; michael@0: michael@0: /** michael@0: * In quirks mode, the size of the table background is reduced michael@0: * by the outer BC border. Compute the reduction needed. michael@0: */ michael@0: nsMargin GetDeflationForBackground(nsPresContext* aPresContext) const; michael@0: michael@0: /** Get width of table + colgroup + col collapse: elements that michael@0: * continue along the length of the whole left side. michael@0: * see nsTablePainter about continuous borders michael@0: */ michael@0: nscoord GetContinuousLeftBCBorderWidth() const; michael@0: void SetContinuousLeftBCBorderWidth(nscoord aValue); michael@0: michael@0: friend class nsDelayedCalcBCBorders; michael@0: michael@0: void AddBCDamageArea(const nsIntRect& aValue); michael@0: bool BCRecalcNeeded(nsStyleContext* aOldStyleContext, michael@0: nsStyleContext* aNewStyleContext); michael@0: void PaintBCBorders(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect); michael@0: michael@0: virtual void MarkIntrinsicWidthsDirty() MOZ_OVERRIDE; michael@0: // For border-collapse tables, the caller must not add padding and michael@0: // border to the results of these functions. michael@0: virtual nscoord GetMinWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual nscoord GetPrefWidth(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE; michael@0: virtual IntrinsicWidthOffsetData michael@0: IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) MOZ_OVERRIDE; michael@0: michael@0: virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, nsSize aPadding, michael@0: uint32_t aFlags) MOZ_OVERRIDE; michael@0: virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) MOZ_OVERRIDE; michael@0: /** michael@0: * A copy of nsFrame::ShrinkWidthToFit that calls a different michael@0: * GetPrefWidth, since tables have two different ones. michael@0: */ michael@0: nscoord TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, michael@0: nscoord aWidthInCB); michael@0: michael@0: // XXXldb REWRITE THIS COMMENT! michael@0: /** inner tables are reflowed in two steps. michael@0: *
michael@0:     * if mFirstPassValid is false, this is our first time through since content was last changed
michael@0:     *   set pass to 1
michael@0:     *   do pass 1
michael@0:     *     get min/max info for all cells in an infinite space
michael@0:     *   do column balancing
michael@0:     *   set mFirstPassValid to true
michael@0:     *   do pass 2
michael@0:     *     use column widths to Reflow cells
michael@0:     * 
michael@0: * michael@0: * @see nsIFrame::Reflow michael@0: */ michael@0: virtual nsresult Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) MOZ_OVERRIDE; michael@0: michael@0: nsresult ReflowTable(nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nscoord aAvailHeight, michael@0: nsIFrame*& aLastChildReflowed, michael@0: nsReflowStatus& aStatus); michael@0: michael@0: nsFrameList& GetColGroups(); michael@0: michael@0: virtual nsIFrame* GetParentStyleContextFrame() const MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Get the "type" of the frame michael@0: * michael@0: * @see nsGkAtoms::tableFrame michael@0: */ michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: if (aFlags & eSupportsCSSTransforms) { michael@0: return false; michael@0: } michael@0: return nsContainerFrame::IsFrameOfType(aFlags); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: /** @see nsIFrame::GetFrameName */ michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: /** return the width of the column at aColIndex */ michael@0: int32_t GetColumnWidth(int32_t aColIndex); michael@0: michael@0: /** helper to get the cell spacing X style value */ michael@0: nscoord GetCellSpacingX(); michael@0: michael@0: /** helper to get the cell spacing Y style value */ michael@0: nscoord GetCellSpacingY(); michael@0: michael@0: virtual nscoord GetBaseline() const MOZ_OVERRIDE; michael@0: /** return the row span of a cell, taking into account row span magic at the bottom michael@0: * of a table. The row span equals the number of rows spanned by aCell starting at michael@0: * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row michael@0: * index in which aCell originates. michael@0: * michael@0: * @param aStartRowIndex the cell michael@0: * @param aCell the cell michael@0: * michael@0: * @return the row span, correcting for row spans that extend beyond the bottom michael@0: * of the table. michael@0: */ michael@0: int32_t GetEffectiveRowSpan(int32_t aStartRowIndex, michael@0: const nsTableCellFrame& aCell) const; michael@0: int32_t GetEffectiveRowSpan(const nsTableCellFrame& aCell, michael@0: nsCellMap* aCellMap = nullptr); michael@0: michael@0: /** return the col span of a cell, taking into account col span magic at the edge michael@0: * of a table. michael@0: * michael@0: * @param aCell the cell michael@0: * michael@0: * @return the col span, correcting for col spans that extend beyond the edge michael@0: * of the table. michael@0: */ michael@0: int32_t GetEffectiveColSpan(const nsTableCellFrame& aCell, michael@0: nsCellMap* aCellMap = nullptr) const; michael@0: michael@0: /** indicate whether the row has more than one cell that either originates michael@0: * or is spanned from the rows above michael@0: */ michael@0: bool HasMoreThanOneCell(int32_t aRowIndex) const; michael@0: michael@0: /** return the column frame associated with aColIndex michael@0: * returns nullptr if the col frame has not yet been allocated, or if michael@0: * aColIndex is out of range michael@0: */ michael@0: nsTableColFrame* GetColFrame(int32_t aColIndex) const; michael@0: michael@0: /** Insert a col frame reference into the colframe cache and adapt the cellmap michael@0: * @param aColFrame - the column frame michael@0: * @param aColIndex - index where the column should be inserted into the michael@0: * colframe cache michael@0: */ michael@0: void InsertCol(nsTableColFrame& aColFrame, michael@0: int32_t aColIndex); michael@0: michael@0: nsTableColGroupFrame* CreateAnonymousColGroupFrame(nsTableColGroupType aType); michael@0: michael@0: int32_t DestroyAnonymousColFrames(int32_t aNumFrames); michael@0: michael@0: // Append aNumColsToAdd anonymous col frames of type eColAnonymousCell to our michael@0: // last eColGroupAnonymousCell colgroup. If we have no such colgroup, then michael@0: // create one. michael@0: void AppendAnonymousColFrames(int32_t aNumColsToAdd); michael@0: michael@0: // Append aNumColsToAdd anonymous col frames of type aColType to michael@0: // aColGroupFrame. If aAddToTable is true, also call AddColsToTable on the michael@0: // new cols. michael@0: void AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame, michael@0: int32_t aNumColsToAdd, michael@0: nsTableColType aColType, michael@0: bool aAddToTable); michael@0: michael@0: void MatchCellMapToColCache(nsTableCellMap* aCellMap); michael@0: /** empty the column frame cache */ michael@0: void ClearColCache(); michael@0: michael@0: void DidResizeColumns(); michael@0: michael@0: void AppendCell(nsTableCellFrame& aCellFrame, michael@0: int32_t aRowIndex); michael@0: michael@0: void InsertCells(nsTArray& aCellFrames, michael@0: int32_t aRowIndex, michael@0: int32_t aColIndexBefore); michael@0: michael@0: void RemoveCell(nsTableCellFrame* aCellFrame, michael@0: int32_t aRowIndex); michael@0: michael@0: void AppendRows(nsTableRowGroupFrame* aRowGroupFrame, michael@0: int32_t aRowIndex, michael@0: nsTArray& aRowFrames); michael@0: michael@0: int32_t InsertRows(nsTableRowGroupFrame* aRowGroupFrame, michael@0: nsTArray& aFrames, michael@0: int32_t aRowIndex, michael@0: bool aConsiderSpans); michael@0: michael@0: void RemoveRows(nsTableRowFrame& aFirstRowFrame, michael@0: int32_t aNumRowsToRemove, michael@0: bool aConsiderSpans); michael@0: michael@0: /** Insert multiple rowgroups into the table cellmap handling michael@0: * @param aRowGroups - iterator that iterates over the rowgroups to insert michael@0: */ michael@0: void InsertRowGroups(const nsFrameList::Slice& aRowGroups); michael@0: michael@0: void InsertColGroups(int32_t aStartColIndex, michael@0: const nsFrameList::Slice& aColgroups); michael@0: michael@0: void RemoveCol(nsTableColGroupFrame* aColGroupFrame, michael@0: int32_t aColIndex, michael@0: bool aRemoveFromCache, michael@0: bool aRemoveFromCellMap); michael@0: michael@0: bool ColumnHasCellSpacingBefore(int32_t aColIndex) const; michael@0: michael@0: bool HasPctCol() const; michael@0: void SetHasPctCol(bool aValue); michael@0: michael@0: bool HasCellSpanningPctCol() const; michael@0: void SetHasCellSpanningPctCol(bool aValue); michael@0: michael@0: /** michael@0: * To be called on a frame by its parent after setting its size/position and michael@0: * calling DidReflow (possibly via FinishReflowChild()). This can also be michael@0: * used for child frames which are not being reflowed but did have their size michael@0: * or position changed. michael@0: * michael@0: * @param aFrame The frame to invalidate michael@0: * @param aOrigRect The original rect of aFrame (before the change). michael@0: * @param aOrigVisualOverflow The original overflow rect of aFrame. michael@0: * @param aIsFirstReflow True if the size/position change is due to the michael@0: * first reflow of aFrame. michael@0: */ michael@0: static void InvalidateTableFrame(nsIFrame* aFrame, michael@0: const nsRect& aOrigRect, michael@0: const nsRect& aOrigVisualOverflow, michael@0: bool aIsFirstReflow); michael@0: michael@0: virtual bool UpdateOverflow() MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: michael@0: /** protected constructor. michael@0: * @see NewFrame michael@0: */ michael@0: nsTableFrame(nsStyleContext* aContext); michael@0: michael@0: /** destructor, responsible for mColumnLayoutData */ michael@0: virtual ~nsTableFrame(); michael@0: michael@0: void InitChildReflowState(nsHTMLReflowState& aReflowState); michael@0: michael@0: virtual int GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const MOZ_OVERRIDE; michael@0: michael@0: public: michael@0: bool IsRowInserted() const; michael@0: void SetRowInserted(bool aValue); michael@0: michael@0: protected: michael@0: michael@0: // A helper function to reflow a header or footer with unconstrained height michael@0: // to see if it should be made repeatable and also to determine its desired michael@0: // height. michael@0: nsresult SetupHeaderFooterChild(const nsTableReflowState& aReflowState, michael@0: nsTableRowGroupFrame* aFrame, michael@0: nscoord* aDesiredHeight); michael@0: michael@0: nsresult ReflowChildren(nsTableReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: nsIFrame*& aLastChildReflowed, michael@0: nsOverflowAreas& aOverflowAreas); michael@0: michael@0: // This calls the col group and column reflow methods, which do two things: michael@0: // (1) set all the dimensions to 0 michael@0: // (2) notify the table about colgroups or columns with hidden visibility michael@0: void ReflowColGroups(nsRenderingContext* aRenderingContext); michael@0: michael@0: /** return the width of the table taking into account visibility collapse michael@0: * on columns and colgroups michael@0: * @param aBorderPadding the border and padding of the table michael@0: */ michael@0: nscoord GetCollapsedWidth(nsMargin aBorderPadding); michael@0: michael@0: michael@0: /** Adjust the table for visibility.collapse set on rowgroups, rows, michael@0: * colgroups and cols michael@0: * @param aDesiredSize the metrics of the table michael@0: * @param aBorderPadding the border and padding of the table michael@0: */ michael@0: void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, michael@0: nsMargin aBorderPadding); michael@0: michael@0: /** FixupPositionedTableParts is called at the end of table reflow to reflow michael@0: * the absolutely positioned descendants of positioned table parts. This is michael@0: * necessary because the dimensions of table parts may change after they've michael@0: * been reflowed (e.g. in AdjustForCollapsingRowsCols). michael@0: */ michael@0: void FixupPositionedTableParts(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState); michael@0: michael@0: // Clears the list of positioned table parts. michael@0: void ClearAllPositionedTableParts(); michael@0: michael@0: nsITableLayoutStrategy* LayoutStrategy() const { michael@0: return static_cast(FirstInFlow())-> michael@0: mTableLayoutStrategy; michael@0: } michael@0: michael@0: // Helper for InsertFrames. michael@0: void HomogenousInsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList); michael@0: private: michael@0: /* Handle a row that got inserted during reflow. aNewHeight is the michael@0: new height of the table after reflow. */ michael@0: void ProcessRowInserted(nscoord aNewHeight); michael@0: michael@0: // WIDTH AND HEIGHT CALCULATION michael@0: michael@0: public: michael@0: michael@0: // calculate the computed height of aFrame including its border and padding given michael@0: // its reflow state. michael@0: nscoord CalcBorderBoxHeight(const nsHTMLReflowState& aReflowState); michael@0: michael@0: protected: michael@0: michael@0: // update the desired height of this table taking into account the current michael@0: // reflow state, the table attributes and the content driven rowgroup heights michael@0: // this function can change the overflow area michael@0: void CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize); michael@0: michael@0: // The following is a helper for CalcDesiredHeight michael@0: michael@0: void DistributeHeightToRows(const nsHTMLReflowState& aReflowState, michael@0: nscoord aAmount); michael@0: michael@0: void PlaceChild(nsTableReflowState& aReflowState, michael@0: nsIFrame* aKidFrame, michael@0: nsHTMLReflowMetrics& aKidDesiredSize, michael@0: const nsRect& aOriginalKidRect, michael@0: const nsRect& aOriginalKidVisualOverflow); michael@0: void PlaceRepeatedFooter(nsTableReflowState& aReflowState, michael@0: nsTableRowGroupFrame *aTfoot, michael@0: nscoord aFooterHeight); michael@0: michael@0: nsIFrame* GetFirstBodyRowGroupFrame(); michael@0: public: michael@0: typedef nsAutoTArray RowGroupArray; michael@0: /** michael@0: * Push all our child frames from the aRowGroups array, in order, starting michael@0: * from the frame at aPushFrom to the end of the array. The frames are put on michael@0: * our overflow list or moved directly to our next-in-flow if one exists. michael@0: */ michael@0: protected: michael@0: void PushChildren(const RowGroupArray& aRowGroups, int32_t aPushFrom); michael@0: michael@0: public: michael@0: // put the children frames in the display order (e.g. thead before tbodies michael@0: // before tfoot). This will handle calling GetRowGroupFrame() on the michael@0: // children, and not append nulls, so the array is guaranteed to contain michael@0: // nsTableRowGroupFrames. If there are multiple theads or tfoots, all but michael@0: // the first one are treated as tbodies instead. michael@0: michael@0: void OrderRowGroups(RowGroupArray& aChildren, michael@0: nsTableRowGroupFrame** aHead = nullptr, michael@0: nsTableRowGroupFrame** aFoot = nullptr) const; michael@0: michael@0: // Return the thead, if any michael@0: nsTableRowGroupFrame* GetTHead() const; michael@0: michael@0: // Return the tfoot, if any michael@0: nsTableRowGroupFrame* GetTFoot() const; michael@0: michael@0: // Returns true if there are any cells above the row at michael@0: // aRowIndex and spanning into the row at aRowIndex, the number of michael@0: // effective columns limits the search up to that column michael@0: bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols); michael@0: michael@0: // Returns true if there is a cell originating in aRowIndex michael@0: // which spans into the next row, the number of effective michael@0: // columns limits the search up to that column michael@0: bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols); michael@0: michael@0: protected: michael@0: michael@0: bool HaveReflowedColGroups() const; michael@0: void SetHaveReflowedColGroups(bool aValue); michael@0: michael@0: public: michael@0: bool IsBorderCollapse() const; michael@0: michael@0: bool NeedToCalcBCBorders() const; michael@0: void SetNeedToCalcBCBorders(bool aValue); michael@0: michael@0: bool NeedToCollapse() const; michael@0: void SetNeedToCollapse(bool aValue); michael@0: michael@0: bool HasZeroColSpans() const; michael@0: void SetHasZeroColSpans(bool aValue); michael@0: michael@0: bool NeedColSpanExpansion() const; michael@0: void SetNeedColSpanExpansion(bool aValue); michael@0: michael@0: /** The GeometryDirty bit is similar to the NS_FRAME_IS_DIRTY frame michael@0: * state bit, which implies that all descendants are dirty. The michael@0: * GeometryDirty still implies that all the parts of the table are michael@0: * dirty, but resizing optimizations should still apply to the michael@0: * contents of the individual cells. michael@0: */ michael@0: void SetGeometryDirty() { mBits.mGeometryDirty = true; } michael@0: void ClearGeometryDirty() { mBits.mGeometryDirty = false; } michael@0: bool IsGeometryDirty() const { return mBits.mGeometryDirty; } michael@0: michael@0: /** Get the cell map for this table frame. It is not always mCellMap. michael@0: * Only the firstInFlow has a legit cell map michael@0: */ michael@0: nsTableCellMap* GetCellMap() const; michael@0: michael@0: /** Iterate over the row groups and adjust the row indices of all rows michael@0: * whose index is >= aRowIndex. michael@0: * @param aRowIndex - start adjusting with this index michael@0: * @param aAdjustment - shift the row index by this amount michael@0: */ michael@0: void AdjustRowIndices(int32_t aRowIndex, michael@0: int32_t aAdjustment); michael@0: michael@0: /** Reset the rowindices of all rows as they might have changed due to michael@0: * rowgroup reordering, exclude new row group frames that show in the michael@0: * reordering but are not yet inserted into the cellmap michael@0: * @param aRowGroupsToExclude - an iterator that will produce the row groups michael@0: * to exclude. michael@0: */ michael@0: void ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude); michael@0: michael@0: nsTArray& GetColCache(); michael@0: michael@0: michael@0: protected: michael@0: michael@0: void SetBorderCollapse(bool aValue); michael@0: michael@0: BCPropertyData* GetBCProperty(bool aCreateIfNecessary = false) const; michael@0: void SetFullBCDamageArea(); michael@0: void CalcBCBorders(); michael@0: michael@0: void ExpandBCDamageArea(nsIntRect& aRect) const; michael@0: michael@0: void SetColumnDimensions(nscoord aHeight, michael@0: const nsMargin& aReflowState); michael@0: michael@0: int32_t CollectRows(nsIFrame* aFrame, michael@0: nsTArray& aCollection); michael@0: michael@0: public: /* ----- Cell Map public methods ----- */ michael@0: michael@0: int32_t GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame); michael@0: michael@0: /** returns the number of rows in this table. michael@0: */ michael@0: int32_t GetRowCount () const michael@0: { michael@0: return GetCellMap()->GetRowCount(); michael@0: } michael@0: michael@0: /** returns the number of columns in this table after redundant columns have been removed michael@0: */ michael@0: int32_t GetEffectiveColCount() const; michael@0: michael@0: /* return the col count including dead cols */ michael@0: int32_t GetColCount () const michael@0: { michael@0: return GetCellMap()->GetColCount(); michael@0: } michael@0: michael@0: // return the last col index which isn't of type eColAnonymousCell michael@0: int32_t GetIndexOfLastRealCol(); michael@0: michael@0: /** returns true if table-layout:auto */ michael@0: bool IsAutoLayout(); michael@0: michael@0: public: michael@0: michael@0: #ifdef DEBUG michael@0: void Dump(bool aDumpRows, michael@0: bool aDumpCols, michael@0: bool aDumpCellMap); michael@0: #endif michael@0: michael@0: protected: michael@0: /** michael@0: * Helper method for RemoveFrame. michael@0: */ michael@0: void DoRemoveFrame(ChildListID aListID, nsIFrame* aOldFrame); michael@0: #ifdef DEBUG michael@0: void DumpRowGroup(nsIFrame* aChildFrame); michael@0: #endif michael@0: // DATA MEMBERS michael@0: nsAutoTArray mColFrames; michael@0: michael@0: struct TableBits { michael@0: uint32_t mHaveReflowedColGroups:1; // have the col groups gotten their initial reflow michael@0: uint32_t mHasPctCol:1; // does any cell or col have a pct width michael@0: uint32_t mCellSpansPctCol:1; // does any cell span a col with a pct width (or containing a cell with a pct width) michael@0: uint32_t mIsBorderCollapse:1; // border collapsing model vs. separate model michael@0: uint32_t mRowInserted:1; michael@0: uint32_t mNeedToCalcBCBorders:1; michael@0: uint32_t mGeometryDirty:1; michael@0: uint32_t mLeftContBCBorder:8; michael@0: uint32_t mNeedToCollapse:1; // rows, cols that have visibility:collapse need to be collapsed michael@0: uint32_t mHasZeroColSpans:1; michael@0: uint32_t mNeedColSpanExpansion:1; michael@0: uint32_t mResizedColumns:1; // have we resized columns since last reflow? michael@0: } mBits; michael@0: michael@0: nsTableCellMap* mCellMap; // maintains the relationships between rows, cols, and cells michael@0: nsITableLayoutStrategy* mTableLayoutStrategy;// the layout strategy for this frame michael@0: nsFrameList mColGroups; // the list of colgroup frames michael@0: }; michael@0: michael@0: michael@0: inline bool nsTableFrame::IsRowGroup(int32_t aDisplayType) const michael@0: { michael@0: return bool((NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == aDisplayType) || michael@0: (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == aDisplayType) || michael@0: (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == aDisplayType)); michael@0: } michael@0: michael@0: inline void nsTableFrame::SetHaveReflowedColGroups(bool aValue) michael@0: { michael@0: mBits.mHaveReflowedColGroups = aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::HaveReflowedColGroups() const michael@0: { michael@0: return (bool)mBits.mHaveReflowedColGroups; michael@0: } michael@0: michael@0: inline bool nsTableFrame::HasPctCol() const michael@0: { michael@0: return (bool)mBits.mHasPctCol; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetHasPctCol(bool aValue) michael@0: { michael@0: mBits.mHasPctCol = (unsigned)aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::HasCellSpanningPctCol() const michael@0: { michael@0: return (bool)mBits.mCellSpansPctCol; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetHasCellSpanningPctCol(bool aValue) michael@0: { michael@0: mBits.mCellSpansPctCol = (unsigned)aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::IsRowInserted() const michael@0: { michael@0: return (bool)mBits.mRowInserted; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetRowInserted(bool aValue) michael@0: { michael@0: mBits.mRowInserted = (unsigned)aValue; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetNeedToCollapse(bool aValue) michael@0: { michael@0: static_cast(FirstInFlow())->mBits.mNeedToCollapse = (unsigned)aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::NeedToCollapse() const michael@0: { michael@0: return (bool) static_cast(FirstInFlow())->mBits.mNeedToCollapse; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetHasZeroColSpans(bool aValue) michael@0: { michael@0: mBits.mHasZeroColSpans = (unsigned)aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::HasZeroColSpans() const michael@0: { michael@0: return (bool)mBits.mHasZeroColSpans; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetNeedColSpanExpansion(bool aValue) michael@0: { michael@0: mBits.mNeedColSpanExpansion = (unsigned)aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::NeedColSpanExpansion() const michael@0: { michael@0: return (bool)mBits.mNeedColSpanExpansion; michael@0: } michael@0: michael@0: michael@0: inline nsFrameList& nsTableFrame::GetColGroups() michael@0: { michael@0: return static_cast(FirstInFlow())->mColGroups; michael@0: } michael@0: michael@0: inline nsTArray& nsTableFrame::GetColCache() michael@0: { michael@0: return mColFrames; michael@0: } michael@0: michael@0: inline bool nsTableFrame::IsBorderCollapse() const michael@0: { michael@0: return (bool)mBits.mIsBorderCollapse; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetBorderCollapse(bool aValue) michael@0: { michael@0: mBits.mIsBorderCollapse = aValue; michael@0: } michael@0: michael@0: inline bool nsTableFrame::NeedToCalcBCBorders() const michael@0: { michael@0: return (bool)mBits.mNeedToCalcBCBorders; michael@0: } michael@0: michael@0: inline void nsTableFrame::SetNeedToCalcBCBorders(bool aValue) michael@0: { michael@0: mBits.mNeedToCalcBCBorders = (unsigned)aValue; michael@0: } michael@0: michael@0: inline nscoord michael@0: nsTableFrame::GetContinuousLeftBCBorderWidth() const michael@0: { michael@0: int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); michael@0: return BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mBits.mLeftContBCBorder); michael@0: } michael@0: michael@0: inline void nsTableFrame::SetContinuousLeftBCBorderWidth(nscoord aValue) michael@0: { michael@0: mBits.mLeftContBCBorder = (unsigned) aValue; michael@0: } michael@0: michael@0: class nsTableIterator michael@0: { michael@0: public: michael@0: nsTableIterator(nsIFrame& aSource); michael@0: nsTableIterator(nsFrameList& aSource); michael@0: nsIFrame* First(); michael@0: nsIFrame* Next(); michael@0: bool IsLeftToRight(); michael@0: int32_t Count(); michael@0: michael@0: protected: michael@0: void Init(nsIFrame* aFirstChild); michael@0: bool mLeftToRight; michael@0: nsIFrame* mFirstListChild; michael@0: nsIFrame* mFirstChild; michael@0: nsIFrame* mCurrentChild; michael@0: int32_t mCount; michael@0: }; michael@0: michael@0: #define ABORT0() \ michael@0: {NS_ASSERTION(false, "CellIterator program error"); \ michael@0: return;} michael@0: michael@0: #define ABORT1(aReturn) \ michael@0: {NS_ASSERTION(false, "CellIterator program error"); \ michael@0: return aReturn;} michael@0: michael@0: #endif