Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / generic / nsLineLayout.h
blob9b619a7926b37ab6198b19df01616aa847f7db75
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim:cindent:ts=2:et:sw=2:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Steve Clark <buster@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK *****
40 * This Original Code has been modified by IBM Corporation. Modifications made by IBM
41 * described herein are Copyright (c) International Business Machines Corporation, 2000.
42 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
44 * Date Modified by Description of modification
45 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
48 /* state and methods used while laying out a single line of a block frame */
50 #ifndef nsLineLayout_h___
51 #define nsLineLayout_h___
53 #include "nsFrame.h"
54 #include "nsDeque.h"
55 #include "nsLineBox.h"
56 #include "nsBlockReflowState.h"
57 #include "plarena.h"
58 #include "gfxTypes.h"
60 class nsBlockFrame;
62 class nsFloatManager;
63 class nsPlaceholderFrame;
64 struct nsStyleText;
66 class nsLineLayout {
67 public:
68 nsLineLayout(nsPresContext* aPresContext,
69 nsFloatManager* aFloatManager,
70 const nsHTMLReflowState* aOuterReflowState,
71 const nsLineList::iterator* aLine);
72 ~nsLineLayout();
74 void Init(nsBlockReflowState* aState, nscoord aMinLineHeight,
75 PRInt32 aLineNumber) {
76 mBlockRS = aState;
77 mMinLineHeight = aMinLineHeight;
78 mLineNumber = aLineNumber;
81 PRInt32 GetLineNumber() const {
82 return mLineNumber;
85 void BeginLineReflow(nscoord aX, nscoord aY,
86 nscoord aWidth, nscoord aHeight,
87 PRBool aImpactedByFloats,
88 PRBool aIsTopOfPage);
90 void EndLineReflow();
92 /**
93 * Called when a float has been placed. This method updates the
94 * inline frame and span data to account for any change in positions
95 * due to available space for the line boxes changing.
96 * @param aX/aY/aWidth/aHeight are the new available
97 * space rectangle, relative to the containing block.
98 * @param aFloatFrame the float frame that was placed.
100 void UpdateBand(const nsRect& aNewAvailableSpace,
101 nsIFrame* aFloatFrame);
103 nsresult BeginSpan(nsIFrame* aFrame,
104 const nsHTMLReflowState* aSpanReflowState,
105 nscoord aLeftEdge,
106 nscoord aRightEdge);
108 // Returns the width of the span
109 nscoord EndSpan(nsIFrame* aFrame);
111 PRInt32 GetCurrentSpanCount() const;
113 void SplitLineTo(PRInt32 aNewCount);
115 PRBool IsZeroHeight();
117 // Reflows the frame and returns the reflow status. aPushedFrame is PR_TRUE
118 // if the frame is pushed to the next line because it doesn't fit
119 nsresult ReflowFrame(nsIFrame* aFrame,
120 nsReflowStatus& aReflowStatus,
121 nsHTMLReflowMetrics* aMetrics,
122 PRBool& aPushedFrame);
124 nsresult AddBulletFrame(nsIFrame* aFrame,
125 const nsHTMLReflowMetrics& aMetrics);
127 void RemoveBulletFrame(nsIFrame* aFrame) {
128 PushFrame(aFrame);
131 void VerticalAlignLine();
133 PRBool TrimTrailingWhiteSpace();
135 void HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify);
138 * Handle all the relative positioning in the line, compute the
139 * combined area (== overflow area) for the line, and handle view
140 * sizing/positioning and the setting of the overflow rect.
142 void RelativePositionFrames(nsOverflowAreas& aOverflowAreas);
144 //----------------------------------------
146 // Supporting methods and data for flags
147 protected:
148 #define LL_FIRSTLETTERSTYLEOK 0x00000008
149 #define LL_ISTOPOFPAGE 0x00000010
150 #define LL_IMPACTEDBYFLOATS 0x00000040
151 #define LL_LASTFLOATWASLETTERFRAME 0x00000080
152 #define LL_LINEISEMPTY 0x00000100
153 #define LL_LINEENDSINBR 0x00000200
154 #define LL_NEEDBACKUP 0x00000400
155 #define LL_INFIRSTLINE 0x00000800
156 #define LL_GOTLINEBOX 0x00001000
157 #define LL_INFIRSTLETTER 0x00002000
158 #define LL_HASBULLET 0x00004000
159 #define LL_DIRTYNEXTLINE 0x00008000
160 #define LL_LINEATSTART 0x00010000
161 #define LL_LASTFLAG LL_LINEATSTART
163 void SetFlag(PRUint32 aFlag, PRBool aValue)
165 NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag");
166 NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value");
167 if (aValue) { // set flag
168 mFlags |= aFlag;
170 else { // unset flag
171 mFlags &= ~aFlag;
175 PRBool GetFlag(PRUint32 aFlag) const
177 NS_ASSERTION(aFlag<=LL_LASTFLAG, "bad flag");
178 return !!(mFlags & aFlag);
181 public:
183 // Support methods for word-wrapping during line reflow
185 void SetTextJustificationWeights(PRInt32 aNumSpaces, PRInt32 aNumLetters) {
186 mTextJustificationNumSpaces = aNumSpaces;
187 mTextJustificationNumLetters = aNumLetters;
191 * @return true if so far during reflow no non-empty content has been
192 * placed in the line (according to nsIFrame::IsEmpty())
194 PRBool LineIsEmpty() const
196 return GetFlag(LL_LINEISEMPTY);
200 * @return true if so far during reflow no non-empty leaf content
201 * (non-collapsed whitespace, replaced element, inline-block, etc) has been
202 * placed in the line
204 PRBool LineAtStart() const
206 return GetFlag(LL_LINEATSTART);
209 PRBool LineIsBreakable() const;
211 PRBool GetLineEndsInBR() const
213 return GetFlag(LL_LINEENDSINBR);
216 void SetLineEndsInBR(PRBool aOn)
218 SetFlag(LL_LINEENDSINBR, aOn);
221 //----------------------------------------
222 // Inform the line-layout about the presence of a floating frame
223 // XXX get rid of this: use get-frame-type?
224 PRBool AddFloat(nsIFrame* aFloat, nscoord aAvailableWidth)
226 return mBlockRS->AddFloat(this, aFloat, aAvailableWidth);
229 void SetTrimmableWidth(nscoord aTrimmableWidth) {
230 mTrimmableWidth = aTrimmableWidth;
233 //----------------------------------------
235 PRBool GetFirstLetterStyleOK() const {
236 return GetFlag(LL_FIRSTLETTERSTYLEOK);
239 void SetFirstLetterStyleOK(PRBool aSetting) {
240 SetFlag(LL_FIRSTLETTERSTYLEOK, aSetting);
243 PRBool GetInFirstLetter() const {
244 return GetFlag(LL_INFIRSTLETTER);
247 void SetInFirstLetter(PRBool aSetting) {
248 SetFlag(LL_INFIRSTLETTER, aSetting);
251 PRBool GetInFirstLine() const {
252 return GetFlag(LL_INFIRSTLINE);
255 void SetInFirstLine(PRBool aSetting) {
256 SetFlag(LL_INFIRSTLINE, aSetting);
259 // Calling this during block reflow ensures that the next line of inlines
260 // will be marked dirty, if there is one.
261 void SetDirtyNextLine() {
262 SetFlag(LL_DIRTYNEXTLINE, PR_TRUE);
264 PRBool GetDirtyNextLine() {
265 return GetFlag(LL_DIRTYNEXTLINE);
268 //----------------------------------------
270 nsPresContext* mPresContext;
273 * Record where an optional break could have been placed. During line reflow,
274 * frames containing optional break points (e.g., whitespace in text frames)
275 * can call SetLastOptionalBreakPosition to record where a break could
276 * have been made, but wasn't because we decided to place more content on
277 * the line. For non-text frames, offset 0 means
278 * before the content, offset PR_INT32_MAX means after the content.
280 * Currently this is used to handle cases where a single word comprises
281 * multiple frames, and the first frame fits on the line but the whole word
282 * doesn't. We look back to the last optional break position and
283 * reflow the whole line again, forcing a break at that position. The last
284 * optional break position could be in a text frame or else after a frame
285 * that cannot be part of a text run, so those are the positions we record.
287 * @param aFits set to true if the break position is within the available width.
289 * @param aPriority the priority of the break opportunity. If we are
290 * prioritizing break opportunities, we will not set a break if we have
291 * already set a break with a higher priority. @see gfxBreakPriority.
293 * @return PR_TRUE if we are actually reflowing with forced break position and we
294 * should break here
296 PRBool NotifyOptionalBreakPosition(nsIContent* aContent, PRInt32 aOffset,
297 PRBool aFits, gfxBreakPriority aPriority) {
298 NS_ASSERTION(!aFits || !GetFlag(LL_NEEDBACKUP),
299 "Shouldn't be updating the break position with a break that fits after we've already flagged an overrun");
300 // Remember the last break position that fits; if there was no break that fit,
301 // just remember the first break
302 if ((aFits && aPriority >= mLastOptionalBreakPriority) ||
303 !mLastOptionalBreakContent) {
304 mLastOptionalBreakContent = aContent;
305 mLastOptionalBreakContentOffset = aOffset;
306 mLastOptionalBreakPriority = aPriority;
308 return aContent && mForceBreakContent == aContent &&
309 mForceBreakContentOffset == aOffset;
312 * Like NotifyOptionalBreakPosition, but here it's OK for LL_NEEDBACKUP
313 * to be set, because the caller is merely pruning some saved break position(s)
314 * that are actually not feasible.
316 void RestoreSavedBreakPosition(nsIContent* aContent, PRInt32 aOffset,
317 gfxBreakPriority aPriority) {
318 mLastOptionalBreakContent = aContent;
319 mLastOptionalBreakContentOffset = aOffset;
320 mLastOptionalBreakPriority = aPriority;
323 * Signal that no backing up will be required after all.
325 void ClearOptionalBreakPosition() {
326 SetFlag(LL_NEEDBACKUP, PR_FALSE);
327 mLastOptionalBreakContent = nsnull;
328 mLastOptionalBreakContentOffset = -1;
329 mLastOptionalBreakPriority = eNoBreak;
331 // Retrieve last set optional break position. When this returns null, no
332 // optional break has been recorded (which means that the line can't break yet).
333 nsIContent* GetLastOptionalBreakPosition(PRInt32* aOffset,
334 gfxBreakPriority* aPriority) {
335 *aOffset = mLastOptionalBreakContentOffset;
336 *aPriority = mLastOptionalBreakPriority;
337 return mLastOptionalBreakContent;
341 * Check whether frames overflowed the available width and CanPlaceFrame
342 * requested backing up to a saved break position.
344 PRBool NeedsBackup() { return GetFlag(LL_NEEDBACKUP); }
346 // Line layout may place too much content on a line, overflowing its available
347 // width. When that happens, if SetLastOptionalBreakPosition has been
348 // used to record an optional break that wasn't taken, we can reflow the line
349 // again and force the break to happen at that point (i.e., backtracking
350 // to the last choice point).
352 // Record that we want to break at the given content+offset (which
353 // should have been previously returned by GetLastOptionalBreakPosition
354 // from another nsLineLayout).
355 void ForceBreakAtPosition(nsIContent* aContent, PRInt32 aOffset) {
356 mForceBreakContent = aContent;
357 mForceBreakContentOffset = aOffset;
359 PRBool HaveForcedBreakPosition() { return mForceBreakContent != nsnull; }
360 PRInt32 GetForcedBreakPosition(nsIContent* aContent) {
361 return mForceBreakContent == aContent ? mForceBreakContentOffset : -1;
365 * This can't be null. It usually returns a block frame but may return
366 * some other kind of frame when inline frames are reflowed in a non-block
367 * context (e.g. MathML or floating first-letter).
369 nsIFrame* GetLineContainerFrame() const { return mBlockReflowState->frame; }
370 const nsLineList::iterator* GetLine() const {
371 return GetFlag(LL_GOTLINEBOX) ? &mLineBox : nsnull;
373 nsLineList::iterator* GetLine() {
374 return GetFlag(LL_GOTLINEBOX) ? &mLineBox : nsnull;
378 * Returns the accumulated advance width of frames before the current frame
379 * on the line, plus the line container's left border+padding.
380 * This is always positive, the advance width is measured from
381 * the right edge for RTL blocks and from the left edge for LTR blocks.
382 * In other words, the current frame's distance from the line container's
383 * start content edge is:
384 * <code>GetCurrentFrameXDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code>
385 * Note the use of <code>.left</code> for both LTR and RTL line containers.
387 nscoord GetCurrentFrameXDistanceFromBlock();
389 protected:
390 // This state is constant for a given block frame doing line layout
391 nsFloatManager* mFloatManager;
392 const nsStyleText* mStyleText; // for the block
393 const nsHTMLReflowState* mBlockReflowState;
395 nsIContent* mLastOptionalBreakContent;
396 nsIContent* mForceBreakContent;
398 // XXX remove this when landing bug 154892 (splitting absolute positioned frames)
399 friend class nsInlineFrame;
401 nsBlockReflowState* mBlockRS;/* XXX hack! */
403 nsLineList::iterator mLineBox;
405 // Per-frame data recorded by the line-layout reflow logic. This
406 // state is the state needed to post-process the line after reflow
407 // has completed (vertical alignment, horizontal alignment,
408 // justification and relative positioning).
410 struct PerSpanData;
411 struct PerFrameData;
412 friend struct PerSpanData;
413 friend struct PerFrameData;
414 struct PerFrameData {
415 // link to next/prev frame in same span
416 PerFrameData* mNext;
417 PerFrameData* mPrev;
419 // pointer to child span data if this is an inline container frame
420 PerSpanData* mSpan;
422 // The frame
423 nsIFrame* mFrame;
425 // From metrics
426 nscoord mAscent;
427 nsRect mBounds;
428 nsOverflowAreas mOverflowAreas;
430 // From reflow-state
431 nsMargin mMargin;
432 nsMargin mBorderPadding;
433 nsMargin mOffsets;
435 // state for text justification
436 PRInt32 mJustificationNumSpaces;
437 PRInt32 mJustificationNumLetters;
439 // Other state we use
440 PRUint8 mVerticalAlign;
442 // PerFrameData flags
443 #define PFD_RELATIVEPOS 0x00000001
444 #define PFD_ISTEXTFRAME 0x00000002
445 #define PFD_ISNONEMPTYTEXTFRAME 0x00000004
446 #define PFD_ISNONWHITESPACETEXTFRAME 0x00000008
447 #define PFD_ISLETTERFRAME 0x00000010
448 #define PFD_RECOMPUTEOVERFLOW 0x00000020
449 #define PFD_ISBULLET 0x00000040
450 #define PFD_SKIPWHENTRIMMINGWHITESPACE 0x00000080
451 #define PFD_LASTFLAG PFD_SKIPWHENTRIMMINGWHITESPACE
453 PRUint8 mFlags;
455 void SetFlag(PRUint32 aFlag, PRBool aValue)
457 NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
458 NS_ASSERTION(aFlag<=PR_UINT8_MAX, "bad flag");
459 NS_ASSERTION(aValue==PR_FALSE || aValue==PR_TRUE, "bad value");
460 if (aValue) { // set flag
461 mFlags |= aFlag;
463 else { // unset flag
464 mFlags &= ~aFlag;
468 PRBool GetFlag(PRUint32 aFlag) const
470 NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
471 return !!(mFlags & aFlag);
475 PerFrameData* Last() {
476 PerFrameData* pfd = this;
477 while (pfd->mNext) {
478 pfd = pfd->mNext;
480 return pfd;
483 PerFrameData* mFrameFreeList;
485 struct PerSpanData {
486 union {
487 PerSpanData* mParent;
488 PerSpanData* mNextFreeSpan;
490 PerFrameData* mFrame;
491 PerFrameData* mFirstFrame;
492 PerFrameData* mLastFrame;
494 const nsHTMLReflowState* mReflowState;
495 PRPackedBool mNoWrap;
496 PRUint8 mDirection;
497 PRPackedBool mChangedFrameDirection;
498 PRPackedBool mZeroEffectiveSpanBox;
499 PRPackedBool mContainsFloat;
500 PRPackedBool mHasNonemptyContent;
502 nscoord mLeftEdge;
503 nscoord mX;
504 nscoord mRightEdge;
506 nscoord mTopLeading, mBottomLeading;
507 nscoord mLogicalHeight;
508 nscoord mMinY, mMaxY;
510 void AppendFrame(PerFrameData* pfd) {
511 if (nsnull == mLastFrame) {
512 mFirstFrame = pfd;
514 else {
515 mLastFrame->mNext = pfd;
516 pfd->mPrev = mLastFrame;
518 mLastFrame = pfd;
521 PerSpanData* mSpanFreeList;
522 PerSpanData* mRootSpan;
523 PerSpanData* mCurrentSpan;
525 gfxBreakPriority mLastOptionalBreakPriority;
526 PRInt32 mLastOptionalBreakContentOffset;
527 PRInt32 mForceBreakContentOffset;
529 nscoord mMinLineHeight;
531 // The amount of text indent that we applied to this line, needed for
532 // max-element-size calculation.
533 nscoord mTextIndent;
535 // This state varies during the reflow of a line but is line
536 // "global" state not span "local" state.
537 PRInt32 mLineNumber;
538 PRInt32 mTextJustificationNumSpaces;
539 PRInt32 mTextJustificationNumLetters;
541 PRInt32 mTotalPlacedFrames;
543 nscoord mTopEdge;
544 nscoord mMaxTopBoxHeight;
545 nscoord mMaxBottomBoxHeight;
547 // Final computed line-height value after VerticalAlignFrames for
548 // the block has been called.
549 nscoord mFinalLineHeight;
551 // Amount of trimmable whitespace width for the trailing text frame, if any
552 nscoord mTrimmableWidth;
554 PRInt32 mSpanDepth;
555 #ifdef DEBUG
556 PRInt32 mSpansAllocated, mSpansFreed;
557 PRInt32 mFramesAllocated, mFramesFreed;
558 #endif
559 PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
561 PRUint32 mFlags;
563 PRUint8 mTextAlign;
565 nsresult NewPerFrameData(PerFrameData** aResult);
567 nsresult NewPerSpanData(PerSpanData** aResult);
569 void FreeSpan(PerSpanData* psd);
571 PRBool InBlockContext() const {
572 return mSpanDepth == 0;
575 void PushFrame(nsIFrame* aFrame);
577 void ApplyStartMargin(PerFrameData* pfd,
578 nsHTMLReflowState& aReflowState);
580 PRBool CanPlaceFrame(PerFrameData* pfd,
581 PRUint8 aFrameDirection,
582 PRBool aNotSafeToBreak,
583 PRBool aFrameCanContinueTextRun,
584 PRBool aCanRollBackBeforeFrame,
585 nsHTMLReflowMetrics& aMetrics,
586 nsReflowStatus& aStatus,
587 PRBool* aOptionalBreakAfterFits);
589 void PlaceFrame(PerFrameData* pfd,
590 nsHTMLReflowMetrics& aMetrics);
592 void VerticalAlignFrames(PerSpanData* psd);
594 void PlaceTopBottomFrames(PerSpanData* psd,
595 nscoord aDistanceFromTop,
596 nscoord aLineHeight);
598 void RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas);
600 PRBool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaWidth);
602 void ComputeJustificationWeights(PerSpanData* psd, PRInt32* numSpaces, PRInt32* numLetters);
604 struct FrameJustificationState {
605 PRInt32 mTotalNumSpaces;
606 PRInt32 mTotalNumLetters;
607 nscoord mTotalWidthForSpaces;
608 nscoord mTotalWidthForLetters;
609 PRInt32 mNumSpacesProcessed;
610 PRInt32 mNumLettersProcessed;
611 nscoord mWidthForSpacesProcessed;
612 nscoord mWidthForLettersProcessed;
615 // Apply justification. The return value is the amount by which the width of
616 // the span corresponding to aPSD got increased due to justification.
617 nscoord ApplyFrameJustification(PerSpanData* aPSD,
618 FrameJustificationState* aState);
621 #ifdef DEBUG
622 void DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent);
623 #endif
626 #endif /* nsLineLayout_h___ */