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 nsTableRowGroupFrame_h__ michael@0: #define nsTableRowGroupFrame_h__ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nscore.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsILineIterator.h" michael@0: #include "nsTablePainter.h" michael@0: #include "nsTArray.h" michael@0: michael@0: class nsTableFrame; michael@0: class nsTableRowFrame; michael@0: class nsTableCellFrame; michael@0: michael@0: struct nsRowGroupReflowState { michael@0: const nsHTMLReflowState& reflowState; // Our reflow state michael@0: michael@0: nsTableFrame* tableFrame; michael@0: michael@0: // The available size (computed from the parent) michael@0: nsSize availSize; michael@0: michael@0: // Running y-offset michael@0: nscoord y; michael@0: michael@0: nsRowGroupReflowState(const nsHTMLReflowState& aReflowState, michael@0: nsTableFrame* aTableFrame) michael@0: :reflowState(aReflowState), tableFrame(aTableFrame) michael@0: { michael@0: availSize.width = reflowState.AvailableWidth(); michael@0: availSize.height = reflowState.AvailableHeight(); michael@0: y = 0; michael@0: } michael@0: michael@0: ~nsRowGroupReflowState() {} michael@0: }; michael@0: michael@0: #define MIN_ROWS_NEEDING_CURSOR 20 michael@0: michael@0: /** michael@0: * nsTableRowGroupFrame is the frame that maps row groups michael@0: * (HTML tags THEAD, TFOOT, and TBODY). This class cannot be reused michael@0: * outside of an nsTableFrame. It assumes that its parent is an nsTableFrame, and michael@0: * its children are nsTableRowFrames. michael@0: * michael@0: * @see nsTableFrame michael@0: * @see nsTableRowFrame michael@0: */ michael@0: class nsTableRowGroupFrame michael@0: : public nsContainerFrame michael@0: , public nsILineIterator michael@0: { michael@0: public: michael@0: NS_DECL_QUERYFRAME_TARGET(nsTableRowGroupFrame) michael@0: NS_DECL_QUERYFRAME michael@0: NS_DECL_FRAMEARENA_HELPERS 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_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: virtual ~nsTableRowGroupFrame(); michael@0: 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: michael@0: virtual nsresult InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) MOZ_OVERRIDE; michael@0: michael@0: virtual nsMargin GetUsedMargin() const MOZ_OVERRIDE; michael@0: virtual nsMargin GetUsedBorder() const MOZ_OVERRIDE; michael@0: virtual nsMargin GetUsedPadding() 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: /** calls Reflow for all of its child rows. michael@0: * Rows are all set to the same width and stacked vertically. michael@0: *

