diff options
Diffstat (limited to '')
-rw-r--r-- | layout/generic/BlockReflowState.h | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/layout/generic/BlockReflowState.h b/layout/generic/BlockReflowState.h new file mode 100644 index 0000000000..9f219cc3f9 --- /dev/null +++ b/layout/generic/BlockReflowState.h @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 http://mozilla.org/MPL/2.0/. */ + +/* state used in reflow of block frames */ + +#ifndef BlockReflowState_h +#define BlockReflowState_h + +#include <tuple> + +#include "mozilla/ReflowInput.h" +#include "nsFloatManager.h" +#include "nsLineBox.h" + +class nsBlockFrame; +class nsFrameList; +class nsOverflowContinuationTracker; + +namespace mozilla { + +// BlockReflowState contains additional reflow input information that the +// block frame uses along with ReflowInput. Like ReflowInput, this +// is read-only data that is passed down from a parent frame to its children. +class BlockReflowState { + using BandInfoType = nsFloatManager::BandInfoType; + using ShapeType = nsFloatManager::ShapeType; + + // Block reflow input flags. + struct Flags { + Flags() + : mIsBStartMarginRoot(false), + mIsBEndMarginRoot(false), + mShouldApplyBStartMargin(false), + mHasLineAdjacentToTop(false), + mBlockNeedsFloatManager(false), + mIsLineLayoutEmpty(false), + mIsFloatListInBlockPropertyTable(false), + mCanHaveOverflowMarkers(false) {} + + // Set in the BlockReflowState constructor when reflowing a "block margin + // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for + // which margins apply by default). + // + // The flag is also set when reflowing a frame whose computed BStart border + // padding is non-zero. + bool mIsBStartMarginRoot : 1; + + // Set in the BlockReflowState constructor when reflowing a "block margin + // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for + // which margins apply by default). + // + // The flag is also set when reflowing a frame whose computed BEnd border + // padding is non-zero. + bool mIsBEndMarginRoot : 1; + + // Set if the BStart margin should be considered when placing a linebox that + // contains a block frame. It may be set as a side-effect of calling + // nsBlockFrame::ShouldApplyBStartMargin(); once set, + // ShouldApplyBStartMargin() uses it as a fast-path way to return whether + // the BStart margin should apply. + // + // If the flag hasn't been set in the block reflow state, then + // ShouldApplyBStartMargin() will crawl the line list to see if a block + // frame precedes the specified frame. If so, the BStart margin should be + // applied, and the flag is set to cache the result. (If not, the BStart + // margin will be applied as a result of the generational margin collapsing + // logic in nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this + // case, the flag won't be set, so subsequent calls to + // ShouldApplyBStartMargin() will continue crawl the line list.) + // + // This flag is also set in the BlockReflowState constructor if + // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin + // root by default. + bool mShouldApplyBStartMargin : 1; + + // Set when mLineAdjacentToTop is valid. + bool mHasLineAdjacentToTop : 1; + + // Set when the block has the equivalent of NS_BLOCK_FLOAT_MGR. + bool mBlockNeedsFloatManager : 1; + + // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing + // the current line. + bool mIsLineLayoutEmpty : 1; + + // Set when our mPushedFloats list is stored on the block's property table. + bool mIsFloatListInBlockPropertyTable : 1; + + // Set when we need text-overflow or -webkit-line-clamp processing. + bool mCanHaveOverflowMarkers : 1; + }; + + public: + BlockReflowState(const ReflowInput& aReflowInput, nsPresContext* aPresContext, + nsBlockFrame* aFrame, bool aBStartMarginRoot, + bool aBEndMarginRoot, bool aBlockNeedsFloatManager, + const nscoord aConsumedBSize, + const nscoord aEffectiveContentBoxBSize); + + /** + * Get the available reflow space (the area not occupied by floats) + * for the current y coordinate. The available space is relative to + * our coordinate system, which is the content box, with (0, 0) in the + * upper left. + * + * Returns whether there are floats present at the given block-direction + * coordinate and within the inline size of the content rect. + * + * Note: some codepaths clamp this structure's inline-size to be >=0 "for + * compatibility with nsSpaceManager". So if you encounter a nsFlowAreaRect + * which appears to have an ISize of 0, you can't necessarily assume that a + * 0-ISize float-avoiding block would actually fit; you need to check the + * InitialISizeIsNegative flag to see whether that 0 is actually a clamped + * negative value (in which case a 0-ISize float-avoiding block *should not* + * be considered as fitting, because it would intersect some float). + */ + nsFlowAreaRect GetFloatAvailableSpace() const { + return GetFloatAvailableSpace(mBCoord); + } + nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord) const { + return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::Margin, nullptr); + } + nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const { + return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::ShapeOutside, + nullptr); + } + nsFlowAreaRect GetFloatAvailableSpaceWithState( + nscoord aBCoord, ShapeType aShapeType, + nsFloatManager::SavedState* aState) const; + nsFlowAreaRect GetFloatAvailableSpaceForBSize( + nscoord aBCoord, nscoord aBSize, + nsFloatManager::SavedState* aState) const; + + // @return true if AddFloat was able to place the float; false if the float + // did not fit in available space. + // + // Note: if it returns false, then the float's position and size should be + // considered stale/invalid (until the float is successfully placed). + bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, + nscoord aAvailableISize); + + enum class PlaceFloatResult : uint8_t { + Placed, + ShouldPlaceBelowCurrentLine, + ShouldPlaceInNextContinuation, + }; + // @param aAvailableISizeInCurrentLine the available inline-size of the + // current line if current line is not empty. + PlaceFloatResult FlowAndPlaceFloat( + nsIFrame* aFloat, mozilla::Maybe<nscoord> aAvailableISizeInCurrentLine = + mozilla::Nothing()); + + void PlaceBelowCurrentLineFloats(nsLineBox* aLine); + + // Returns the first coordinate >= aBCoord that clears the + // floats indicated by aClearType and has enough inline size between floats + // (or no floats remaining) to accomodate aFloatAvoidingBlock. + enum class ClearFloatsResult : uint8_t { + BCoordNoChange, + BCoordAdvanced, + FloatsPushedOrSplit, + }; + std::tuple<nscoord, ClearFloatsResult> ClearFloats( + nscoord aBCoord, StyleClear aClearType, + nsIFrame* aFloatAvoidingBlock = nullptr); + + nsFloatManager* FloatManager() const { + MOZ_ASSERT(mReflowInput.mFloatManager, + "Float manager should be valid during the lifetime of " + "BlockReflowState!"); + return mReflowInput.mFloatManager; + } + + // Advances to the next band, i.e., the next horizontal stripe in + // which there is a different set of floats. + // Return false if it did not advance, which only happens for + // constrained heights (and means that we should get pushed to the + // next column/page). + bool AdvanceToNextBand(const LogicalRect& aFloatAvailableSpace, + nscoord* aBCoord) const { + WritingMode wm = mReflowInput.GetWritingMode(); + if (aFloatAvailableSpace.BSize(wm) > 0) { + // See if there's room in the next band. + *aBCoord += aFloatAvailableSpace.BSize(wm); + } else { + if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { + // Stop trying to clear here; we'll just get pushed to the + // next column or page and try again there. + return false; + } + MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!"); + *aBCoord += 1; + } + return true; + } + + bool FloatAvoidingBlockFitsInAvailSpace( + nsIFrame* aFloatAvoidingBlock, + const nsFlowAreaRect& aFloatAvailableSpace) const; + + // True if the current block-direction coordinate, for placing the children + // within the content area, is still adjacent with the block-start of the + // content area. + bool IsAdjacentWithBStart() const { return mBCoord == ContentBStart(); } + + const LogicalMargin& BorderPadding() const { return mBorderPadding; } + + // Reconstruct the previous block-end margin that goes before |aLine|. + void ReconstructMarginBefore(nsLineList::iterator aLine); + + // Caller must have called GetFloatAvailableSpace for the correct position + // (which need not be the current mBCoord). + void ComputeFloatAvoidingOffsets(nsIFrame* aFloatAvoidingBlock, + const LogicalRect& aFloatAvailableSpace, + nscoord& aIStartResult, + nscoord& aIEndResult) const; + + // Compute the amount of available space for reflowing a block frame at the + // current block-direction coordinate mBCoord. Caller must have called + // GetFloatAvailableSpace for the current mBCoord. + LogicalRect ComputeBlockAvailSpace(nsIFrame* aFrame, + const nsFlowAreaRect& aFloatAvailableSpace, + bool aBlockAvoidsFloats); + + LogicalSize ComputeAvailableSizeForFloat() const; + + void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord); + + void AdvanceToNextLine() { + if (mFlags.mIsLineLayoutEmpty) { + mFlags.mIsLineLayoutEmpty = false; + } else { + mLineNumber++; + } + } + + //---------------------------------------- + + // This state is the "global" state computed once for the reflow of + // the block. + + // The block frame that is using this object + nsBlockFrame* mBlock; + + nsPresContext* mPresContext; + + const ReflowInput& mReflowInput; + + // The coordinates within the float manager where the block is being + // placed <b>after</b> taking into account the blocks border and + // padding. This, therefore, represents the inner "content area" (in + // float manager coordinates) where child frames will be placed, + // including child blocks and floats. + nscoord mFloatManagerI, mFloatManagerB; + + // XXX get rid of this + nsReflowStatus mReflowStatus; + + // The float manager state as it was before the contents of this + // block. This is needed for positioning bullets, since we only want + // to move the bullet to flow around floats that were before this + // block, not floats inside of it. + nsFloatManager::SavedState mFloatManagerStateBefore; + + // The content area to reflow child frames within. This is within + // this frame's coordinate system and writing mode, which means + // mContentArea.IStart == BorderPadding().IStart and + // mContentArea.BStart == BorderPadding().BStart. + // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there + // is no page/column boundary below (the common case). + // mContentArea.BEnd() should only be called after checking that + // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise + // coordinate overflow may occur. + LogicalRect mContentArea; + nscoord ContentIStart() const { + return mContentArea.IStart(mReflowInput.GetWritingMode()); + } + nscoord ContentISize() const { + return mContentArea.ISize(mReflowInput.GetWritingMode()); + } + nscoord ContentIEnd() const { + return mContentArea.IEnd(mReflowInput.GetWritingMode()); + } + nscoord ContentBStart() const { + return mContentArea.BStart(mReflowInput.GetWritingMode()); + } + nscoord ContentBSize() const { + return mContentArea.BSize(mReflowInput.GetWritingMode()); + } + nscoord ContentBEnd() const { + NS_ASSERTION( + ContentBSize() != NS_UNCONSTRAINEDSIZE, + "ContentBSize() is unconstrained, so ContentBEnd() may overflow."); + return mContentArea.BEnd(mReflowInput.GetWritingMode()); + } + LogicalSize ContentSize(WritingMode aWM) const { + WritingMode wm = mReflowInput.GetWritingMode(); + return mContentArea.Size(wm).ConvertTo(aWM, wm); + } + + // Physical size. Use only for physical <-> logical coordinate conversion. + nsSize mContainerSize; + const nsSize& ContainerSize() const { return mContainerSize; } + + // Continuation out-of-flow float frames that need to move to our + // next in flow are placed here during reflow. It's a pointer to + // a frame list stored in the block's property table. + nsFrameList* mPushedFloats; + // This method makes sure pushed floats are accessible to + // StealFrame. Call it before adding any frames to mPushedFloats. + void SetupPushedFloatList(); + /** + * Append aFloatCont and its next-in-flows within the same block to + * mPushedFloats. aFloatCont should not be on any child list when + * making this call. Its next-in-flows will be removed from + * mBlock using StealFrame() before being added to mPushedFloats. + * All appended frames will be marked NS_FRAME_IS_PUSHED_FLOAT. + */ + void AppendPushedFloatChain(nsIFrame* aFloatCont); + + // Track child overflow continuations. + nsOverflowContinuationTracker* mOverflowTracker; + + //---------------------------------------- + + // This state is "running" state updated by the reflow of each line + // in the block. This same state is "recovered" when a line is not + // dirty and is passed over during incremental reflow. + + // The current line being reflowed + // If it is mBlock->end_lines(), then it is invalid. + nsLineList::iterator mCurrentLine; + + // When mHasLineAdjacentToTop is set, this refers to a line + // which we know is adjacent to the top of the block (in other words, + // all lines before it are empty and do not have clearance. This line is + // always before the current line. + nsLineList::iterator mLineAdjacentToTop; + + // The current block-direction coordinate in the block + nscoord mBCoord; + + // mBlock's computed logical border+padding with pre-reflow skip sides applied + // (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides). + LogicalMargin mBorderPadding; + + // The overflow areas of all floats placed so far + OverflowAreas mFloatOverflowAreas; + + // Previous child. This is used when pulling up a frame to update + // the sibling list. + nsIFrame* mPrevChild; + + // The previous child frames collapsed bottom margin value. + nsCollapsingMargin mPrevBEndMargin; + + // The current next-in-flow for the block. When lines are pulled + // from a next-in-flow, this is used to know which next-in-flow to + // pull from. When a next-in-flow is emptied of lines, we advance + // this to the next next-in-flow. + nsBlockFrame* mNextInFlow; + + //---------------------------------------- + + // Temporary state, for line-reflow. This state is used during the reflow + // of a given line, but doesn't have meaning before or after. + + // The list of floats that are "current-line" floats. These are + // added to the line after the line has been reflowed, to keep the + // list fiddling from being N^2. + nsTArray<nsIFrame*> mCurrentLineFloats; + + // The list of floats which are "below current-line" + // floats. These are reflowed/placed after the line is reflowed + // and placed. Again, this is done to keep the list fiddling from + // being N^2. + nsTArray<nsIFrame*> mBelowCurrentLineFloats; + + // The list of floats that are waiting on a break opportunity in order to be + // placed, since we're on a nowrap context. + nsTArray<nsIFrame*> mNoWrapFloats; + + nscoord mMinLineHeight; + + int32_t mLineNumber; + + Flags mFlags; + + // Cache the result of nsBlockFrame::FindTrailingClear() from mBlock's + // prev-in-flows. See nsBlockFrame::ReflowPushedFloats(). + StyleClear mTrailingClearFromPIF; + + // The amount of computed content block-size "consumed" by our previous + // continuations. + const nscoord mConsumedBSize; + + // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to + // place the line. When redoing the line, it will be used to query the + // accurate float available space in AddFloat() and + // nsBlockFrame::PlaceLine(). + Maybe<nscoord> mLineBSize; + + private: + bool CanPlaceFloat(nscoord aFloatISize, + const nsFlowAreaRect& aFloatAvailableSpace); + + void PushFloatPastBreak(nsIFrame* aFloat); + + void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord); +}; + +}; // namespace mozilla + +#endif // BlockReflowState_h |