tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BlockReflowState.h (17527B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* state used in reflow of block frames */
      8 
      9 #ifndef BlockReflowState_h
     10 #define BlockReflowState_h
     11 
     12 #include <tuple>
     13 
     14 #include "mozilla/ReflowInput.h"
     15 #include "nsFloatManager.h"
     16 #include "nsLineBox.h"
     17 
     18 class nsBlockFrame;
     19 class nsFrameList;
     20 class nsOverflowContinuationTracker;
     21 
     22 namespace mozilla {
     23 
     24 // BlockReflowState contains additional reflow input information that the
     25 // block frame uses along with ReflowInput. Like ReflowInput, this
     26 // is read-only data that is passed down from a parent frame to its children.
     27 class BlockReflowState {
     28  using BandInfoType = nsFloatManager::BandInfoType;
     29  using ShapeType = nsFloatManager::ShapeType;
     30 
     31  // Block reflow input flags.
     32  struct Flags {
     33    Flags()
     34        : mIsBStartMarginRoot(false),
     35          mIsBEndMarginRoot(false),
     36          mShouldApplyBStartMargin(false),
     37          mHasLineAdjacentToTop(false),
     38          mBlockNeedsFloatManager(false),
     39          mIsLineLayoutEmpty(false),
     40          mCanHaveOverflowMarkers(false) {}
     41 
     42    // Set in the BlockReflowState constructor when reflowing a "block margin
     43    // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for
     44    // which margins apply by default).
     45    //
     46    // The flag is also set when reflowing a frame whose computed BStart border
     47    // padding is non-zero.
     48    bool mIsBStartMarginRoot : 1;
     49 
     50    // Set in the BlockReflowState constructor when reflowing a "block margin
     51    // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for
     52    // which margins apply by default).
     53    //
     54    // The flag is also set when reflowing a frame whose computed BEnd border
     55    // padding is non-zero.
     56    bool mIsBEndMarginRoot : 1;
     57 
     58    // Set if the BStart margin should be considered when placing a linebox that
     59    // contains a block frame. It may be set as a side-effect of calling
     60    // nsBlockFrame::ShouldApplyBStartMargin(); once set,
     61    // ShouldApplyBStartMargin() uses it as a fast-path way to return whether
     62    // the BStart margin should apply.
     63    //
     64    // If the flag hasn't been set in the block reflow state, then
     65    // ShouldApplyBStartMargin() will crawl the line list to see if a block
     66    // frame precedes the specified frame. If so, the BStart margin should be
     67    // applied, and the flag is set to cache the result. (If not, the BStart
     68    // margin will be applied as a result of the generational margin collapsing
     69    // logic in nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this
     70    // case, the flag won't be set, so subsequent calls to
     71    // ShouldApplyBStartMargin() will continue crawl the line list.)
     72    //
     73    // This flag is also set in the BlockReflowState constructor if
     74    // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin
     75    // root by default.
     76    bool mShouldApplyBStartMargin : 1;
     77 
     78    // Set when mLineAdjacentToTop is valid.
     79    bool mHasLineAdjacentToTop : 1;
     80 
     81    // Set when the block has the equivalent of NS_BLOCK_*_BFC.
     82    bool mBlockNeedsFloatManager : 1;
     83 
     84    // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing
     85    // the current line.
     86    bool mIsLineLayoutEmpty : 1;
     87 
     88    // Set when we need text-overflow or -webkit-line-clamp processing.
     89    bool mCanHaveOverflowMarkers : 1;
     90  };
     91 
     92 public:
     93  BlockReflowState(const ReflowInput& aReflowInput, nsPresContext* aPresContext,
     94                   nsBlockFrame* aFrame, bool aBStartMarginRoot,
     95                   bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
     96                   const nscoord aConsumedBSize,
     97                   const nscoord aEffectiveContentBoxBSize,
     98                   const nscoord aInset = 0);
     99 
    100  /**
    101   * Unshifts coords, restores availableBSize to reality.
    102   * (Constructor applies any cached shift before reflow
    103   *  so that frames are reflowed with cached shift)
    104   */
    105  void UndoAlignContentShift();
    106 
    107  /**
    108   * Get the available reflow space (the area not occupied by floats)
    109   * for the current y coordinate. The available space is relative to
    110   * our coordinate system, which is the content box, with (0, 0) in the
    111   * upper left.
    112   *
    113   * The parameter aCBWM is the containing block's writing mode, which is
    114   * NOT necessarily the mode currently being used by the float manager.
    115   *
    116   * Returns whether there are floats present at the given block-direction
    117   * coordinate and within the inline size of the content rect.
    118   *
    119   * Note: some codepaths clamp this structure's inline-size to be >=0 "for
    120   * compatibility with nsSpaceManager". So if you encounter a nsFlowAreaRect
    121   * which appears to have an ISize of 0, you can't necessarily assume that a
    122   * 0-ISize float-avoiding block would actually fit; you need to check the
    123   * InitialISizeIsNegative flag to see whether that 0 is actually a clamped
    124   * negative value (in which case a 0-ISize float-avoiding block *should not*
    125   * be considered as fitting, because it would intersect some float).
    126   */
    127  nsFlowAreaRect GetFloatAvailableSpace(WritingMode aCBWM) const {
    128    return GetFloatAvailableSpace(aCBWM, mBCoord);
    129  }
    130  nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(WritingMode aCBWM,
    131                                                       nscoord aBCoord) const {
    132    return GetFloatAvailableSpaceWithState(aCBWM, aBCoord, ShapeType::Margin,
    133                                           nullptr);
    134  }
    135  nsFlowAreaRect GetFloatAvailableSpace(WritingMode aCBWM,
    136                                        nscoord aBCoord) const {
    137    return GetFloatAvailableSpaceWithState(aCBWM, aBCoord,
    138                                           ShapeType::ShapeOutside, nullptr);
    139  }
    140  nsFlowAreaRect GetFloatAvailableSpaceWithState(
    141      WritingMode aCBWM, nscoord aBCoord, ShapeType aShapeType,
    142      nsFloatManager::SavedState* aState) const;
    143  nsFlowAreaRect GetFloatAvailableSpaceForBSize(
    144      WritingMode aCBWM, nscoord aBCoord, nscoord aBSize,
    145      nsFloatManager::SavedState* aState) const;
    146 
    147  // @return true if AddFloat was able to place the float; false if the float
    148  // did not fit in available space.
    149  //
    150  // Note: if it returns false, then the float's position and size should be
    151  // considered stale/invalid (until the float is successfully placed).
    152  bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat,
    153                nscoord aAvailableISize);
    154 
    155  enum class PlaceFloatResult : uint8_t {
    156    Placed,
    157    ShouldPlaceBelowCurrentLine,
    158    ShouldPlaceInNextContinuation,
    159  };
    160  // @param aAvailableISizeInCurrentLine the available inline-size of the
    161  //        current line if current line is not empty.
    162  PlaceFloatResult FlowAndPlaceFloat(
    163      nsIFrame* aFloat, mozilla::Maybe<nscoord> aAvailableISizeInCurrentLine =
    164                            mozilla::Nothing());
    165 
    166  void PlaceBelowCurrentLineFloats(nsLineBox* aLine);
    167 
    168  // Returns the first coordinate >= aBCoord that clears the
    169  // floats indicated by aClearType and has enough inline size between floats
    170  // (or no floats remaining) to accomodate aFloatAvoidingBlock.
    171  enum class ClearFloatsResult : uint8_t {
    172    BCoordNoChange,
    173    BCoordAdvanced,
    174    FloatsPushedOrSplit,
    175  };
    176  std::tuple<nscoord, ClearFloatsResult> ClearFloats(
    177      nscoord aBCoord, UsedClear aClearType,
    178      nsIFrame* aFloatAvoidingBlock = nullptr);
    179 
    180  nsFloatManager* FloatManager() const {
    181    MOZ_ASSERT(mReflowInput.mFloatManager,
    182               "Float manager should be valid during the lifetime of "
    183               "BlockReflowState!");
    184    return mReflowInput.mFloatManager;
    185  }
    186 
    187  // Advances to the next band, i.e., the next horizontal stripe in
    188  // which there is a different set of floats.
    189  // Return false if it did not advance, which only happens for
    190  // constrained heights (and means that we should get pushed to the
    191  // next column/page).
    192  bool AdvanceToNextBand(const LogicalRect& aFloatAvailableSpace,
    193                         nscoord* aBCoord) const {
    194    WritingMode wm = mReflowInput.GetWritingMode();
    195    if (aFloatAvailableSpace.BSize(wm) > 0) {
    196      // See if there's room in the next band.
    197      *aBCoord += aFloatAvailableSpace.BSize(wm);
    198    } else {
    199      if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
    200        // Stop trying to clear here; we'll just get pushed to the
    201        // next column or page and try again there.
    202        return false;
    203      }
    204      MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!");
    205      *aBCoord += 1;
    206    }
    207    return true;
    208  }
    209 
    210  bool FloatAvoidingBlockFitsInAvailSpace(
    211      nsIFrame* aFloatAvoidingBlock,
    212      const nsFlowAreaRect& aFloatAvailableSpace) const;
    213 
    214  // True if the current block-direction coordinate, for placing the children
    215  // within the content area, is still adjacent with the block-start of the
    216  // content area.
    217  bool IsAdjacentWithBStart() const { return mBCoord == ContentBStart(); }
    218 
    219  const LogicalMargin& BorderPadding() const { return mBorderPadding; }
    220 
    221  // Reconstruct the previous block-end margin that goes before |aLine|.
    222  void ReconstructMarginBefore(nsLineList::iterator aLine);
    223 
    224  // Caller must have called GetFloatAvailableSpace for the correct position
    225  // (which need not be the current mBCoord).
    226  void ComputeFloatAvoidingOffsets(nsIFrame* aFloatAvoidingBlock,
    227                                   const LogicalRect& aFloatAvailableSpace,
    228                                   nscoord& aIStartResult,
    229                                   nscoord& aIEndResult) const;
    230 
    231  // Compute the amount of available space for reflowing a block frame at the
    232  // current block-direction coordinate mBCoord. Caller must have called
    233  // GetFloatAvailableSpace for the current mBCoord.
    234  LogicalRect ComputeBlockAvailSpace(nsIFrame* aFrame,
    235                                     const nsFlowAreaRect& aFloatAvailableSpace,
    236                                     bool aBlockAvoidsFloats);
    237 
    238  LogicalSize ComputeAvailableSizeForFloat() const;
    239 
    240  void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord);
    241 
    242  void AdvanceToNextLine() {
    243    if (mFlags.mIsLineLayoutEmpty) {
    244      mFlags.mIsLineLayoutEmpty = false;
    245    } else {
    246      mLineNumber++;
    247    }
    248  }
    249 
    250  //----------------------------------------
    251 
    252  // This state is the "global" state computed once for the reflow of
    253  // the block.
    254 
    255  // The block frame that is using this object
    256  nsBlockFrame* const mBlock;
    257 
    258  nsPresContext* const mPresContext;
    259 
    260  const ReflowInput& mReflowInput;
    261 
    262  // The coordinates within the float manager where the block is being
    263  // placed <b>after</b> taking into account the blocks border and
    264  // padding. This, therefore, represents the inner "content area" (in
    265  // float manager coordinates) where child frames will be placed,
    266  // including child blocks and floats.
    267  nscoord mFloatManagerI, mFloatManagerB;
    268 
    269  // XXX get rid of this
    270  nsReflowStatus mReflowStatus;
    271 
    272  // The float manager state as it was before the contents of this
    273  // block.  This is needed for positioning bullets, since we only want
    274  // to move the bullet to flow around floats that were before this
    275  // block, not floats inside of it.
    276  nsFloatManager::SavedState mFloatManagerStateBefore;
    277 
    278  // The content area to reflow child frames within.  This is within
    279  // this frame's coordinate system and writing mode, which means
    280  // mContentArea.IStart == BorderPadding().IStart and
    281  // mContentArea.BStart == BorderPadding().BStart.
    282  // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there
    283  // is no page/column boundary below (the common case).
    284  // mContentArea.BEnd() should only be called after checking that
    285  // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise
    286  // coordinate overflow may occur.
    287  LogicalRect mContentArea;
    288  nscoord ContentIStart() const {
    289    return mContentArea.IStart(mReflowInput.GetWritingMode());
    290  }
    291  nscoord ContentISize() const {
    292    return mContentArea.ISize(mReflowInput.GetWritingMode());
    293  }
    294  nscoord ContentIEnd() const {
    295    return mContentArea.IEnd(mReflowInput.GetWritingMode());
    296  }
    297  nscoord ContentBStart() const {
    298    return mContentArea.BStart(mReflowInput.GetWritingMode());
    299  }
    300  nscoord ContentBSize() const {
    301    return mContentArea.BSize(mReflowInput.GetWritingMode());
    302  }
    303  nscoord ContentBEnd() const {
    304    NS_ASSERTION(
    305        ContentBSize() != NS_UNCONSTRAINEDSIZE,
    306        "ContentBSize() is unconstrained, so ContentBEnd() may overflow.");
    307    return mContentArea.BEnd(mReflowInput.GetWritingMode());
    308  }
    309  LogicalSize ContentSize(WritingMode aWM) const {
    310    WritingMode wm = mReflowInput.GetWritingMode();
    311    return mContentArea.Size(wm).ConvertTo(aWM, wm);
    312  }
    313 
    314  // Amount of inset to apply during line-breaking, used by text-wrap:balance
    315  // to adjust line-breaks for more consistent lengths throughout the block.
    316  nscoord mInsetForBalance;
    317 
    318  // Physical size. Use only for physical <-> logical coordinate conversion.
    319  //
    320  // Note: for vertical-rl writing-mode, if mContainerSize's width is
    321  // initialized to zero due to unconstrained block-size, lines will be
    322  // positioned (physically) incorrectly. We will fix them up at the end of
    323  // nsBlockFrame::Reflow() after we know the total block-size of the frame.
    324  nsSize mContainerSize;
    325  const nsSize& ContainerSize() const { return mContainerSize; }
    326 
    327  /**
    328   * Append aFloatCont and its next-in-flows within the same block to mBlock's
    329   * pushed floats list. aFloatCont should not be on any child list when making
    330   * this call. Its next-in-flows will be removed from mBlock using StealFrame()
    331   * before being added to mBlock's pushed floats list. All appended frames will
    332   * be marked NS_FRAME_IS_PUSHED_OUT_OF_FLOW.
    333   */
    334  void AppendPushedFloatChain(nsIFrame* aFloatCont);
    335 
    336  // Track child overflow continuations.
    337  nsOverflowContinuationTracker* mOverflowTracker;
    338 
    339  //----------------------------------------
    340 
    341  // This state is "running" state updated by the reflow of each line
    342  // in the block. This same state is "recovered" when a line is not
    343  // dirty and is passed over during incremental reflow.
    344 
    345  // The current line being reflowed
    346  // If it is mBlock->end_lines(), then it is invalid.
    347  nsLineList::iterator mCurrentLine;
    348 
    349  // When mHasLineAdjacentToTop is set, this refers to a line
    350  // which we know is adjacent to the top of the block (in other words,
    351  // all lines before it are empty and do not have clearance. This line is
    352  // always before the current line.
    353  nsLineList::iterator mLineAdjacentToTop;
    354 
    355  // The current block-direction coordinate in the block
    356  nscoord mBCoord;
    357 
    358  // mBlock's computed logical border+padding with pre-reflow skip sides applied
    359  // (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides).
    360  const LogicalMargin mBorderPadding;
    361 
    362  // The overflow areas of all floats placed so far
    363  OverflowAreas mFloatOverflowAreas;
    364 
    365  // Previous child. This is used when pulling up a frame to update
    366  // the sibling list.
    367  nsIFrame* mPrevChild;
    368 
    369  // The previous child frames collapsed bottom margin value.
    370  CollapsingMargin mPrevBEndMargin;
    371 
    372  // The current next-in-flow for the block. When lines are pulled
    373  // from a next-in-flow, this is used to know which next-in-flow to
    374  // pull from. When a next-in-flow is emptied of lines, we advance
    375  // this to the next next-in-flow.
    376  nsBlockFrame* mNextInFlow;
    377 
    378  //----------------------------------------
    379 
    380  // Temporary state, for line-reflow. This state is used during the reflow
    381  // of a given line, but doesn't have meaning before or after.
    382 
    383  // The list of floats that are "current-line" floats. These are
    384  // added to the line after the line has been reflowed, to keep the
    385  // list fiddling from being N^2.
    386  nsTArray<nsIFrame*> mCurrentLineFloats;
    387 
    388  // The list of floats which are "below current-line"
    389  // floats. These are reflowed/placed after the line is reflowed
    390  // and placed. Again, this is done to keep the list fiddling from
    391  // being N^2.
    392  nsTArray<nsIFrame*> mBelowCurrentLineFloats;
    393 
    394  // The list of floats that are waiting on a break opportunity in order to be
    395  // placed, since we're on a nowrap context.
    396  nsTArray<nsIFrame*> mNoWrapFloats;
    397 
    398  const nscoord mMinLineHeight;
    399 
    400  int32_t mLineNumber;
    401 
    402  Flags mFlags;
    403 
    404  // Cache the result of nsBlockFrame::FindTrailingClear() from mBlock's
    405  // prev-in-flows. See nsBlockFrame::ReflowPushedFloats().
    406  UsedClear mTrailingClearFromPIF;
    407 
    408  // The amount of computed content block-size "consumed" by our previous
    409  // continuations.
    410  const nscoord mConsumedBSize;
    411 
    412  // The amount of block-axis alignment shift to assume during reflow.
    413  // Cached between reflows in the AlignContentShift property.
    414  // (This system optimizes reflow for not changing the shift.)
    415  nscoord mAlignContentShift;
    416 
    417  // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to
    418  // place the line. When redoing the line, it will be used to query the
    419  // accurate float available space in AddFloat() and
    420  // nsBlockFrame::PlaceLine().
    421  Maybe<nscoord> mLineBSize;
    422 
    423 private:
    424  bool CanPlaceFloat(nscoord aFloatISize,
    425                     const nsFlowAreaRect& aFloatAvailableSpace);
    426 
    427  void PushFloatPastBreak(nsIFrame* aFloat);
    428 
    429  void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord);
    430 };
    431 
    432 };  // namespace mozilla
    433 
    434 #endif  // BlockReflowState_h