rows are not split unless absolutely necessary. michael@0: * michael@0: * @param aDesiredSize width set to width of rows, height set to michael@0: * sum of height of rows that fit in aMaxSize.height. 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: virtual bool UpdateOverflow() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Get the "type" of the frame michael@0: * michael@0: * @see nsGkAtoms::tableRowGroupFrame michael@0: */ michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: nsTableRowFrame* GetFirstRow(); michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: /** return the number of child rows (not necessarily == number of child frames) */ michael@0: int32_t GetRowCount(); michael@0: michael@0: /** return the table-relative row index of the first row in this rowgroup. michael@0: * if there are no rows, -1 is returned. michael@0: */ michael@0: int32_t GetStartRowIndex(); michael@0: michael@0: /** Adjust the row indices of all rows 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 anAdjustment); michael@0: michael@0: /** michael@0: * Used for header and footer row group frames that are repeated when michael@0: * splitting a table frame. michael@0: * michael@0: * Performs any table specific initialization michael@0: * michael@0: * @param aHeaderFooterFrame the original header or footer row group frame michael@0: * that was repeated michael@0: */ michael@0: nsresult InitRepeatedFrame(nsPresContext* aPresContext, michael@0: nsTableRowGroupFrame* aHeaderFooterFrame); michael@0: michael@0: michael@0: /** michael@0: * Get the total height of all the row rects michael@0: */ michael@0: nscoord GetHeightBasis(const nsHTMLReflowState& aReflowState); michael@0: michael@0: nsMargin* GetBCBorderWidth(nsMargin& aBorder); michael@0: michael@0: /** michael@0: * Gets inner border widths before collapsing with cell borders michael@0: * Caller must get top border from previous row group or from table michael@0: * GetContinuousBCBorderWidth will not overwrite aBorder.top michael@0: * see nsTablePainter about continuous borders michael@0: */ michael@0: void GetContinuousBCBorderWidth(nsMargin& aBorder); michael@0: /** michael@0: * Sets full border widths before collapsing with cell borders michael@0: * @param aForSide - side to set; only right, left, and bottom valid michael@0: */ michael@0: void SetContinuousBCBorderWidth(uint8_t aForSide, michael@0: BCPixelSize aPixelValue); michael@0: /** michael@0: * Adjust to the effect of visibibility:collapse on the row group and michael@0: * its children michael@0: * @return additional shift upward that should be applied to michael@0: * subsequent rowgroups due to rows and this rowgroup michael@0: * being collapsed michael@0: * @param aYTotalOffset the total amount that the rowgroup is shifted up michael@0: * @param aWidth new width of the rowgroup michael@0: */ michael@0: nscoord CollapseRowGroupIfNecessary(nscoord aYTotalOffset, michael@0: nscoord aWidth); michael@0: michael@0: // nsILineIterator methods michael@0: public: michael@0: virtual void DisposeLineIterator() MOZ_OVERRIDE { } michael@0: michael@0: // The table row is the equivalent to a line in block layout. michael@0: // The nsILineIterator assumes that a line resides in a block, this role is michael@0: // fullfilled by the row group. Rows in table are counted relative to the michael@0: // table. The row index of row corresponds to the cellmap coordinates. The michael@0: // line index with respect to a row group can be computed by substracting the michael@0: // row index of the first row in the row group. michael@0: michael@0: /** Get the number of rows in a row group michael@0: * @return the number of lines in a row group michael@0: */ michael@0: virtual int32_t GetNumLines() MOZ_OVERRIDE; michael@0: michael@0: /** @see nsILineIterator.h GetDirection michael@0: * @return true if the table is rtl michael@0: */ michael@0: virtual bool GetDirection() MOZ_OVERRIDE; michael@0: michael@0: /** Return structural information about a line. michael@0: * @param aLineNumber - the index of the row relative to the row group michael@0: * If the line-number is invalid then michael@0: * aFirstFrameOnLine will be nullptr and michael@0: * aNumFramesOnLine will be zero. michael@0: * @param aFirstFrameOnLine - the first cell frame that originates in row michael@0: * with a rowindex that matches a line number michael@0: * @param aNumFramesOnLine - return the numbers of cells originating in michael@0: * this row michael@0: * @param aLineBounds - rect of the row michael@0: * @param aLineFlags - unused set to 0 michael@0: */ michael@0: NS_IMETHOD GetLine(int32_t aLineNumber, michael@0: nsIFrame** aFirstFrameOnLine, michael@0: int32_t* aNumFramesOnLine, michael@0: nsRect& aLineBounds, michael@0: uint32_t* aLineFlags) MOZ_OVERRIDE; michael@0: michael@0: /** Given a frame that's a child of the rowgroup, find which line its on. michael@0: * @param aFrame - frame, should be a row michael@0: * @param aStartLine - minimal index to return michael@0: * @return row index relative to the row group if this a row michael@0: * frame and the index is at least aStartLine. michael@0: * -1 if the frame cannot be found. michael@0: */ michael@0: virtual int32_t FindLineContaining(nsIFrame* aFrame, int32_t aStartLine = 0) MOZ_OVERRIDE; michael@0: michael@0: /** Find the orginating cell frame on a row that is the nearest to the michael@0: * coordinate X. michael@0: * @param aLineNumber - the index of the row relative to the row group michael@0: * @param aX - X coordinate in twips relative to the michael@0: * origin of the row group michael@0: * @param aFrameFound - pointer to the cellframe michael@0: * @param aXIsBeforeFirstFrame - the point is before the first originating michael@0: * cellframe michael@0: * @param aXIsAfterLastFrame - the point is after the last originating michael@0: * cellframe michael@0: */ michael@0: NS_IMETHOD FindFrameAt(int32_t aLineNumber, michael@0: nscoord aX, michael@0: nsIFrame** aFrameFound, michael@0: bool* aXIsBeforeFirstFrame, michael@0: bool* aXIsAfterLastFrame) MOZ_OVERRIDE; michael@0: michael@0: /** Check whether visual and logical order of cell frames within a line are michael@0: * identical. As the layout will reorder them this is always the case michael@0: * @param aLine - the index of the row relative to the table michael@0: * @param aIsReordered - returns false michael@0: * @param aFirstVisual - if the table is rtl first originating cell frame michael@0: * @param aLastVisual - if the table is rtl last originating cell frame michael@0: */ michael@0: michael@0: NS_IMETHOD CheckLineOrder(int32_t aLine, michael@0: bool *aIsReordered, michael@0: nsIFrame **aFirstVisual, michael@0: nsIFrame **aLastVisual) MOZ_OVERRIDE; michael@0: michael@0: /** Find the next originating cell frame that originates in the row. michael@0: * @param aFrame - cell frame to start with, will return the next cell michael@0: * originating in a row michael@0: * @param aLineNumber - the index of the row relative to the table michael@0: */ michael@0: NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) MOZ_OVERRIDE; michael@0: michael@0: // row cursor methods to speed up searching for the row(s) michael@0: // containing a point. The basic idea is that we set the cursor michael@0: // property if the rows' y and yMosts are non-decreasing (considering only michael@0: // rows with nonempty overflowAreas --- empty overflowAreas never participate michael@0: // in event handling or painting), and the rowgroup has sufficient number of michael@0: // rows. The cursor property points to a "recently used" row. If we get a michael@0: // series of requests that work on rows "near" the cursor, then we can find michael@0: // those nearby rows quickly by starting our search at the cursor. michael@0: // This code is based on the line cursor code in nsBlockFrame. It's more general michael@0: // though, and could be extracted and used elsewhere. michael@0: struct FrameCursorData { michael@0: nsTArray mFrames; michael@0: uint32_t mCursorIndex; michael@0: nscoord mOverflowAbove; michael@0: nscoord mOverflowBelow; michael@0: michael@0: FrameCursorData() michael@0: : mFrames(MIN_ROWS_NEEDING_CURSOR), mCursorIndex(0), mOverflowAbove(0), michael@0: mOverflowBelow(0) {} michael@0: michael@0: bool AppendFrame(nsIFrame* aFrame); michael@0: michael@0: void FinishBuildingCursor() { michael@0: mFrames.Compact(); michael@0: } michael@0: }; michael@0: michael@0: // Clear out row cursor because we're disturbing the rows (e.g., Reflow) michael@0: void ClearRowCursor(); michael@0: michael@0: /** michael@0: * Get the first row that might contain y-coord 'aY', or nullptr if you must search michael@0: * all rows. michael@0: * The actual row returned might not contain 'aY', but if not, it is guaranteed michael@0: * to be before any row which does contain 'aY'. michael@0: * aOverflowAbove is the maximum over all rows of -row.GetOverflowRect().y. michael@0: * To find all rows that intersect the vertical interval aY/aYMost, call michael@0: * GetFirstRowContaining(aY, &overflowAbove), and then iterate through all michael@0: * rows until reaching a row where row->GetRect().y - overflowAbove >= aYMost. michael@0: * That row and all subsequent rows cannot intersect the interval. michael@0: */ michael@0: nsIFrame* GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove); michael@0: michael@0: /** michael@0: * Set up the row cursor. After this, call AppendFrame for every michael@0: * child frame in sibling order. Ensure that the child frame y and YMost values michael@0: * form non-decreasing sequences (should always be true for table rows); michael@0: * if this is violated, call ClearRowCursor(). If we return nullptr, then we michael@0: * decided not to use a cursor or we already have one set up. michael@0: */ michael@0: FrameCursorData* SetupRowCursor(); michael@0: michael@0: virtual nsILineIterator* GetLineIterator() MOZ_OVERRIDE { return this; } michael@0: michael@0: virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE michael@0: { michael@0: return nsContainerFrame::IsFrameOfType(aFlags & ~(nsIFrame::eTablePart)); michael@0: } michael@0: michael@0: virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) MOZ_OVERRIDE; michael@0: virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) MOZ_OVERRIDE; michael@0: virtual void InvalidateFrameForRemoval() MOZ_OVERRIDE { InvalidateFrameSubtree(); } michael@0: michael@0: protected: michael@0: nsTableRowGroupFrame(nsStyleContext* aContext); michael@0: michael@0: void InitChildReflowState(nsPresContext& aPresContext, michael@0: bool aBorderCollapse, michael@0: nsHTMLReflowState& aReflowState); michael@0: michael@0: virtual int GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const MOZ_OVERRIDE; michael@0: michael@0: void PlaceChild(nsPresContext* aPresContext, michael@0: nsRowGroupReflowState& aReflowState, michael@0: nsIFrame* aKidFrame, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsRect& aOriginalKidRect, michael@0: const nsRect& aOriginalKidVisualOverflow); michael@0: michael@0: void CalculateRowHeights(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState); michael@0: michael@0: void DidResizeRows(nsHTMLReflowMetrics& aDesiredSize); michael@0: michael@0: void SlideChild(nsRowGroupReflowState& aReflowState, michael@0: nsIFrame* aKidFrame); michael@0: michael@0: /** michael@0: * Reflow the frames we've already created michael@0: * michael@0: * @param aPresContext presentation context to use michael@0: * @param aReflowState current inline state michael@0: * @return true if we successfully reflowed all the mapped children and false michael@0: * otherwise, e.g. we pushed children to the next in flow michael@0: */ michael@0: nsresult ReflowChildren(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: nsRowGroupReflowState& aReflowState, michael@0: nsReflowStatus& aStatus, michael@0: bool* aPageBreakBeforeEnd = nullptr); michael@0: michael@0: nsresult SplitRowGroup(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsTableFrame* aTableFrame, michael@0: nsReflowStatus& aStatus, michael@0: bool aRowForcedPageBreak); michael@0: michael@0: void SplitSpanningCells(nsPresContext& aPresContext, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsTableFrame& aTableFrame, michael@0: nsTableRowFrame& aFirstRow, michael@0: nsTableRowFrame& aLastRow, michael@0: bool aFirstRowIsTopOfPage, michael@0: nscoord aSpanningRowBottom, michael@0: nsTableRowFrame*& aContRowFrame, michael@0: nsTableRowFrame*& aFirstTruncatedRow, michael@0: nscoord& aDesiredHeight); michael@0: michael@0: void CreateContinuingRowFrame(nsPresContext& aPresContext, michael@0: nsIFrame& aRowFrame, michael@0: nsIFrame** aContRowFrame); michael@0: michael@0: bool IsSimpleRowFrame(nsTableFrame* aTableFrame, michael@0: nsIFrame* aFrame); michael@0: michael@0: void GetNextRowSibling(nsIFrame** aRowFrame); michael@0: michael@0: void UndoContinuedRow(nsPresContext* aPresContext, michael@0: nsTableRowFrame* aRow); michael@0: michael@0: private: michael@0: // border widths in pixels in the collapsing border model michael@0: BCPixelSize mRightContBorderWidth; michael@0: BCPixelSize mBottomContBorderWidth; michael@0: BCPixelSize mLeftContBorderWidth; michael@0: michael@0: public: michael@0: bool IsRepeatable() const; michael@0: void SetRepeatable(bool aRepeatable); michael@0: bool HasStyleHeight() const; michael@0: void SetHasStyleHeight(bool aValue); michael@0: bool HasInternalBreakBefore() const; michael@0: bool HasInternalBreakAfter() const; michael@0: }; michael@0: michael@0: michael@0: inline bool nsTableRowGroupFrame::IsRepeatable() const michael@0: { michael@0: return (mState & NS_ROWGROUP_REPEATABLE) == NS_ROWGROUP_REPEATABLE; michael@0: } michael@0: michael@0: inline void nsTableRowGroupFrame::SetRepeatable(bool aRepeatable) michael@0: { michael@0: if (aRepeatable) { michael@0: mState |= NS_ROWGROUP_REPEATABLE; michael@0: } else { michael@0: mState &= ~NS_ROWGROUP_REPEATABLE; michael@0: } michael@0: } michael@0: michael@0: inline bool nsTableRowGroupFrame::HasStyleHeight() const michael@0: { michael@0: return (mState & NS_ROWGROUP_HAS_STYLE_HEIGHT) == NS_ROWGROUP_HAS_STYLE_HEIGHT; michael@0: } michael@0: michael@0: inline void nsTableRowGroupFrame::SetHasStyleHeight(bool aValue) michael@0: { michael@0: if (aValue) { michael@0: mState |= NS_ROWGROUP_HAS_STYLE_HEIGHT; michael@0: } else { michael@0: mState &= ~NS_ROWGROUP_HAS_STYLE_HEIGHT; michael@0: } michael@0: } michael@0: michael@0: inline void michael@0: nsTableRowGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder) michael@0: { michael@0: int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); michael@0: aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, michael@0: mRightContBorderWidth); michael@0: aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, michael@0: mBottomContBorderWidth); michael@0: aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, michael@0: mLeftContBorderWidth); michael@0: return; michael@0: } michael@0: #endif