layout/generic/nsLineLayout.h

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a9c5d34f9508
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim:cindent:ts=2:et:sw=2:
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 * This Original Code has been modified by IBM Corporation. Modifications made by IBM
8 * described herein are Copyright (c) International Business Machines Corporation, 2000.
9 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
10 *
11 * Date Modified by Description of modification
12 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
13 */
14
15 /* state and methods used while laying out a single line of a block frame */
16
17 #ifndef nsLineLayout_h___
18 #define nsLineLayout_h___
19
20 #include "nsLineBox.h"
21 #include "nsBlockReflowState.h"
22 #include "plarena.h"
23 #include "gfxTypes.h"
24 #include "WritingModes.h"
25
26 class nsFloatManager;
27 struct nsStyleText;
28
29 class nsLineLayout {
30 public:
31 nsLineLayout(nsPresContext* aPresContext,
32 nsFloatManager* aFloatManager,
33 const nsHTMLReflowState* aOuterReflowState,
34 const nsLineList::iterator* aLine);
35 ~nsLineLayout();
36
37 void Init(nsBlockReflowState* aState, nscoord aMinLineBSize,
38 int32_t aLineNumber) {
39 mBlockRS = aState;
40 mMinLineBSize = aMinLineBSize;
41 mLineNumber = aLineNumber;
42 }
43
44 int32_t GetLineNumber() const {
45 return mLineNumber;
46 }
47
48 void BeginLineReflow(nscoord aICoord, nscoord aBCoord,
49 nscoord aISize, nscoord aBSize,
50 bool aImpactedByFloats,
51 bool aIsTopOfPage,
52 mozilla::WritingMode aWritingMode,
53 nscoord aContainerWidth);
54
55 void EndLineReflow();
56
57 /**
58 * Called when a float has been placed. This method updates the
59 * inline frame and span data to account for any change in positions
60 * due to available space for the line boxes changing.
61 * @param aX/aY/aWidth/aHeight are the new available
62 * space rectangle, relative to the containing block.
63 * @param aFloatFrame the float frame that was placed.
64 */
65 void UpdateBand(const nsRect& aNewAvailableSpace,
66 nsIFrame* aFloatFrame);
67
68 void BeginSpan(nsIFrame* aFrame, const nsHTMLReflowState* aSpanReflowState,
69 nscoord aLeftEdge, nscoord aRightEdge, nscoord* aBaseline);
70
71 // Returns the width of the span
72 nscoord EndSpan(nsIFrame* aFrame);
73
74 int32_t GetCurrentSpanCount() const;
75
76 void SplitLineTo(int32_t aNewCount);
77
78 bool IsZeroBSize();
79
80 // Reflows the frame and returns the reflow status. aPushedFrame is true
81 // if the frame is pushed to the next line because it doesn't fit
82 nsresult ReflowFrame(nsIFrame* aFrame,
83 nsReflowStatus& aReflowStatus,
84 nsHTMLReflowMetrics* aMetrics,
85 bool& aPushedFrame);
86
87 void AddBulletFrame(nsIFrame* aFrame, const nsHTMLReflowMetrics& aMetrics);
88
89 void RemoveBulletFrame(nsIFrame* aFrame) {
90 PushFrame(aFrame);
91 }
92
93 void BlockDirAlignLine();
94
95 bool TrimTrailingWhiteSpace();
96
97 void InlineDirAlignFrames(nsLineBox* aLine, bool aIsLastLine);
98
99 /**
100 * Handle all the relative positioning in the line, compute the
101 * combined area (== overflow area) for the line, and handle view
102 * sizing/positioning and the setting of the overflow rect.
103 */
104 void RelativePositionFrames(nsOverflowAreas& aOverflowAreas);
105
106 // Support methods for word-wrapping during line reflow
107
108 void SetTextJustificationWeights(int32_t aNumSpaces, int32_t aNumLetters) {
109 mTextJustificationNumSpaces = aNumSpaces;
110 mTextJustificationNumLetters = aNumLetters;
111 }
112
113 /**
114 * @return true if so far during reflow no non-empty content has been
115 * placed in the line (according to nsIFrame::IsEmpty())
116 */
117 bool LineIsEmpty() const
118 {
119 return mLineIsEmpty;
120 }
121
122 /**
123 * @return true if so far during reflow no non-empty leaf content
124 * (non-collapsed whitespace, replaced element, inline-block, etc) has been
125 * placed in the line
126 */
127 bool LineAtStart() const
128 {
129 return mLineAtStart;
130 }
131
132 bool LineIsBreakable() const;
133
134 bool GetLineEndsInBR() const
135 {
136 return mLineEndsInBR;
137 }
138
139 void SetLineEndsInBR(bool aOn)
140 {
141 mLineEndsInBR = aOn;
142 }
143
144 //----------------------------------------
145 // Inform the line-layout about the presence of a floating frame
146 // XXX get rid of this: use get-frame-type?
147 bool AddFloat(nsIFrame* aFloat, nscoord aAvailableWidth)
148 {
149 return mBlockRS->AddFloat(this, aFloat, aAvailableWidth);
150 }
151
152 void SetTrimmableWidth(nscoord aTrimmableWidth) {
153 mTrimmableWidth = aTrimmableWidth;
154 }
155
156 //----------------------------------------
157
158 bool GetFirstLetterStyleOK() const {
159 return mFirstLetterStyleOK;
160 }
161
162 void SetFirstLetterStyleOK(bool aSetting) {
163 mFirstLetterStyleOK = aSetting;
164 }
165
166 bool GetInFirstLetter() const {
167 return mInFirstLetter;
168 }
169
170 void SetInFirstLetter(bool aSetting) {
171 mInFirstLetter = aSetting;
172 }
173
174 bool GetInFirstLine() const {
175 return mInFirstLine;
176 }
177
178 void SetInFirstLine(bool aSetting) {
179 mInFirstLine = aSetting;
180 }
181
182 // Calling this during block reflow ensures that the next line of inlines
183 // will be marked dirty, if there is one.
184 void SetDirtyNextLine() {
185 mDirtyNextLine = true;
186 }
187 bool GetDirtyNextLine() {
188 return mDirtyNextLine;
189 }
190
191 //----------------------------------------
192
193 nsPresContext* mPresContext;
194
195 /**
196 * Record where an optional break could have been placed. During line reflow,
197 * frames containing optional break points (e.g., whitespace in text frames)
198 * can call SetLastOptionalBreakPosition to record where a break could
199 * have been made, but wasn't because we decided to place more content on
200 * the line. For non-text frames, offset 0 means
201 * before the content, offset INT32_MAX means after the content.
202 *
203 * Currently this is used to handle cases where a single word comprises
204 * multiple frames, and the first frame fits on the line but the whole word
205 * doesn't. We look back to the last optional break position and
206 * reflow the whole line again, forcing a break at that position. The last
207 * optional break position could be in a text frame or else after a frame
208 * that cannot be part of a text run, so those are the positions we record.
209 *
210 * @param aFits set to true if the break position is within the available width.
211 *
212 * @param aPriority the priority of the break opportunity. If we are
213 * prioritizing break opportunities, we will not set a break if we have
214 * already set a break with a higher priority. @see gfxBreakPriority.
215 *
216 * @return true if we are actually reflowing with forced break position and we
217 * should break here
218 */
219 bool NotifyOptionalBreakPosition(nsIContent* aContent, int32_t aOffset,
220 bool aFits, gfxBreakPriority aPriority) {
221 NS_ASSERTION(!aFits || !mNeedBackup,
222 "Shouldn't be updating the break position with a break that fits after we've already flagged an overrun");
223 // Remember the last break position that fits; if there was no break that fit,
224 // just remember the first break
225 if ((aFits && aPriority >= mLastOptionalBreakPriority) ||
226 !mLastOptionalBreakContent) {
227 mLastOptionalBreakContent = aContent;
228 mLastOptionalBreakContentOffset = aOffset;
229 mLastOptionalBreakPriority = aPriority;
230 }
231 return aContent && mForceBreakContent == aContent &&
232 mForceBreakContentOffset == aOffset;
233 }
234 /**
235 * Like NotifyOptionalBreakPosition, but here it's OK for mNeedBackup
236 * to be set, because the caller is merely pruning some saved break position(s)
237 * that are actually not feasible.
238 */
239 void RestoreSavedBreakPosition(nsIContent* aContent, int32_t aOffset,
240 gfxBreakPriority aPriority) {
241 mLastOptionalBreakContent = aContent;
242 mLastOptionalBreakContentOffset = aOffset;
243 mLastOptionalBreakPriority = aPriority;
244 }
245 /**
246 * Signal that no backing up will be required after all.
247 */
248 void ClearOptionalBreakPosition() {
249 mNeedBackup = false;
250 mLastOptionalBreakContent = nullptr;
251 mLastOptionalBreakContentOffset = -1;
252 mLastOptionalBreakPriority = gfxBreakPriority::eNoBreak;
253 }
254 // Retrieve last set optional break position. When this returns null, no
255 // optional break has been recorded (which means that the line can't break yet).
256 nsIContent* GetLastOptionalBreakPosition(int32_t* aOffset,
257 gfxBreakPriority* aPriority) {
258 *aOffset = mLastOptionalBreakContentOffset;
259 *aPriority = mLastOptionalBreakPriority;
260 return mLastOptionalBreakContent;
261 }
262
263 /**
264 * Check whether frames overflowed the available width and CanPlaceFrame
265 * requested backing up to a saved break position.
266 */
267 bool NeedsBackup() { return mNeedBackup; }
268
269 // Line layout may place too much content on a line, overflowing its available
270 // width. When that happens, if SetLastOptionalBreakPosition has been
271 // used to record an optional break that wasn't taken, we can reflow the line
272 // again and force the break to happen at that point (i.e., backtracking
273 // to the last choice point).
274
275 // Record that we want to break at the given content+offset (which
276 // should have been previously returned by GetLastOptionalBreakPosition
277 // from another nsLineLayout).
278 void ForceBreakAtPosition(nsIContent* aContent, int32_t aOffset) {
279 mForceBreakContent = aContent;
280 mForceBreakContentOffset = aOffset;
281 }
282 bool HaveForcedBreakPosition() { return mForceBreakContent != nullptr; }
283 int32_t GetForcedBreakPosition(nsIContent* aContent) {
284 return mForceBreakContent == aContent ? mForceBreakContentOffset : -1;
285 }
286
287 /**
288 * This can't be null. It usually returns a block frame but may return
289 * some other kind of frame when inline frames are reflowed in a non-block
290 * context (e.g. MathML or floating first-letter).
291 */
292 nsIFrame* LineContainerFrame() const { return mBlockReflowState->frame; }
293 const nsHTMLReflowState* LineContainerRS() const { return mBlockReflowState; }
294 const nsLineList::iterator* GetLine() const {
295 return mGotLineBox ? &mLineBox : nullptr;
296 }
297 nsLineList::iterator* GetLine() {
298 return mGotLineBox ? &mLineBox : nullptr;
299 }
300
301 /**
302 * Returns the accumulated advance width of frames before the current frame
303 * on the line, plus the line container's left border+padding.
304 * This is always positive, the advance width is measured from
305 * the right edge for RTL blocks and from the left edge for LTR blocks.
306 * In other words, the current frame's distance from the line container's
307 * start content edge is:
308 * <code>GetCurrentFrameInlineDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code>
309 * Note the use of <code>.left</code> for both LTR and RTL line containers.
310 */
311 nscoord GetCurrentFrameInlineDistanceFromBlock();
312
313 protected:
314 // This state is constant for a given block frame doing line layout
315 nsFloatManager* mFloatManager;
316 const nsStyleText* mStyleText; // for the block
317 const nsHTMLReflowState* mBlockReflowState;
318
319 nsIContent* mLastOptionalBreakContent;
320 nsIContent* mForceBreakContent;
321
322 // XXX remove this when landing bug 154892 (splitting absolute positioned frames)
323 friend class nsInlineFrame;
324
325 nsBlockReflowState* mBlockRS;/* XXX hack! */
326
327 nsLineList::iterator mLineBox;
328
329 // Per-frame data recorded by the line-layout reflow logic. This
330 // state is the state needed to post-process the line after reflow
331 // has completed (block-direction alignment, inline-direction alignment,
332 // justification and relative positioning).
333
334 struct PerSpanData;
335 struct PerFrameData;
336 friend struct PerSpanData;
337 friend struct PerFrameData;
338 struct PerFrameData
339 {
340 PerFrameData(mozilla::WritingMode aWritingMode)
341 : mBounds(aWritingMode)
342 , mMargin(aWritingMode)
343 , mBorderPadding(aWritingMode)
344 , mOffsets(aWritingMode)
345 {}
346
347 // link to next/prev frame in same span
348 PerFrameData* mNext;
349 PerFrameData* mPrev;
350
351 // pointer to child span data if this is an inline container frame
352 PerSpanData* mSpan;
353
354 // The frame
355 nsIFrame* mFrame;
356
357 // From metrics
358 nscoord mAscent;
359 // note that mBounds is a logical rect in the *line*'s writing mode.
360 // When setting frame coordinates, we have to convert to the frame's
361 // writing mode
362 mozilla::LogicalRect mBounds;
363 nsOverflowAreas mOverflowAreas;
364
365 // From reflow-state
366 mozilla::LogicalMargin mMargin;
367 mozilla::LogicalMargin mBorderPadding;
368 mozilla::LogicalMargin mOffsets;
369
370 // state for text justification
371 int32_t mJustificationNumSpaces;
372 int32_t mJustificationNumLetters;
373
374 // Other state we use
375 uint8_t mBlockDirAlign;
376
377 // PerFrameData flags
378 #define PFD_RELATIVEPOS 0x00000001
379 #define PFD_ISTEXTFRAME 0x00000002
380 #define PFD_ISNONEMPTYTEXTFRAME 0x00000004
381 #define PFD_ISNONWHITESPACETEXTFRAME 0x00000008
382 #define PFD_ISLETTERFRAME 0x00000010
383 #define PFD_RECOMPUTEOVERFLOW 0x00000020
384 #define PFD_ISBULLET 0x00000040
385 #define PFD_SKIPWHENTRIMMINGWHITESPACE 0x00000080
386 #define PFD_LASTFLAG PFD_SKIPWHENTRIMMINGWHITESPACE
387
388 uint8_t mFlags;
389
390 void SetFlag(uint32_t aFlag, bool aValue)
391 {
392 NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
393 NS_ASSERTION(aFlag<=UINT8_MAX, "bad flag");
394 if (aValue) { // set flag
395 mFlags |= aFlag;
396 }
397 else { // unset flag
398 mFlags &= ~aFlag;
399 }
400 }
401
402 bool GetFlag(uint32_t aFlag) const
403 {
404 NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag");
405 return !!(mFlags & aFlag);
406 }
407
408
409 PerFrameData* Last() {
410 PerFrameData* pfd = this;
411 while (pfd->mNext) {
412 pfd = pfd->mNext;
413 }
414 return pfd;
415 }
416 };
417 PerFrameData* mFrameFreeList;
418
419 struct PerSpanData {
420 union {
421 PerSpanData* mParent;
422 PerSpanData* mNextFreeSpan;
423 };
424 PerFrameData* mFrame;
425 PerFrameData* mFirstFrame;
426 PerFrameData* mLastFrame;
427
428 const nsHTMLReflowState* mReflowState;
429 bool mNoWrap;
430 mozilla::WritingMode mWritingMode;
431 bool mZeroEffectiveSpanBox;
432 bool mContainsFloat;
433 bool mHasNonemptyContent;
434
435 nscoord mIStart;
436 nscoord mICoord;
437 nscoord mIEnd;
438
439 nscoord mBStartLeading, mBEndLeading;
440 nscoord mLogicalBSize;
441 nscoord mMinBCoord, mMaxBCoord;
442 nscoord* mBaseline;
443
444 void AppendFrame(PerFrameData* pfd) {
445 if (nullptr == mLastFrame) {
446 mFirstFrame = pfd;
447 }
448 else {
449 mLastFrame->mNext = pfd;
450 pfd->mPrev = mLastFrame;
451 }
452 mLastFrame = pfd;
453 }
454 };
455 PerSpanData* mSpanFreeList;
456 PerSpanData* mRootSpan;
457 PerSpanData* mCurrentSpan;
458
459 gfxBreakPriority mLastOptionalBreakPriority;
460 int32_t mLastOptionalBreakContentOffset;
461 int32_t mForceBreakContentOffset;
462
463 nscoord mMinLineBSize;
464
465 // The amount of text indent that we applied to this line, needed for
466 // max-element-size calculation.
467 nscoord mTextIndent;
468
469 // This state varies during the reflow of a line but is line
470 // "global" state not span "local" state.
471 int32_t mLineNumber;
472 int32_t mTextJustificationNumSpaces;
473 int32_t mTextJustificationNumLetters;
474
475 int32_t mTotalPlacedFrames;
476
477 nscoord mBStartEdge;
478 nscoord mMaxStartBoxBSize;
479 nscoord mMaxEndBoxBSize;
480
481 nscoord mInflationMinFontSize;
482
483 // Final computed line-bSize value after BlockDirAlignFrames for
484 // the block has been called.
485 nscoord mFinalLineBSize;
486
487 // Amount of trimmable whitespace width for the trailing text frame, if any
488 nscoord mTrimmableWidth;
489
490 nscoord mContainerWidth;
491
492 bool mFirstLetterStyleOK : 1;
493 bool mIsTopOfPage : 1;
494 bool mImpactedByFloats : 1;
495 bool mLastFloatWasLetterFrame : 1;
496 bool mLineIsEmpty : 1;
497 bool mLineEndsInBR : 1;
498 bool mNeedBackup : 1;
499 bool mInFirstLine : 1;
500 bool mGotLineBox : 1;
501 bool mInFirstLetter : 1;
502 bool mHasBullet : 1;
503 bool mDirtyNextLine : 1;
504 bool mLineAtStart : 1;
505
506 int32_t mSpanDepth;
507 #ifdef DEBUG
508 int32_t mSpansAllocated, mSpansFreed;
509 int32_t mFramesAllocated, mFramesFreed;
510 #endif
511 PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
512
513 /**
514 * Allocate a PerFrameData from the mArena pool. The allocation is infallible.
515 */
516 PerFrameData* NewPerFrameData(nsIFrame* aFrame);
517
518 /**
519 * Allocate a PerSpanData from the mArena pool. The allocation is infallible.
520 */
521 PerSpanData* NewPerSpanData();
522
523 void FreeSpan(PerSpanData* psd);
524
525 bool InBlockContext() const {
526 return mSpanDepth == 0;
527 }
528
529 void PushFrame(nsIFrame* aFrame);
530
531 void AllowForStartMargin(PerFrameData* pfd,
532 nsHTMLReflowState& aReflowState);
533
534 bool CanPlaceFrame(PerFrameData* pfd,
535 bool aNotSafeToBreak,
536 bool aFrameCanContinueTextRun,
537 bool aCanRollBackBeforeFrame,
538 nsHTMLReflowMetrics& aMetrics,
539 nsReflowStatus& aStatus,
540 bool* aOptionalBreakAfterFits);
541
542 void PlaceFrame(PerFrameData* pfd,
543 nsHTMLReflowMetrics& aMetrics);
544
545 void BlockDirAlignFrames(PerSpanData* psd);
546
547 void PlaceStartEndFrames(PerSpanData* psd,
548 nscoord aDistanceFromStart,
549 nscoord aLineBSize);
550
551 void RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas);
552
553 bool TrimTrailingWhiteSpaceIn(PerSpanData* psd, nscoord* aDeltaISize);
554
555 void ComputeJustificationWeights(PerSpanData* psd, int32_t* numSpaces, int32_t* numLetters);
556
557 struct FrameJustificationState {
558 int32_t mTotalNumSpaces;
559 int32_t mTotalNumLetters;
560 nscoord mTotalWidthForSpaces;
561 nscoord mTotalWidthForLetters;
562 int32_t mNumSpacesProcessed;
563 int32_t mNumLettersProcessed;
564 nscoord mWidthForSpacesProcessed;
565 nscoord mWidthForLettersProcessed;
566 };
567
568 // Apply justification. The return value is the amount by which the width of
569 // the span corresponding to aPSD got increased due to justification.
570 nscoord ApplyFrameJustification(PerSpanData* aPSD,
571 FrameJustificationState* aState);
572
573
574 #ifdef DEBUG
575 void DumpPerSpanData(PerSpanData* psd, int32_t aIndent);
576 #endif
577 };
578
579 #endif /* nsLineLayout_h___ */

mercurial