1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsFloatManager.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,349 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim:cindent:ts=2:et:sw=2: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* class that manages rules for positioning floats */ 1.11 + 1.12 +#ifndef nsFloatManager_h_ 1.13 +#define nsFloatManager_h_ 1.14 + 1.15 +#include "mozilla/Attributes.h" 1.16 + 1.17 +#include "nsIntervalSet.h" 1.18 +#include "nsCoord.h" 1.19 +#include "nsRect.h" 1.20 +#include "nsTArray.h" 1.21 +#include "nsFrameList.h" // for DEBUG_FRAME_DUMP 1.22 + 1.23 +class nsIPresShell; 1.24 +class nsIFrame; 1.25 +struct nsHTMLReflowState; 1.26 +class nsPresContext; 1.27 + 1.28 +/** 1.29 + * The available space for content not occupied by floats is divided 1.30 + * into a (vertical) sequence of rectangles. However, we need to know 1.31 + * not only the rectangle, but also whether it was reduced (from the 1.32 + * content rectangle) by floats that actually intruded into the content 1.33 + * rectangle. 1.34 + */ 1.35 +struct nsFlowAreaRect { 1.36 + nsRect mRect; 1.37 + bool mHasFloats; 1.38 + 1.39 + nsFlowAreaRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, 1.40 + bool aHasFloats) 1.41 + : mRect(aX, aY, aWidth, aHeight), mHasFloats(aHasFloats) {} 1.42 +}; 1.43 + 1.44 +#define NS_FLOAT_MANAGER_CACHE_SIZE 4 1.45 + 1.46 +class nsFloatManager { 1.47 +public: 1.48 + nsFloatManager(nsIPresShell* aPresShell); 1.49 + ~nsFloatManager(); 1.50 + 1.51 + void* operator new(size_t aSize) CPP_THROW_NEW; 1.52 + void operator delete(void* aPtr, size_t aSize); 1.53 + 1.54 + static void Shutdown(); 1.55 + 1.56 + /** 1.57 + * Get float region stored on the frame. (Defaults to mRect if it's 1.58 + * not there.) The float region is the area impacted by this float; 1.59 + * the coordinates are relative to the containing block frame. 1.60 + */ 1.61 + static nsRect GetRegionFor(nsIFrame* aFloatFrame); 1.62 + /** 1.63 + * Calculate the float region for this frame using aMargin and the 1.64 + * frame's mRect. The region includes the margins around the float, 1.65 + * but doesn't include the relative offsets. 1.66 + * Note that if the frame is or has a continuation, aMargin's top 1.67 + * and/or bottom must be zeroed by the caller. 1.68 + */ 1.69 + static nsRect CalculateRegionFor(nsIFrame* aFloatFrame, 1.70 + const nsMargin& aMargin); 1.71 + /** 1.72 + * Store the float region on the frame. The region is stored 1.73 + * as a delta against the mRect, so repositioning the frame will 1.74 + * also reposition the float region. 1.75 + */ 1.76 + static void StoreRegionFor(nsIFrame* aFloat, nsRect& aRegion); 1.77 + 1.78 + // Structure that stores the current state of a frame manager for 1.79 + // Save/Restore purposes. 1.80 + struct SavedState; 1.81 + friend struct SavedState; 1.82 + struct SavedState { 1.83 + private: 1.84 + uint32_t mFloatInfoCount; 1.85 + nscoord mX, mY; 1.86 + bool mPushedLeftFloatPastBreak; 1.87 + bool mPushedRightFloatPastBreak; 1.88 + bool mSplitLeftFloatAcrossBreak; 1.89 + bool mSplitRightFloatAcrossBreak; 1.90 + 1.91 + friend class nsFloatManager; 1.92 + }; 1.93 + 1.94 + /** 1.95 + * Translate the current origin by the specified (dx, dy). This 1.96 + * creates a new local coordinate space relative to the current 1.97 + * coordinate space. 1.98 + */ 1.99 + void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; } 1.100 + 1.101 + /** 1.102 + * Returns the current translation from local coordinate space to 1.103 + * world coordinate space. This represents the accumulated calls to 1.104 + * Translate(). 1.105 + */ 1.106 + void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; } 1.107 + 1.108 + /** 1.109 + * Get information about the area available to content that flows 1.110 + * around floats. Two different types of space can be requested: 1.111 + * BAND_FROM_POINT: returns the band containing vertical coordinate 1.112 + * |aY| (though actually with the top truncated to begin at aY), 1.113 + * but up to at most |aHeight| (which may be nscoord_MAX). 1.114 + * This will return the tallest rectangle whose top is |aY| and in 1.115 + * which there are no changes in what floats are on the sides of 1.116 + * that rectangle, but will limit the height of the rectangle to 1.117 + * |aHeight|. The left and right edges of the rectangle give the 1.118 + * area available for line boxes in that space. The width of this 1.119 + * resulting rectangle will not be negative. 1.120 + * WIDTH_WITHIN_HEIGHT: This returns a rectangle whose top is aY and 1.121 + * whose height is exactly aHeight. Its left and right edges give 1.122 + * the left and right edges of the space that can be used for line 1.123 + * boxes *throughout* that space. (It is possible that more 1.124 + * horizontal space could be used in part of the space if a float 1.125 + * begins or ends in it.) The width of the resulting rectangle 1.126 + * can be negative. 1.127 + * 1.128 + * @param aY [in] vertical coordinate for top of available space 1.129 + * desired 1.130 + * @param aHeight [in] see above 1.131 + * @param aContentArea [in] an nsRect representing the content area 1.132 + * @param aState [in] If null, use the current state, otherwise, do 1.133 + * computation based only on floats present in the given 1.134 + * saved state. 1.135 + * @return An nsFlowAreaRect whose: 1.136 + * mRect is the resulting rectangle for line boxes. It will not 1.137 + * extend beyond aContentArea's horizontal bounds, but may be 1.138 + * narrower when floats are present. 1.139 + * mBandHasFloats is whether there are floats at the sides of the 1.140 + * return value including those that do not reduce the line box 1.141 + * width at all (because they are entirely in the margins) 1.142 + * 1.143 + * aY and aAvailSpace are positioned relative to the current translation 1.144 + */ 1.145 + enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT }; 1.146 + nsFlowAreaRect GetFlowArea(nscoord aY, BandInfoType aInfoType, 1.147 + nscoord aHeight, nsRect aContentArea, 1.148 + SavedState* aState) const; 1.149 + 1.150 + /** 1.151 + * Add a float that comes after all floats previously added. Its top 1.152 + * must be even with or below the top of all previous floats. 1.153 + * 1.154 + * aMarginRect is relative to the current translation. The caller 1.155 + * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0. 1.156 + */ 1.157 + nsresult AddFloat(nsIFrame* aFloatFrame, const nsRect& aMarginRect); 1.158 + 1.159 + /** 1.160 + * Notify that we tried to place a float that could not fit at all and 1.161 + * had to be pushed to the next page/column? (If so, we can't place 1.162 + * any more floats in this page/column because of the rule that the 1.163 + * top of a float cannot be above the top of an earlier float. It 1.164 + * also means that any clear needs to continue to the next column.) 1.165 + */ 1.166 + void SetPushedLeftFloatPastBreak() 1.167 + { mPushedLeftFloatPastBreak = true; } 1.168 + void SetPushedRightFloatPastBreak() 1.169 + { mPushedRightFloatPastBreak = true; } 1.170 + 1.171 + /** 1.172 + * Notify that we split a float, with part of it needing to be pushed 1.173 + * to the next page/column. (This means that any 'clear' needs to 1.174 + * continue to the next page/column.) 1.175 + */ 1.176 + void SetSplitLeftFloatAcrossBreak() 1.177 + { mSplitLeftFloatAcrossBreak = true; } 1.178 + void SetSplitRightFloatAcrossBreak() 1.179 + { mSplitRightFloatAcrossBreak = true; } 1.180 + 1.181 + /** 1.182 + * Remove the regions associated with this floating frame and its 1.183 + * next-sibling list. Some of the frames may never have been added; 1.184 + * we just skip those. This is not fully general; it only works as 1.185 + * long as the N frames to be removed are the last N frames to have 1.186 + * been added; if there's a frame in the middle of them that should 1.187 + * not be removed, YOU LOSE. 1.188 + */ 1.189 + nsresult RemoveTrailingRegions(nsIFrame* aFrameList); 1.190 + 1.191 +private: 1.192 + struct FloatInfo; 1.193 +public: 1.194 + 1.195 + bool HasAnyFloats() const { return !mFloats.IsEmpty(); } 1.196 + 1.197 + /** 1.198 + * Methods for dealing with the propagation of float damage during 1.199 + * reflow. 1.200 + */ 1.201 + bool HasFloatDamage() const 1.202 + { 1.203 + return !mFloatDamage.IsEmpty(); 1.204 + } 1.205 + 1.206 + void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) 1.207 + { 1.208 + mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY); 1.209 + } 1.210 + 1.211 + bool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd) const 1.212 + { 1.213 + return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY); 1.214 + } 1.215 + 1.216 + /** 1.217 + * Saves the current state of the float manager into aState. 1.218 + */ 1.219 + void PushState(SavedState* aState); 1.220 + 1.221 + /** 1.222 + * Restores the float manager to the saved state. 1.223 + * 1.224 + * These states must be managed using stack discipline. PopState can only 1.225 + * be used after PushState has been used to save the state, and it can only 1.226 + * be used once --- although it can be omitted; saved states can be ignored. 1.227 + * States must be popped in the reverse order they were pushed. A 1.228 + * call to PopState invalidates any saved states Pushed after the 1.229 + * state passed to PopState was pushed. 1.230 + */ 1.231 + void PopState(SavedState* aState); 1.232 + 1.233 + /** 1.234 + * Get the top of the last float placed into the float manager, to 1.235 + * enforce the rule that a float can't be above an earlier float. 1.236 + * Returns the minimum nscoord value if there are no floats. 1.237 + * 1.238 + * The result is relative to the current translation. 1.239 + */ 1.240 + nscoord GetLowestFloatTop() const; 1.241 + 1.242 + /** 1.243 + * Return the coordinate of the lowest float matching aBreakType in this 1.244 + * float manager. Returns aY if there are no matching floats. 1.245 + * 1.246 + * Both aY and the result are relative to the current translation. 1.247 + */ 1.248 + enum { 1.249 + // Tell ClearFloats not to push to nscoord_MAX when floats have been 1.250 + // pushed to the next page/column. 1.251 + DONT_CLEAR_PUSHED_FLOATS = (1<<0) 1.252 + }; 1.253 + nscoord ClearFloats(nscoord aY, uint8_t aBreakType, uint32_t aFlags = 0) const; 1.254 + 1.255 + /** 1.256 + * Checks if clear would pass into the floats' BFC's next-in-flow, 1.257 + * i.e. whether floats affecting this clear have continuations. 1.258 + */ 1.259 + bool ClearContinues(uint8_t aBreakType) const; 1.260 + 1.261 + void AssertStateMatches(SavedState *aState) const 1.262 + { 1.263 + NS_ASSERTION(aState->mX == mX && aState->mY == mY && 1.264 + aState->mPushedLeftFloatPastBreak == 1.265 + mPushedLeftFloatPastBreak && 1.266 + aState->mPushedRightFloatPastBreak == 1.267 + mPushedRightFloatPastBreak && 1.268 + aState->mSplitLeftFloatAcrossBreak == 1.269 + mSplitLeftFloatAcrossBreak && 1.270 + aState->mSplitRightFloatAcrossBreak == 1.271 + mSplitRightFloatAcrossBreak && 1.272 + aState->mFloatInfoCount == mFloats.Length(), 1.273 + "float manager state should match saved state"); 1.274 + } 1.275 + 1.276 +#ifdef DEBUG_FRAME_DUMP 1.277 + /** 1.278 + * Dump the state of the float manager out to a file. 1.279 + */ 1.280 + nsresult List(FILE* out) const; 1.281 +#endif 1.282 + 1.283 +private: 1.284 + 1.285 + struct FloatInfo { 1.286 + nsIFrame *const mFrame; 1.287 + nsRect mRect; 1.288 + // The lowest bottoms of left/right floats up to and including this one. 1.289 + nscoord mLeftYMost, mRightYMost; 1.290 + 1.291 + FloatInfo(nsIFrame* aFrame, const nsRect& aRect); 1.292 +#ifdef NS_BUILD_REFCNT_LOGGING 1.293 + FloatInfo(const FloatInfo& aOther); 1.294 + ~FloatInfo(); 1.295 +#endif 1.296 + }; 1.297 + 1.298 + nscoord mX, mY; // translation from local to global coordinate space 1.299 + nsTArray<FloatInfo> mFloats; 1.300 + nsIntervalSet mFloatDamage; 1.301 + 1.302 + // Did we try to place a float that could not fit at all and had to be 1.303 + // pushed to the next page/column? If so, we can't place any more 1.304 + // floats in this page/column because of the rule that the top of a 1.305 + // float cannot be above the top of an earlier float. And we also 1.306 + // need to apply this information to 'clear', and thus need to 1.307 + // separate left and right floats. 1.308 + bool mPushedLeftFloatPastBreak; 1.309 + bool mPushedRightFloatPastBreak; 1.310 + 1.311 + // Did we split a float, with part of it needing to be pushed to the 1.312 + // next page/column. This means that any 'clear' needs to continue to 1.313 + // the next page/column. 1.314 + bool mSplitLeftFloatAcrossBreak; 1.315 + bool mSplitRightFloatAcrossBreak; 1.316 + 1.317 + static int32_t sCachedFloatManagerCount; 1.318 + static void* sCachedFloatManagers[NS_FLOAT_MANAGER_CACHE_SIZE]; 1.319 + 1.320 + nsFloatManager(const nsFloatManager&) MOZ_DELETE; 1.321 + void operator=(const nsFloatManager&) MOZ_DELETE; 1.322 +}; 1.323 + 1.324 +/** 1.325 + * A helper class to manage maintenance of the float manager during 1.326 + * nsBlockFrame::Reflow. It automatically restores the old float 1.327 + * manager in the reflow state when the object goes out of scope. 1.328 + */ 1.329 +class nsAutoFloatManager { 1.330 +public: 1.331 + nsAutoFloatManager(nsHTMLReflowState& aReflowState) 1.332 + : mReflowState(aReflowState), 1.333 + mNew(nullptr), 1.334 + mOld(nullptr) {} 1.335 + 1.336 + ~nsAutoFloatManager(); 1.337 + 1.338 + /** 1.339 + * Create a new float manager for the specified frame. This will 1.340 + * `remember' the old float manager, and install the new float 1.341 + * manager in the reflow state. 1.342 + */ 1.343 + nsresult 1.344 + CreateFloatManager(nsPresContext *aPresContext); 1.345 + 1.346 +protected: 1.347 + nsHTMLReflowState &mReflowState; 1.348 + nsFloatManager *mNew; 1.349 + nsFloatManager *mOld; 1.350 +}; 1.351 + 1.352 +#endif /* !defined(nsFloatManager_h_) */