mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
552 lines
18 KiB
C++
552 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
// vim:cindent:ts=2:et:sw=2:
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* class that manages regions of 2-D space, originally designed
|
|
* generally but actually specific to space occupied by floats
|
|
*/
|
|
|
|
#ifndef nsSpaceManager_h___
|
|
#define nsSpaceManager_h___
|
|
|
|
#include "prclist.h"
|
|
#include "nsIntervalSet.h"
|
|
#include "nsISupports.h"
|
|
#include "nsCoord.h"
|
|
#include "nsRect.h"
|
|
|
|
class nsIPresShell;
|
|
class nsIFrame;
|
|
class nsVoidArray;
|
|
struct nsSize;
|
|
struct nsHTMLReflowState;
|
|
class nsPresContext;
|
|
|
|
#define NS_SPACE_MANAGER_CACHE_SIZE 4
|
|
|
|
/**
|
|
* Information about a particular trapezoid within a band. The space described
|
|
* by the trapezoid is in one of three states:
|
|
* <ul>
|
|
* <li>available
|
|
* <li>occupied by one frame
|
|
* <li>occupied by more than one frame
|
|
* </ul>
|
|
*/
|
|
struct nsBandTrapezoid {
|
|
enum State {Available, Occupied, OccupiedMultiple};
|
|
|
|
nscoord mTopY, mBottomY; // top and bottom y-coordinates
|
|
nscoord mTopLeftX, mBottomLeftX; // left edge x-coordinates
|
|
nscoord mTopRightX, mBottomRightX; // right edge x-coordinates
|
|
State mState; // state of the space
|
|
union {
|
|
nsIFrame* mFrame; // single frame occupying the space
|
|
const nsVoidArray* mFrames; // list of frames occupying the space
|
|
};
|
|
|
|
// Get the height of the trapezoid
|
|
nscoord GetHeight() const {return mBottomY - mTopY;}
|
|
|
|
// Get the bounding rect of the trapezoid
|
|
inline void GetRect(nsRect& aRect) const;
|
|
|
|
// Set the trapezoid from a rectangle
|
|
inline void operator=(const nsRect& aRect);
|
|
|
|
// Do these trapezoids have the same geometry, frame, and state?
|
|
inline PRBool Equals(const nsBandTrapezoid& aTrap) const;
|
|
|
|
// Do these trapezoids have the same geometry?
|
|
inline PRBool EqualGeometry(const nsBandTrapezoid& aTrap) const;
|
|
|
|
nsBandTrapezoid()
|
|
: mTopY(0),
|
|
mBottomY(0),
|
|
mTopLeftX(0),
|
|
mBottomLeftX(0),
|
|
mTopRightX(0),
|
|
mBottomRightX(0),
|
|
mFrame(nsnull)
|
|
{
|
|
}
|
|
};
|
|
|
|
inline void nsBandTrapezoid::GetRect(nsRect& aRect) const
|
|
{
|
|
aRect.x = PR_MIN(mTopLeftX, mBottomLeftX);
|
|
aRect.y = mTopY;
|
|
aRect.width = PR_MAX(mTopRightX, mBottomRightX);
|
|
if (NS_MAXSIZE != aRect.width) {
|
|
aRect.width -= aRect.x;
|
|
}
|
|
aRect.height = (NS_MAXSIZE == mBottomY) ? NS_MAXSIZE : mBottomY - mTopY;
|
|
}
|
|
|
|
inline void nsBandTrapezoid::operator=(const nsRect& aRect)
|
|
{
|
|
mTopLeftX = mBottomLeftX = aRect.x;
|
|
mTopRightX = mBottomRightX = aRect.XMost();
|
|
mTopY = aRect.y;
|
|
mBottomY = aRect.YMost();
|
|
}
|
|
|
|
inline PRBool nsBandTrapezoid::Equals(const nsBandTrapezoid& aTrap) const
|
|
{
|
|
return (
|
|
mTopLeftX == aTrap.mTopLeftX &&
|
|
mBottomLeftX == aTrap.mBottomLeftX &&
|
|
mTopRightX == aTrap.mTopRightX &&
|
|
mBottomRightX == aTrap.mBottomRightX &&
|
|
mTopY == aTrap.mTopY &&
|
|
mBottomY == aTrap.mBottomY &&
|
|
mState == aTrap.mState &&
|
|
mFrame == aTrap.mFrame
|
|
);
|
|
}
|
|
|
|
inline PRBool nsBandTrapezoid::EqualGeometry(const nsBandTrapezoid& aTrap) const
|
|
{
|
|
return (
|
|
mTopLeftX == aTrap.mTopLeftX &&
|
|
mBottomLeftX == aTrap.mBottomLeftX &&
|
|
mTopRightX == aTrap.mTopRightX &&
|
|
mBottomRightX == aTrap.mBottomRightX &&
|
|
mTopY == aTrap.mTopY &&
|
|
mBottomY == aTrap.mBottomY
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Structure used for describing the space within a band.
|
|
* @see #GetBandData()
|
|
*/
|
|
struct nsBandData {
|
|
PRInt32 mCount; // [out] actual number of trapezoids in the band data
|
|
PRInt32 mSize; // [in] the size of the array (number of trapezoids)
|
|
nsBandTrapezoid* mTrapezoids; // [out] array of length 'size'
|
|
};
|
|
|
|
/**
|
|
* Class for dealing with bands of available space. The space manager
|
|
* defines a coordinate space with an origin at (0, 0) that grows down
|
|
* and to the right.
|
|
*/
|
|
class nsSpaceManager {
|
|
public:
|
|
nsSpaceManager(nsIPresShell* aPresShell, nsIFrame* aFrame);
|
|
~nsSpaceManager();
|
|
|
|
void* operator new(size_t aSize) CPP_THROW_NEW;
|
|
void operator delete(void* aPtr, size_t aSize);
|
|
|
|
static void Shutdown();
|
|
|
|
/*
|
|
* Get the frame that's associated with the space manager. This frame
|
|
* created the space manager, and the world coordinate space is
|
|
* relative to this frame.
|
|
*
|
|
* You can use QueryInterface() on this frame to get any additional
|
|
* interfaces.
|
|
*/
|
|
nsIFrame* GetFrame() const { return mFrame; }
|
|
|
|
/**
|
|
* Translate the current origin by the specified (dx, dy). This
|
|
* creates a new local coordinate space relative to the current
|
|
* coordinate space.
|
|
*/
|
|
void Translate(nscoord aDx, nscoord aDy) { mX += aDx; mY += aDy; }
|
|
|
|
/**
|
|
* Returns the current translation from local coordinate space to
|
|
* world coordinate space. This represents the accumulated calls to
|
|
* Translate().
|
|
*/
|
|
void GetTranslation(nscoord& aX, nscoord& aY) const { aX = mX; aY = mY; }
|
|
|
|
/**
|
|
* Returns the x-most rect in the space manager, or 0 if there are no
|
|
* rects.
|
|
*
|
|
* @return PR_TRUE if there are bands and PR_FALSE if there are no bands
|
|
*/
|
|
PRBool XMost(nscoord& aXMost) const;
|
|
|
|
/**
|
|
* Returns the y-most of the bottommost band or 0 if there are no bands.
|
|
*
|
|
* @return PR_TRUE if there are bands and PR_FALSE if there are no bands
|
|
*/
|
|
PRBool YMost(nscoord& aYMost) const;
|
|
|
|
/**
|
|
* Returns a band starting at the specified y-offset. The band data
|
|
* indicates which parts of the band are available, and which parts
|
|
* are unavailable
|
|
*
|
|
* The band data that is returned is in the coordinate space of the
|
|
* local coordinate system.
|
|
*
|
|
* The local coordinate space origin, the y-offset, and the max size
|
|
* describe a rectangle that's used to clip the underlying band of
|
|
* available space, i.e.
|
|
* {0, aYOffset, aMaxSize.width, aMaxSize.height} in the local
|
|
* coordinate space
|
|
*
|
|
* @param aYOffset the y-offset of where the band begins. The coordinate is
|
|
* relative to the upper-left corner of the local coordinate space
|
|
* @param aMaxSize the size to use to constrain the band data
|
|
* @param aBandData [in,out] used to return the list of trapezoids that
|
|
* describe the available space and the unavailable space
|
|
* @return NS_OK if successful and NS_ERROR_FAILURE if the band data is not
|
|
* not large enough. The 'count' member of the band data struct
|
|
* indicates how large the array of trapezoids needs to be
|
|
*/
|
|
nsresult GetBandData(nscoord aYOffset,
|
|
const nsSize& aMaxSize,
|
|
nsBandData& aBandData) const;
|
|
|
|
/**
|
|
* Add a rectangular region of unavailable space. The space is
|
|
* relative to the local coordinate system.
|
|
*
|
|
* The region is tagged with a frame
|
|
*
|
|
* @param aFrame the frame used to identify the region. Must not be NULL
|
|
* @param aUnavailableSpace the bounding rect of the unavailable space
|
|
* @return NS_OK if successful
|
|
* NS_ERROR_FAILURE if there is already a region tagged with aFrame
|
|
*/
|
|
nsresult AddRectRegion(nsIFrame* aFrame,
|
|
const nsRect& aUnavailableSpace);
|
|
|
|
/**
|
|
* Remove the regions associated with this floating frame and its
|
|
* next-sibling list. Some of the frames may never have been added;
|
|
* we just skip those. This is not fully general; it only works as
|
|
* long as the N frames to be removed are the last N frames to have
|
|
* been added; if there's a frame in the middle of them that should
|
|
* not be removed, YOU LOSE.
|
|
*
|
|
* This can only be done at the end of the life of this space manager. The only
|
|
* methods it is safe to call after this are XMost() and YMost().
|
|
*/
|
|
nsresult RemoveTrailingRegions(nsIFrame* aFrameList);
|
|
|
|
protected:
|
|
/**
|
|
* Remove the region associated with aFrane.
|
|
*
|
|
* doesn't work in the general case!
|
|
*
|
|
* Returns NS_OK if successful and NS_ERROR_INVALID_ARG if there is no region
|
|
* tagged with aFrame
|
|
*/
|
|
nsresult RemoveRegion(nsIFrame* aFrame);
|
|
|
|
public:
|
|
// Structure that stores the current state of a frame manager for
|
|
// Save/Restore purposes.
|
|
struct SavedState {
|
|
private:
|
|
nsIFrame *mLastFrame;
|
|
nscoord mX, mY;
|
|
nscoord mLowestTop;
|
|
nscoord mMaximalLeftYMost;
|
|
nscoord mMaximalRightYMost;
|
|
PRPackedBool mHaveCachedLeftYMost;
|
|
PRPackedBool mHaveCachedRightYMost;
|
|
|
|
friend class nsSpaceManager;
|
|
};
|
|
|
|
/**
|
|
* Clears the list of regions representing the unavailable space.
|
|
*/
|
|
void ClearRegions();
|
|
|
|
PRBool HasAnyFloats() { return mFrameInfoMap != nsnull; }
|
|
|
|
/**
|
|
* Methods for dealing with the propagation of float damage during
|
|
* reflow.
|
|
*/
|
|
PRBool HasFloatDamage()
|
|
{
|
|
return !mFloatDamage.IsEmpty();
|
|
}
|
|
|
|
void IncludeInDamage(nscoord aIntervalBegin, nscoord aIntervalEnd)
|
|
{
|
|
mFloatDamage.IncludeInterval(aIntervalBegin + mY, aIntervalEnd + mY);
|
|
}
|
|
|
|
PRBool IntersectsDamage(nscoord aIntervalBegin, nscoord aIntervalEnd)
|
|
{
|
|
return mFloatDamage.Intersects(aIntervalBegin + mY, aIntervalEnd + mY);
|
|
}
|
|
|
|
/**
|
|
* Saves the current state of the space manager into aState.
|
|
*/
|
|
void PushState(SavedState* aState);
|
|
|
|
/**
|
|
* Restores the space manager to the saved state.
|
|
*
|
|
* These states must be managed using stack discipline. PopState can only
|
|
* be used after PushState has been used to save the state, and it can only
|
|
* be used once --- although it can be omitted; saved states can be ignored.
|
|
* States must be popped in the reverse order they were pushed.
|
|
*/
|
|
void PopState(SavedState* aState);
|
|
|
|
/**
|
|
* Get the top of the last region placed into the space manager, to
|
|
* enforce the rule that a float can't be above an earlier float.
|
|
* Returns the minimum nscoord value if there are no regions.
|
|
*/
|
|
nscoord GetLowestRegionTop();
|
|
|
|
/**
|
|
* Return the coordinate of the lowest float matching aBreakType in this
|
|
* space manager. Returns aY if there are no matching floats.
|
|
*/
|
|
nscoord ClearFloats(nscoord aY, PRUint8 aBreakType);
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* Dump the state of the spacemanager out to a file
|
|
*/
|
|
nsresult List(FILE* out);
|
|
#endif
|
|
|
|
protected:
|
|
// Structure that maintains information about the region associated
|
|
// with a particular frame
|
|
struct FrameInfo {
|
|
nsIFrame* const mFrame;
|
|
nsRect mRect; // rectangular region
|
|
FrameInfo* mNext;
|
|
|
|
FrameInfo(nsIFrame* aFrame, const nsRect& aRect);
|
|
#ifdef NS_BUILD_REFCNT_LOGGING
|
|
~FrameInfo();
|
|
#endif
|
|
};
|
|
|
|
public:
|
|
// Doubly linked list of band rects
|
|
struct BandRect : PRCListStr {
|
|
nscoord mLeft, mTop;
|
|
nscoord mRight, mBottom;
|
|
PRInt32 mNumFrames; // number of frames occupying this rect
|
|
union {
|
|
nsIFrame* mFrame; // single frame occupying the space
|
|
nsVoidArray* mFrames; // list of frames occupying the space
|
|
};
|
|
|
|
BandRect(nscoord aLeft, nscoord aTop,
|
|
nscoord aRight, nscoord aBottom,
|
|
nsIFrame*);
|
|
BandRect(nscoord aLeft, nscoord aTop,
|
|
nscoord aRight, nscoord aBottom,
|
|
nsVoidArray*);
|
|
~BandRect();
|
|
|
|
// List operations
|
|
BandRect* Next() const {return (BandRect*)PR_NEXT_LINK(this);}
|
|
BandRect* Prev() const {return (BandRect*)PR_PREV_LINK(this);}
|
|
void InsertBefore(BandRect* aBandRect) {PR_INSERT_BEFORE(aBandRect, this);}
|
|
void InsertAfter(BandRect* aBandRect) {PR_INSERT_AFTER(aBandRect, this);}
|
|
void Remove() {PR_REMOVE_LINK(this);}
|
|
|
|
// Split the band rect into two vertically, with this band rect becoming
|
|
// the top part, and a new band rect being allocated and returned for the
|
|
// bottom part
|
|
//
|
|
// Does not insert the new band rect into the linked list
|
|
BandRect* SplitVertically(nscoord aBottom);
|
|
|
|
// Split the band rect into two horizontally, with this band rect becoming
|
|
// the left part, and a new band rect being allocated and returned for the
|
|
// right part
|
|
//
|
|
// Does not insert the new band rect into the linked list
|
|
BandRect* SplitHorizontally(nscoord aRight);
|
|
|
|
// Accessor functions
|
|
PRBool IsOccupiedBy(const nsIFrame*) const;
|
|
void AddFrame(const nsIFrame*);
|
|
void RemoveFrame(const nsIFrame*);
|
|
PRBool HasSameFrameList(const BandRect* aBandRect) const;
|
|
PRInt32 Length() const;
|
|
};
|
|
|
|
// Circular linked list of band rects
|
|
struct BandList : BandRect {
|
|
BandList();
|
|
|
|
// Accessors
|
|
PRBool IsEmpty() const {return PR_CLIST_IS_EMPTY((PRCListStr*)this);}
|
|
BandRect* Head() const {return (BandRect*)PR_LIST_HEAD(this);}
|
|
BandRect* Tail() const {return (BandRect*)PR_LIST_TAIL(this);}
|
|
|
|
// Operations
|
|
void Append(BandRect* aBandRect) {PR_APPEND_LINK(aBandRect, this);}
|
|
|
|
// Remove and delete all the band rects in the list
|
|
void Clear();
|
|
};
|
|
|
|
protected:
|
|
nsIFrame* const mFrame; // frame associated with the space manager
|
|
nscoord mX, mY; // translation from local to global coordinate space
|
|
BandList mBandList; // header/sentinel for circular linked list of band rects
|
|
nscoord mLowestTop; // the lowest *top*
|
|
FrameInfo* mFrameInfoMap;
|
|
nsIntervalSet mFloatDamage;
|
|
PRPackedBool mHaveCachedLeftYMost; // If true, mMaximalLeftYMost is set
|
|
PRPackedBool mHaveCachedRightYMost; // If true, mMaximalRightYMost is set
|
|
nscoord mMaximalLeftYMost; // The maximal YMost of our FrameInfo
|
|
// rects for left floats. Only makes
|
|
// sense when mHaveCachedLeftYMost is
|
|
// true.
|
|
nscoord mMaximalRightYMost; // The maximal YMost of our FrameInfo
|
|
// rects for right floats. Only makes
|
|
// sense when mHaveCachedLeftYMost is
|
|
// true.
|
|
// We keep track of the last BandRect* we worked with so that we can
|
|
// make use of locality of reference in situations where people want
|
|
// to do a bunch of operations in a row.
|
|
BandRect* mCachedBandPosition;
|
|
|
|
protected:
|
|
FrameInfo* GetFrameInfoFor(nsIFrame* aFrame);
|
|
FrameInfo* CreateFrameInfo(nsIFrame* aFrame, const nsRect& aRect);
|
|
void DestroyFrameInfo(FrameInfo*);
|
|
|
|
void ClearFrameInfo();
|
|
void ClearBandRects();
|
|
|
|
BandRect* GetNextBand(const BandRect* aBandRect) const;
|
|
BandRect* GetPrevBand(const BandRect* aBandRect) const;
|
|
void DivideBand(BandRect* aBand, nscoord aBottom);
|
|
PRBool CanJoinBands(BandRect* aBand, BandRect* aPrevBand);
|
|
PRBool JoinBands(BandRect* aBand, BandRect* aPrevBand);
|
|
void AddRectToBand(BandRect* aBand, BandRect* aBandRect);
|
|
void InsertBandRect(BandRect* aBandRect);
|
|
|
|
nsresult GetBandAvailableSpace(const BandRect* aBand,
|
|
nscoord aY,
|
|
const nsSize& aMaxSize,
|
|
nsBandData& aAvailableSpace) const;
|
|
|
|
// Return a band guaranteed to have its top at or above aYOffset or the first
|
|
// band if there is no band with its top above aYOffset. This method will
|
|
// use mCachedBandPosition to maybe get such a band that's not too far up.
|
|
// This function should not be called if there are no bands.
|
|
// This function never returns null.
|
|
BandRect* GuessBandWithTopAbove(nscoord aYOffset) const;
|
|
|
|
void SetCachedBandPosition(BandRect* aBandRect) {
|
|
NS_ASSERTION(!aBandRect ||
|
|
aBandRect == mBandList.Head() ||
|
|
aBandRect->Prev()->mBottom != aBandRect->mBottom,
|
|
"aBandRect should be first rect within its band");
|
|
mCachedBandPosition = aBandRect;
|
|
}
|
|
|
|
|
|
private:
|
|
static PRInt32 sCachedSpaceManagerCount;
|
|
static void* sCachedSpaceManagers[NS_SPACE_MANAGER_CACHE_SIZE];
|
|
|
|
nsSpaceManager(const nsSpaceManager&); // no implementation
|
|
void operator=(const nsSpaceManager&); // no implementation
|
|
};
|
|
|
|
/**
|
|
* A helper class to manage maintenance of the space manager during
|
|
* nsBlockFrame::Reflow. It automatically restores the old space
|
|
* manager in the reflow state when the object goes out of scope.
|
|
*/
|
|
class nsAutoSpaceManager {
|
|
public:
|
|
nsAutoSpaceManager(nsHTMLReflowState& aReflowState)
|
|
: mReflowState(aReflowState),
|
|
#ifdef DEBUG
|
|
mOwns(PR_TRUE),
|
|
#endif
|
|
mNew(nsnull),
|
|
mOld(nsnull) {}
|
|
|
|
~nsAutoSpaceManager();
|
|
|
|
/**
|
|
* Create a new space manager for the specified frame. This will
|
|
* `remember' the old space manager, and install the new space
|
|
* manager in the reflow state.
|
|
*/
|
|
nsresult
|
|
CreateSpaceManagerFor(nsPresContext *aPresContext,
|
|
nsIFrame *aFrame);
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* `Orphan' any space manager that the nsAutoSpaceManager created;
|
|
* i.e., make it so that we don't destroy the space manager when we
|
|
* go out of scope.
|
|
*/
|
|
void DebugOrphanSpaceManager() { mOwns = PR_FALSE; }
|
|
#endif
|
|
|
|
protected:
|
|
nsHTMLReflowState &mReflowState;
|
|
#ifdef DEBUG
|
|
PRBool mOwns;
|
|
#endif
|
|
nsSpaceManager *mNew;
|
|
nsSpaceManager *mOld;
|
|
};
|
|
|
|
#endif /* nsSpaceManager_h___ */
|
|
|