summaryrefslogtreecommitdiffstats
path: root/layout/generic/BlockReflowState.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--layout/generic/BlockReflowState.h417
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