1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* state and methods used while laying out a single line of a block frame */
9 #ifndef nsLineLayout_h___
10 #define nsLineLayout_h___
13 #include "JustificationUtils.h"
14 #include "mozilla/ArenaAllocator.h"
15 #include "mozilla/WritingModes.h"
16 #include "BlockReflowInput.h"
17 #include "nsLineBox.h"
23 using BlockReflowInput
= mozilla::BlockReflowInput
;
24 using ReflowInput
= mozilla::ReflowInput
;
25 using ReflowOutput
= mozilla::ReflowOutput
;
29 * @param aBaseLineLayout the nsLineLayout for ruby base,
30 * nullptr if no separate base nsLineLayout is needed.
32 nsLineLayout(nsPresContext
* aPresContext
, nsFloatManager
* aFloatManager
,
33 const ReflowInput
* aOuterReflowInput
,
34 const nsLineList::iterator
* aLine
,
35 nsLineLayout
* aBaseLineLayout
);
38 void Init(BlockReflowInput
* aState
, nscoord aMinLineBSize
,
39 int32_t aLineNumber
) {
41 mMinLineBSize
= aMinLineBSize
;
42 mLineNumber
= aLineNumber
;
45 int32_t GetLineNumber() const { return mLineNumber
; }
47 void BeginLineReflow(nscoord aICoord
, nscoord aBCoord
, nscoord aISize
,
48 nscoord aBSize
, bool aImpactedByFloats
,
49 bool aIsTopOfPage
, mozilla::WritingMode aWritingMode
,
50 const nsSize
& aContainerSize
);
55 * Called when a float has been placed. This method updates the
56 * inline frame and span data to account for any change in positions
57 * due to available space for the line boxes changing.
58 * @param aX/aY/aWidth/aHeight are the new available
59 * space rectangle, relative to the containing block.
60 * @param aFloatFrame the float frame that was placed.
62 void UpdateBand(mozilla::WritingMode aWM
,
63 const mozilla::LogicalRect
& aNewAvailableSpace
,
64 nsIFrame
* aFloatFrame
);
66 void BeginSpan(nsIFrame
* aFrame
, const ReflowInput
* aSpanReflowInput
,
67 nscoord aLeftEdge
, nscoord aRightEdge
, nscoord
* aBaseline
);
69 // Returns the width of the span
70 nscoord
EndSpan(nsIFrame
* aFrame
);
72 // This method attaches the last frame reflowed in this line layout
73 // to that in the base line layout.
74 void AttachLastFrameToBaseLineLayout() {
75 AttachFrameToBaseLineLayout(LastFrame());
78 // This method attaches the root frame of this line layout to the
79 // last reflowed frame in the base line layout.
80 void AttachRootFrameToBaseLineLayout() {
81 AttachFrameToBaseLineLayout(mRootSpan
->mFrame
);
84 int32_t GetCurrentSpanCount() const;
86 void SplitLineTo(int32_t aNewCount
);
90 // Reflows the frame and returns the reflow status. aPushedFrame is true
91 // if the frame is pushed to the next line because it doesn't fit.
92 void ReflowFrame(nsIFrame
* aFrame
, nsReflowStatus
& aReflowStatus
,
93 ReflowOutput
* aMetrics
, bool& aPushedFrame
);
95 void AddMarkerFrame(nsIFrame
* aFrame
, const ReflowOutput
& aMetrics
);
97 void RemoveMarkerFrame(nsIFrame
* aFrame
);
100 * Place frames in the block direction (CSS property vertical-align)
102 void VerticalAlignLine();
104 bool TrimTrailingWhiteSpace();
107 * Place frames in the inline direction (CSS property text-align).
109 void TextAlignLine(nsLineBox
* aLine
, bool aIsLastLine
);
112 * Handle all the relative positioning in the line, compute the
113 * combined area (== overflow area) for the line, and handle view
114 * sizing/positioning and the setting of the overflow rect.
116 void RelativePositionFrames(nsOverflowAreas
& aOverflowAreas
) {
117 RelativePositionFrames(mRootSpan
, aOverflowAreas
);
120 // Support methods for word-wrapping during line reflow
122 void SetJustificationInfo(const mozilla::JustificationInfo
& aInfo
) {
123 mJustificationInfo
= aInfo
;
127 * @return true if so far during reflow no non-empty content has been
128 * placed in the line (according to nsIFrame::IsEmpty())
130 bool LineIsEmpty() const { return mLineIsEmpty
; }
133 * @return true if so far during reflow no non-empty leaf content
134 * (non-collapsed whitespace, replaced element, inline-block, etc) has been
137 bool LineAtStart() const { return mLineAtStart
; }
139 bool LineIsBreakable() const;
141 bool GetLineEndsInBR() const { return mLineEndsInBR
; }
143 void SetLineEndsInBR(bool aOn
) { mLineEndsInBR
= aOn
; }
145 //----------------------------------------
146 // Inform the line-layout about the presence of a floating frame
147 // XXX get rid of this: use get-frame-type?
148 bool AddFloat(nsIFrame
* aFloat
, nscoord aAvailableISize
) {
149 // When reflowing ruby text frames, no block reflow input is
150 // provided to the line layout. However, floats should never be
151 // associated with ruby text containers, hence this method should
152 // not be called in that case.
154 "Should not call this method if there is no block reflow input "
156 return mBlockRI
->AddFloat(this, aFloat
, aAvailableISize
);
159 void SetTrimmableISize(nscoord aTrimmableISize
) {
160 mTrimmableISize
= aTrimmableISize
;
163 //----------------------------------------
165 bool GetFirstLetterStyleOK() const { return mFirstLetterStyleOK
; }
167 void SetFirstLetterStyleOK(bool aSetting
) { mFirstLetterStyleOK
= aSetting
; }
169 bool GetInFirstLetter() const { return mInFirstLetter
; }
171 void SetInFirstLetter(bool aSetting
) { mInFirstLetter
= aSetting
; }
173 bool GetInFirstLine() const { return mInFirstLine
; }
175 void SetInFirstLine(bool aSetting
) { mInFirstLine
= aSetting
; }
177 // Calling this during block reflow ensures that the next line of inlines
178 // will be marked dirty, if there is one.
179 void SetDirtyNextLine() { mDirtyNextLine
= true; }
180 bool GetDirtyNextLine() { return mDirtyNextLine
; }
182 //----------------------------------------
184 nsPresContext
* mPresContext
;
187 * Record where an optional break could have been placed. During line reflow,
188 * frames containing optional break points (e.g., whitespace in text frames)
189 * can call SetLastOptionalBreakPosition to record where a break could
190 * have been made, but wasn't because we decided to place more content on
191 * the line. For non-text frames, offset 0 means before the frame, offset
192 * INT32_MAX means after the frame.
194 * Currently this is used to handle cases where a single word comprises
195 * multiple frames, and the first frame fits on the line but the whole word
196 * doesn't. We look back to the last optional break position and
197 * reflow the whole line again, forcing a break at that position. The last
198 * optional break position could be in a text frame or else after a frame
199 * that cannot be part of a text run, so those are the positions we record.
201 * @param aFrame the frame which contains the optional break position.
203 * @param aFits set to true if the break position is within the available
206 * @param aPriority the priority of the break opportunity. If we are
207 * prioritizing break opportunities, we will not set a break if we have
208 * already set a break with a higher priority. @see gfxBreakPriority.
210 * @return true if we are actually reflowing with forced break position and we
213 bool NotifyOptionalBreakPosition(nsIFrame
* aFrame
, int32_t aOffset
,
214 bool aFits
, gfxBreakPriority aPriority
);
216 // Tries to place a float, and records whether the float actually was placed.
217 bool TryToPlaceFloat(nsIFrame
* aFloat
);
219 // Records a floating frame in a nowrap context for it to be placed on the
220 // next break opportunity.
221 void RecordNoWrapFloat(nsIFrame
* aFloat
);
223 // Tries to place the floats from the nowrap context.
224 void FlushNoWrapFloats();
227 * Like NotifyOptionalBreakPosition, but here it's OK for mNeedBackup
228 * to be set, because the caller is merely pruning some saved break
229 * position(s) that are actually not feasible.
231 void RestoreSavedBreakPosition(nsIFrame
* aFrame
, int32_t aOffset
,
232 gfxBreakPriority aPriority
) {
233 mLastOptionalBreakFrame
= aFrame
;
234 mLastOptionalBreakFrameOffset
= aOffset
;
235 mLastOptionalBreakPriority
= aPriority
;
238 * Signal that no backing up will be required after all.
240 void ClearOptionalBreakPosition() {
242 mLastOptionalBreakFrame
= nullptr;
243 mLastOptionalBreakFrameOffset
= -1;
244 mLastOptionalBreakPriority
= gfxBreakPriority::eNoBreak
;
246 // Retrieve last set optional break position. When this returns null, no
247 // optional break has been recorded (which means that the line can't break
249 nsIFrame
* GetLastOptionalBreakPosition(int32_t* aOffset
,
250 gfxBreakPriority
* aPriority
) {
251 *aOffset
= mLastOptionalBreakFrameOffset
;
252 *aPriority
= mLastOptionalBreakPriority
;
253 return mLastOptionalBreakFrame
;
255 // Whether any optional break position has been recorded.
256 bool HasOptionalBreakPosition() const {
257 return mLastOptionalBreakFrame
!= nullptr;
259 // Get the priority of the last optional break position recorded.
260 gfxBreakPriority
LastOptionalBreakPriority() const {
261 return mLastOptionalBreakPriority
;
265 * Check whether frames overflowed the available width and CanPlaceFrame
266 * requested backing up to a saved break position.
268 bool NeedsBackup() { return mNeedBackup
; }
270 // Line layout may place too much content on a line, overflowing its available
271 // width. When that happens, if SetLastOptionalBreakPosition has been
272 // used to record an optional break that wasn't taken, we can reflow the line
273 // again and force the break to happen at that point (i.e., backtracking
274 // to the last choice point).
276 // Record that we want to break at the given content+offset (which
277 // should have been previously returned by GetLastOptionalBreakPosition
278 // from another nsLineLayout).
279 void ForceBreakAtPosition(nsIFrame
* aFrame
, int32_t aOffset
) {
280 mForceBreakFrame
= aFrame
;
281 mForceBreakFrameOffset
= aOffset
;
283 bool HaveForcedBreakPosition() { return mForceBreakFrame
!= nullptr; }
284 int32_t GetForcedBreakPosition(nsIFrame
* aFrame
) {
285 return mForceBreakFrame
== aFrame
? mForceBreakFrameOffset
: -1;
289 * This can't be null. It usually returns a block frame but may return
290 * some other kind of frame when inline frames are reflowed in a non-block
291 * context (e.g. MathML or floating first-letter).
293 nsIFrame
* LineContainerFrame() const { return mBlockReflowInput
->mFrame
; }
294 const ReflowInput
* LineContainerRI() const { return mBlockReflowInput
; }
295 const nsLineList::iterator
* GetLine() const {
296 return mGotLineBox
? &mLineBox
: nullptr;
298 nsLineList::iterator
* GetLine() { return mGotLineBox
? &mLineBox
: nullptr; }
301 * Returns the accumulated advance width of frames before the current frame
302 * on the line, plus the line container's left border+padding.
303 * This is always positive, the advance width is measured from
304 * the right edge for RTL blocks and from the left edge for LTR blocks.
305 * In other words, the current frame's distance from the line container's
306 * start content edge is:
307 * <code>GetCurrentFrameInlineDistanceFromBlock() -
308 * lineContainer->GetUsedBorderAndPadding().left</code> Note the use of
309 * <code>.left</code> for both LTR and RTL line containers.
311 nscoord
GetCurrentFrameInlineDistanceFromBlock();
314 * Move the inline position where the next frame will be reflowed forward by
317 void AdvanceICoord(nscoord aAmount
) { mCurrentSpan
->mICoord
+= aAmount
; }
319 * Returns the writing mode for the root span.
321 mozilla::WritingMode
GetWritingMode() { return mRootSpan
->mWritingMode
; }
323 * Returns the inline position where the next frame will be reflowed.
325 nscoord
GetCurrentICoord() { return mCurrentSpan
->mICoord
; }
327 void SetSuppressLineWrap(bool aEnabled
) { mSuppressLineWrap
= aEnabled
; }
330 // This state is constant for a given block frame doing line layout
332 // A non-owning pointer, which points to the object owned by
333 // nsAutoFloatManager::mNew.
334 nsFloatManager
* mFloatManager
;
336 const nsStyleText
* mStyleText
; // for the block
337 const ReflowInput
* mBlockReflowInput
;
339 // The line layout for the base text. It is usually nullptr.
340 // It becomes not null when the current line layout is for ruby
341 // annotations. When there is nested ruby inside annotation, it
342 // forms a linked list from the inner annotation to the outermost
343 // line layout. The outermost line layout, which has this member
344 // being nullptr, is responsible for managing the life cycle of
345 // per-frame data and per-span data, and handling floats.
346 nsLineLayout
* const mBaseLineLayout
;
348 nsLineLayout
* GetOutermostLineLayout() {
349 nsLineLayout
* lineLayout
= this;
350 while (lineLayout
->mBaseLineLayout
) {
351 lineLayout
= lineLayout
->mBaseLineLayout
;
356 nsIFrame
* mLastOptionalBreakFrame
;
357 nsIFrame
* mForceBreakFrame
;
359 // XXX remove this when landing bug 154892 (splitting absolute positioned
361 friend class nsInlineFrame
;
363 // XXX Take care that nsRubyBaseContainer would give nullptr to this
364 // member. It should not be a problem currently, since the only
365 // code use it is handling float, which does not affect ruby.
366 // See comment in nsLineLayout::AddFloat
367 BlockReflowInput
* mBlockRI
; /* XXX hack! */
369 nsLineList::iterator mLineBox
;
371 // Per-frame data recorded by the line-layout reflow logic. This
372 // state is the state needed to post-process the line after reflow
373 // has completed (block-direction alignment, inline-direction alignment,
374 // justification and relative positioning).
378 friend struct PerSpanData
;
379 friend struct PerFrameData
;
380 struct PerFrameData
{
381 // link to next/prev frame in same span
385 // Link to the frame of next ruby annotation. It is a linked list
386 // through this pointer from ruby base to all its annotations. It
387 // could be nullptr if there is no more annotation.
388 // If PFD_ISLINKEDTOBASE is set, the current PFD is one of the ruby
389 // annotations in the base's list, otherwise it is the ruby base,
390 // and its mNextAnnotation is the start of the linked list.
391 PerFrameData
* mNextAnnotation
;
393 // pointer to child span data if this is an inline container frame
401 // note that mBounds is a logical rect in the *line*'s writing mode.
402 // When setting frame coordinates, we have to convert to the frame's
404 mozilla::LogicalRect mBounds
;
405 nsOverflowAreas mOverflowAreas
;
408 mozilla::LogicalMargin mMargin
; // in *line* writing mode
409 mozilla::LogicalMargin mBorderPadding
; // in *line* writing mode
410 mozilla::LogicalMargin mOffsets
; // in *frame* writing mode
412 // state for text justification
413 // Note that, although all frames would have correct inner
414 // opportunities computed after ComputeFrameJustification, start
415 // and end justifiable info are not reliable for non-text frames.
416 mozilla::JustificationInfo mJustificationInfo
;
417 mozilla::JustificationAssignment mJustificationAssignment
;
419 // PerFrameData flags
420 bool mRelativePos
: 1;
421 bool mIsTextFrame
: 1;
422 bool mIsNonEmptyTextFrame
: 1;
423 bool mIsNonWhitespaceTextFrame
: 1;
424 bool mIsLetterFrame
: 1;
425 bool mRecomputeOverflow
: 1;
427 bool mSkipWhenTrimmingWhitespace
: 1;
429 bool mIsPlaceholder
: 1;
430 bool mIsLinkedToBase
: 1;
432 // Other state we use
433 uint8_t mBlockDirAlign
;
434 mozilla::WritingMode mWritingMode
;
436 PerFrameData
* Last() {
437 PerFrameData
* pfd
= this;
444 bool IsStartJustifiable() const {
445 return mJustificationInfo
.mIsStartJustifiable
;
448 bool IsEndJustifiable() const {
449 return mJustificationInfo
.mIsEndJustifiable
;
452 bool ParticipatesInJustification() const;
454 PerFrameData
* mFrameFreeList
;
456 // In nsLineLayout, a "span" is a container inline frame, and a "frame" is one
459 // nsLineLayout::BeginLineReflow() creates the initial PerSpanData which is
460 // called the "root span". nsInlineFrame::ReflowFrames() creates a new
461 // PerSpanData when it calls nsLineLayout::BeginSpan(); at this time, the
462 // nsLineLayout object's mCurrentSpan is switched to the new span. The new
463 // span records the old mCurrentSpan as its parent. After reflowing the child
464 // inline frames, nsInlineFrame::ReflowFrames() calls nsLineLayout::EndSpan(),
465 // which pops the PerSpanData and re-sets mCurrentSpan.
468 PerSpanData
* mParent
;
469 PerSpanData
* mNextFreeSpan
;
472 // The PerFrameData of the inline frame that "owns" the span, or null if
473 // this is the root span. mFrame is initialized to the containing inline
474 // frame's PerFrameData when a new PerSpanData is pushed in
475 // nsLineLayout::BeginSpan().
476 PerFrameData
* mFrame
;
478 // The first PerFrameData structure in the span.
479 PerFrameData
* mFirstFrame
;
481 // The last PerFrameData structure in the span. PerFrameData structures are
482 // added to the span as they are reflowed. mLastFrame may also be directly
483 // manipulated if a line is split, or if frames are pushed from one line to
485 PerFrameData
* mLastFrame
;
487 const ReflowInput
* mReflowInput
;
489 mozilla::WritingMode mWritingMode
;
491 bool mHasNonemptyContent
;
497 nscoord mBStartLeading
, mBEndLeading
;
498 nscoord mLogicalBSize
;
499 nscoord mMinBCoord
, mMaxBCoord
;
502 void AppendFrame(PerFrameData
* pfd
) {
506 mLastFrame
->mNext
= pfd
;
507 pfd
->mPrev
= mLastFrame
;
512 PerSpanData
* mSpanFreeList
;
513 PerSpanData
* mRootSpan
;
514 PerSpanData
* mCurrentSpan
;
516 // The container size to use when converting between logical and
517 // physical coordinates for frames in this span. For the root span
518 // this is the size of the block cached in mContainerSize; for
519 // child spans it's the size of the root span.
520 nsSize
ContainerSizeForSpan(PerSpanData
* aPSD
) {
521 return (aPSD
== mRootSpan
)
523 : aPSD
->mFrame
->mBounds
.Size(mRootSpan
->mWritingMode
)
524 .GetPhysicalSize(mRootSpan
->mWritingMode
);
527 gfxBreakPriority mLastOptionalBreakPriority
;
528 int32_t mLastOptionalBreakFrameOffset
;
529 int32_t mForceBreakFrameOffset
;
531 nscoord mMinLineBSize
;
533 // The amount of text indent that we applied to this line, needed for
534 // max-element-size calculation.
537 // This state varies during the reflow of a line but is line
538 // "global" state not span "local" state.
540 mozilla::JustificationInfo mJustificationInfo
;
542 int32_t mTotalPlacedFrames
;
545 nscoord mMaxStartBoxBSize
;
546 nscoord mMaxEndBoxBSize
;
548 nscoord mInflationMinFontSize
;
550 // Final computed line-bSize value after VerticalAlignFrames for
551 // the block has been called.
552 nscoord mFinalLineBSize
;
554 // Amount of trimmable whitespace inline size for the trailing text
556 nscoord mTrimmableISize
;
558 // Physical size. Use only for physical <-> logical coordinate conversion.
559 nsSize mContainerSize
;
560 const nsSize
& ContainerSize() const { return mContainerSize
; }
562 bool mFirstLetterStyleOK
: 1;
563 bool mIsTopOfPage
: 1;
564 bool mImpactedByFloats
: 1;
565 bool mLastFloatWasLetterFrame
: 1;
566 bool mLineIsEmpty
: 1;
567 bool mLineEndsInBR
: 1;
568 bool mNeedBackup
: 1;
569 bool mInFirstLine
: 1;
570 bool mGotLineBox
: 1;
571 bool mInFirstLetter
: 1;
573 bool mDirtyNextLine
: 1;
574 bool mLineAtStart
: 1;
576 bool mSuppressLineWrap
: 1;
580 int32_t mSpansAllocated
, mSpansFreed
;
581 int32_t mFramesAllocated
, mFramesFreed
;
585 * Per span and per frame data.
587 mozilla::ArenaAllocator
<1024, sizeof(void*)> mArena
;
590 * Allocate a PerFrameData from the mArena pool. The allocation is infallible.
592 PerFrameData
* NewPerFrameData(nsIFrame
* aFrame
);
595 * Allocate a PerSpanData from the mArena pool. The allocation is infallible.
597 PerSpanData
* NewPerSpanData();
599 PerFrameData
* LastFrame() const { return mCurrentSpan
->mLastFrame
; }
602 * Unlink the given PerFrameData and all the siblings after it from
603 * the span. The unlinked PFDs are usually freed immediately.
604 * However, if PFD_ISLINKEDTOBASE is set, it won't be freed until
605 * the frame of its base is unlinked.
607 void UnlinkFrame(PerFrameData
* pfd
);
610 * Free the given PerFrameData.
612 void FreeFrame(PerFrameData
* pfd
);
614 void FreeSpan(PerSpanData
* psd
);
616 bool InBlockContext() const { return mSpanDepth
== 0; }
618 void PushFrame(nsIFrame
* aFrame
);
620 void AllowForStartMargin(PerFrameData
* pfd
, ReflowInput
& aReflowInput
);
622 void SyncAnnotationBounds(PerFrameData
* aRubyFrame
);
624 bool CanPlaceFrame(PerFrameData
* pfd
, bool aNotSafeToBreak
,
625 bool aFrameCanContinueTextRun
,
626 bool aCanRollBackBeforeFrame
, ReflowOutput
& aMetrics
,
627 nsReflowStatus
& aStatus
, bool* aOptionalBreakAfterFits
);
629 void PlaceFrame(PerFrameData
* pfd
, ReflowOutput
& aMetrics
);
631 void AdjustLeadings(nsIFrame
* spanFrame
, PerSpanData
* psd
,
632 const nsStyleText
* aStyleText
, float aInflation
,
633 bool* aZeroEffectiveSpanBox
);
635 void VerticalAlignFrames(PerSpanData
* psd
);
637 void PlaceTopBottomFrames(PerSpanData
* psd
, nscoord aDistanceFromStart
,
640 void ApplyRelativePositioning(PerFrameData
* aPFD
);
642 void RelativePositionAnnotations(PerSpanData
* aRubyPSD
,
643 nsOverflowAreas
& aOverflowAreas
);
645 void RelativePositionFrames(PerSpanData
* psd
,
646 nsOverflowAreas
& aOverflowAreas
);
648 bool TrimTrailingWhiteSpaceIn(PerSpanData
* psd
, nscoord
* aDeltaISize
);
650 struct JustificationComputationState
;
652 static int AssignInterframeJustificationGaps(
653 PerFrameData
* aFrame
, JustificationComputationState
& aState
);
655 int32_t ComputeFrameJustification(PerSpanData
* psd
,
656 JustificationComputationState
& aState
);
658 void AdvanceAnnotationInlineBounds(PerFrameData
* aPFD
,
659 const nsSize
& aContainerSize
,
660 nscoord aDeltaICoord
, nscoord aDeltaISize
);
662 void ApplyLineJustificationToAnnotations(PerFrameData
* aPFD
,
663 nscoord aDeltaICoord
,
664 nscoord aDeltaISize
);
666 // Apply justification. The return value is the amount by which the width of
667 // the span corresponding to aPSD got increased due to justification.
668 nscoord
ApplyFrameJustification(
669 PerSpanData
* aPSD
, mozilla::JustificationApplicationState
& aState
);
671 void ExpandRubyBox(PerFrameData
* aFrame
, nscoord aReservedISize
,
672 const nsSize
& aContainerSize
);
674 void ExpandRubyBoxWithAnnotations(PerFrameData
* aFrame
,
675 const nsSize
& aContainerSize
);
677 void ExpandInlineRubyBoxes(PerSpanData
* aSpan
);
679 void AttachFrameToBaseLineLayout(PerFrameData
* aFrame
);
682 void DumpPerSpanData(PerSpanData
* psd
, int32_t aIndent
);
686 #endif /* nsLineLayout_h___ */