/* -*- 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 Communicator client 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 ***** */ #ifndef nsBlockFrame_h___ #define nsBlockFrame_h___ #include "nsHTMLContainerFrame.h" #include "nsHTMLParts.h" #include "nsAbsoluteContainingBlock.h" #include "nsLineBox.h" #include "nsReflowPath.h" #include "nsCSSPseudoElements.h" #include "nsStyleSet.h" class nsBlockReflowState; class nsBulletFrame; class nsLineBox; class nsFirstLineFrame; class nsILineIterator; class nsIntervalSet; /** * Child list name indices * @see #GetAdditionalChildListName() */ #define NS_BLOCK_FRAME_FLOAT_LIST_INDEX 0 #define NS_BLOCK_FRAME_BULLET_LIST_INDEX 1 #define NS_BLOCK_FRAME_OVERFLOW_LIST_INDEX 2 #define NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX 3 #define NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX 4 #define NS_BLOCK_FRAME_LAST_LIST_INDEX NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX /** * Some invariants: * -- The overflow out-of-flows list contains the out-of- * flow frames whose placeholders are in the overflow list. * -- A given piece of content has at most one placeholder * frame in a block's normal child list. * -- A given piece of content can have an unlimited number * of placeholder frames in the overflow-lines list. * -- A line containing a continuation placeholder contains * only continuation placeholders. * -- While a block is being reflowed, its overflowPlaceholdersList * frame property points to an nsFrameList in its * nsBlockReflowState. This list contains placeholders for * floats whose prev-in-flow is in the block's regular line * list. The list is always empty/non-existent after the * block has been reflowed. * -- In all these frame lists, if there are two frames for * the same content appearing in the list, then the frames * appear with the prev-in-flow before the next-in-flow. * -- While reflowing a block, its overflow line list * will usually be empty but in some cases will have lines * (while we reflow the block at its shrink-wrap width). * In this case any new overflowing content must be * prepended to the overflow lines. */ // see nsHTMLParts.h for the public block state bits #define NS_BLOCK_HAS_LINE_CURSOR 0x01000000 #define NS_BLOCK_HAS_OVERFLOW_LINES 0x02000000 #define NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS 0x04000000 #define NS_BLOCK_HAS_OVERFLOW_PLACEHOLDERS 0x08000000 // Set on any block that has descendant frames in the normal // flow with 'clear' set to something other than 'none' // (including
frames) #define NS_BLOCK_HAS_CLEAR_CHILDREN 0x10000000 #define nsBlockFrameSuper nsHTMLContainerFrame #define NS_BLOCK_FRAME_CID \ { 0xa6cf90df, 0x15b3, 0x11d2,{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}} extern const nsIID kBlockFrameCID; /* * Base class for block and inline frames. * The block frame has an additional named child list: * - "Absolute-list" which contains the absolutely positioned frames * * @see nsLayoutAtoms::absoluteList */ class nsBlockFrame : public nsBlockFrameSuper { public: typedef nsLineList::iterator line_iterator; typedef nsLineList::const_iterator const_line_iterator; typedef nsLineList::reverse_iterator reverse_line_iterator; typedef nsLineList::const_reverse_iterator const_reverse_line_iterator; line_iterator begin_lines() { return mLines.begin(); } line_iterator end_lines() { return mLines.end(); } const_line_iterator begin_lines() const { return mLines.begin(); } const_line_iterator end_lines() const { return mLines.end(); } reverse_line_iterator rbegin_lines() { return mLines.rbegin(); } reverse_line_iterator rend_lines() { return mLines.rend(); } const_reverse_line_iterator rbegin_lines() const { return mLines.rbegin(); } const_reverse_line_iterator rend_lines() const { return mLines.rend(); } friend nsresult NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags); // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); // nsIFrame NS_IMETHOD Init(nsPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow); NS_IMETHOD SetInitialChildList(nsPresContext* aPresContext, nsIAtom* aListName, nsIFrame* aChildList); NS_IMETHOD AppendFrames(nsIAtom* aListName, nsIFrame* aFrameList); NS_IMETHOD InsertFrames(nsIAtom* aListName, nsIFrame* aPrevFrame, nsIFrame* aFrameList); NS_IMETHOD RemoveFrame(nsIAtom* aListName, nsIFrame* aOldFrame); virtual nsIFrame* GetFirstChild(nsIAtom* aListName) const; NS_IMETHOD SetParent(const nsIFrame* aParent); virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const; NS_IMETHOD Destroy(nsPresContext* aPresContext); NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const; virtual PRBool IsContainingBlock() const; virtual PRBool IsFloatContainingBlock() const; NS_IMETHOD Paint(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0); virtual nsIAtom* GetType() const; #ifdef DEBUG NS_IMETHOD List(FILE* out, PRInt32 aIndent) const; NS_IMETHOD_(nsFrameState) GetDebugStateBits() const; NS_IMETHOD GetFrameName(nsAString& aResult) const; NS_IMETHOD VerifyTree() const; #endif #ifdef ACCESSIBILITY NS_IMETHOD GetAccessible(nsIAccessible** aAccessible); #endif // line cursor methods to speed up searching for the line(s) // containing a point. The basic idea is that we set the cursor // property if the lines' combinedArea.ys and combinedArea.yMosts // are non-decreasing (considering only non-empty combinedAreas; // empty combinedAreas never participate in event handling or // painting), and the block has sufficient number of lines. The // cursor property points to a "recently used" line. If we get a // series of GetFrameForPoint or Paint requests that work on lines // "near" the cursor, then we can find those nearby lines quickly by // starting our search at the cursor. // Clear out line cursor because we're disturbing the lines (i.e., Reflow) void ClearLineCursor(); // Get the first line that might contain y-coord 'y', or nsnull if you must search // all lines. If nonnull is returned then we guarantee that the lines' // combinedArea.ys and combinedArea.yMosts are non-decreasing. // The actual line returned might not contain 'y', but if not, it is guaranteed // to be before any line which does contain 'y'. nsLineBox* GetFirstLineContaining(nscoord y); // Set the line cursor to our first line. Only call this if you // guarantee that the lines' combinedArea.ys and combinedArea.yMosts // are non-decreasing. void SetupLineCursor(); virtual nsIFrame* GetFrameForPointUsing(const nsPoint& aPoint, nsIAtom* aList, nsFramePaintLayer aWhichLayer, PRBool aConsiderSelf); virtual nsIFrame* GetFrameForPoint(const nsPoint& aPoint, nsFramePaintLayer aWhichLayer); NS_IMETHOD HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus); NS_IMETHOD ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild); NS_IMETHOD IsVisibleForPainting(nsPresContext * aPresContext, nsIRenderingContext& aRenderingContext, PRBool aCheckVis, PRBool* aIsVisible); virtual PRBool IsEmpty(); virtual PRBool IsSelfEmpty(); // nsIHTMLReflow NS_IMETHOD Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType); #ifdef DO_SELECTION NS_IMETHOD HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus); NS_IMETHOD HandleDrag(nsPresContext* aPresContext, nsGUIEvent* aEvent, nsEventStatus* aEventStatus); nsIFrame * FindHitFrame(nsBlockFrame * aBlockFrame, const nscoord aX, const nscoord aY, const nsPoint & aPoint); #endif virtual void DeleteNextInFlowChild(nsPresContext* aPresContext, nsIFrame* aNextInFlow); /** * Determines whether the collapsed margin carried out of the last * line includes the margin-top of a line with clearance (in which * case we must avoid collapsing that margin with our bottom margin) */ PRBool CheckForCollapsedBottomMarginFromClearanceLine(); /** return the topmost block child based on y-index. * almost always the first or second line, if there is one. * accounts for lines that hold only compressed white space, etc. */ nsIFrame* GetTopBlockChild(nsPresContext *aPresContext); // Returns the line containing aFrame, or end_lines() if the frame // isn't in the block. line_iterator FindLineFor(nsIFrame* aFrame); static nsresult GetCurrentLine(nsBlockReflowState *aState, nsLineBox **aOutCurrentLine); inline nscoord GetAscent() { return mAscent; } // Create a contination for aPlaceholder and its out of flow frame and // add it to the list of overflow floats nsresult SplitPlaceholder(nsBlockReflowState& aState, nsIFrame* aPlaceholder); void UndoSplitPlaceholders(nsBlockReflowState& aState, nsIFrame* aLastPlaceholder); virtual void PaintChild(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsIFrame* aFrame, nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0) { nsContainerFrame::PaintChild(aPresContext, aRenderingContext, aDirtyRect, aFrame, aWhichLayer, aFlags); } PRBool HandleOverflowPlaceholdersForPulledFrame( nsBlockReflowState& aState, nsIFrame* aFrame); PRBool HandleOverflowPlaceholdersOnPulledLine( nsBlockReflowState& aState, nsLineBox* aLine); protected: nsBlockFrame(); virtual ~nsBlockFrame(); already_AddRefed GetFirstLetterStyle(nsPresContext* aPresContext) { return aPresContext->StyleSet()-> ProbePseudoStyleFor(mContent, nsCSSPseudoElements::firstLetter, mStyleContext); } /* * Overides member function of nsHTMLContainerFrame. Needed to handle the * lines in a nsBlockFrame properly. */ virtual void PaintTextDecorationLines(nsIRenderingContext& aRenderingContext, nscolor aColor, nscoord aOffset, nscoord aAscent, nscoord aSize); /** * GetClosestLine will return the line that VERTICALLY owns the point closest to aPoint.y * aPoint is the point to search for, relative to the origin of the frame that aLI * iterates over. * aClosestLine is the result. */ nsresult GetClosestLine(nsILineIterator *aLI, const nsPoint &aPoint, PRInt32 &aClosestLine); void TryAllLines(nsLineList::iterator* aIterator, nsLineList::iterator* aEndIterator, PRBool* aInOverflowLines); void SetFlags(PRUint32 aFlags) { mState &= ~NS_BLOCK_FLAGS_MASK; mState |= aFlags; } PRBool HaveOutsideBullet() const { #if defined(DEBUG) && !defined(DEBUG_rods) if(mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET) { NS_ASSERTION(mBullet,"NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET flag set and no mBullet"); } #endif return 0 != (mState & NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); } /** move the frames contained by aLine by aDY * if aLine is a block, it's child floats are added to the state manager */ void SlideLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDY); virtual PRIntn GetSkipSides() const; virtual void ComputeFinalSize(const nsHTMLReflowState& aReflowState, nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics); void ComputeCombinedArea(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aMetrics); /** add the frames in aFrameList to this block after aPrevSibling * this block thinks in terms of lines, but the frame construction code * knows nothing about lines at all. So we need to find the line that * contains aPrevSibling and add aFrameList after aPrevSibling on that line. * new lines are created as necessary to handle block data in aFrameList. */ nsresult AddFrames(nsIFrame* aFrameList, nsIFrame* aPrevSibling); public: /** does all the real work for removing aDeletedFrame from this * finds the line containing aFrame. * handled continued frames * marks lines dirty as needed * @param aDestroyFrames if false then we don't actually destroy the * frame or its next in flows, we just remove them. This does NOT work * on out of flow frames so always use PR_TRUE for out of flows. */ nsresult DoRemoveFrame(nsIFrame* aDeletedFrame, PRBool aDestroyFrames = PR_TRUE); protected: /** grab overflow lines from this block's prevInFlow, and make them * part of this block's mLines list. * @return PR_TRUE if any lines were drained. */ PRBool DrainOverflowLines(nsBlockReflowState& aState); /** * Remove a float from our float list and also the float cache * for the line its placeholder is on. */ line_iterator RemoveFloat(nsIFrame* aFloat); void CollectFloats(nsIFrame* aFrame, nsFrameList& aList, nsIFrame** aTail, PRBool aFromOverflow); // Remove a float, abs, rel positioned frame from the appropriate block's list static void DoRemoveOutOfFlowFrame(nsIFrame* aFrame); /** set up the conditions necessary for an initial reflow */ nsresult PrepareInitialReflow(nsBlockReflowState& aState); /** set up the conditions necessary for an styleChanged reflow */ nsresult PrepareStyleChangedReflow(nsBlockReflowState& aState); /** set up the conditions necessary for an incremental reflow. * the primary task is to mark the minimumly sufficient lines dirty. */ nsresult PrepareChildIncrementalReflow(nsBlockReflowState& aState); /** * Retarget an inline incremental reflow from continuing frames that * will be destroyed. * * @param aState |aState.mNextRCFrame| contains the next frame in * the reflow path; this will be ``rewound'' to either the target * frame's primary frame, or to the first continuation frame after a * ``hard break''. In other words, it will be set to the closest * continuation which will not be destroyed by the unconstrained * reflow. The remaining frames in the reflow path for * |aState.mReflowState.reflowCommand| will be altered similarly. * * @param aLine is initially the line box that contains the target * frame. It will be ``rewound'' in lockstep with * |aState.mNextRCFrame|. * * @param aPrevInFlow points to the target frame's prev-in-flow. */ void RetargetInlineIncrementalReflow(nsReflowPath::iterator &aFrame, line_iterator &aLine, nsIFrame *aPrevInFlow); /** set up the conditions necessary for an resize reflow * the primary task is to mark the minimumly sufficient lines dirty. */ nsresult PrepareResizeReflow(nsBlockReflowState& aState); /** reflow all lines that have been marked dirty. * @param aTryPull set this to PR_TRUE if you want to try pulling content from * our next in flow while there is room. */ nsresult ReflowDirtyLines(nsBlockReflowState& aState, PRBool aTryPull); //---------------------------------------- // Methods for line reflow /** * Reflow a line. * @param aState the current reflow state * @param aLine the line to reflow. can contain a single block frame * or contain 1 or more inline frames. * @param aKeepReflowGoing [OUT] indicates whether the caller should continue to reflow more lines * @param aDamageDirtyArea if PR_TRUE, do extra work to mark the changed areas as damaged for painting * this indicates that frames may have changed size, for example */ nsresult ReflowLine(nsBlockReflowState& aState, line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aDamageDirtyArea = PR_FALSE); // Return PR_TRUE if aLine gets pushed. PRBool PlaceLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, line_iterator aLine, PRBool* aKeepReflowGoing, PRBool aUpdateMaximumWidth); /** * Mark |aLine| dirty, and, if necessary because of possible * pull-up, mark the previous line dirty as well. */ nsresult MarkLineDirty(line_iterator aLine); // XXX blech void PostPlaceLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aMaxElementWidth); // XXX where to go PRBool ShouldJustifyLine(nsBlockReflowState& aState, line_iterator aLine); void DeleteLine(nsBlockReflowState& aState, nsLineList::iterator aLine, nsLineList::iterator aLineEnd); //---------------------------------------- // Methods for individual frame reflow PRBool ShouldApplyTopMargin(nsBlockReflowState& aState, nsLineBox* aLine); nsresult ReflowBlockFrame(nsBlockReflowState& aState, line_iterator aLine, PRBool* aKeepGoing); nsresult ReflowInlineFrames(nsBlockReflowState& aState, line_iterator aLine, PRBool* aKeepLineGoing, PRBool aDamageDirtyArea, PRBool aUpdateMaximumWidth = PR_FALSE); nsresult DoReflowInlineFrames(nsBlockReflowState& aState, nsLineLayout& aLineLayout, line_iterator aLine, PRBool* aKeepReflowGoing, PRUint8* aLineReflowStatus, PRBool aUpdateMaximumWidth, PRBool aDamageDirtyArea); nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsLineLayout& aLineLayout, line_iterator aLine, nsIFrame* aFrame, PRUint8* aLineReflowStatus); // An incomplete aReflowStatus indicates the float should be split // but only if the available height is constrained. nsresult ReflowFloat(nsBlockReflowState& aState, nsPlaceholderFrame* aPlaceholder, nsFloatCache* aFloatCache, nsReflowStatus& aReflowStatus); //---------------------------------------- // Methods for pushing/pulling lines/frames virtual nsresult CreateContinuationFor(nsBlockReflowState& aState, nsLineBox* aLine, nsIFrame* aFrame, PRBool& aMadeNewFrame); // Push aLine which contains a positioned element that was truncated. Clean up any // placeholders on the same line that were continued. Set aKeepReflowGoing to false. void PushTruncatedPlaceholderLine(nsBlockReflowState& aState, line_iterator aLine, nsIFrame* aLastPlaceholder, PRBool& aKeepReflowGoing); nsresult SplitLine(nsBlockReflowState& aState, nsLineLayout& aLineLayout, line_iterator aLine, nsIFrame* aFrame); nsresult PullFrame(nsBlockReflowState& aState, line_iterator aLine, PRBool aDamageDeletedLine, nsIFrame*& aFrameResult); PRBool PullFrameFrom(nsBlockReflowState& aState, nsLineBox* aLine, nsBlockFrame* aFromContainer, PRBool aFromOverflowLine, nsLineList::iterator aFromLine, PRBool aDamageDeletedLines, nsIFrame*& aFrameResult); void PushLines(nsBlockReflowState& aState, nsLineList::iterator aLineBefore); void ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, PRBool aFromOverflow); //---------------------------------------- //XXX virtual void PaintChildren(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags = 0); void PaintFloats(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect); void PropagateFloatDamage(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDeltaY); void BuildFloatList(nsBlockReflowState& aState); //---------------------------------------- // List handling kludge void RenumberLists(nsPresContext* aPresContext); PRBool RenumberListsInBlock(nsPresContext* aPresContext, nsBlockFrame* aContainerFrame, PRInt32* aOrdinal, PRInt32 aDepth); PRBool RenumberListsFor(nsPresContext* aPresContext, nsIFrame* aKid, PRInt32* aOrdinal, PRInt32 aDepth); static PRBool FrameStartsCounterScope(nsIFrame* aFrame); nsresult UpdateBulletPosition(nsBlockReflowState& aState); void ReflowBullet(nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics); //---------------------------------------- public: nsLineList* GetOverflowLines() const; protected: nsLineList* RemoveOverflowLines(); nsresult SetOverflowLines(nsLineList* aOverflowLines); nsFrameList* GetOverflowPlaceholders() const; /** * This class is useful for efficiently modifying the out of flow * overflow list. It gives the client direct writable access to * the frame list temporarily but ensures that property is only * written back if absolutely necessary. */ struct nsAutoOOFFrameList { nsFrameList mList; nsAutoOOFFrameList(nsBlockFrame* aBlock) : mList(aBlock->GetOverflowOutOfFlows().FirstChild()), aOldHead(mList.FirstChild()), mBlock(aBlock) {} ~nsAutoOOFFrameList() { if (mList.FirstChild() != aOldHead) { mBlock->SetOverflowOutOfFlows(mList); } } protected: nsIFrame* aOldHead; nsBlockFrame* mBlock; }; friend struct nsAutoOOFFrameList; nsFrameList GetOverflowOutOfFlows() const; void SetOverflowOutOfFlows(const nsFrameList& aList); nsIFrame* LastChild(); #ifdef NS_DEBUG PRBool IsChild(nsIFrame* aFrame); void VerifyLines(PRBool aFinalCheckOK); void VerifyOverflowSituation(); PRInt32 GetDepth() const; #endif // Ascent of our first line to support 'vertical-align: baseline' in table-cells nscoord mAscent; nsLineList mLines; // List of all floats in this block nsFrameList mFloats; // XXX_fix_me: subclass one more time! // For list-item frames, this is the bullet frame. nsBulletFrame* mBullet; friend class nsBlockReflowState; private: nsAbsoluteContainingBlock mAbsoluteContainer; #ifdef DEBUG public: static PRBool gLamePaintMetrics; static PRBool gLameReflowMetrics; static PRBool gNoisy; static PRBool gNoisyDamageRepair; static PRBool gNoisyMaxElementWidth; static PRBool gNoisyReflow; static PRBool gReallyNoisyReflow; static PRBool gNoisySpaceManager; static PRBool gVerifyLines; static PRBool gDisableResizeOpt; static PRInt32 gNoiseIndent; static const char* kReflowCommandType[]; protected: static void InitDebugFlags(); #endif }; #ifdef DEBUG class AutoNoisyIndenter { public: AutoNoisyIndenter(PRBool aDoIndent) : mIndented(aDoIndent) { if (mIndented) { nsBlockFrame::gNoiseIndent++; } } ~AutoNoisyIndenter() { if (mIndented) { nsBlockFrame::gNoiseIndent--; } } private: PRBool mIndented; }; #endif #endif /* nsBlockFrame_h___ */