tor-browser

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

nsFloatManager.h (18682B)


      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 /* class that manages rules for positioning floats */
      8 
      9 #ifndef nsFloatManager_h_
     10 #define nsFloatManager_h_
     11 
     12 #include "mozilla/TypedEnumBits.h"
     13 #include "mozilla/UniquePtr.h"
     14 #include "mozilla/WritingModes.h"
     15 #include "nsCoord.h"
     16 #include "nsFrameList.h"  // for DEBUG_FRAME_DUMP
     17 #include "nsIntervalSet.h"
     18 #include "nsPoint.h"
     19 #include "nsTArray.h"
     20 
     21 class nsIFrame;
     22 class nsPresContext;
     23 namespace mozilla {
     24 struct ReflowInput;
     25 class PresShell;
     26 }  // namespace mozilla
     27 
     28 enum class nsFlowAreaRectFlags : uint32_t {
     29  NoFlags = 0,
     30  HasFloats = 1 << 0,
     31  MayWiden = 1 << 1,
     32  ISizeIsActuallyNegative = 1 << 2,
     33 };
     34 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsFlowAreaRectFlags)
     35 
     36 /**
     37 * The available space for content not occupied by floats is divided
     38 * into a sequence of rectangles in the block direction.  However, we
     39 * need to know not only the rectangle, but also whether it was reduced
     40 * (from the content rectangle) by floats that actually intruded into
     41 * the content rectangle. If it has been reduced by floats, then we also
     42 * track whether the flow area might widen as the floats narrow in the
     43 * block direction.
     44 */
     45 struct nsFlowAreaRect {
     46  mozilla::LogicalRect mRect;
     47 
     48  nsFlowAreaRectFlags mAreaFlags;
     49 
     50  nsFlowAreaRect(mozilla::WritingMode aWritingMode, nscoord aICoord,
     51                 nscoord aBCoord, nscoord aISize, nscoord aBSize,
     52                 nsFlowAreaRectFlags aAreaFlags)
     53      : mRect(aWritingMode, aICoord, aBCoord, aISize, aBSize),
     54        mAreaFlags(aAreaFlags) {}
     55 
     56  bool HasFloats() const {
     57    return (bool)(mAreaFlags & nsFlowAreaRectFlags::HasFloats);
     58  }
     59  bool MayWiden() const {
     60    return (bool)(mAreaFlags & nsFlowAreaRectFlags::MayWiden);
     61  }
     62  bool ISizeIsActuallyNegative() const {
     63    return (bool)(mAreaFlags & nsFlowAreaRectFlags::ISizeIsActuallyNegative);
     64  }
     65 };
     66 
     67 #define NS_FLOAT_MANAGER_CACHE_SIZE 64
     68 
     69 /**
     70 * nsFloatManager is responsible for implementing CSS's rules for
     71 * positioning floats. An nsFloatManager object is created during reflow for
     72 * any block with NS_BLOCK_BFC. During reflow, the float manager for the nearest
     73 * such ancestor block is found in ReflowInput::mFloatManager.
     74 *
     75 * According to the line-relative mappings in CSS Writing Modes spec [1],
     76 * line-right and line-left are calculated with respect to the writing mode
     77 * of the containing block of the floats. All the writing modes passed to
     78 * nsFloatManager methods should be the containing block's writing mode.
     79 *
     80 * However, according to the abstract-to-physical mappings table [2], the
     81 * 'direction' property of the containing block doesn't affect the
     82 * interpretation of line-right and line-left. We actually implement this by
     83 * passing in the writing mode of the block formatting context (BFC), i.e.
     84 * the of BlockReflowState's writing mode.
     85 *
     86 * nsFloatManager uses a special logical coordinate space with inline
     87 * coordinates on the line-axis and block coordinates on the block-axis
     88 * based on the writing mode of the block formatting context. All the
     89 * physical types like nsRect, nsPoint, etc. use this coordinate space. See
     90 * FloatInfo::mRect for an example.
     91 *
     92 * [1] https://drafts.csswg.org/css-writing-modes/#line-mappings
     93 * [2] https://drafts.csswg.org/css-writing-modes/#logical-to-physical
     94 */
     95 class nsFloatManager {
     96 public:
     97  explicit nsFloatManager(mozilla::PresShell* aPresShell,
     98                          mozilla::WritingMode aWM);
     99  ~nsFloatManager();
    100 
    101  void* operator new(size_t aSize) noexcept(true);
    102  void operator delete(void* aPtr, size_t aSize);
    103 
    104  static void Shutdown();
    105 
    106  /**
    107   * Get float region stored on the frame. (Defaults to mRect if it's
    108   * not there.) The float region is the area impacted by this float;
    109   * the coordinates are relative to the containing block frame.
    110   */
    111  static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM,
    112                                           nsIFrame* aFloatFrame,
    113                                           const nsSize& aContainerSize);
    114  /**
    115   * Calculate the float region for this frame using aMargin and the
    116   * frame's mRect. The region includes the margins around the float,
    117   * but doesn't include the relative offsets.
    118   * Note that if the frame is or has a continuation, aMargin's top
    119   * and/or bottom must be zeroed by the caller.
    120   */
    121  static mozilla::LogicalRect CalculateRegionFor(
    122      mozilla::WritingMode aWM, nsIFrame* aFloatFrame,
    123      const mozilla::LogicalMargin& aMargin, const nsSize& aContainerSize);
    124  /**
    125   * Store the float region on the frame. The region is stored
    126   * as a delta against the mRect, so repositioning the frame will
    127   * also reposition the float region.
    128   */
    129  static void StoreRegionFor(mozilla::WritingMode aWM, nsIFrame* aFloat,
    130                             const mozilla::LogicalRect& aRegion,
    131                             const nsSize& aContainerSize);
    132 
    133  // Structure that stores the current state of a float manager for
    134  // Save/Restore purposes.
    135  struct SavedState {
    136    explicit SavedState()
    137        : mFloatInfoCount(0),
    138          mLineLeft(0),
    139          mBlockStart(0),
    140          mPushedLeftFloatPastBreak(false),
    141          mPushedRightFloatPastBreak(false),
    142          mSplitLeftFloatAcrossBreak(false),
    143          mSplitRightFloatAcrossBreak(false) {}
    144 
    145   private:
    146    uint32_t mFloatInfoCount;
    147    nscoord mLineLeft, mBlockStart;
    148    bool mPushedLeftFloatPastBreak;
    149    bool mPushedRightFloatPastBreak;
    150    bool mSplitLeftFloatAcrossBreak;
    151    bool mSplitRightFloatAcrossBreak;
    152 
    153    friend class nsFloatManager;
    154  };
    155 
    156  /**
    157   * Translate the current origin by the specified offsets. This
    158   * creates a new local coordinate space relative to the current
    159   * coordinate space.
    160   */
    161  void Translate(nscoord aLineLeft, nscoord aBlockStart) {
    162    mLineLeft += aLineLeft;
    163    mBlockStart += aBlockStart;
    164  }
    165 
    166  /**
    167   * Returns the current translation from local coordinate space to
    168   * world coordinate space. This represents the accumulated calls to
    169   * Translate().
    170   */
    171  void GetTranslation(nscoord& aLineLeft, nscoord& aBlockStart) const {
    172    aLineLeft = mLineLeft;
    173    aBlockStart = mBlockStart;
    174  }
    175 
    176  /**
    177   * Get information about the area available to content that flows
    178   * around floats.  Two different types of space can be requested:
    179   *   BandFromPoint: returns the band containing block-dir coordinate
    180   *     |aBCoord| (though actually with the top truncated to begin at
    181   *     aBCoord), but up to at most |aBSize| (which may be nscoord_MAX).
    182   *     This will return the tallest rectangle whose block start is
    183   *     |aBCoord| and in which there are no changes in what floats are
    184   *     on the sides of that rectangle, but will limit the block size
    185   *     of the rectangle to |aBSize|.  The inline start and end edges
    186   *     of the rectangle give the area available for line boxes in that
    187   *     space. The inline size of this resulting rectangle will not be
    188   *     negative.
    189   *   WidthWithinHeight: This returns a rectangle whose block start
    190   *     is aBCoord and whose block size is exactly aBSize.  Its inline
    191   *     start and end edges give the corresponding edges of the space
    192   *     that can be used for line boxes *throughout* that space.  (It
    193   *     is possible that more inline space could be used in part of the
    194   *     space if a float begins or ends in it.)  The inline size of the
    195   *     resulting rectangle can be negative.
    196   *
    197   * ShapeType can be used to request two different types of flow areas.
    198   * (This is the float area defined in CSS Shapes Module Level 1 ยง1.4):
    199   *    Margin: uses the float element's margin-box to request the flow area.
    200   *    ShapeOutside: uses the float element's shape-outside value to request
    201   *      the float area.
    202   *
    203   * @param aBCoord [in] block-dir coordinate for block start of available space
    204   *          desired, which are positioned relative to the current translation.
    205   * @param aBSize [in] see above
    206   * @param aContentArea [in] an nsRect representing the content area
    207   * @param aState [in] If null, use the current state, otherwise, do
    208   *                    computation based only on floats present in the given
    209   *                    saved state.
    210   * @return An nsFlowAreaRect whose:
    211   *           mRect is the resulting rectangle for line boxes.  It will not
    212   *             extend beyond aContentArea's inline bounds, but may be
    213   *             narrower when floats are present.
    214   *           mHasFloats is whether there are floats at the sides of the
    215   *             return value including those that do not reduce the line box
    216   *             inline size at all (because they are entirely in the margins)
    217   */
    218  enum class BandInfoType { BandFromPoint, WidthWithinHeight };
    219  enum class ShapeType { Margin, ShapeOutside };
    220  nsFlowAreaRect GetFlowArea(mozilla::WritingMode aCBWM,
    221                             mozilla::WritingMode aWM, nscoord aBCoord,
    222                             nscoord aBSize, BandInfoType aBandInfoType,
    223                             ShapeType aShapeType,
    224                             mozilla::LogicalRect aContentArea,
    225                             SavedState* aState,
    226                             const nsSize& aContainerSize) const;
    227 
    228  /**
    229   * Add a float that comes after all floats previously added.  Its
    230   * block start must be even with or below the top of all previous
    231   * floats.
    232   *
    233   * aMarginRect is relative to the current translation.  The caller
    234   * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0.
    235   */
    236  void AddFloat(nsIFrame* aFloatFrame, const mozilla::LogicalRect& aMarginRect,
    237                mozilla::WritingMode aWM, const nsSize& aContainerSize);
    238 
    239  /**
    240   * Notify that we tried to place a float that could not fit at all and
    241   * had to be pushed to the next page/column?  (If so, we can't place
    242   * any more floats in this page/column because of the rule that the
    243   * top of a float cannot be above the top of an earlier float.  It
    244   * also means that any clear needs to continue to the next column.)
    245   */
    246  void SetPushedLeftFloatPastBreak() { mPushedLeftFloatPastBreak = true; }
    247  void SetPushedRightFloatPastBreak() { mPushedRightFloatPastBreak = true; }
    248 
    249  /**
    250   * Notify that we split a float, with part of it needing to be pushed
    251   * to the next page/column.  (This means that any 'clear' needs to
    252   * continue to the next page/column.)
    253   */
    254  void SetSplitLeftFloatAcrossBreak() { mSplitLeftFloatAcrossBreak = true; }
    255  void SetSplitRightFloatAcrossBreak() { mSplitRightFloatAcrossBreak = true; }
    256 
    257  /**
    258   * Remove the regions associated with this floating frame and its
    259   * next-sibling list.  Some of the frames may never have been added;
    260   * we just skip those. This is not fully general; it only works as
    261   * long as the N frames to be removed are the last N frames to have
    262   * been added; if there's a frame in the middle of them that should
    263   * not be removed, YOU LOSE.
    264   */
    265  nsresult RemoveTrailingRegions(nsIFrame* aFrameList);
    266 
    267  bool HasAnyFloats() const { return !mFloats.IsEmpty(); }
    268 
    269  /**
    270   * Methods for dealing with the propagation of float damage during
    271   * reflow.
    272   */
    273  bool HasFloatDamage() const { return !mFloatDamage.IsEmpty(); }
    274 
    275  void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) {
    276    mFloatDamage.IncludeInterval(aIntervalBegin + mBlockStart,
    277                                 aIntervalEnd + mBlockStart);
    278  }
    279 
    280  bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const {
    281    return mFloatDamage.Intersects(aIntervalBegin + mBlockStart,
    282                                   aIntervalEnd + mBlockStart);
    283  }
    284 
    285  /**
    286   * Saves the current state of the float manager into aState.
    287   */
    288  void PushState(SavedState* aState);
    289 
    290  /**
    291   * Restores the float manager to the saved state.
    292   *
    293   * These states must be managed using stack discipline. PopState can only
    294   * be used after PushState has been used to save the state, and it can only
    295   * be used once --- although it can be omitted; saved states can be ignored.
    296   * States must be popped in the reverse order they were pushed.  A
    297   * call to PopState invalidates any saved states Pushed after the
    298   * state passed to PopState was pushed.
    299   */
    300  void PopState(SavedState* aState);
    301 
    302  /**
    303   * Get the block start of the last float placed into the float
    304   * manager, to enforce the rule that a float can't be above an earlier
    305   * float. Returns the minimum nscoord value if there are no floats.
    306   *
    307   * The result is relative to the current translation.
    308   */
    309  nscoord LowestFloatBStart() const;
    310 
    311  /**
    312   * Return the coordinate of the lowest float matching aClearType in
    313   * this float manager. Returns aBCoord if there are no matching
    314   * floats.
    315   *
    316   * Both aBCoord and the result are relative to the current translation.
    317   */
    318  nscoord ClearFloats(nscoord aBCoord, mozilla::UsedClear aClearType) const;
    319 
    320  /**
    321   * Checks if clear would pass into the floats' BFC's next-in-flow,
    322   * i.e. whether floats affecting this clear have continuations.
    323   */
    324  bool ClearContinues(mozilla::UsedClear aClearType) const;
    325 
    326  void AssertStateMatches(SavedState* aState) const {
    327    NS_ASSERTION(
    328        aState->mLineLeft == mLineLeft && aState->mBlockStart == mBlockStart &&
    329            aState->mPushedLeftFloatPastBreak == mPushedLeftFloatPastBreak &&
    330            aState->mPushedRightFloatPastBreak == mPushedRightFloatPastBreak &&
    331            aState->mSplitLeftFloatAcrossBreak == mSplitLeftFloatAcrossBreak &&
    332            aState->mSplitRightFloatAcrossBreak ==
    333                mSplitRightFloatAcrossBreak &&
    334            aState->mFloatInfoCount == mFloats.Length(),
    335        "float manager state should match saved state");
    336  }
    337 
    338 #ifdef DEBUG_FRAME_DUMP
    339  /**
    340   * Dump the state of the float manager out to a file.
    341   */
    342  nsresult List(FILE* out) const;
    343 #endif
    344 
    345 private:
    346  class ShapeInfo;
    347  class RoundedBoxShapeInfo;
    348  class EllipseShapeInfo;
    349  class PolygonShapeInfo;
    350  class ImageShapeInfo;
    351 
    352  struct FloatInfo {
    353    nsIFrame* const mFrame;
    354    // The lowest block-ends of left/right floats up to and including
    355    // this one.
    356    nscoord mLeftBEnd, mRightBEnd;
    357 
    358    FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBlockStart,
    359              const mozilla::LogicalRect& aMarginRect, mozilla::WritingMode aWM,
    360              const nsSize& aContainerSize);
    361 
    362    nscoord LineLeft() const { return mRect.x; }
    363    nscoord LineRight() const { return mRect.XMost(); }
    364    nscoord ISize() const { return mRect.width; }
    365    nscoord BStart() const { return mRect.y; }
    366    nscoord BEnd() const { return mRect.YMost(); }
    367    nscoord BSize() const { return mRect.height; }
    368    bool IsEmpty() const { return mRect.IsEmpty(); }
    369 
    370    // aBStart and aBEnd are the starting and ending coordinate of a band.
    371    // LineLeft() and LineRight() return the innermost line-left extent and
    372    // line-right extent within the given band, respectively.
    373    nscoord LineLeft(ShapeType aShapeType, const nscoord aBStart,
    374                     const nscoord aBEnd) const;
    375    nscoord LineRight(ShapeType aShapeType, const nscoord aBStart,
    376                      const nscoord aBEnd) const;
    377    nscoord BStart(ShapeType aShapeType) const;
    378    nscoord BEnd(ShapeType aShapeType) const;
    379    bool IsEmpty(ShapeType aShapeType) const;
    380    bool MayNarrowInBlockDirection(ShapeType aShapeType) const;
    381 
    382 #ifdef NS_BUILD_REFCNT_LOGGING
    383    FloatInfo(FloatInfo&& aOther);
    384    ~FloatInfo();
    385 #endif
    386 
    387    // NB! This is really a logical rect in a writing mode suitable for
    388    // placing floats, which is not necessarily the actual writing mode
    389    // either of the block which created the float manager or the block
    390    // that is calling the float manager. The inline coordinates are in
    391    // the line-relative axis of the float manager and its block
    392    // coordinates are in the float manager's block direction.
    393    nsRect mRect;
    394    // Pointer to a concrete subclass of ShapeInfo or null, which means that
    395    // there is no shape-outside.
    396    mozilla::UniquePtr<ShapeInfo> mShapeInfo;
    397  };
    398 
    399 #ifdef DEBUG
    400  // Store the writing mode from the block frame which establishes the block
    401  // formatting context (BFC) when the nsFloatManager is created.
    402  mozilla::WritingMode mWritingMode;
    403 #endif
    404 
    405  // Translation from local to global coordinate space.
    406  nscoord mLineLeft, mBlockStart;
    407  // We use 11 here in order to fill up the jemalloc allocatoed chunk nicely,
    408  // see https://bugzilla.mozilla.org/show_bug.cgi?id=1362876#c6.
    409  AutoTArray<FloatInfo, 11> mFloats;
    410  nsIntervalSet mFloatDamage;
    411 
    412  // Did we try to place a float that could not fit at all and had to be
    413  // pushed to the next page/column?  If so, we can't place any more
    414  // floats in this page/column because of the rule that the top of a
    415  // float cannot be above the top of an earlier float.  And we also
    416  // need to apply this information to 'clear', and thus need to
    417  // separate left and right floats.
    418  bool mPushedLeftFloatPastBreak;
    419  bool mPushedRightFloatPastBreak;
    420 
    421  // Did we split a float, with part of it needing to be pushed to the
    422  // next page/column.  This means that any 'clear' needs to continue to
    423  // the next page/column.
    424  bool mSplitLeftFloatAcrossBreak;
    425  bool mSplitRightFloatAcrossBreak;
    426 
    427  static int32_t sCachedFloatManagerCount;
    428  static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE];
    429 
    430  nsFloatManager(const nsFloatManager&) = delete;
    431  void operator=(const nsFloatManager&) = delete;
    432 };
    433 
    434 /**
    435 * A helper class to manage maintenance of the float manager during
    436 * nsBlockFrame::Reflow. It automatically restores the old float
    437 * manager in the reflow input when the object goes out of scope.
    438 */
    439 class nsAutoFloatManager {
    440  using ReflowInput = mozilla::ReflowInput;
    441 
    442 public:
    443  explicit nsAutoFloatManager(ReflowInput& aReflowInput)
    444      : mReflowInput(aReflowInput), mOld(nullptr) {}
    445 
    446  ~nsAutoFloatManager();
    447 
    448  /**
    449   * Create a new float manager for the specified frame. This will
    450   * `remember' the old float manager, and install the new float
    451   * manager in the reflow input.
    452   */
    453  void CreateFloatManager(nsPresContext* aPresContext);
    454 
    455 protected:
    456  ReflowInput& mReflowInput;
    457  mozilla::UniquePtr<nsFloatManager> mNew;
    458 
    459  // A non-owning pointer, which points to the object owned by
    460  // nsAutoFloatManager::mNew.
    461  nsFloatManager* mOld;
    462 };
    463 
    464 #endif /* !defined(nsFloatManager_h_) */