Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | // vim:cindent:ts=2:et:sw=2: |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* class that manages rules for positioning floats */ |
michael@0 | 8 | |
michael@0 | 9 | #ifndef nsFloatManager_h_ |
michael@0 | 10 | #define nsFloatManager_h_ |
michael@0 | 11 | |
michael@0 | 12 | #include "mozilla/Attributes.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "nsIntervalSet.h" |
michael@0 | 15 | #include "nsCoord.h" |
michael@0 | 16 | #include "nsRect.h" |
michael@0 | 17 | #include "nsTArray.h" |
michael@0 | 18 | #include "nsFrameList.h" // for DEBUG_FRAME_DUMP |
michael@0 | 19 | |
michael@0 | 20 | class nsIPresShell; |
michael@0 | 21 | class nsIFrame; |
michael@0 | 22 | struct nsHTMLReflowState; |
michael@0 | 23 | class nsPresContext; |
michael@0 | 24 | |
michael@0 | 25 | /** |
michael@0 | 26 | * The available space for content not occupied by floats is divided |
michael@0 | 27 | * into a (vertical) sequence of rectangles. However, we need to know |
michael@0 | 28 | * not only the rectangle, but also whether it was reduced (from the |
michael@0 | 29 | * content rectangle) by floats that actually intruded into the content |
michael@0 | 30 | * rectangle. |
michael@0 | 31 | */ |
michael@0 | 32 | struct nsFlowAreaRect { |
michael@0 | 33 | nsRect mRect; |
michael@0 | 34 | bool mHasFloats; |
michael@0 | 35 | |
michael@0 | 36 | nsFlowAreaRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, |
michael@0 | 37 | bool aHasFloats) |
michael@0 | 38 | : mRect(aX, aY, aWidth, aHeight), mHasFloats(aHasFloats) {} |
michael@0 | 39 | }; |
michael@0 | 40 | |
michael@0 | 41 | #define NS_FLOAT_MANAGER_CACHE_SIZE 4 |
michael@0 | 42 | |
michael@0 | 43 | class nsFloatManager { |
michael@0 | 44 | public: |
michael@0 | 45 | nsFloatManager(nsIPresShell* aPresShell); |
michael@0 | 46 | ~nsFloatManager(); |
michael@0 | 47 | |
michael@0 | 48 | void* operator new(size_t aSize) CPP_THROW_NEW; |
michael@0 | 49 | void operator delete(void* aPtr, size_t aSize); |
michael@0 | 50 | |
michael@0 | 51 | static void Shutdown(); |
michael@0 | 52 | |
michael@0 | 53 | /** |
michael@0 | 54 | * Get float region stored on the frame. (Defaults to mRect if it's |
michael@0 | 55 | * not there.) The float region is the area impacted by this float; |
michael@0 | 56 | * the coordinates are relative to the containing block frame. |
michael@0 | 57 | */ |
michael@0 | 58 | static nsRect GetRegionFor(nsIFrame* aFloatFrame); |
michael@0 | 59 | /** |
michael@0 | 60 | * Calculate the float region for this frame using aMargin and the |
michael@0 | 61 | * frame's mRect. The region includes the margins around the float, |
michael@0 | 62 | * but doesn't include the relative offsets. |
michael@0 | 63 | * Note that if the frame is or has a continuation, aMargin's top |
michael@0 | 64 | * and/or bottom must be zeroed by the caller. |
michael@0 | 65 | */ |
michael@0 | 66 | static nsRect CalculateRegionFor(nsIFrame* aFloatFrame, |
michael@0 | 67 | const nsMargin& aMargin); |
michael@0 | 68 | /** |
michael@0 | 69 | * Store the float region on the frame. The region is stored |
michael@0 | 70 | * as a delta against the mRect, so repositioning the frame will |
michael@0 | 71 | * also reposition the float region. |
michael@0 | 72 | */ |
michael@0 | 73 | static void StoreRegionFor(nsIFrame* aFloat, nsRect& aRegion); |
michael@0 | 74 | |
michael@0 | 75 | // Structure that stores the current state of a frame manager for |
michael@0 | 76 | // Save/Restore purposes. |
michael@0 | 77 | struct SavedState; |
michael@0 | 78 | friend struct SavedState; |
michael@0 | 79 | struct SavedState { |
michael@0 | 80 | private: |
michael@0 | 81 | uint32_t mFloatInfoCount; |
michael@0 | 82 | nscoord mX, mY; |
michael@0 | 83 | bool mPushedLeftFloatPastBreak; |
michael@0 | 84 | bool mPushedRightFloatPastBreak; |
michael@0 | 85 | bool mSplitLeftFloatAcrossBreak; |
michael@0 | 86 | bool mSplitRightFloatAcrossBreak; |
michael@0 | 87 | |
michael@0 | 88 | friend class nsFloatManager; |
michael@0 | 89 | }; |
michael@0 | 90 | |
michael@0 | 91 | /** |
michael@0 | 92 | * Translate the current origin by the specified (dx, dy). This |
michael@0 | 93 | * creates a new local coordinate space relative to the current |
michael@0 | 94 | * coordinate space. |
michael@0 | 95 | */ |
michael@0 | 96 | void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; } |
michael@0 | 97 | |
michael@0 | 98 | /** |
michael@0 | 99 | * Returns the current translation from local coordinate space to |
michael@0 | 100 | * world coordinate space. This represents the accumulated calls to |
michael@0 | 101 | * Translate(). |
michael@0 | 102 | */ |
michael@0 | 103 | void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; } |
michael@0 | 104 | |
michael@0 | 105 | /** |
michael@0 | 106 | * Get information about the area available to content that flows |
michael@0 | 107 | * around floats. Two different types of space can be requested: |
michael@0 | 108 | * BAND_FROM_POINT: returns the band containing vertical coordinate |
michael@0 | 109 | * |aY| (though actually with the top truncated to begin at aY), |
michael@0 | 110 | * but up to at most |aHeight| (which may be nscoord_MAX). |
michael@0 | 111 | * This will return the tallest rectangle whose top is |aY| and in |
michael@0 | 112 | * which there are no changes in what floats are on the sides of |
michael@0 | 113 | * that rectangle, but will limit the height of the rectangle to |
michael@0 | 114 | * |aHeight|. The left and right edges of the rectangle give the |
michael@0 | 115 | * area available for line boxes in that space. The width of this |
michael@0 | 116 | * resulting rectangle will not be negative. |
michael@0 | 117 | * WIDTH_WITHIN_HEIGHT: This returns a rectangle whose top is aY and |
michael@0 | 118 | * whose height is exactly aHeight. Its left and right edges give |
michael@0 | 119 | * the left and right edges of the space that can be used for line |
michael@0 | 120 | * boxes *throughout* that space. (It is possible that more |
michael@0 | 121 | * horizontal space could be used in part of the space if a float |
michael@0 | 122 | * begins or ends in it.) The width of the resulting rectangle |
michael@0 | 123 | * can be negative. |
michael@0 | 124 | * |
michael@0 | 125 | * @param aY [in] vertical coordinate for top of available space |
michael@0 | 126 | * desired |
michael@0 | 127 | * @param aHeight [in] see above |
michael@0 | 128 | * @param aContentArea [in] an nsRect representing the content area |
michael@0 | 129 | * @param aState [in] If null, use the current state, otherwise, do |
michael@0 | 130 | * computation based only on floats present in the given |
michael@0 | 131 | * saved state. |
michael@0 | 132 | * @return An nsFlowAreaRect whose: |
michael@0 | 133 | * mRect is the resulting rectangle for line boxes. It will not |
michael@0 | 134 | * extend beyond aContentArea's horizontal bounds, but may be |
michael@0 | 135 | * narrower when floats are present. |
michael@0 | 136 | * mBandHasFloats is whether there are floats at the sides of the |
michael@0 | 137 | * return value including those that do not reduce the line box |
michael@0 | 138 | * width at all (because they are entirely in the margins) |
michael@0 | 139 | * |
michael@0 | 140 | * aY and aAvailSpace are positioned relative to the current translation |
michael@0 | 141 | */ |
michael@0 | 142 | enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT }; |
michael@0 | 143 | nsFlowAreaRect GetFlowArea(nscoord aY, BandInfoType aInfoType, |
michael@0 | 144 | nscoord aHeight, nsRect aContentArea, |
michael@0 | 145 | SavedState* aState) const; |
michael@0 | 146 | |
michael@0 | 147 | /** |
michael@0 | 148 | * Add a float that comes after all floats previously added. Its top |
michael@0 | 149 | * must be even with or below the top of all previous floats. |
michael@0 | 150 | * |
michael@0 | 151 | * aMarginRect is relative to the current translation. The caller |
michael@0 | 152 | * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0. |
michael@0 | 153 | */ |
michael@0 | 154 | nsresult AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect); |
michael@0 | 155 | |
michael@0 | 156 | /** |
michael@0 | 157 | * Notify that we tried to place a float that could not fit at all and |
michael@0 | 158 | * had to be pushed to the next page/column? (If so, we can't place |
michael@0 | 159 | * any more floats in this page/column because of the rule that the |
michael@0 | 160 | * top of a float cannot be above the top of an earlier float. It |
michael@0 | 161 | * also means that any clear needs to continue to the next column.) |
michael@0 | 162 | */ |
michael@0 | 163 | void SetPushedLeftFloatPastBreak() |
michael@0 | 164 | { mPushedLeftFloatPastBreak = true; } |
michael@0 | 165 | void SetPushedRightFloatPastBreak() |
michael@0 | 166 | { mPushedRightFloatPastBreak = true; } |
michael@0 | 167 | |
michael@0 | 168 | /** |
michael@0 | 169 | * Notify that we split a float, with part of it needing to be pushed |
michael@0 | 170 | * to the next page/column. (This means that any 'clear' needs to |
michael@0 | 171 | * continue to the next page/column.) |
michael@0 | 172 | */ |
michael@0 | 173 | void SetSplitLeftFloatAcrossBreak() |
michael@0 | 174 | { mSplitLeftFloatAcrossBreak = true; } |
michael@0 | 175 | void SetSplitRightFloatAcrossBreak() |
michael@0 | 176 | { mSplitRightFloatAcrossBreak = true; } |
michael@0 | 177 | |
michael@0 | 178 | /** |
michael@0 | 179 | * Remove the regions associated with this floating frame and its |
michael@0 | 180 | * next-sibling list. Some of the frames may never have been added; |
michael@0 | 181 | * we just skip those. This is not fully general; it only works as |
michael@0 | 182 | * long as the N frames to be removed are the last N frames to have |
michael@0 | 183 | * been added; if there's a frame in the middle of them that should |
michael@0 | 184 | * not be removed, YOU LOSE. |
michael@0 | 185 | */ |
michael@0 | 186 | nsresult RemoveTrailingRegions(nsIFrame* aFrameList); |
michael@0 | 187 | |
michael@0 | 188 | private: |
michael@0 | 189 | struct FloatInfo; |
michael@0 | 190 | public: |
michael@0 | 191 | |
michael@0 | 192 | bool HasAnyFloats() const { return !mFloats.IsEmpty(); } |
michael@0 | 193 | |
michael@0 | 194 | /** |
michael@0 | 195 | * Methods for dealing with the propagation of float damage during |
michael@0 | 196 | * reflow. |
michael@0 | 197 | */ |
michael@0 | 198 | bool HasFloatDamage() const |
michael@0 | 199 | { |
michael@0 | 200 | return !mFloatDamage.IsEmpty(); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) |
michael@0 | 204 | { |
michael@0 | 205 | mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const |
michael@0 | 209 | { |
michael@0 | 210 | return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | /** |
michael@0 | 214 | * Saves the current state of the float manager into aState. |
michael@0 | 215 | */ |
michael@0 | 216 | void PushState(SavedState* aState); |
michael@0 | 217 | |
michael@0 | 218 | /** |
michael@0 | 219 | * Restores the float manager to the saved state. |
michael@0 | 220 | * |
michael@0 | 221 | * These states must be managed using stack discipline. PopState can only |
michael@0 | 222 | * be used after PushState has been used to save the state, and it can only |
michael@0 | 223 | * be used once --- although it can be omitted; saved states can be ignored. |
michael@0 | 224 | * States must be popped in the reverse order they were pushed. A |
michael@0 | 225 | * call to PopState invalidates any saved states Pushed after the |
michael@0 | 226 | * state passed to PopState was pushed. |
michael@0 | 227 | */ |
michael@0 | 228 | void PopState(SavedState* aState); |
michael@0 | 229 | |
michael@0 | 230 | /** |
michael@0 | 231 | * Get the top of the last float placed into the float manager, to |
michael@0 | 232 | * enforce the rule that a float can't be above an earlier float. |
michael@0 | 233 | * Returns the minimum nscoord value if there are no floats. |
michael@0 | 234 | * |
michael@0 | 235 | * The result is relative to the current translation. |
michael@0 | 236 | */ |
michael@0 | 237 | nscoord GetLowestFloatTop() const; |
michael@0 | 238 | |
michael@0 | 239 | /** |
michael@0 | 240 | * Return the coordinate of the lowest float matching aBreakType in this |
michael@0 | 241 | * float manager. Returns aY if there are no matching floats. |
michael@0 | 242 | * |
michael@0 | 243 | * Both aY and the result are relative to the current translation. |
michael@0 | 244 | */ |
michael@0 | 245 | enum { |
michael@0 | 246 | // Tell ClearFloats not to push to nscoord_MAX when floats have been |
michael@0 | 247 | // pushed to the next page/column. |
michael@0 | 248 | DONT_CLEAR_PUSHED_FLOATS = (1<<0) |
michael@0 | 249 | }; |
michael@0 | 250 | nscoord ClearFloats(nscoord aY, uint8_t aBreakType, uint32_t aFlags = 0) const; |
michael@0 | 251 | |
michael@0 | 252 | /** |
michael@0 | 253 | * Checks if clear would pass into the floats' BFC's next-in-flow, |
michael@0 | 254 | * i.e. whether floats affecting this clear have continuations. |
michael@0 | 255 | */ |
michael@0 | 256 | bool ClearContinues(uint8_t aBreakType) const; |
michael@0 | 257 | |
michael@0 | 258 | void AssertStateMatches(SavedState *aState) const |
michael@0 | 259 | { |
michael@0 | 260 | NS_ASSERTION(aState->mX == mX && aState->mY == mY && |
michael@0 | 261 | aState->mPushedLeftFloatPastBreak == |
michael@0 | 262 | mPushedLeftFloatPastBreak && |
michael@0 | 263 | aState->mPushedRightFloatPastBreak == |
michael@0 | 264 | mPushedRightFloatPastBreak && |
michael@0 | 265 | aState->mSplitLeftFloatAcrossBreak == |
michael@0 | 266 | mSplitLeftFloatAcrossBreak && |
michael@0 | 267 | aState->mSplitRightFloatAcrossBreak == |
michael@0 | 268 | mSplitRightFloatAcrossBreak && |
michael@0 | 269 | aState->mFloatInfoCount == mFloats.Length(), |
michael@0 | 270 | "float manager state should match saved state"); |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 274 | /** |
michael@0 | 275 | * Dump the state of the float manager out to a file. |
michael@0 | 276 | */ |
michael@0 | 277 | nsresult List(FILE* out) const; |
michael@0 | 278 | #endif |
michael@0 | 279 | |
michael@0 | 280 | private: |
michael@0 | 281 | |
michael@0 | 282 | struct FloatInfo { |
michael@0 | 283 | nsIFrame *const mFrame; |
michael@0 | 284 | nsRect mRect; |
michael@0 | 285 | // The lowest bottoms of left/right floats up to and including this one. |
michael@0 | 286 | nscoord mLeftYMost, mRightYMost; |
michael@0 | 287 | |
michael@0 | 288 | FloatInfo(nsIFrame* aFrame, const nsRect& aRect); |
michael@0 | 289 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 290 | FloatInfo(const FloatInfo& aOther); |
michael@0 | 291 | ~FloatInfo(); |
michael@0 | 292 | #endif |
michael@0 | 293 | }; |
michael@0 | 294 | |
michael@0 | 295 | nscoord mX, mY; // translation from local to global coordinate space |
michael@0 | 296 | nsTArray<FloatInfo> mFloats; |
michael@0 | 297 | nsIntervalSet mFloatDamage; |
michael@0 | 298 | |
michael@0 | 299 | // Did we try to place a float that could not fit at all and had to be |
michael@0 | 300 | // pushed to the next page/column? If so, we can't place any more |
michael@0 | 301 | // floats in this page/column because of the rule that the top of a |
michael@0 | 302 | // float cannot be above the top of an earlier float. And we also |
michael@0 | 303 | // need to apply this information to 'clear', and thus need to |
michael@0 | 304 | // separate left and right floats. |
michael@0 | 305 | bool mPushedLeftFloatPastBreak; |
michael@0 | 306 | bool mPushedRightFloatPastBreak; |
michael@0 | 307 | |
michael@0 | 308 | // Did we split a float, with part of it needing to be pushed to the |
michael@0 | 309 | // next page/column. This means that any 'clear' needs to continue to |
michael@0 | 310 | // the next page/column. |
michael@0 | 311 | bool mSplitLeftFloatAcrossBreak; |
michael@0 | 312 | bool mSplitRightFloatAcrossBreak; |
michael@0 | 313 | |
michael@0 | 314 | static int32_t sCachedFloatManagerCount; |
michael@0 | 315 | static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; |
michael@0 | 316 | |
michael@0 | 317 | nsFloatManager(const nsFloatManager&) MOZ_DELETE; |
michael@0 | 318 | void operator=(const nsFloatManager&) MOZ_DELETE; |
michael@0 | 319 | }; |
michael@0 | 320 | |
michael@0 | 321 | /** |
michael@0 | 322 | * A helper class to manage maintenance of the float manager during |
michael@0 | 323 | * nsBlockFrame::Reflow. It automatically restores the old float |
michael@0 | 324 | * manager in the reflow state when the object goes out of scope. |
michael@0 | 325 | */ |
michael@0 | 326 | class nsAutoFloatManager { |
michael@0 | 327 | public: |
michael@0 | 328 | nsAutoFloatManager(nsHTMLReflowState& aReflowState) |
michael@0 | 329 | : mReflowState(aReflowState), |
michael@0 | 330 | mNew(nullptr), |
michael@0 | 331 | mOld(nullptr) {} |
michael@0 | 332 | |
michael@0 | 333 | ~nsAutoFloatManager(); |
michael@0 | 334 | |
michael@0 | 335 | /** |
michael@0 | 336 | * Create a new float manager for the specified frame. This will |
michael@0 | 337 | * `remember' the old float manager, and install the new float |
michael@0 | 338 | * manager in the reflow state. |
michael@0 | 339 | */ |
michael@0 | 340 | nsresult |
michael@0 | 341 | CreateFloatManager(nsPresContext *aPresContext); |
michael@0 | 342 | |
michael@0 | 343 | protected: |
michael@0 | 344 | nsHTMLReflowState &mReflowState; |
michael@0 | 345 | nsFloatManager *mNew; |
michael@0 | 346 | nsFloatManager *mOld; |
michael@0 | 347 | }; |
michael@0 | 348 | |
michael@0 | 349 | #endif /* !defined(nsFloatManager_h_) */ |