/src/mozilla-central/layout/generic/nsLineLayout.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | /* state and methods used while laying out a single line of a block frame */ |
8 | | |
9 | | #include "nsLineLayout.h" |
10 | | |
11 | | #include "mozilla/ComputedStyle.h" |
12 | | |
13 | | #include "LayoutLogging.h" |
14 | | #include "SVGTextFrame.h" |
15 | | #include "nsBlockFrame.h" |
16 | | #include "nsBulletFrame.h" |
17 | | #include "nsFontMetrics.h" |
18 | | #include "nsStyleConsts.h" |
19 | | #include "nsContainerFrame.h" |
20 | | #include "nsFloatManager.h" |
21 | | #include "nsPresContext.h" |
22 | | #include "nsGkAtoms.h" |
23 | | #include "nsIContent.h" |
24 | | #include "nsLayoutUtils.h" |
25 | | #include "nsTextFrame.h" |
26 | | #include "nsStyleStructInlines.h" |
27 | | #include "nsBidiPresUtils.h" |
28 | | #include "nsRubyFrame.h" |
29 | | #include "nsRubyTextFrame.h" |
30 | | #include "RubyUtils.h" |
31 | | #include <algorithm> |
32 | | |
33 | | #ifdef DEBUG |
34 | | #undef NOISY_INLINEDIR_ALIGN |
35 | | #undef NOISY_BLOCKDIR_ALIGN |
36 | | #undef NOISY_REFLOW |
37 | | #undef REALLY_NOISY_REFLOW |
38 | | #undef NOISY_PUSHING |
39 | | #undef REALLY_NOISY_PUSHING |
40 | | #undef NOISY_CAN_PLACE_FRAME |
41 | | #undef NOISY_TRIM |
42 | | #undef REALLY_NOISY_TRIM |
43 | | #endif |
44 | | |
45 | | using namespace mozilla; |
46 | | |
47 | | //---------------------------------------------------------------------- |
48 | | |
49 | | nsLineLayout::nsLineLayout(nsPresContext* aPresContext, |
50 | | nsFloatManager* aFloatManager, |
51 | | const ReflowInput* aOuterReflowInput, |
52 | | const nsLineList::iterator* aLine, |
53 | | nsLineLayout* aBaseLineLayout) |
54 | | : mPresContext(aPresContext), |
55 | | mFloatManager(aFloatManager), |
56 | | mBlockReflowInput(aOuterReflowInput), |
57 | | mBaseLineLayout(aBaseLineLayout), |
58 | | mLastOptionalBreakFrame(nullptr), |
59 | | mForceBreakFrame(nullptr), |
60 | | mBlockRI(nullptr),/* XXX temporary */ |
61 | | mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak), |
62 | | mLastOptionalBreakFrameOffset(-1), |
63 | | mForceBreakFrameOffset(-1), |
64 | | mMinLineBSize(0), |
65 | | mTextIndent(0), |
66 | | mMaxStartBoxBSize(0), |
67 | | mMaxEndBoxBSize(0), |
68 | | mFinalLineBSize(0), |
69 | | mFirstLetterStyleOK(false), |
70 | | mIsTopOfPage(false), |
71 | | mImpactedByFloats(false), |
72 | | mLastFloatWasLetterFrame(false), |
73 | | mLineIsEmpty(false), |
74 | | mLineEndsInBR(false), |
75 | | mNeedBackup(false), |
76 | | mInFirstLine(false), |
77 | | mGotLineBox(false), |
78 | | mInFirstLetter(false), |
79 | | mHasBullet(false), |
80 | | mDirtyNextLine(false), |
81 | | mLineAtStart(false), |
82 | | mHasRuby(false), |
83 | | mSuppressLineWrap(nsSVGUtils::IsInSVGTextSubtree(aOuterReflowInput->mFrame)) |
84 | | #ifdef DEBUG |
85 | | , |
86 | | mSpansAllocated(0), |
87 | | mSpansFreed(0), |
88 | | mFramesAllocated(0), |
89 | | mFramesFreed(0) |
90 | | #endif |
91 | 0 | { |
92 | 0 | MOZ_ASSERT(aOuterReflowInput, "aOuterReflowInput must not be null"); |
93 | 0 | NS_ASSERTION(aFloatManager || aOuterReflowInput->mFrame->IsLetterFrame(), |
94 | 0 | "float manager should be present"); |
95 | 0 | MOZ_ASSERT((!!mBaseLineLayout) == |
96 | 0 | aOuterReflowInput->mFrame->IsRubyTextContainerFrame(), |
97 | 0 | "Only ruby text container frames have " |
98 | 0 | "a different base line layout"); |
99 | 0 | MOZ_COUNT_CTOR(nsLineLayout); |
100 | 0 |
|
101 | 0 | // Stash away some style data that we need |
102 | 0 | nsBlockFrame* blockFrame = do_QueryFrame(aOuterReflowInput->mFrame); |
103 | 0 | if (blockFrame) |
104 | 0 | mStyleText = blockFrame->StyleTextForLineLayout(); |
105 | 0 | else |
106 | 0 | mStyleText = aOuterReflowInput->mFrame->StyleText(); |
107 | 0 |
|
108 | 0 | mLineNumber = 0; |
109 | 0 | mTotalPlacedFrames = 0; |
110 | 0 | mBStartEdge = 0; |
111 | 0 | mTrimmableISize = 0; |
112 | 0 |
|
113 | 0 | mInflationMinFontSize = |
114 | 0 | nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowInput->mFrame); |
115 | 0 |
|
116 | 0 | // Instead of always pre-initializing the free-lists for frames and |
117 | 0 | // spans, we do it on demand so that situations that only use a few |
118 | 0 | // frames and spans won't waste a lot of time in unneeded |
119 | 0 | // initialization. |
120 | 0 | mFrameFreeList = nullptr; |
121 | 0 | mSpanFreeList = nullptr; |
122 | 0 |
|
123 | 0 | mCurrentSpan = mRootSpan = nullptr; |
124 | 0 | mSpanDepth = 0; |
125 | 0 |
|
126 | 0 | if (aLine) { |
127 | 0 | mGotLineBox = true; |
128 | 0 | mLineBox = *aLine; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | nsLineLayout::~nsLineLayout() |
133 | 0 | { |
134 | 0 | MOZ_COUNT_DTOR(nsLineLayout); |
135 | 0 |
|
136 | 0 | NS_ASSERTION(nullptr == mRootSpan, "bad line-layout user"); |
137 | 0 | } |
138 | | |
139 | | // Find out if the frame has a non-null prev-in-flow, i.e., whether it |
140 | | // is a continuation. |
141 | | inline bool |
142 | | HasPrevInFlow(nsIFrame *aFrame) |
143 | 0 | { |
144 | 0 | nsIFrame *prevInFlow = aFrame->GetPrevInFlow(); |
145 | 0 | return prevInFlow != nullptr; |
146 | 0 | } |
147 | | |
148 | | void |
149 | | nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord, |
150 | | nscoord aISize, nscoord aBSize, |
151 | | bool aImpactedByFloats, |
152 | | bool aIsTopOfPage, |
153 | | WritingMode aWritingMode, |
154 | | const nsSize& aContainerSize) |
155 | 0 | { |
156 | 0 | NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user"); |
157 | 0 | LAYOUT_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE, |
158 | 0 | "have unconstrained width; this should only result from " |
159 | 0 | "very large sizes, not attempts at intrinsic width " |
160 | 0 | "calculation"); |
161 | | #ifdef DEBUG |
162 | | if ((aISize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aISize) && |
163 | | !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) { |
164 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
165 | | printf(": Init: bad caller: width WAS %d(0x%x)\n", |
166 | | aISize, aISize); |
167 | | } |
168 | | if ((aBSize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aBSize) && |
169 | | !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) { |
170 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
171 | | printf(": Init: bad caller: height WAS %d(0x%x)\n", |
172 | | aBSize, aBSize); |
173 | | } |
174 | | #endif |
175 | | #ifdef NOISY_REFLOW |
176 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
177 | | printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n", |
178 | | aICoord, aBCoord, aISize, aBSize, |
179 | | aImpactedByFloats?"true":"false", |
180 | | aIsTopOfPage ? "top-of-page" : ""); |
181 | | #endif |
182 | | #ifdef DEBUG |
183 | | mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0; |
184 | | #endif |
185 | |
|
186 | 0 | mFirstLetterStyleOK = false; |
187 | 0 | mIsTopOfPage = aIsTopOfPage; |
188 | 0 | mImpactedByFloats = aImpactedByFloats; |
189 | 0 | mTotalPlacedFrames = 0; |
190 | 0 | if (!mBaseLineLayout) { |
191 | 0 | mLineIsEmpty = true; |
192 | 0 | mLineAtStart = true; |
193 | 0 | } else { |
194 | 0 | mLineIsEmpty = false; |
195 | 0 | mLineAtStart = false; |
196 | 0 | } |
197 | 0 | mLineEndsInBR = false; |
198 | 0 | mSpanDepth = 0; |
199 | 0 | mMaxStartBoxBSize = mMaxEndBoxBSize = 0; |
200 | 0 |
|
201 | 0 | if (mGotLineBox) { |
202 | 0 | mLineBox->ClearHasBullet(); |
203 | 0 | } |
204 | 0 |
|
205 | 0 | PerSpanData* psd = NewPerSpanData(); |
206 | 0 | mCurrentSpan = mRootSpan = psd; |
207 | 0 | psd->mReflowInput = mBlockReflowInput; |
208 | 0 | psd->mIStart = aICoord; |
209 | 0 | psd->mICoord = aICoord; |
210 | 0 | psd->mIEnd = aICoord + aISize; |
211 | 0 | mContainerSize = aContainerSize; |
212 | 0 |
|
213 | 0 | mBStartEdge = aBCoord; |
214 | 0 |
|
215 | 0 | psd->mNoWrap = !mStyleText->WhiteSpaceCanWrapStyle() || mSuppressLineWrap; |
216 | 0 | psd->mWritingMode = aWritingMode; |
217 | 0 |
|
218 | 0 | // If this is the first line of a block then see if the text-indent |
219 | 0 | // property amounts to anything. |
220 | 0 |
|
221 | 0 | if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowInput->mFrame)) { |
222 | 0 | const nsStyleCoord &textIndent = mStyleText->mTextIndent; |
223 | 0 | nscoord pctBasis = 0; |
224 | 0 | if (textIndent.HasPercent()) { |
225 | 0 | pctBasis = |
226 | 0 | mBlockReflowInput->GetContainingBlockContentISize(aWritingMode); |
227 | 0 | } |
228 | 0 | nscoord indent = textIndent.ComputeCoordPercentCalc(pctBasis); |
229 | 0 |
|
230 | 0 | mTextIndent = indent; |
231 | 0 |
|
232 | 0 | psd->mICoord += indent; |
233 | 0 | } |
234 | 0 |
|
235 | 0 | PerFrameData* pfd = NewPerFrameData(mBlockReflowInput->mFrame); |
236 | 0 | pfd->mAscent = 0; |
237 | 0 | pfd->mSpan = psd; |
238 | 0 | psd->mFrame = pfd; |
239 | 0 | nsIFrame* frame = mBlockReflowInput->mFrame; |
240 | 0 | if (frame->IsRubyTextContainerFrame()) { |
241 | 0 | // Ruby text container won't be reflowed via ReflowFrame, hence the |
242 | 0 | // relative positioning information should be recorded here. |
243 | 0 | MOZ_ASSERT(mBaseLineLayout != this); |
244 | 0 | pfd->mRelativePos = |
245 | 0 | mBlockReflowInput->mStyleDisplay->IsRelativelyPositionedStyle(); |
246 | 0 | if (pfd->mRelativePos) { |
247 | 0 | MOZ_ASSERT( |
248 | 0 | mBlockReflowInput->GetWritingMode() == pfd->mWritingMode, |
249 | 0 | "mBlockReflowInput->frame == frame, " |
250 | 0 | "hence they should have identical writing mode"); |
251 | 0 | pfd->mOffsets = mBlockReflowInput->ComputedLogicalOffsets(); |
252 | 0 | } |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | | void |
257 | | nsLineLayout::EndLineReflow() |
258 | 0 | { |
259 | | #ifdef NOISY_REFLOW |
260 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
261 | | printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart); |
262 | | #endif |
263 | |
|
264 | 0 | NS_ASSERTION(!mBaseLineLayout || |
265 | 0 | (!mSpansAllocated && !mSpansFreed && !mSpanFreeList && |
266 | 0 | !mFramesAllocated && !mFramesFreed && !mFrameFreeList), |
267 | 0 | "Allocated frames or spans on non-base line layout?"); |
268 | 0 | MOZ_ASSERT(mRootSpan == mCurrentSpan); |
269 | 0 |
|
270 | 0 | UnlinkFrame(mRootSpan->mFrame); |
271 | 0 | mCurrentSpan = mRootSpan = nullptr; |
272 | 0 |
|
273 | 0 | NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak"); |
274 | 0 | NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak"); |
275 | 0 |
|
276 | | #if 0 |
277 | | static int32_t maxSpansAllocated = NS_LINELAYOUT_NUM_SPANS; |
278 | | static int32_t maxFramesAllocated = NS_LINELAYOUT_NUM_FRAMES; |
279 | | if (mSpansAllocated > maxSpansAllocated) { |
280 | | printf("XXX: saw a line with %d spans\n", mSpansAllocated); |
281 | | maxSpansAllocated = mSpansAllocated; |
282 | | } |
283 | | if (mFramesAllocated > maxFramesAllocated) { |
284 | | printf("XXX: saw a line with %d frames\n", mFramesAllocated); |
285 | | maxFramesAllocated = mFramesAllocated; |
286 | | } |
287 | | #endif |
288 | | } |
289 | | |
290 | | // XXX swtich to a single mAvailLineWidth that we adjust as each frame |
291 | | // on the line is placed. Each span can still have a per-span mICoord that |
292 | | // tracks where a child frame is going in its span; they don't need a |
293 | | // per-span mIStart? |
294 | | |
295 | | void |
296 | | nsLineLayout::UpdateBand(WritingMode aWM, |
297 | | const LogicalRect& aNewAvailSpace, |
298 | | nsIFrame* aFloatFrame) |
299 | 0 | { |
300 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
301 | 0 | // need to convert to our writing mode, because we might have a different |
302 | 0 | // mode from the caller due to dir: auto |
303 | 0 | LogicalRect availSpace = aNewAvailSpace.ConvertTo(lineWM, aWM, |
304 | 0 | ContainerSize()); |
305 | | #ifdef REALLY_NOISY_REFLOW |
306 | | printf("nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n", |
307 | | aNewAvailSpace.IStart(aWM), aNewAvailSpace.BStart(aWM), |
308 | | aNewAvailSpace.ISize(aWM), aNewAvailSpace.BSize(aWM), |
309 | | availSpace.IStart(lineWM), availSpace.BStart(lineWM), |
310 | | availSpace.ISize(lineWM), availSpace.BSize(lineWM), |
311 | | aFloatFrame); |
312 | | #endif |
313 | | #ifdef DEBUG |
314 | | if ((availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE) && |
315 | | CRAZY_SIZE(availSpace.ISize(lineWM)) && |
316 | | !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) { |
317 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
318 | | printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n", |
319 | | availSpace.ISize(lineWM), availSpace.ISize(lineWM)); |
320 | | } |
321 | | if ((availSpace.BSize(lineWM) != NS_UNCONSTRAINEDSIZE) && |
322 | | CRAZY_SIZE(availSpace.BSize(lineWM)) && |
323 | | !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) { |
324 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
325 | | printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n", |
326 | | availSpace.BSize(lineWM), availSpace.BSize(lineWM)); |
327 | | } |
328 | | #endif |
329 | |
|
330 | 0 | // Compute the difference between last times width and the new width |
331 | 0 | NS_WARNING_ASSERTION( |
332 | 0 | mRootSpan->mIEnd != NS_UNCONSTRAINEDSIZE && |
333 | 0 | availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE, |
334 | 0 | "have unconstrained inline size; this should only result from very large " |
335 | 0 | "sizes, not attempts at intrinsic width calculation"); |
336 | 0 | // The root span's mIStart moves to aICoord |
337 | 0 | nscoord deltaICoord = availSpace.IStart(lineWM) - mRootSpan->mIStart; |
338 | 0 | // The inline size of all spans changes by this much (the root span's |
339 | 0 | // mIEnd moves to aICoord + aISize, its new inline size is aISize) |
340 | 0 | nscoord deltaISize = availSpace.ISize(lineWM) - |
341 | 0 | (mRootSpan->mIEnd - mRootSpan->mIStart); |
342 | | #ifdef NOISY_REFLOW |
343 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
344 | | printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n", |
345 | | availSpace.IStart(lineWM), availSpace.BStart(lineWM), |
346 | | availSpace.ISize(lineWM), availSpace.BSize(lineWM), |
347 | | deltaISize, deltaICoord); |
348 | | #endif |
349 | |
|
350 | 0 | // Update the root span position |
351 | 0 | mRootSpan->mIStart += deltaICoord; |
352 | 0 | mRootSpan->mIEnd += deltaICoord; |
353 | 0 | mRootSpan->mICoord += deltaICoord; |
354 | 0 |
|
355 | 0 | // Now update the right edges of the open spans to account for any |
356 | 0 | // change in available space width |
357 | 0 | for (PerSpanData* psd = mCurrentSpan; psd; psd = psd->mParent) { |
358 | 0 | psd->mIEnd += deltaISize; |
359 | 0 | psd->mContainsFloat = true; |
360 | | #ifdef NOISY_REFLOW |
361 | | printf(" span %p: oldIEnd=%d newIEnd=%d\n", |
362 | | psd, psd->mIEnd - deltaISize, psd->mIEnd); |
363 | | #endif |
364 | | } |
365 | 0 | NS_ASSERTION(mRootSpan->mContainsFloat && |
366 | 0 | mRootSpan->mIStart == availSpace.IStart(lineWM) && |
367 | 0 | mRootSpan->mIEnd == availSpace.IEnd(lineWM), |
368 | 0 | "root span was updated incorrectly?"); |
369 | 0 |
|
370 | 0 | // Update frame bounds |
371 | 0 | // Note: Only adjust the outermost frames (the ones that are direct |
372 | 0 | // children of the block), not the ones in the child spans. The reason |
373 | 0 | // is simple: the frames in the spans have coordinates local to their |
374 | 0 | // parent therefore they are moved when their parent span is moved. |
375 | 0 | if (deltaICoord != 0) { |
376 | 0 | for (PerFrameData* pfd = mRootSpan->mFirstFrame; pfd; pfd = pfd->mNext) { |
377 | 0 | pfd->mBounds.IStart(lineWM) += deltaICoord; |
378 | 0 | } |
379 | 0 | } |
380 | 0 |
|
381 | 0 | mBStartEdge = availSpace.BStart(lineWM); |
382 | 0 | mImpactedByFloats = true; |
383 | 0 |
|
384 | 0 | mLastFloatWasLetterFrame = aFloatFrame->IsLetterFrame(); |
385 | 0 | } |
386 | | |
387 | | nsLineLayout::PerSpanData* |
388 | | nsLineLayout::NewPerSpanData() |
389 | 0 | { |
390 | 0 | nsLineLayout* outerLineLayout = GetOutermostLineLayout(); |
391 | 0 | PerSpanData* psd = outerLineLayout->mSpanFreeList; |
392 | 0 | if (!psd) { |
393 | 0 | void *mem = outerLineLayout->mArena.Allocate(sizeof(PerSpanData)); |
394 | 0 | psd = reinterpret_cast<PerSpanData*>(mem); |
395 | 0 | } |
396 | 0 | else { |
397 | 0 | outerLineLayout->mSpanFreeList = psd->mNextFreeSpan; |
398 | 0 | } |
399 | 0 | psd->mParent = nullptr; |
400 | 0 | psd->mFrame = nullptr; |
401 | 0 | psd->mFirstFrame = nullptr; |
402 | 0 | psd->mLastFrame = nullptr; |
403 | 0 | psd->mContainsFloat = false; |
404 | 0 | psd->mHasNonemptyContent = false; |
405 | 0 |
|
406 | | #ifdef DEBUG |
407 | | outerLineLayout->mSpansAllocated++; |
408 | | #endif |
409 | | return psd; |
410 | 0 | } |
411 | | |
412 | | void |
413 | | nsLineLayout::BeginSpan(nsIFrame* aFrame, |
414 | | const ReflowInput* aSpanReflowInput, |
415 | | nscoord aIStart, nscoord aIEnd, |
416 | | nscoord* aBaseline) |
417 | 0 | { |
418 | 0 | NS_ASSERTION(aIEnd != NS_UNCONSTRAINEDSIZE, |
419 | 0 | "should no longer be using unconstrained sizes"); |
420 | | #ifdef NOISY_REFLOW |
421 | | nsFrame::IndentBy(stdout, mSpanDepth+1); |
422 | | nsFrame::ListTag(stdout, aFrame); |
423 | | printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart, aIEnd); |
424 | | #endif |
425 | |
|
426 | 0 | PerSpanData* psd = NewPerSpanData(); |
427 | 0 | // Link up span frame's pfd to point to its child span data |
428 | 0 | PerFrameData* pfd = mCurrentSpan->mLastFrame; |
429 | 0 | NS_ASSERTION(pfd->mFrame == aFrame, "huh?"); |
430 | 0 | pfd->mSpan = psd; |
431 | 0 |
|
432 | 0 | // Init new span |
433 | 0 | psd->mFrame = pfd; |
434 | 0 | psd->mParent = mCurrentSpan; |
435 | 0 | psd->mReflowInput = aSpanReflowInput; |
436 | 0 | psd->mIStart = aIStart; |
437 | 0 | psd->mICoord = aIStart; |
438 | 0 | psd->mIEnd = aIEnd; |
439 | 0 | psd->mBaseline = aBaseline; |
440 | 0 |
|
441 | 0 | nsIFrame* frame = aSpanReflowInput->mFrame; |
442 | 0 | psd->mNoWrap = !frame->StyleText()->WhiteSpaceCanWrap(frame) || |
443 | 0 | mSuppressLineWrap || |
444 | 0 | frame->Style()->ShouldSuppressLineBreak(); |
445 | 0 | psd->mWritingMode = aSpanReflowInput->GetWritingMode(); |
446 | 0 |
|
447 | 0 | // Switch to new span |
448 | 0 | mCurrentSpan = psd; |
449 | 0 | mSpanDepth++; |
450 | 0 | } |
451 | | |
452 | | nscoord |
453 | | nsLineLayout::EndSpan(nsIFrame* aFrame) |
454 | 0 | { |
455 | 0 | NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span"); |
456 | | #ifdef NOISY_REFLOW |
457 | | nsFrame::IndentBy(stdout, mSpanDepth); |
458 | | nsFrame::ListTag(stdout, aFrame); |
459 | | printf(": EndSpan width=%d\n", mCurrentSpan->mICoord - mCurrentSpan->mIStart); |
460 | | #endif |
461 | | PerSpanData* psd = mCurrentSpan; |
462 | 0 | MOZ_ASSERT(psd->mParent, "We never call this on the root"); |
463 | 0 |
|
464 | 0 | if (psd->mNoWrap && !psd->mParent->mNoWrap) { |
465 | 0 | FlushNoWrapFloats(); |
466 | 0 | } |
467 | 0 |
|
468 | 0 | nscoord iSizeResult = psd->mLastFrame ? (psd->mICoord - psd->mIStart) : 0; |
469 | 0 |
|
470 | 0 | mSpanDepth--; |
471 | 0 | mCurrentSpan->mReflowInput = nullptr; // no longer valid so null it out! |
472 | 0 | mCurrentSpan = mCurrentSpan->mParent; |
473 | 0 | return iSizeResult; |
474 | 0 | } |
475 | | |
476 | | void |
477 | | nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData* aFrame) |
478 | 0 | { |
479 | 0 | MOZ_ASSERT(mBaseLineLayout, |
480 | 0 | "This method must not be called in a base line layout."); |
481 | 0 |
|
482 | 0 | PerFrameData* baseFrame = mBaseLineLayout->LastFrame(); |
483 | 0 | MOZ_ASSERT(aFrame && baseFrame); |
484 | 0 | MOZ_ASSERT(!aFrame->mIsLinkedToBase, |
485 | 0 | "The frame must not have been linked with the base"); |
486 | | #ifdef DEBUG |
487 | | LayoutFrameType baseType = baseFrame->mFrame->Type(); |
488 | | LayoutFrameType annotationType = aFrame->mFrame->Type(); |
489 | | MOZ_ASSERT((baseType == LayoutFrameType::RubyBaseContainer && |
490 | | annotationType == LayoutFrameType::RubyTextContainer) || |
491 | | (baseType == LayoutFrameType::RubyBase && |
492 | | annotationType == LayoutFrameType::RubyText)); |
493 | | #endif |
494 | |
|
495 | 0 | aFrame->mNextAnnotation = baseFrame->mNextAnnotation; |
496 | 0 | baseFrame->mNextAnnotation = aFrame; |
497 | 0 | aFrame->mIsLinkedToBase = true; |
498 | 0 | } |
499 | | |
500 | | int32_t |
501 | | nsLineLayout::GetCurrentSpanCount() const |
502 | 0 | { |
503 | 0 | NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
504 | 0 | int32_t count = 0; |
505 | 0 | PerFrameData* pfd = mRootSpan->mFirstFrame; |
506 | 0 | while (nullptr != pfd) { |
507 | 0 | count++; |
508 | 0 | pfd = pfd->mNext; |
509 | 0 | } |
510 | 0 | return count; |
511 | 0 | } |
512 | | |
513 | | void |
514 | | nsLineLayout::SplitLineTo(int32_t aNewCount) |
515 | 0 | { |
516 | 0 | NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
517 | 0 |
|
518 | | #ifdef REALLY_NOISY_PUSHING |
519 | | printf("SplitLineTo %d (current count=%d); before:\n", aNewCount, |
520 | | GetCurrentSpanCount()); |
521 | | DumpPerSpanData(mRootSpan, 1); |
522 | | #endif |
523 | | PerSpanData* psd = mRootSpan; |
524 | 0 | PerFrameData* pfd = psd->mFirstFrame; |
525 | 0 | while (nullptr != pfd) { |
526 | 0 | if (--aNewCount == 0) { |
527 | 0 | // Truncate list at pfd (we keep pfd, but anything following is freed) |
528 | 0 | PerFrameData* next = pfd->mNext; |
529 | 0 | pfd->mNext = nullptr; |
530 | 0 | psd->mLastFrame = pfd; |
531 | 0 |
|
532 | 0 | // Now unlink all of the frames following pfd |
533 | 0 | UnlinkFrame(next); |
534 | 0 | break; |
535 | 0 | } |
536 | 0 | pfd = pfd->mNext; |
537 | 0 | } |
538 | | #ifdef NOISY_PUSHING |
539 | | printf("SplitLineTo %d (current count=%d); after:\n", aNewCount, |
540 | | GetCurrentSpanCount()); |
541 | | DumpPerSpanData(mRootSpan, 1); |
542 | | #endif |
543 | | } |
544 | | |
545 | | void |
546 | | nsLineLayout::PushFrame(nsIFrame* aFrame) |
547 | 0 | { |
548 | 0 | PerSpanData* psd = mCurrentSpan; |
549 | 0 | NS_ASSERTION(psd->mLastFrame->mFrame == aFrame, "pushing non-last frame"); |
550 | 0 |
|
551 | | #ifdef REALLY_NOISY_PUSHING |
552 | | nsFrame::IndentBy(stdout, mSpanDepth); |
553 | | printf("PushFrame %p, before:\n", psd); |
554 | | DumpPerSpanData(psd, 1); |
555 | | #endif |
556 | |
|
557 | 0 | // Take the last frame off of the span's frame list |
558 | 0 | PerFrameData* pfd = psd->mLastFrame; |
559 | 0 | if (pfd == psd->mFirstFrame) { |
560 | 0 | // We are pushing away the only frame...empty the list |
561 | 0 | psd->mFirstFrame = nullptr; |
562 | 0 | psd->mLastFrame = nullptr; |
563 | 0 | } |
564 | 0 | else { |
565 | 0 | PerFrameData* prevFrame = pfd->mPrev; |
566 | 0 | prevFrame->mNext = nullptr; |
567 | 0 | psd->mLastFrame = prevFrame; |
568 | 0 | } |
569 | 0 |
|
570 | 0 | // Now unlink the frame |
571 | 0 | MOZ_ASSERT(!pfd->mNext); |
572 | 0 | UnlinkFrame(pfd); |
573 | | #ifdef NOISY_PUSHING |
574 | | nsFrame::IndentBy(stdout, mSpanDepth); |
575 | | printf("PushFrame: %p after:\n", psd); |
576 | | DumpPerSpanData(psd, 1); |
577 | | #endif |
578 | | } |
579 | | |
580 | | void |
581 | | nsLineLayout::UnlinkFrame(PerFrameData* pfd) |
582 | 0 | { |
583 | 0 | while (nullptr != pfd) { |
584 | 0 | PerFrameData* next = pfd->mNext; |
585 | 0 | if (pfd->mIsLinkedToBase) { |
586 | 0 | // This frame is linked to a ruby base, and should not be freed |
587 | 0 | // now. Just unlink it from the span. It will be freed when its |
588 | 0 | // base frame gets unlinked. |
589 | 0 | pfd->mNext = pfd->mPrev = nullptr; |
590 | 0 | pfd = next; |
591 | 0 | continue; |
592 | 0 | } |
593 | 0 | |
594 | 0 | // It is a ruby base frame. If there are any annotations |
595 | 0 | // linked to this frame, free them first. |
596 | 0 | PerFrameData* annotationPFD = pfd->mNextAnnotation; |
597 | 0 | while (annotationPFD) { |
598 | 0 | PerFrameData* nextAnnotation = annotationPFD->mNextAnnotation; |
599 | 0 | MOZ_ASSERT(annotationPFD->mNext == nullptr && |
600 | 0 | annotationPFD->mPrev == nullptr, |
601 | 0 | "PFD in annotations should have been unlinked."); |
602 | 0 | FreeFrame(annotationPFD); |
603 | 0 | annotationPFD = nextAnnotation; |
604 | 0 | } |
605 | 0 |
|
606 | 0 | FreeFrame(pfd); |
607 | 0 | pfd = next; |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | void |
612 | | nsLineLayout::FreeFrame(PerFrameData* pfd) |
613 | 0 | { |
614 | 0 | if (nullptr != pfd->mSpan) { |
615 | 0 | FreeSpan(pfd->mSpan); |
616 | 0 | } |
617 | 0 | nsLineLayout* outerLineLayout = GetOutermostLineLayout(); |
618 | 0 | pfd->mNext = outerLineLayout->mFrameFreeList; |
619 | 0 | outerLineLayout->mFrameFreeList = pfd; |
620 | | #ifdef DEBUG |
621 | | outerLineLayout->mFramesFreed++; |
622 | | #endif |
623 | | } |
624 | | |
625 | | void |
626 | | nsLineLayout::FreeSpan(PerSpanData* psd) |
627 | 0 | { |
628 | 0 | // Unlink its frames |
629 | 0 | UnlinkFrame(psd->mFirstFrame); |
630 | 0 |
|
631 | 0 | nsLineLayout* outerLineLayout = GetOutermostLineLayout(); |
632 | 0 | // Now put the span on the free list since it's free too |
633 | 0 | psd->mNextFreeSpan = outerLineLayout->mSpanFreeList; |
634 | 0 | outerLineLayout->mSpanFreeList = psd; |
635 | | #ifdef DEBUG |
636 | | outerLineLayout->mSpansFreed++; |
637 | | #endif |
638 | | } |
639 | | |
640 | | bool |
641 | | nsLineLayout::IsZeroBSize() |
642 | 0 | { |
643 | 0 | PerSpanData* psd = mCurrentSpan; |
644 | 0 | PerFrameData* pfd = psd->mFirstFrame; |
645 | 0 | while (nullptr != pfd) { |
646 | 0 | if (0 != pfd->mBounds.BSize(psd->mWritingMode)) { |
647 | 0 | return false; |
648 | 0 | } |
649 | 0 | pfd = pfd->mNext; |
650 | 0 | } |
651 | 0 | return true; |
652 | 0 | } |
653 | | |
654 | | nsLineLayout::PerFrameData* |
655 | | nsLineLayout::NewPerFrameData(nsIFrame* aFrame) |
656 | 0 | { |
657 | 0 | nsLineLayout* outerLineLayout = GetOutermostLineLayout(); |
658 | 0 | PerFrameData* pfd = outerLineLayout->mFrameFreeList; |
659 | 0 | if (!pfd) { |
660 | 0 | void *mem = outerLineLayout->mArena.Allocate(sizeof(PerFrameData)); |
661 | 0 | pfd = reinterpret_cast<PerFrameData*>(mem); |
662 | 0 | } |
663 | 0 | else { |
664 | 0 | outerLineLayout->mFrameFreeList = pfd->mNext; |
665 | 0 | } |
666 | 0 | pfd->mSpan = nullptr; |
667 | 0 | pfd->mNext = nullptr; |
668 | 0 | pfd->mPrev = nullptr; |
669 | 0 | pfd->mNextAnnotation = nullptr; |
670 | 0 | pfd->mFrame = aFrame; |
671 | 0 |
|
672 | 0 | // all flags default to false |
673 | 0 | pfd->mRelativePos = false; |
674 | 0 | pfd->mIsTextFrame = false; |
675 | 0 | pfd->mIsNonEmptyTextFrame = false; |
676 | 0 | pfd->mIsNonWhitespaceTextFrame = false; |
677 | 0 | pfd->mIsLetterFrame = false; |
678 | 0 | pfd->mRecomputeOverflow = false; |
679 | 0 | pfd->mIsBullet = false; |
680 | 0 | pfd->mSkipWhenTrimmingWhitespace = false; |
681 | 0 | pfd->mIsEmpty = false; |
682 | 0 | pfd->mIsPlaceholder = false; |
683 | 0 | pfd->mIsLinkedToBase = false; |
684 | 0 |
|
685 | 0 | pfd->mWritingMode = aFrame->GetWritingMode(); |
686 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
687 | 0 | pfd->mBounds = LogicalRect(lineWM); |
688 | 0 | pfd->mOverflowAreas.Clear(); |
689 | 0 | pfd->mMargin = LogicalMargin(lineWM); |
690 | 0 | pfd->mBorderPadding = LogicalMargin(lineWM); |
691 | 0 | pfd->mOffsets = LogicalMargin(pfd->mWritingMode); |
692 | 0 |
|
693 | 0 | pfd->mJustificationInfo = JustificationInfo(); |
694 | 0 | pfd->mJustificationAssignment = JustificationAssignment(); |
695 | 0 |
|
696 | | #ifdef DEBUG |
697 | | pfd->mBlockDirAlign = 0xFF; |
698 | | outerLineLayout->mFramesAllocated++; |
699 | | #endif |
700 | | return pfd; |
701 | 0 | } |
702 | | |
703 | | bool |
704 | | nsLineLayout::LineIsBreakable() const |
705 | 0 | { |
706 | 0 | // XXX mTotalPlacedFrames should go away and we should just use |
707 | 0 | // mLineIsEmpty here instead |
708 | 0 | if ((0 != mTotalPlacedFrames) || mImpactedByFloats) { |
709 | 0 | return true; |
710 | 0 | } |
711 | 0 | return false; |
712 | 0 | } |
713 | | |
714 | | // Checks all four sides for percentage units. This means it should |
715 | | // only be used for things (margin, padding) where percentages on top |
716 | | // and bottom depend on the *width* just like percentages on left and |
717 | | // right. |
718 | | static bool |
719 | | HasPercentageUnitSide(const nsStyleSides& aSides) |
720 | 0 | { |
721 | 0 | NS_FOR_CSS_SIDES(side) { |
722 | 0 | if (aSides.Get(side).HasPercent()) |
723 | 0 | return true; |
724 | 0 | } |
725 | 0 | return false; |
726 | 0 | } |
727 | | |
728 | | static bool |
729 | | IsPercentageAware(const nsIFrame* aFrame) |
730 | 0 | { |
731 | 0 | NS_ASSERTION(aFrame, "null frame is not allowed"); |
732 | 0 |
|
733 | 0 | LayoutFrameType fType = aFrame->Type(); |
734 | 0 | if (fType == LayoutFrameType::Text) { |
735 | 0 | // None of these things can ever be true for text frames. |
736 | 0 | return false; |
737 | 0 | } |
738 | 0 | |
739 | 0 | // Some of these things don't apply to non-replaced inline frames |
740 | 0 | // (that is, fType == LayoutFrameType::Inline), but we won't bother making |
741 | 0 | // things unnecessarily complicated, since they'll probably be set |
742 | 0 | // quite rarely. |
743 | 0 | |
744 | 0 | const nsStyleMargin* margin = aFrame->StyleMargin(); |
745 | 0 | if (HasPercentageUnitSide(margin->mMargin)) { |
746 | 0 | return true; |
747 | 0 | } |
748 | 0 | |
749 | 0 | const nsStylePadding* padding = aFrame->StylePadding(); |
750 | 0 | if (HasPercentageUnitSide(padding->mPadding)) { |
751 | 0 | return true; |
752 | 0 | } |
753 | 0 | |
754 | 0 | // Note that borders can't be aware of percentages |
755 | 0 | |
756 | 0 | const nsStylePosition* pos = aFrame->StylePosition(); |
757 | 0 |
|
758 | 0 | if ((pos->WidthDependsOnContainer() && |
759 | 0 | pos->mWidth.GetUnit() != eStyleUnit_Auto) || |
760 | 0 | pos->MaxWidthDependsOnContainer() || |
761 | 0 | pos->MinWidthDependsOnContainer() || |
762 | 0 | pos->OffsetHasPercent(eSideRight) || |
763 | 0 | pos->OffsetHasPercent(eSideLeft)) { |
764 | 0 | return true; |
765 | 0 | } |
766 | 0 | |
767 | 0 | if (eStyleUnit_Auto == pos->mWidth.GetUnit()) { |
768 | 0 | // We need to check for frames that shrink-wrap when they're auto |
769 | 0 | // width. |
770 | 0 | const nsStyleDisplay* disp = aFrame->StyleDisplay(); |
771 | 0 | if (disp->mDisplay == StyleDisplay::InlineBlock || |
772 | 0 | disp->mDisplay == StyleDisplay::InlineTable || |
773 | 0 | fType == LayoutFrameType::HTMLButtonControl || |
774 | 0 | fType == LayoutFrameType::GfxButtonControl || |
775 | 0 | fType == LayoutFrameType::FieldSet || |
776 | 0 | fType == LayoutFrameType::ComboboxDisplay) { |
777 | 0 | return true; |
778 | 0 | } |
779 | 0 | |
780 | 0 | // Per CSS 2.1, section 10.3.2: |
781 | 0 | // If 'height' and 'width' both have computed values of 'auto' and |
782 | 0 | // the element has an intrinsic ratio but no intrinsic height or |
783 | 0 | // width and the containing block's width does not itself depend |
784 | 0 | // on the replaced element's width, then the used value of 'width' |
785 | 0 | // is calculated from the constraint equation used for |
786 | 0 | // block-level, non-replaced elements in normal flow. |
787 | 0 | nsIFrame *f = const_cast<nsIFrame*>(aFrame); |
788 | 0 | if (f->GetIntrinsicRatio() != nsSize(0, 0) && |
789 | 0 | // Some percents are treated like 'auto', so check != coord |
790 | 0 | pos->mHeight.GetUnit() != eStyleUnit_Coord) { |
791 | 0 | const IntrinsicSize &intrinsicSize = f->GetIntrinsicSize(); |
792 | 0 | if (intrinsicSize.width.GetUnit() == eStyleUnit_None && |
793 | 0 | intrinsicSize.height.GetUnit() == eStyleUnit_None) { |
794 | 0 | return true; |
795 | 0 | } |
796 | 0 | } |
797 | 0 | } |
798 | 0 | |
799 | 0 | return false; |
800 | 0 | } |
801 | | |
802 | | void |
803 | | nsLineLayout::ReflowFrame(nsIFrame* aFrame, |
804 | | nsReflowStatus& aReflowStatus, |
805 | | ReflowOutput* aMetrics, |
806 | | bool& aPushedFrame) |
807 | 0 | { |
808 | 0 | // Initialize OUT parameter |
809 | 0 | aPushedFrame = false; |
810 | 0 |
|
811 | 0 | PerFrameData* pfd = NewPerFrameData(aFrame); |
812 | 0 | PerSpanData* psd = mCurrentSpan; |
813 | 0 | psd->AppendFrame(pfd); |
814 | 0 |
|
815 | | #ifdef REALLY_NOISY_REFLOW |
816 | | nsFrame::IndentBy(stdout, mSpanDepth); |
817 | | printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd); |
818 | | nsFrame::ListTag(stdout, aFrame); |
819 | | printf("\n"); |
820 | | #endif |
821 | |
|
822 | 0 | if (mCurrentSpan == mRootSpan) { |
823 | 0 | pfd->mFrame->RemoveProperty(nsIFrame::LineBaselineOffset()); |
824 | 0 | } else { |
825 | | #ifdef DEBUG |
826 | | bool hasLineOffset; |
827 | | pfd->mFrame->GetProperty(nsIFrame::LineBaselineOffset(), &hasLineOffset); |
828 | | NS_ASSERTION(!hasLineOffset, "LineBaselineOffset was set but was not expected"); |
829 | | #endif |
830 | | } |
831 | 0 |
|
832 | 0 | mJustificationInfo = JustificationInfo(); |
833 | 0 |
|
834 | 0 | // Stash copies of some of the computed state away for later |
835 | 0 | // (block-direction alignment, for example) |
836 | 0 | WritingMode frameWM = pfd->mWritingMode; |
837 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
838 | 0 |
|
839 | 0 | // NOTE: While the inline direction coordinate remains relative to the |
840 | 0 | // parent span, the block direction coordinate is fixed at the top |
841 | 0 | // edge for the line. During VerticalAlignFrames we will repair this |
842 | 0 | // so that the block direction coordinate is properly set and relative |
843 | 0 | // to the appropriate span. |
844 | 0 | pfd->mBounds.IStart(lineWM) = psd->mICoord; |
845 | 0 | pfd->mBounds.BStart(lineWM) = mBStartEdge; |
846 | 0 |
|
847 | 0 | // We want to guarantee that we always make progress when |
848 | 0 | // formatting. Therefore, if the object being placed on the line is |
849 | 0 | // too big for the line, but it is the only thing on the line and is not |
850 | 0 | // impacted by a float, then we go ahead and place it anyway. (If the line |
851 | 0 | // is impacted by one or more floats, then it is safe to break because |
852 | 0 | // we can move the line down below float(s).) |
853 | 0 | // |
854 | 0 | // Capture this state *before* we reflow the frame in case it clears |
855 | 0 | // the state out. We need to know how to treat the current frame |
856 | 0 | // when breaking. |
857 | 0 | bool notSafeToBreak = LineIsEmpty() && !mImpactedByFloats; |
858 | 0 |
|
859 | 0 | // Figure out whether we're talking about a textframe here |
860 | 0 | LayoutFrameType frameType = aFrame->Type(); |
861 | 0 | const bool isText = frameType == LayoutFrameType::Text; |
862 | 0 |
|
863 | 0 | // Inline-ish and text-ish things don't compute their width; |
864 | 0 | // everything else does. We need to give them an available width that |
865 | 0 | // reflects the space left on the line. |
866 | 0 | LAYOUT_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE, |
867 | 0 | "have unconstrained width; this should only result from " |
868 | 0 | "very large sizes, not attempts at intrinsic width " |
869 | 0 | "calculation"); |
870 | 0 | nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord; |
871 | 0 |
|
872 | 0 | // Setup reflow state for reflowing the frame |
873 | 0 | Maybe<ReflowInput> reflowInputHolder; |
874 | 0 | if (!isText) { |
875 | 0 | // Compute the available size for the frame. This available width |
876 | 0 | // includes room for the side margins. |
877 | 0 | // For now, set the available block-size to unconstrained always. |
878 | 0 | LogicalSize availSize = mBlockReflowInput->ComputedSize(frameWM); |
879 | 0 | availSize.BSize(frameWM) = NS_UNCONSTRAINEDSIZE; |
880 | 0 | reflowInputHolder.emplace(mPresContext, *psd->mReflowInput, |
881 | 0 | aFrame, availSize); |
882 | 0 | ReflowInput& reflowInput = *reflowInputHolder; |
883 | 0 | reflowInput.mLineLayout = this; |
884 | 0 | reflowInput.mFlags.mIsTopOfPage = mIsTopOfPage; |
885 | 0 | if (reflowInput.ComputedISize() == NS_UNCONSTRAINEDSIZE) { |
886 | 0 | reflowInput.AvailableISize() = availableSpaceOnLine; |
887 | 0 | } |
888 | 0 | WritingMode stateWM = reflowInput.GetWritingMode(); |
889 | 0 | pfd->mMargin = |
890 | 0 | reflowInput.ComputedLogicalMargin().ConvertTo(lineWM, stateWM); |
891 | 0 | pfd->mBorderPadding = |
892 | 0 | reflowInput.ComputedLogicalBorderPadding().ConvertTo(lineWM, stateWM); |
893 | 0 | pfd->mRelativePos = |
894 | 0 | reflowInput.mStyleDisplay->IsRelativelyPositionedStyle(); |
895 | 0 | if (pfd->mRelativePos) { |
896 | 0 | pfd->mOffsets = |
897 | 0 | reflowInput.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM); |
898 | 0 | } |
899 | 0 |
|
900 | 0 | // Calculate whether the the frame should have a start margin and |
901 | 0 | // subtract the margin from the available width if necessary. |
902 | 0 | // The margin will be applied to the starting inline coordinates of |
903 | 0 | // the frame in CanPlaceFrame() after reflowing the frame. |
904 | 0 | AllowForStartMargin(pfd, reflowInput); |
905 | 0 | } |
906 | 0 | // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent, |
907 | 0 | // because reflow doesn't look at the dirty bits on the frame being reflowed. |
908 | 0 |
|
909 | 0 | // See if this frame depends on the width of its containing block. If |
910 | 0 | // so, disable resize reflow optimizations for the line. (Note that, |
911 | 0 | // to be conservative, we do this if we *try* to fit a frame on a |
912 | 0 | // line, even if we don't succeed.) (Note also that we can only make |
913 | 0 | // this IsPercentageAware check *after* we've constructed our |
914 | 0 | // ReflowInput, because that construction may be what forces aFrame |
915 | 0 | // to lazily initialize its (possibly-percent-valued) intrinsic size.) |
916 | 0 | if (mGotLineBox && IsPercentageAware(aFrame)) { |
917 | 0 | mLineBox->DisableResizeReflowOptimization(); |
918 | 0 | } |
919 | 0 |
|
920 | 0 | // Note that we don't bother positioning the frame yet, because we're probably |
921 | 0 | // going to end up moving it when we do the block-direction alignment. |
922 | 0 |
|
923 | 0 | // Adjust float manager coordinate system for the frame. |
924 | 0 | ReflowOutput reflowOutput(lineWM); |
925 | | #ifdef DEBUG |
926 | | reflowOutput.ISize(lineWM) = nscoord(0xdeadbeef); |
927 | | reflowOutput.BSize(lineWM) = nscoord(0xdeadbeef); |
928 | | #endif |
929 | | nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerSize()); |
930 | 0 | nscoord tB = pfd->mBounds.BStart(lineWM); |
931 | 0 | mFloatManager->Translate(tI, tB); |
932 | 0 |
|
933 | 0 | int32_t savedOptionalBreakOffset; |
934 | 0 | gfxBreakPriority savedOptionalBreakPriority; |
935 | 0 | nsIFrame* savedOptionalBreakFrame = |
936 | 0 | GetLastOptionalBreakPosition(&savedOptionalBreakOffset, |
937 | 0 | &savedOptionalBreakPriority); |
938 | 0 |
|
939 | 0 | if (!isText) { |
940 | 0 | aFrame->Reflow(mPresContext, reflowOutput, *reflowInputHolder, aReflowStatus); |
941 | 0 | } else { |
942 | 0 | static_cast<nsTextFrame*>(aFrame)-> |
943 | 0 | ReflowText(*this, availableSpaceOnLine, |
944 | 0 | psd->mReflowInput->mRenderingContext->GetDrawTarget(), |
945 | 0 | reflowOutput, aReflowStatus); |
946 | 0 | } |
947 | 0 |
|
948 | 0 | pfd->mJustificationInfo = mJustificationInfo; |
949 | 0 | mJustificationInfo = JustificationInfo(); |
950 | 0 |
|
951 | 0 | // See if the frame is a placeholderFrame and if it is process |
952 | 0 | // the float. At the same time, check if the frame has any non-collapsed-away |
953 | 0 | // content. |
954 | 0 | bool placedFloat = false; |
955 | 0 | bool isEmpty; |
956 | 0 | if (frameType == LayoutFrameType::None) { |
957 | 0 | isEmpty = pfd->mFrame->IsEmpty(); |
958 | 0 | } else { |
959 | 0 | if (LayoutFrameType::Placeholder == frameType) { |
960 | 0 | isEmpty = true; |
961 | 0 | pfd->mIsPlaceholder = true; |
962 | 0 | pfd->mSkipWhenTrimmingWhitespace = true; |
963 | 0 | nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); |
964 | 0 | if (outOfFlowFrame) { |
965 | 0 | if (psd->mNoWrap && |
966 | 0 | // We can always place floats in an empty line. |
967 | 0 | !LineIsEmpty() && |
968 | 0 | // We always place floating letter frames. This kinda sucks. They'd |
969 | 0 | // usually fall into the LineIsEmpty() check anyway, except when |
970 | 0 | // there's something like a bullet before or what not. We actually |
971 | 0 | // need to place them now, because they're pretty nasty and they |
972 | 0 | // create continuations that are in flow and not a kid of the |
973 | 0 | // previous continuation's parent. We don't want the deferred reflow |
974 | 0 | // of the letter frame to kill a continuation after we've stored it |
975 | 0 | // in the line layout data structures. See bug 1490281 to fix the |
976 | 0 | // underlying issue. When that's fixed this check should be removed. |
977 | 0 | !outOfFlowFrame->IsLetterFrame() && |
978 | 0 | !GetOutermostLineLayout()->mBlockRI->mFlags.mCanHaveTextOverflow) { |
979 | 0 | // We'll do this at the next break opportunity. |
980 | 0 | RecordNoWrapFloat(outOfFlowFrame); |
981 | 0 | } else { |
982 | 0 | placedFloat = TryToPlaceFloat(outOfFlowFrame); |
983 | 0 | } |
984 | 0 | } |
985 | 0 | } |
986 | 0 | else if (isText) { |
987 | 0 | // Note non-empty text-frames for inline frame compatibility hackery |
988 | 0 | pfd->mIsTextFrame = true; |
989 | 0 | nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame); |
990 | 0 | isEmpty = !textFrame->HasNoncollapsedCharacters(); |
991 | 0 | if (!isEmpty) { |
992 | 0 | pfd->mIsNonEmptyTextFrame = true; |
993 | 0 | nsIContent* content = textFrame->GetContent(); |
994 | 0 |
|
995 | 0 | const nsTextFragment* frag = content->GetText(); |
996 | 0 | if (frag) { |
997 | 0 | pfd->mIsNonWhitespaceTextFrame = !content->TextIsOnlyWhitespace(); |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | } else if (LayoutFrameType::Br == frameType) { |
1001 | 0 | pfd->mSkipWhenTrimmingWhitespace = true; |
1002 | 0 | isEmpty = false; |
1003 | 0 | } else { |
1004 | 0 | if (LayoutFrameType::Letter == frameType) { |
1005 | 0 | pfd->mIsLetterFrame = true; |
1006 | 0 | } |
1007 | 0 | if (pfd->mSpan) { |
1008 | 0 | isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty(); |
1009 | 0 | } else { |
1010 | 0 | isEmpty = pfd->mFrame->IsEmpty(); |
1011 | 0 | } |
1012 | 0 | } |
1013 | 0 | } |
1014 | 0 | pfd->mIsEmpty = isEmpty; |
1015 | 0 |
|
1016 | 0 | mFloatManager->Translate(-tI, -tB); |
1017 | 0 |
|
1018 | 0 | NS_ASSERTION(reflowOutput.ISize(lineWM) >= 0, "bad inline size"); |
1019 | 0 | NS_ASSERTION(reflowOutput.BSize(lineWM) >= 0,"bad block size"); |
1020 | 0 | if (reflowOutput.ISize(lineWM) < 0) { |
1021 | 0 | reflowOutput.ISize(lineWM) = 0; |
1022 | 0 | } |
1023 | 0 | if (reflowOutput.BSize(lineWM) < 0) { |
1024 | 0 | reflowOutput.BSize(lineWM) = 0; |
1025 | 0 | } |
1026 | 0 |
|
1027 | | #ifdef DEBUG |
1028 | | // Note: break-before means ignore the reflow metrics since the |
1029 | | // frame will be reflowed another time. |
1030 | | if (!aReflowStatus.IsInlineBreakBefore()) { |
1031 | | if ((CRAZY_SIZE(reflowOutput.ISize(lineWM)) || |
1032 | | CRAZY_SIZE(reflowOutput.BSize(lineWM))) && |
1033 | | !LineContainerFrame()->GetParent()->IsCrazySizeAssertSuppressed()) { |
1034 | | printf("nsLineLayout: "); |
1035 | | nsFrame::ListTag(stdout, aFrame); |
1036 | | printf(" metrics=%d,%d!\n", reflowOutput.Width(), reflowOutput.Height()); |
1037 | | } |
1038 | | if ((reflowOutput.Width() == nscoord(0xdeadbeef)) || |
1039 | | (reflowOutput.Height() == nscoord(0xdeadbeef))) { |
1040 | | printf("nsLineLayout: "); |
1041 | | nsFrame::ListTag(stdout, aFrame); |
1042 | | printf(" didn't set w/h %d,%d!\n", reflowOutput.Width(), reflowOutput.Height()); |
1043 | | } |
1044 | | } |
1045 | | #endif |
1046 | |
|
1047 | 0 | // Unlike with non-inline reflow, the overflow area here does *not* |
1048 | 0 | // include the accumulation of the frame's bounds and its inline |
1049 | 0 | // descendants' bounds. Nor does it include the outline area; it's |
1050 | 0 | // just the union of the bounds of any absolute children. That is |
1051 | 0 | // added in later by nsLineLayout::ReflowInlineFrames. |
1052 | 0 | pfd->mOverflowAreas = reflowOutput.mOverflowAreas; |
1053 | 0 |
|
1054 | 0 | pfd->mBounds.ISize(lineWM) = reflowOutput.ISize(lineWM); |
1055 | 0 | pfd->mBounds.BSize(lineWM) = reflowOutput.BSize(lineWM); |
1056 | 0 |
|
1057 | 0 | // Size the frame, but |RelativePositionFrames| will size the view. |
1058 | 0 | aFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd)); |
1059 | 0 |
|
1060 | 0 | // Tell the frame that we're done reflowing it |
1061 | 0 | aFrame->DidReflow(mPresContext, |
1062 | 0 | isText ? nullptr : reflowInputHolder.ptr()); |
1063 | 0 |
|
1064 | 0 | if (aMetrics) { |
1065 | 0 | *aMetrics = reflowOutput; |
1066 | 0 | } |
1067 | 0 |
|
1068 | 0 | if (!aReflowStatus.IsInlineBreakBefore()) { |
1069 | 0 | // If frame is complete and has a next-in-flow, we need to delete |
1070 | 0 | // them now. Do not do this when a break-before is signaled because |
1071 | 0 | // the frame is going to get reflowed again (and may end up wanting |
1072 | 0 | // a next-in-flow where it ends up). |
1073 | 0 | if (aReflowStatus.IsComplete()) { |
1074 | 0 | nsIFrame* kidNextInFlow = aFrame->GetNextInFlow(); |
1075 | 0 | if (nullptr != kidNextInFlow) { |
1076 | 0 | // Remove all of the childs next-in-flows. Make sure that we ask |
1077 | 0 | // the right parent to do the removal (it's possible that the |
1078 | 0 | // parent is not this because we are executing pullup code) |
1079 | 0 | kidNextInFlow->GetParent()-> |
1080 | 0 | DeleteNextInFlowChild(kidNextInFlow, true); |
1081 | 0 | } |
1082 | 0 | } |
1083 | 0 |
|
1084 | 0 | // Check whether this frame breaks up text runs. All frames break up text |
1085 | 0 | // runs (hence return false here) except for text frames and inline containers. |
1086 | 0 | bool continuingTextRun = aFrame->CanContinueTextRun(); |
1087 | 0 |
|
1088 | 0 | // Clear any residual mTrimmableISize if this isn't a text frame |
1089 | 0 | if (!continuingTextRun && !pfd->mSkipWhenTrimmingWhitespace) { |
1090 | 0 | mTrimmableISize = 0; |
1091 | 0 | } |
1092 | 0 |
|
1093 | 0 | // See if we can place the frame. If we can't fit it, then we |
1094 | 0 | // return now. |
1095 | 0 | bool optionalBreakAfterFits; |
1096 | 0 | NS_ASSERTION(isText || |
1097 | 0 | !reflowInputHolder->IsFloating(), |
1098 | 0 | "How'd we get a floated inline frame? " |
1099 | 0 | "The frame ctor should've dealt with this."); |
1100 | 0 | if (CanPlaceFrame(pfd, notSafeToBreak, continuingTextRun, |
1101 | 0 | savedOptionalBreakFrame != nullptr, reflowOutput, |
1102 | 0 | aReflowStatus, &optionalBreakAfterFits)) { |
1103 | 0 | if (!isEmpty) { |
1104 | 0 | psd->mHasNonemptyContent = true; |
1105 | 0 | mLineIsEmpty = false; |
1106 | 0 | if (!pfd->mSpan) { |
1107 | 0 | // nonempty leaf content has been placed |
1108 | 0 | mLineAtStart = false; |
1109 | 0 | } |
1110 | 0 | if (LayoutFrameType::Ruby == frameType) { |
1111 | 0 | mHasRuby = true; |
1112 | 0 | SyncAnnotationBounds(pfd); |
1113 | 0 | } |
1114 | 0 | } |
1115 | 0 |
|
1116 | 0 | // Place the frame, updating aBounds with the final size and |
1117 | 0 | // location. Then apply the bottom+right margins (as |
1118 | 0 | // appropriate) to the frame. |
1119 | 0 | PlaceFrame(pfd, reflowOutput); |
1120 | 0 | PerSpanData* span = pfd->mSpan; |
1121 | 0 | if (span) { |
1122 | 0 | // The frame we just finished reflowing is an inline |
1123 | 0 | // container. It needs its child frames aligned in the block direction, |
1124 | 0 | // so do most of it now. |
1125 | 0 | VerticalAlignFrames(span); |
1126 | 0 | } |
1127 | 0 |
|
1128 | 0 | if (!continuingTextRun && !psd->mNoWrap) { |
1129 | 0 | if (!LineIsEmpty() || placedFloat) { |
1130 | 0 | // record soft break opportunity after this content that can't be |
1131 | 0 | // part of a text run. This is not a text frame so we know |
1132 | 0 | // that offset INT32_MAX means "after the content". |
1133 | 0 | if (NotifyOptionalBreakPosition(aFrame, INT32_MAX, |
1134 | 0 | optionalBreakAfterFits, |
1135 | 0 | gfxBreakPriority::eNormalBreak)) { |
1136 | 0 | // If this returns true then we are being told to actually break here. |
1137 | 0 | aReflowStatus.SetInlineLineBreakAfter(); |
1138 | 0 | } |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | } |
1142 | 0 | else { |
1143 | 0 | PushFrame(aFrame); |
1144 | 0 | aPushedFrame = true; |
1145 | 0 | // Undo any saved break positions that the frame might have told us about, |
1146 | 0 | // since we didn't end up placing it |
1147 | 0 | RestoreSavedBreakPosition(savedOptionalBreakFrame, |
1148 | 0 | savedOptionalBreakOffset, |
1149 | 0 | savedOptionalBreakPriority); |
1150 | 0 | } |
1151 | 0 | } |
1152 | 0 | else { |
1153 | 0 | PushFrame(aFrame); |
1154 | 0 | aPushedFrame = true; |
1155 | 0 | } |
1156 | 0 |
|
1157 | | #ifdef REALLY_NOISY_REFLOW |
1158 | | nsFrame::IndentBy(stdout, mSpanDepth); |
1159 | | printf("End ReflowFrame "); |
1160 | | nsFrame::ListTag(stdout, aFrame); |
1161 | | printf(" status=%x\n", aReflowStatus); |
1162 | | #endif |
1163 | | } |
1164 | | |
1165 | | void |
1166 | | nsLineLayout::AllowForStartMargin(PerFrameData* pfd, |
1167 | | ReflowInput& aReflowInput) |
1168 | 0 | { |
1169 | 0 | NS_ASSERTION(!aReflowInput.IsFloating(), |
1170 | 0 | "How'd we get a floated inline frame? " |
1171 | 0 | "The frame ctor should've dealt with this."); |
1172 | 0 |
|
1173 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1174 | 0 |
|
1175 | 0 | // Only apply start-margin on the first-in flow for inline frames, |
1176 | 0 | // and make sure to not apply it to any inline other than the first |
1177 | 0 | // in an ib split. Note that the ib sibling (block-in-inline |
1178 | 0 | // sibling) annotations only live on the first continuation, but we |
1179 | 0 | // don't want to apply the start margin for later continuations |
1180 | 0 | // anyway. For box-decoration-break:clone we apply the start-margin |
1181 | 0 | // on all continuations. |
1182 | 0 | if ((pfd->mFrame->GetPrevContinuation() || |
1183 | 0 | pfd->mFrame->FrameIsNonFirstInIBSplit()) && |
1184 | 0 | aReflowInput.mStyleBorder->mBoxDecorationBreak == |
1185 | 0 | StyleBoxDecorationBreak::Slice) { |
1186 | 0 | // Zero this out so that when we compute the max-element-width of |
1187 | 0 | // the frame we will properly avoid adding in the starting margin. |
1188 | 0 | pfd->mMargin.IStart(lineWM) = 0; |
1189 | 0 | } else if (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedISize()) { |
1190 | 0 | NS_WARNING_ASSERTION( |
1191 | 0 | NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableISize(), |
1192 | 0 | "have unconstrained inline-size; this should only result from very " |
1193 | 0 | "large sizes, not attempts at intrinsic inline-size calculation"); |
1194 | 0 | // For inline-ish and text-ish things (which don't compute widths |
1195 | 0 | // in the reflow state), adjust available inline-size to account |
1196 | 0 | // for the start margin. The end margin will be accounted for when |
1197 | 0 | // we finish flowing the frame. |
1198 | 0 | WritingMode wm = aReflowInput.GetWritingMode(); |
1199 | 0 | aReflowInput.AvailableISize() -= |
1200 | 0 | pfd->mMargin.ConvertTo(wm, lineWM).IStart(wm); |
1201 | 0 | } |
1202 | 0 | } |
1203 | | |
1204 | | nscoord |
1205 | | nsLineLayout::GetCurrentFrameInlineDistanceFromBlock() |
1206 | 0 | { |
1207 | 0 | PerSpanData* psd; |
1208 | 0 | nscoord x = 0; |
1209 | 0 | for (psd = mCurrentSpan; psd; psd = psd->mParent) { |
1210 | 0 | x += psd->mICoord; |
1211 | 0 | } |
1212 | 0 | return x; |
1213 | 0 | } |
1214 | | |
1215 | | /** |
1216 | | * This method syncs bounds of ruby annotations and ruby annotation |
1217 | | * containers from their rect. It is necessary because: |
1218 | | * Containers are not part of the line in their levels, which means |
1219 | | * their bounds are not set properly before. |
1220 | | * Ruby annotations' position may have been changed when reflowing |
1221 | | * their containers. |
1222 | | */ |
1223 | | void |
1224 | | nsLineLayout::SyncAnnotationBounds(PerFrameData* aRubyFrame) |
1225 | 0 | { |
1226 | 0 | MOZ_ASSERT(aRubyFrame->mFrame->IsRubyFrame()); |
1227 | 0 | MOZ_ASSERT(aRubyFrame->mSpan); |
1228 | 0 |
|
1229 | 0 | PerSpanData* span = aRubyFrame->mSpan; |
1230 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1231 | 0 | for (PerFrameData* pfd = span->mFirstFrame; pfd; pfd = pfd->mNext) { |
1232 | 0 | for (PerFrameData* rtc = pfd->mNextAnnotation; |
1233 | 0 | rtc; rtc = rtc->mNextAnnotation) { |
1234 | 0 | if (lineWM.IsOrthogonalTo(rtc->mFrame->GetWritingMode())) { |
1235 | 0 | // Inter-character case: don't attempt to sync annotation bounds. |
1236 | 0 | continue; |
1237 | 0 | } |
1238 | 0 | // When the annotation container is reflowed, the width of the |
1239 | 0 | // ruby container is unknown so we use a dummy container size; |
1240 | 0 | // in the case of RTL block direction, the final position will be |
1241 | 0 | // fixed up later. |
1242 | 0 | const nsSize dummyContainerSize; |
1243 | 0 | LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(), |
1244 | 0 | dummyContainerSize); |
1245 | 0 | rtc->mBounds = rtcBounds; |
1246 | 0 | nsSize rtcSize = rtcBounds.Size(lineWM).GetPhysicalSize(lineWM); |
1247 | 0 | for (PerFrameData* rt = rtc->mSpan->mFirstFrame; rt; rt = rt->mNext) { |
1248 | 0 | LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcSize); |
1249 | 0 | MOZ_ASSERT(rt->mBounds.Size(lineWM) == rtBounds.Size(lineWM), |
1250 | 0 | "Size of the annotation should not have been changed"); |
1251 | 0 | rt->mBounds.SetOrigin(lineWM, rtBounds.Origin(lineWM)); |
1252 | 0 | } |
1253 | 0 | } |
1254 | 0 | } |
1255 | 0 | } |
1256 | | |
1257 | | /** |
1258 | | * See if the frame can be placed now that we know it's desired size. |
1259 | | * We can always place the frame if the line is empty. Note that we |
1260 | | * know that the reflow-status is not a break-before because if it was |
1261 | | * ReflowFrame above would have returned false, preventing this method |
1262 | | * from being called. The logic in this method assumes that. |
1263 | | * |
1264 | | * Note that there is no check against the Y coordinate because we |
1265 | | * assume that the caller will take care of that. |
1266 | | */ |
1267 | | bool |
1268 | | nsLineLayout::CanPlaceFrame(PerFrameData* pfd, |
1269 | | bool aNotSafeToBreak, |
1270 | | bool aFrameCanContinueTextRun, |
1271 | | bool aCanRollBackBeforeFrame, |
1272 | | ReflowOutput& aMetrics, |
1273 | | nsReflowStatus& aStatus, |
1274 | | bool* aOptionalBreakAfterFits) |
1275 | 0 | { |
1276 | 0 | MOZ_ASSERT(pfd && pfd->mFrame, "bad args, null pointers for frame data"); |
1277 | 0 |
|
1278 | 0 | *aOptionalBreakAfterFits = true; |
1279 | 0 |
|
1280 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1281 | 0 | /* |
1282 | 0 | * We want to only apply the end margin if we're the last continuation and |
1283 | 0 | * either not in an {ib} split or the last inline in it. In all other |
1284 | 0 | * cases we want to zero it out. That means zeroing it out if any of these |
1285 | 0 | * conditions hold: |
1286 | 0 | * 1) The frame is not complete (in this case it will get a next-in-flow) |
1287 | 0 | * 2) The frame is complete but has a non-fluid continuation on its |
1288 | 0 | * continuation chain. Note that if it has a fluid continuation, that |
1289 | 0 | * continuation will get destroyed later, so we don't want to drop the |
1290 | 0 | * end-margin in that case. |
1291 | 0 | * 3) The frame is in an {ib} split and is not the last part. |
1292 | 0 | * |
1293 | 0 | * However, none of that applies if this is a letter frame (XXXbz why?) |
1294 | 0 | * |
1295 | 0 | * For box-decoration-break:clone we apply the end margin on all |
1296 | 0 | * continuations (that are not letter frames). |
1297 | 0 | */ |
1298 | 0 | if ((aStatus.IsIncomplete() || |
1299 | 0 | pfd->mFrame->LastInFlow()->GetNextContinuation() || |
1300 | 0 | pfd->mFrame->FrameIsNonLastInIBSplit()) && |
1301 | 0 | !pfd->mIsLetterFrame && |
1302 | 0 | pfd->mFrame->StyleBorder()->mBoxDecorationBreak == |
1303 | 0 | StyleBoxDecorationBreak::Slice) { |
1304 | 0 | pfd->mMargin.IEnd(lineWM) = 0; |
1305 | 0 | } |
1306 | 0 |
|
1307 | 0 | // Apply the start margin to the frame bounds. |
1308 | 0 | nscoord startMargin = pfd->mMargin.IStart(lineWM); |
1309 | 0 | nscoord endMargin = pfd->mMargin.IEnd(lineWM); |
1310 | 0 |
|
1311 | 0 | pfd->mBounds.IStart(lineWM) += startMargin; |
1312 | 0 |
|
1313 | 0 | PerSpanData* psd = mCurrentSpan; |
1314 | 0 | if (psd->mNoWrap) { |
1315 | 0 | // When wrapping is off, everything fits. |
1316 | 0 | return true; |
1317 | 0 | } |
1318 | 0 | |
1319 | | #ifdef NOISY_CAN_PLACE_FRAME |
1320 | | if (nullptr != psd->mFrame) { |
1321 | | nsFrame::ListTag(stdout, psd->mFrame->mFrame); |
1322 | | } |
1323 | | printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false"); |
1324 | | nsFrame::ListTag(stdout, pfd->mFrame); |
1325 | | printf(" frameWidth=%d, margins=%d,%d\n", |
1326 | | pfd->mBounds.IEnd(lineWM) + endMargin - psd->mICoord, |
1327 | | startMargin, endMargin); |
1328 | | #endif |
1329 | | |
1330 | 0 | // Set outside to true if the result of the reflow leads to the |
1331 | 0 | // frame sticking outside of our available area. |
1332 | 0 | bool outside = pfd->mBounds.IEnd(lineWM) - mTrimmableISize + endMargin > |
1333 | 0 | psd->mIEnd; |
1334 | 0 | if (!outside) { |
1335 | 0 | // If it fits, it fits |
1336 | | #ifdef NOISY_CAN_PLACE_FRAME |
1337 | | printf(" ==> inside\n"); |
1338 | | #endif |
1339 | | return true; |
1340 | 0 | } |
1341 | 0 | *aOptionalBreakAfterFits = false; |
1342 | 0 |
|
1343 | 0 | // When it doesn't fit, check for a few special conditions where we |
1344 | 0 | // allow it to fit anyway. |
1345 | 0 | if (0 == startMargin + pfd->mBounds.ISize(lineWM) + endMargin) { |
1346 | 0 | // Empty frames always fit right where they are |
1347 | | #ifdef NOISY_CAN_PLACE_FRAME |
1348 | | printf(" ==> empty frame fits\n"); |
1349 | | #endif |
1350 | | return true; |
1351 | 0 | } |
1352 | 0 |
|
1353 | 0 | // another special case: always place a BR |
1354 | 0 | if (pfd->mFrame->IsBrFrame()) { |
1355 | | #ifdef NOISY_CAN_PLACE_FRAME |
1356 | | printf(" ==> BR frame fits\n"); |
1357 | | #endif |
1358 | | return true; |
1359 | 0 | } |
1360 | 0 |
|
1361 | 0 | if (aNotSafeToBreak) { |
1362 | 0 | // There are no frames on the line that take up width and the line is |
1363 | 0 | // not impacted by floats, so we must allow the current frame to be |
1364 | 0 | // placed on the line |
1365 | | #ifdef NOISY_CAN_PLACE_FRAME |
1366 | | printf(" ==> not-safe and not-impacted fits: "); |
1367 | | while (nullptr != psd) { |
1368 | | printf("<psd=%p x=%d left=%d> ", psd, psd->mICoord, psd->mIStart); |
1369 | | psd = psd->mParent; |
1370 | | } |
1371 | | printf("\n"); |
1372 | | #endif |
1373 | | return true; |
1374 | 0 | } |
1375 | 0 |
|
1376 | 0 | // Special check for span frames |
1377 | 0 | if (pfd->mSpan && pfd->mSpan->mContainsFloat) { |
1378 | 0 | // If the span either directly or indirectly contains a float then |
1379 | 0 | // it fits. Why? It's kind of complicated, but here goes: |
1380 | 0 | // |
1381 | 0 | // 1. CanPlaceFrame is used for all frame placements on a line, |
1382 | 0 | // and in a span. This includes recursively placement of frames |
1383 | 0 | // inside of spans, and the span itself. Because the logic always |
1384 | 0 | // checks for room before proceeding (the code above here), the |
1385 | 0 | // only things on a line will be those things that "fit". |
1386 | 0 | // |
1387 | 0 | // 2. Before a float is placed on a line, the line has to be empty |
1388 | 0 | // (otherwise it's a "below current line" float and will be placed |
1389 | 0 | // after the line). |
1390 | 0 | // |
1391 | 0 | // Therefore, if the span directly or indirectly has a float |
1392 | 0 | // then it means that at the time of the placement of the float |
1393 | 0 | // the line was empty. Because of #1, only the frames that fit can |
1394 | 0 | // be added after that point, therefore we can assume that the |
1395 | 0 | // current span being placed has fit. |
1396 | 0 | // |
1397 | 0 | // So how do we get here and have a span that should already fit |
1398 | 0 | // and yet doesn't: Simple: span's that have the no-wrap attribute |
1399 | 0 | // set on them and contain a float and are placed where they |
1400 | 0 | // don't naturally fit. |
1401 | 0 | return true; |
1402 | 0 | } |
1403 | 0 | |
1404 | 0 | if (aFrameCanContinueTextRun) { |
1405 | 0 | // Let it fit, but we reserve the right to roll back. |
1406 | 0 | // Note that we usually won't get here because a text frame will break |
1407 | 0 | // itself to avoid exceeding the available width. |
1408 | 0 | // We'll only get here for text frames that couldn't break early enough. |
1409 | | #ifdef NOISY_CAN_PLACE_FRAME |
1410 | | printf(" ==> placing overflowing textrun, requesting backup\n"); |
1411 | | #endif |
1412 | |
|
1413 | 0 | // We will want to try backup. |
1414 | 0 | mNeedBackup = true; |
1415 | 0 | return true; |
1416 | 0 | } |
1417 | 0 |
|
1418 | | #ifdef NOISY_CAN_PLACE_FRAME |
1419 | | printf(" ==> didn't fit\n"); |
1420 | | #endif |
1421 | 0 | aStatus.SetInlineLineBreakBeforeAndReset(); |
1422 | 0 | return false; |
1423 | 0 | } |
1424 | | |
1425 | | /** |
1426 | | * Place the frame. Update running counters. |
1427 | | */ |
1428 | | void |
1429 | | nsLineLayout::PlaceFrame(PerFrameData* pfd, ReflowOutput& aMetrics) |
1430 | 0 | { |
1431 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1432 | 0 |
|
1433 | 0 | // If the frame's block direction does not match the line's, we can't use |
1434 | 0 | // its ascent; instead, treat it as a block with baseline at the block-end |
1435 | 0 | // edge (or block-begin in the case of an "inverted" line). |
1436 | 0 | if (pfd->mWritingMode.GetBlockDir() != lineWM.GetBlockDir()) { |
1437 | 0 | pfd->mAscent = lineWM.IsLineInverted() ? 0 : aMetrics.BSize(lineWM); |
1438 | 0 | } else { |
1439 | 0 | if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) { |
1440 | 0 | pfd->mAscent = pfd->mFrame->GetLogicalBaseline(lineWM); |
1441 | 0 | } else { |
1442 | 0 | pfd->mAscent = aMetrics.BlockStartAscent(); |
1443 | 0 | } |
1444 | 0 | } |
1445 | 0 |
|
1446 | 0 | // Advance to next inline coordinate |
1447 | 0 | mCurrentSpan->mICoord = pfd->mBounds.IEnd(lineWM) + |
1448 | 0 | pfd->mMargin.IEnd(lineWM); |
1449 | 0 |
|
1450 | 0 | // Count the number of non-placeholder frames on the line... |
1451 | 0 | if (pfd->mFrame->IsPlaceholderFrame()) { |
1452 | 0 | NS_ASSERTION(pfd->mBounds.ISize(lineWM) == 0 && |
1453 | 0 | pfd->mBounds.BSize(lineWM) == 0, |
1454 | 0 | "placeholders should have 0 width/height (checking " |
1455 | 0 | "placeholders were never counted by the old code in " |
1456 | 0 | "this function)"); |
1457 | 0 | } else { |
1458 | 0 | mTotalPlacedFrames++; |
1459 | 0 | } |
1460 | 0 | } |
1461 | | |
1462 | | void |
1463 | | nsLineLayout::AddBulletFrame(nsBulletFrame* aFrame, |
1464 | | const ReflowOutput& aMetrics) |
1465 | 0 | { |
1466 | 0 | NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
1467 | 0 | NS_ASSERTION(mGotLineBox, "must have line box"); |
1468 | 0 |
|
1469 | 0 | nsIFrame *blockFrame = mBlockReflowInput->mFrame; |
1470 | 0 | NS_ASSERTION(blockFrame->IsFrameOfType(nsIFrame::eBlockFrame), |
1471 | 0 | "must be for block"); |
1472 | 0 | if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) { |
1473 | 0 | mHasBullet = true; |
1474 | 0 | mLineBox->SetHasBullet(); |
1475 | 0 | } |
1476 | 0 |
|
1477 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1478 | 0 | PerFrameData* pfd = NewPerFrameData(aFrame); |
1479 | 0 | PerSpanData* psd = mRootSpan; |
1480 | 0 |
|
1481 | 0 | MOZ_ASSERT(psd->mFirstFrame, "adding bullet to an empty line?"); |
1482 | 0 | // Prepend the bullet frame to the line. |
1483 | 0 | psd->mFirstFrame->mPrev = pfd; |
1484 | 0 | pfd->mNext = psd->mFirstFrame; |
1485 | 0 | psd->mFirstFrame = pfd; |
1486 | 0 |
|
1487 | 0 | pfd->mIsBullet = true; |
1488 | 0 | if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) { |
1489 | 0 | pfd->mAscent = aFrame->GetLogicalBaseline(lineWM); |
1490 | 0 | } else { |
1491 | 0 | pfd->mAscent = aMetrics.BlockStartAscent(); |
1492 | 0 | } |
1493 | 0 |
|
1494 | 0 | // Note: block-coord value will be updated during block-direction alignment |
1495 | 0 | pfd->mBounds = LogicalRect(lineWM, aFrame->GetRect(), ContainerSize()); |
1496 | 0 | pfd->mOverflowAreas = aMetrics.mOverflowAreas; |
1497 | 0 | } |
1498 | | |
1499 | | void |
1500 | | nsLineLayout::RemoveBulletFrame(nsBulletFrame* aFrame) |
1501 | 0 | { |
1502 | 0 | PerSpanData* psd = mCurrentSpan; |
1503 | 0 | MOZ_ASSERT(psd == mRootSpan, "bullet on non-root span?"); |
1504 | 0 | MOZ_ASSERT(psd->mFirstFrame->mFrame == aFrame, |
1505 | 0 | "bullet is not the first frame?"); |
1506 | 0 | PerFrameData* pfd = psd->mFirstFrame; |
1507 | 0 | MOZ_ASSERT(pfd != psd->mLastFrame, |
1508 | 0 | "bullet is the only frame?"); |
1509 | 0 | pfd->mNext->mPrev = nullptr; |
1510 | 0 | psd->mFirstFrame = pfd->mNext; |
1511 | 0 | FreeFrame(pfd); |
1512 | 0 | } |
1513 | | |
1514 | | #ifdef DEBUG |
1515 | | void |
1516 | | nsLineLayout::DumpPerSpanData(PerSpanData* psd, int32_t aIndent) |
1517 | | { |
1518 | | nsFrame::IndentBy(stdout, aIndent); |
1519 | | printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd), |
1520 | | psd->mIStart, psd->mICoord, psd->mIEnd); |
1521 | | PerFrameData* pfd = psd->mFirstFrame; |
1522 | | while (nullptr != pfd) { |
1523 | | nsFrame::IndentBy(stdout, aIndent+1); |
1524 | | nsFrame::ListTag(stdout, pfd->mFrame); |
1525 | | nsRect rect = pfd->mBounds.GetPhysicalRect(psd->mWritingMode, |
1526 | | ContainerSize()); |
1527 | | printf(" %d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); |
1528 | | if (pfd->mSpan) { |
1529 | | DumpPerSpanData(pfd->mSpan, aIndent + 1); |
1530 | | } |
1531 | | pfd = pfd->mNext; |
1532 | | } |
1533 | | } |
1534 | | #endif |
1535 | | |
1536 | | void |
1537 | | nsLineLayout::RecordNoWrapFloat(nsIFrame* aFloat) |
1538 | 0 | { |
1539 | 0 | GetOutermostLineLayout()->mBlockRI->mNoWrapFloats.AppendElement(aFloat); |
1540 | 0 | } |
1541 | | |
1542 | | void |
1543 | | nsLineLayout::FlushNoWrapFloats() |
1544 | 0 | { |
1545 | 0 | auto& noWrapFloats = GetOutermostLineLayout()->mBlockRI->mNoWrapFloats; |
1546 | 0 | for (nsIFrame* floatedFrame : noWrapFloats) { |
1547 | 0 | TryToPlaceFloat(floatedFrame); |
1548 | 0 | } |
1549 | 0 | noWrapFloats.Clear(); |
1550 | 0 | } |
1551 | | |
1552 | | bool |
1553 | | nsLineLayout::TryToPlaceFloat(nsIFrame* aFloat) |
1554 | 0 | { |
1555 | 0 | // Add mTrimmableISize to the available width since if the line ends here, the |
1556 | 0 | // width of the inline content will be reduced by mTrimmableISize. |
1557 | 0 | nscoord availableISize = mCurrentSpan->mIEnd - (mCurrentSpan->mICoord - mTrimmableISize); |
1558 | 0 | NS_ASSERTION(!(aFloat->IsLetterFrame() && GetFirstLetterStyleOK()), |
1559 | 0 | "FirstLetterStyle set on line with floating first letter"); |
1560 | 0 | return GetOutermostLineLayout()->AddFloat(aFloat, availableISize); |
1561 | 0 | } |
1562 | | |
1563 | | bool |
1564 | | nsLineLayout::NotifyOptionalBreakPosition(nsIFrame* aFrame, |
1565 | | int32_t aOffset, |
1566 | | bool aFits, |
1567 | | gfxBreakPriority aPriority) |
1568 | 0 | { |
1569 | 0 | MOZ_ASSERT(!aFits || !mNeedBackup, |
1570 | 0 | "Shouldn't be updating the break position with a break that fits " |
1571 | 0 | "after we've already flagged an overrun"); |
1572 | 0 | MOZ_ASSERT(mCurrentSpan, "Should be doing line layout"); |
1573 | 0 | if (mCurrentSpan->mNoWrap) { |
1574 | 0 | FlushNoWrapFloats(); |
1575 | 0 | } |
1576 | 0 |
|
1577 | 0 | // Remember the last break position that fits; if there was no break that fit, |
1578 | 0 | // just remember the first break |
1579 | 0 | if ((aFits && aPriority >= mLastOptionalBreakPriority) || |
1580 | 0 | !mLastOptionalBreakFrame) { |
1581 | 0 | mLastOptionalBreakFrame = aFrame; |
1582 | 0 | mLastOptionalBreakFrameOffset = aOffset; |
1583 | 0 | mLastOptionalBreakPriority = aPriority; |
1584 | 0 | } |
1585 | 0 | return aFrame && mForceBreakFrame == aFrame && |
1586 | 0 | mForceBreakFrameOffset == aOffset; |
1587 | 0 | } |
1588 | | |
1589 | | |
1590 | 0 | #define VALIGN_OTHER 0 |
1591 | 0 | #define VALIGN_TOP 1 |
1592 | 0 | #define VALIGN_BOTTOM 2 |
1593 | | |
1594 | | void |
1595 | | nsLineLayout::VerticalAlignLine() |
1596 | 0 | { |
1597 | 0 | // Partially place the children of the block frame. The baseline for |
1598 | 0 | // this operation is set to zero so that the y coordinates for all |
1599 | 0 | // of the placed children will be relative to there. |
1600 | 0 | PerSpanData* psd = mRootSpan; |
1601 | 0 | VerticalAlignFrames(psd); |
1602 | 0 |
|
1603 | 0 | // *** Note that comments here still use the anachronistic term |
1604 | 0 | // "line-height" when we really mean "size of the line in the block |
1605 | 0 | // direction", "vertical-align" when we really mean "alignment in |
1606 | 0 | // the block direction", and "top" and "bottom" when we really mean |
1607 | 0 | // "block start" and "block end". This is partly for brevity and |
1608 | 0 | // partly to retain the association with the CSS line-height and |
1609 | 0 | // vertical-align properties. |
1610 | 0 | // |
1611 | 0 | // Compute the line-height. The line-height will be the larger of: |
1612 | 0 | // |
1613 | 0 | // [1] maxBCoord - minBCoord (the distance between the first child's |
1614 | 0 | // block-start edge and the last child's block-end edge) |
1615 | 0 | // |
1616 | 0 | // [2] the maximum logical box block size (since not every frame may have |
1617 | 0 | // participated in #1; for example: "top" and "botttom" aligned frames) |
1618 | 0 | // |
1619 | 0 | // [3] the minimum line height ("line-height" property set on the |
1620 | 0 | // block frame) |
1621 | 0 | nscoord lineBSize = psd->mMaxBCoord - psd->mMinBCoord; |
1622 | 0 |
|
1623 | 0 | // Now that the line-height is computed, we need to know where the |
1624 | 0 | // baseline is in the line. Position baseline so that mMinBCoord is just |
1625 | 0 | // inside the start of the line box. |
1626 | 0 | nscoord baselineBCoord; |
1627 | 0 | if (psd->mMinBCoord < 0) { |
1628 | 0 | baselineBCoord = mBStartEdge - psd->mMinBCoord; |
1629 | 0 | } |
1630 | 0 | else { |
1631 | 0 | baselineBCoord = mBStartEdge; |
1632 | 0 | } |
1633 | 0 |
|
1634 | 0 | // It's also possible that the line block-size isn't tall enough because |
1635 | 0 | // of "top" and "bottom" aligned elements that were not accounted for in |
1636 | 0 | // min/max BCoord. |
1637 | 0 | // |
1638 | 0 | // The CSS2 spec doesn't really say what happens when to the |
1639 | 0 | // baseline in this situations. What we do is if the largest start |
1640 | 0 | // aligned box block size is greater than the line block-size then we leave |
1641 | 0 | // the baseline alone. If the largest end aligned box is greater |
1642 | 0 | // than the line block-size then we slide the baseline forward by the extra |
1643 | 0 | // amount. |
1644 | 0 | // |
1645 | 0 | // Navigator 4 gives precedence to the first top/bottom aligned |
1646 | 0 | // object. We just let block end aligned objects win. |
1647 | 0 | if (lineBSize < mMaxEndBoxBSize) { |
1648 | 0 | // When the line is shorter than the maximum block start aligned box |
1649 | 0 | nscoord extra = mMaxEndBoxBSize - lineBSize; |
1650 | 0 | baselineBCoord += extra; |
1651 | 0 | lineBSize = mMaxEndBoxBSize; |
1652 | 0 | } |
1653 | 0 | if (lineBSize < mMaxStartBoxBSize) { |
1654 | 0 | lineBSize = mMaxStartBoxBSize; |
1655 | 0 | } |
1656 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1657 | | printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize, baselineBCoord); |
1658 | | #endif |
1659 | |
|
1660 | 0 | // Now position all of the frames in the root span. We will also |
1661 | 0 | // recurse over the child spans and place any frames we find with |
1662 | 0 | // vertical-align: top or bottom. |
1663 | 0 | // XXX PERFORMANCE: set a bit per-span to avoid the extra work |
1664 | 0 | // (propagate it upward too) |
1665 | 0 | WritingMode lineWM = psd->mWritingMode; |
1666 | 0 | for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
1667 | 0 | if (pfd->mBlockDirAlign == VALIGN_OTHER) { |
1668 | 0 | pfd->mBounds.BStart(lineWM) += baselineBCoord; |
1669 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSize()); |
1670 | 0 | } |
1671 | 0 | } |
1672 | 0 | PlaceTopBottomFrames(psd, -mBStartEdge, lineBSize); |
1673 | 0 |
|
1674 | 0 | mFinalLineBSize = lineBSize; |
1675 | 0 | if (mGotLineBox) { |
1676 | 0 | // Fill in returned line-box and max-element-width data |
1677 | 0 | mLineBox->SetBounds(lineWM, |
1678 | 0 | psd->mIStart, mBStartEdge, |
1679 | 0 | psd->mICoord - psd->mIStart, lineBSize, |
1680 | 0 | ContainerSize()); |
1681 | 0 |
|
1682 | 0 | mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge); |
1683 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1684 | | printf( |
1685 | | " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n", |
1686 | | mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM), |
1687 | | mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM), |
1688 | | mFinalLineBSize, mLineBox->GetLogicalAscent()); |
1689 | | #endif |
1690 | | } |
1691 | 0 | } |
1692 | | |
1693 | | // Place frames with CSS property vertical-align: top or bottom. |
1694 | | void |
1695 | | nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd, |
1696 | | nscoord aDistanceFromStart, |
1697 | | nscoord aLineBSize) |
1698 | 0 | { |
1699 | 0 | for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
1700 | 0 | PerSpanData* span = pfd->mSpan; |
1701 | | #ifdef DEBUG |
1702 | | NS_ASSERTION(0xFF != pfd->mBlockDirAlign, "umr"); |
1703 | | #endif |
1704 | | WritingMode lineWM = mRootSpan->mWritingMode; |
1705 | 0 | nsSize containerSize = ContainerSizeForSpan(psd); |
1706 | 0 | switch (pfd->mBlockDirAlign) { |
1707 | 0 | case VALIGN_TOP: |
1708 | 0 | if (span) { |
1709 | 0 | pfd->mBounds.BStart(lineWM) = -aDistanceFromStart - span->mMinBCoord; |
1710 | 0 | } |
1711 | 0 | else { |
1712 | 0 | pfd->mBounds.BStart(lineWM) = |
1713 | 0 | -aDistanceFromStart + pfd->mMargin.BStart(lineWM); |
1714 | 0 | } |
1715 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerSize); |
1716 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1717 | | printf(" "); |
1718 | | nsFrame::ListTag(stdout, pfd->mFrame); |
1719 | | printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n", |
1720 | | pfd->mBounds.BStart(lineWM), aDistanceFromStart, |
1721 | | span ? pfd->mBorderPadding.BStart(lineWM) : 0, |
1722 | | span ? span->mBStartLeading : 0); |
1723 | | #endif |
1724 | | break; |
1725 | 0 | case VALIGN_BOTTOM: |
1726 | 0 | if (span) { |
1727 | 0 | // Compute bottom leading |
1728 | 0 | pfd->mBounds.BStart(lineWM) = |
1729 | 0 | -aDistanceFromStart + aLineBSize - span->mMaxBCoord; |
1730 | 0 | } |
1731 | 0 | else { |
1732 | 0 | pfd->mBounds.BStart(lineWM) = -aDistanceFromStart + aLineBSize - |
1733 | 0 | pfd->mMargin.BEnd(lineWM) - pfd->mBounds.BSize(lineWM); |
1734 | 0 | } |
1735 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, containerSize); |
1736 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1737 | | printf(" "); |
1738 | | nsFrame::ListTag(stdout, pfd->mFrame); |
1739 | | printf(": y=%d\n", pfd->mBounds.BStart(lineWM)); |
1740 | | #endif |
1741 | | break; |
1742 | 0 | } |
1743 | 0 | if (span) { |
1744 | 0 | nscoord fromStart = aDistanceFromStart + pfd->mBounds.BStart(lineWM); |
1745 | 0 | PlaceTopBottomFrames(span, fromStart, aLineBSize); |
1746 | 0 | } |
1747 | 0 | } |
1748 | 0 | } |
1749 | | |
1750 | | static nscoord |
1751 | | GetBSizeOfEmphasisMarks(nsIFrame* aSpanFrame, float aInflation) |
1752 | 0 | { |
1753 | 0 | RefPtr<nsFontMetrics> fm = |
1754 | 0 | nsLayoutUtils::GetFontMetricsOfEmphasisMarks(aSpanFrame->Style(), |
1755 | 0 | aSpanFrame->PresContext(), |
1756 | 0 | aInflation); |
1757 | 0 | return fm->MaxHeight(); |
1758 | 0 | } |
1759 | | |
1760 | | void |
1761 | | nsLineLayout::AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd, |
1762 | | const nsStyleText* aStyleText, float aInflation, |
1763 | | bool* aZeroEffectiveSpanBox) |
1764 | 0 | { |
1765 | 0 | MOZ_ASSERT(spanFrame == psd->mFrame->mFrame); |
1766 | 0 | nscoord requiredStartLeading = 0; |
1767 | 0 | nscoord requiredEndLeading = 0; |
1768 | 0 | if (spanFrame->IsRubyFrame()) { |
1769 | 0 | // We may need to extend leadings here for ruby annotations as |
1770 | 0 | // required by section Line Spacing in the CSS Ruby spec. |
1771 | 0 | // See http://dev.w3.org/csswg/css-ruby/#line-height |
1772 | 0 | auto rubyFrame = static_cast<nsRubyFrame*>(spanFrame); |
1773 | 0 | RubyBlockLeadings leadings = rubyFrame->GetBlockLeadings(); |
1774 | 0 | requiredStartLeading += leadings.mStart; |
1775 | 0 | requiredEndLeading += leadings.mEnd; |
1776 | 0 | } |
1777 | 0 | if (aStyleText->HasTextEmphasis()) { |
1778 | 0 | nscoord bsize = GetBSizeOfEmphasisMarks(spanFrame, aInflation); |
1779 | 0 | LogicalSide side = aStyleText->TextEmphasisSide(mRootSpan->mWritingMode); |
1780 | 0 | if (side == eLogicalSideBStart) { |
1781 | 0 | requiredStartLeading += bsize; |
1782 | 0 | } else { |
1783 | 0 | MOZ_ASSERT(side == eLogicalSideBEnd, |
1784 | 0 | "emphasis marks must be in block axis"); |
1785 | 0 | requiredEndLeading += bsize; |
1786 | 0 | } |
1787 | 0 | } |
1788 | 0 |
|
1789 | 0 | nscoord requiredLeading = requiredStartLeading + requiredEndLeading; |
1790 | 0 | // If we do not require any additional leadings, don't touch anything |
1791 | 0 | // here even if it is greater than the original leading, because the |
1792 | 0 | // latter could be negative. |
1793 | 0 | if (requiredLeading != 0) { |
1794 | 0 | nscoord leading = psd->mBStartLeading + psd->mBEndLeading; |
1795 | 0 | nscoord deltaLeading = requiredLeading - leading; |
1796 | 0 | if (deltaLeading > 0) { |
1797 | 0 | // If the total leading is not wide enough for ruby annotations |
1798 | 0 | // and/or emphasis marks, extend the side which is not enough. If |
1799 | 0 | // both sides are not wide enough, replace the leadings with the |
1800 | 0 | // requested values. |
1801 | 0 | if (requiredStartLeading < psd->mBStartLeading) { |
1802 | 0 | psd->mBEndLeading += deltaLeading; |
1803 | 0 | } else if (requiredEndLeading < psd->mBEndLeading) { |
1804 | 0 | psd->mBStartLeading += deltaLeading; |
1805 | 0 | } else { |
1806 | 0 | psd->mBStartLeading = requiredStartLeading; |
1807 | 0 | psd->mBEndLeading = requiredEndLeading; |
1808 | 0 | } |
1809 | 0 | psd->mLogicalBSize += deltaLeading; |
1810 | 0 | // We have adjusted the leadings, it is no longer a zero |
1811 | 0 | // effective span box. |
1812 | 0 | *aZeroEffectiveSpanBox = false; |
1813 | 0 | } |
1814 | 0 | } |
1815 | 0 | } |
1816 | | |
1817 | | static float |
1818 | | GetInflationForBlockDirAlignment(nsIFrame* aFrame, |
1819 | | nscoord aInflationMinFontSize) |
1820 | 0 | { |
1821 | 0 | if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) { |
1822 | 0 | const nsIFrame* container = |
1823 | 0 | nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText); |
1824 | 0 | NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame"); |
1825 | 0 | return |
1826 | 0 | static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor(); |
1827 | 0 | } |
1828 | 0 | return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize); |
1829 | 0 | } |
1830 | | |
1831 | 0 | #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX |
1832 | 0 | #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN |
1833 | | |
1834 | | // Place frames in the block direction within a given span (CSS property |
1835 | | // vertical-align) Note: this doesn't place frames with vertical-align: |
1836 | | // top or bottom as those have to wait until the entire line box block |
1837 | | // size is known. This is called after the span frame has finished being |
1838 | | // reflowed so that we know its block size. |
1839 | | void |
1840 | | nsLineLayout::VerticalAlignFrames(PerSpanData* psd) |
1841 | 0 | { |
1842 | 0 | // Get parent frame info |
1843 | 0 | PerFrameData* spanFramePFD = psd->mFrame; |
1844 | 0 | nsIFrame* spanFrame = spanFramePFD->mFrame; |
1845 | 0 |
|
1846 | 0 | // Get the parent frame's font for all of the frames in this span |
1847 | 0 | float inflation = |
1848 | 0 | GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize); |
1849 | 0 | RefPtr<nsFontMetrics> fm = |
1850 | 0 | nsLayoutUtils::GetFontMetricsForFrame(spanFrame, inflation); |
1851 | 0 |
|
1852 | 0 | bool preMode = mStyleText->WhiteSpaceIsSignificant(); |
1853 | 0 |
|
1854 | 0 | // See if the span is an empty continuation. It's an empty continuation iff: |
1855 | 0 | // - it has a prev-in-flow |
1856 | 0 | // - it has no next in flow |
1857 | 0 | // - it's zero sized |
1858 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
1859 | 0 | bool emptyContinuation = psd != mRootSpan && |
1860 | 0 | spanFrame->GetPrevInFlow() && !spanFrame->GetNextInFlow() && |
1861 | 0 | spanFramePFD->mBounds.IsZeroSize(); |
1862 | 0 |
|
1863 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1864 | | printf("[%sSpan]", (psd == mRootSpan)?"Root":""); |
1865 | | nsFrame::ListTag(stdout, spanFrame); |
1866 | | printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s", |
1867 | | preMode ? "yes" : "no", |
1868 | | mPresContext->CompatibilityMode() != eCompatibility_NavQuirks ? "yes" : "no", |
1869 | | spanFramePFD->mBounds.ISize(lineWM), |
1870 | | spanFramePFD->mBounds.BSize(lineWM), |
1871 | | emptyContinuation ? "yes" : "no"); |
1872 | | if (psd != mRootSpan) { |
1873 | | printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d", |
1874 | | spanFramePFD->mBorderPadding.Top(lineWM), |
1875 | | spanFramePFD->mBorderPadding.Right(lineWM), |
1876 | | spanFramePFD->mBorderPadding.Bottom(lineWM), |
1877 | | spanFramePFD->mBorderPadding.Left(lineWM), |
1878 | | spanFramePFD->mMargin.Top(lineWM), |
1879 | | spanFramePFD->mMargin.Right(lineWM), |
1880 | | spanFramePFD->mMargin.Bottom(lineWM), |
1881 | | spanFramePFD->mMargin.Left(lineWM)); |
1882 | | } |
1883 | | printf("\n"); |
1884 | | #endif |
1885 | |
|
1886 | 0 | // Compute the span's zeroEffectiveSpanBox flag. What we are trying |
1887 | 0 | // to determine is how we should treat the span: should it act |
1888 | 0 | // "normally" according to css2 or should it effectively |
1889 | 0 | // "disappear". |
1890 | 0 | // |
1891 | 0 | // In general, if the document being processed is in full standards |
1892 | 0 | // mode then it should act normally (with one exception). The |
1893 | 0 | // exception case is when a span is continued and yet the span is |
1894 | 0 | // empty (e.g. compressed whitespace). For this kind of span we treat |
1895 | 0 | // it as if it were not there so that it doesn't impact the |
1896 | 0 | // line block-size. |
1897 | 0 | // |
1898 | 0 | // In almost standards mode or quirks mode, we should sometimes make |
1899 | 0 | // it disappear. The cases that matter are those where the span |
1900 | 0 | // contains no real text elements that would provide an ascent and |
1901 | 0 | // descent and height. However, if css style elements have been |
1902 | 0 | // applied to the span (border/padding/margin) so that it's clear the |
1903 | 0 | // document author is intending css2 behavior then we act as if strict |
1904 | 0 | // mode is set. |
1905 | 0 | // |
1906 | 0 | // This code works correctly for preMode, because a blank line |
1907 | 0 | // in PRE mode is encoded as a text node with a LF in it, since |
1908 | 0 | // text nodes with only whitespace are considered in preMode. |
1909 | 0 | // |
1910 | 0 | // Much of this logic is shared with the various implementations of |
1911 | 0 | // nsIFrame::IsEmpty since they need to duplicate the way it makes |
1912 | 0 | // some lines empty. However, nsIFrame::IsEmpty can't be reused here |
1913 | 0 | // since this code sets zeroEffectiveSpanBox even when there are |
1914 | 0 | // non-empty children. |
1915 | 0 | bool zeroEffectiveSpanBox = false; |
1916 | 0 | // XXXldb If we really have empty continuations, then all these other |
1917 | 0 | // checks don't make sense for them. |
1918 | 0 | // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that |
1919 | 0 | // it agrees with this code. (If it doesn't agree, it probably should.) |
1920 | 0 | if ((emptyContinuation || |
1921 | 0 | mPresContext->CompatibilityMode() != eCompatibility_FullStandards) && |
1922 | 0 | ((psd == mRootSpan) || |
1923 | 0 | (spanFramePFD->mBorderPadding.IsAllZero() && |
1924 | 0 | spanFramePFD->mMargin.IsAllZero()))) { |
1925 | 0 | // This code handles an issue with compatibility with non-css |
1926 | 0 | // conformant browsers. In particular, there are some cases |
1927 | 0 | // where the font-size and line-height for a span must be |
1928 | 0 | // ignored and instead the span must *act* as if it were zero |
1929 | 0 | // sized. In general, if the span contains any non-compressed |
1930 | 0 | // text then we don't use this logic. |
1931 | 0 | // However, this is not propagated outwards, since (in compatibility |
1932 | 0 | // mode) we don't want big line heights for things like |
1933 | 0 | // <p><font size="-1">Text</font></p> |
1934 | 0 |
|
1935 | 0 | // We shouldn't include any whitespace that collapses, unless we're |
1936 | 0 | // preformatted (in which case it shouldn't, but the width=0 test is |
1937 | 0 | // perhaps incorrect). This includes whitespace at the beginning of |
1938 | 0 | // a line and whitespace preceded (?) by other whitespace. |
1939 | 0 | // See bug 134580 and bug 155333. |
1940 | 0 | zeroEffectiveSpanBox = true; |
1941 | 0 | for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
1942 | 0 | if (pfd->mIsTextFrame && |
1943 | 0 | (pfd->mIsNonWhitespaceTextFrame || preMode || |
1944 | 0 | pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) { |
1945 | 0 | zeroEffectiveSpanBox = false; |
1946 | 0 | break; |
1947 | 0 | } |
1948 | 0 | } |
1949 | 0 | } |
1950 | 0 |
|
1951 | 0 | // Setup baselineBCoord, minBCoord, and maxBCoord |
1952 | 0 | nscoord baselineBCoord, minBCoord, maxBCoord; |
1953 | 0 | if (psd == mRootSpan) { |
1954 | 0 | // Use a zero baselineBCoord since we don't yet know where the baseline |
1955 | 0 | // will be (until we know how tall the line is; then we will |
1956 | 0 | // know). In addition, use extreme values for the minBCoord and maxBCoord |
1957 | 0 | // values so that only the child frames will impact their values |
1958 | 0 | // (since these are children of the block, there is no span box to |
1959 | 0 | // provide initial values). |
1960 | 0 | baselineBCoord = 0; |
1961 | 0 | minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
1962 | 0 | maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
1963 | | #ifdef NOISY_BLOCKDIR_ALIGN |
1964 | | printf("[RootSpan]"); |
1965 | | nsFrame::ListTag(stdout, spanFrame); |
1966 | | printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n", |
1967 | | mBStartEdge, mMinLineBSize, |
1968 | | zeroEffectiveSpanBox ? "yes" : "no"); |
1969 | | #endif |
1970 | | } |
1971 | 0 | else { |
1972 | 0 | // Compute the logical block size for this span. The logical block size |
1973 | 0 | // is based on the "line-height" value, not the font-size. Also |
1974 | 0 | // compute the top leading. |
1975 | 0 | float inflation = |
1976 | 0 | GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize); |
1977 | 0 | nscoord logicalBSize = |
1978 | 0 | ReflowInput::CalcLineHeight(spanFrame->GetContent(), |
1979 | 0 | spanFrame->Style(), |
1980 | 0 | spanFrame->PresContext(), |
1981 | 0 | mBlockReflowInput->ComputedHeight(), |
1982 | 0 | inflation); |
1983 | 0 | nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) - |
1984 | 0 | spanFramePFD->mBorderPadding.BStartEnd(lineWM); |
1985 | 0 |
|
1986 | 0 | // Special-case for a ::first-letter frame, set the line height to |
1987 | 0 | // the frame block size if the user has left line-height == normal |
1988 | 0 | const nsStyleText* styleText = spanFrame->StyleText(); |
1989 | 0 | if (spanFramePFD->mIsLetterFrame && |
1990 | 0 | !spanFrame->GetPrevInFlow() && |
1991 | 0 | styleText->mLineHeight.GetUnit() == eStyleUnit_Normal) { |
1992 | 0 | logicalBSize = spanFramePFD->mBounds.BSize(lineWM); |
1993 | 0 | } |
1994 | 0 |
|
1995 | 0 | nscoord leading = logicalBSize - contentBSize; |
1996 | 0 | psd->mBStartLeading = leading / 2; |
1997 | 0 | psd->mBEndLeading = leading - psd->mBStartLeading; |
1998 | 0 | psd->mLogicalBSize = logicalBSize; |
1999 | 0 | AdjustLeadings(spanFrame, psd, styleText, inflation, |
2000 | 0 | &zeroEffectiveSpanBox); |
2001 | 0 |
|
2002 | 0 | if (zeroEffectiveSpanBox) { |
2003 | 0 | // When the span-box is to be ignored, zero out the initial |
2004 | 0 | // values so that the span doesn't impact the final line |
2005 | 0 | // height. The contents of the span can impact the final line |
2006 | 0 | // height. |
2007 | 0 |
|
2008 | 0 | // Note that things are readjusted for this span after its children |
2009 | 0 | // are reflowed |
2010 | 0 | minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
2011 | 0 | maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
2012 | 0 | } |
2013 | 0 | else { |
2014 | 0 |
|
2015 | 0 | // The initial values for the min and max block coord values are in the |
2016 | 0 | // span's coordinate space, and cover the logical block size of the span. |
2017 | 0 | // If there are child frames in this span that stick out of this area |
2018 | 0 | // then the minBCoord and maxBCoord are updated by the amount of logical |
2019 | 0 | // blockSize that is outside this range. |
2020 | 0 | minBCoord = spanFramePFD->mBorderPadding.BStart(lineWM) - |
2021 | 0 | psd->mBStartLeading; |
2022 | 0 | maxBCoord = minBCoord + psd->mLogicalBSize; |
2023 | 0 | } |
2024 | 0 |
|
2025 | 0 | // This is the distance from the top edge of the parents visual |
2026 | 0 | // box to the baseline. The span already computed this for us, |
2027 | 0 | // so just use it. |
2028 | 0 | *psd->mBaseline = baselineBCoord = spanFramePFD->mAscent; |
2029 | 0 |
|
2030 | 0 |
|
2031 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2032 | | printf("[%sSpan]", (psd == mRootSpan)?"Root":""); |
2033 | | nsFrame::ListTag(stdout, spanFrame); |
2034 | | printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n", |
2035 | | baselineBCoord, psd->mLogicalBSize, psd->mBStartLeading, |
2036 | | spanFramePFD->mBounds.BSize(lineWM), |
2037 | | spanFramePFD->mBorderPadding.Top(lineWM), |
2038 | | spanFramePFD->mBorderPadding.Bottom(lineWM), |
2039 | | zeroEffectiveSpanBox ? "yes" : "no"); |
2040 | | #endif |
2041 | | } |
2042 | 0 |
|
2043 | 0 | nscoord maxStartBoxBSize = 0; |
2044 | 0 | nscoord maxEndBoxBSize = 0; |
2045 | 0 | PerFrameData* pfd = psd->mFirstFrame; |
2046 | 0 | while (nullptr != pfd) { |
2047 | 0 | nsIFrame* frame = pfd->mFrame; |
2048 | 0 |
|
2049 | 0 | // sanity check (see bug 105168, non-reproducible crashes from null frame) |
2050 | 0 | NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad"); |
2051 | 0 | if (!frame) { |
2052 | 0 | return; |
2053 | 0 | } |
2054 | 0 | |
2055 | 0 | // Compute the logical block size of the frame |
2056 | 0 | nscoord logicalBSize; |
2057 | 0 | PerSpanData* frameSpan = pfd->mSpan; |
2058 | 0 | if (frameSpan) { |
2059 | 0 | // For span frames the logical-block-size and start-leading were |
2060 | 0 | // pre-computed when the span was reflowed. |
2061 | 0 | logicalBSize = frameSpan->mLogicalBSize; |
2062 | 0 | } |
2063 | 0 | else { |
2064 | 0 | // For other elements the logical block size is the same as the |
2065 | 0 | // frame's block size plus its margins. |
2066 | 0 | logicalBSize = pfd->mBounds.BSize(lineWM) + |
2067 | 0 | pfd->mMargin.BStartEnd(lineWM); |
2068 | 0 | if (logicalBSize < 0 && |
2069 | 0 | mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) { |
2070 | 0 | pfd->mAscent -= logicalBSize; |
2071 | 0 | logicalBSize = 0; |
2072 | 0 | } |
2073 | 0 | } |
2074 | 0 |
|
2075 | 0 | // Get vertical-align property ("vertical-align" is the CSS name for |
2076 | 0 | // block-direction align) |
2077 | 0 | const nsStyleCoord& verticalAlign = frame->StyleDisplay()->mVerticalAlign; |
2078 | 0 | uint8_t verticalAlignEnum = frame->VerticalAlignEnum(); |
2079 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2080 | | printf(" [frame]"); |
2081 | | nsFrame::ListTag(stdout, frame); |
2082 | | printf(": verticalAlignUnit=%d (enum == %d", |
2083 | | verticalAlign.GetUnit(), |
2084 | | ((eStyleUnit_Enumerated == verticalAlign.GetUnit()) |
2085 | | ? verticalAlign.GetIntValue() |
2086 | | : -1)); |
2087 | | if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) { |
2088 | | printf(", after SVG dominant-baseline conversion == %d", |
2089 | | verticalAlignEnum); |
2090 | | } |
2091 | | printf(")\n"); |
2092 | | #endif |
2093 | |
|
2094 | 0 | if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) { |
2095 | 0 | if (lineWM.IsVertical()) { |
2096 | 0 | if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_MIDDLE) { |
2097 | 0 | // For vertical writing mode where the dominant baseline is centered |
2098 | 0 | // (i.e. text-orientation is not sideways-*), we remap 'middle' to |
2099 | 0 | // 'middle-with-baseline' so that images align sensibly with the |
2100 | 0 | // center-baseline-aligned text. |
2101 | 0 | if (!lineWM.IsSideways()) { |
2102 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE; |
2103 | 0 | } |
2104 | 0 | } else if (lineWM.IsLineInverted()) { |
2105 | 0 | // Swap the meanings of top and bottom when line is inverted |
2106 | 0 | // relative to block direction. |
2107 | 0 | switch (verticalAlignEnum) { |
2108 | 0 | case NS_STYLE_VERTICAL_ALIGN_TOP: |
2109 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BOTTOM; |
2110 | 0 | break; |
2111 | 0 | case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
2112 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TOP; |
2113 | 0 | break; |
2114 | 0 | case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: |
2115 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM; |
2116 | 0 | break; |
2117 | 0 | case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: |
2118 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_TOP; |
2119 | 0 | break; |
2120 | 0 | } |
2121 | 0 | } |
2122 | 0 | } |
2123 | 0 |
|
2124 | 0 | // baseline coord that may be adjusted for script offset |
2125 | 0 | nscoord revisedBaselineBCoord = baselineBCoord; |
2126 | 0 |
|
2127 | 0 | // For superscript and subscript, raise or lower the baseline of the box |
2128 | 0 | // to the proper offset of the parent's box, then proceed as for BASELINE |
2129 | 0 | if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB || |
2130 | 0 | verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUPER) { |
2131 | 0 | revisedBaselineBCoord += lineWM.FlowRelativeToLineRelativeFactor() * |
2132 | 0 | (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB |
2133 | 0 | ? fm->SubscriptOffset() : -fm->SuperscriptOffset()); |
2134 | 0 | verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE; |
2135 | 0 | } |
2136 | 0 |
|
2137 | 0 | switch (verticalAlignEnum) { |
2138 | 0 | default: |
2139 | 0 | case NS_STYLE_VERTICAL_ALIGN_BASELINE: |
2140 | 0 | if (lineWM.IsVertical() && !lineWM.IsSideways()) { |
2141 | 0 | if (frameSpan) { |
2142 | 0 | pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - |
2143 | 0 | pfd->mBounds.BSize(lineWM)/2; |
2144 | 0 | } else { |
2145 | 0 | pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - |
2146 | 0 | logicalBSize/2 + |
2147 | 0 | pfd->mMargin.BStart(lineWM); |
2148 | 0 | } |
2149 | 0 | } else { |
2150 | 0 | pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; |
2151 | 0 | } |
2152 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2153 | 0 | break; |
2154 | 0 |
|
2155 | 0 | case NS_STYLE_VERTICAL_ALIGN_TOP: |
2156 | 0 | { |
2157 | 0 | pfd->mBlockDirAlign = VALIGN_TOP; |
2158 | 0 | nscoord subtreeBSize = logicalBSize; |
2159 | 0 | if (frameSpan) { |
2160 | 0 | subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord; |
2161 | 0 | NS_ASSERTION(subtreeBSize >= logicalBSize, |
2162 | 0 | "unexpected subtree block size"); |
2163 | 0 | } |
2164 | 0 | if (subtreeBSize > maxStartBoxBSize) { |
2165 | 0 | maxStartBoxBSize = subtreeBSize; |
2166 | 0 | } |
2167 | 0 | break; |
2168 | 0 | } |
2169 | 0 |
|
2170 | 0 | case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
2171 | 0 | { |
2172 | 0 | pfd->mBlockDirAlign = VALIGN_BOTTOM; |
2173 | 0 | nscoord subtreeBSize = logicalBSize; |
2174 | 0 | if (frameSpan) { |
2175 | 0 | subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord; |
2176 | 0 | NS_ASSERTION(subtreeBSize >= logicalBSize, |
2177 | 0 | "unexpected subtree block size"); |
2178 | 0 | } |
2179 | 0 | if (subtreeBSize > maxEndBoxBSize) { |
2180 | 0 | maxEndBoxBSize = subtreeBSize; |
2181 | 0 | } |
2182 | 0 | break; |
2183 | 0 | } |
2184 | 0 |
|
2185 | 0 | case NS_STYLE_VERTICAL_ALIGN_MIDDLE: |
2186 | 0 | { |
2187 | 0 | // Align the midpoint of the frame with 1/2 the parents |
2188 | 0 | // x-height above the baseline. |
2189 | 0 | nscoord parentXHeight = |
2190 | 0 | lineWM.FlowRelativeToLineRelativeFactor() * fm->XHeight(); |
2191 | 0 | if (frameSpan) { |
2192 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - |
2193 | 0 | (parentXHeight + pfd->mBounds.BSize(lineWM))/2; |
2194 | 0 | } |
2195 | 0 | else { |
2196 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - |
2197 | 0 | (parentXHeight + logicalBSize)/2 + |
2198 | 0 | pfd->mMargin.BStart(lineWM); |
2199 | 0 | } |
2200 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2201 | 0 | break; |
2202 | 0 | } |
2203 | 0 |
|
2204 | 0 | case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: |
2205 | 0 | { |
2206 | 0 | // The top of the logical box is aligned with the top of |
2207 | 0 | // the parent element's text. |
2208 | 0 | // XXX For vertical text we will need a new API to get the logical |
2209 | 0 | // max-ascent here |
2210 | 0 | nscoord parentAscent = |
2211 | 0 | lineWM.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent(); |
2212 | 0 | if (frameSpan) { |
2213 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent - |
2214 | 0 | pfd->mBorderPadding.BStart(lineWM) + frameSpan->mBStartLeading; |
2215 | 0 | } |
2216 | 0 | else { |
2217 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent + |
2218 | 0 | pfd->mMargin.BStart(lineWM); |
2219 | 0 | } |
2220 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2221 | 0 | break; |
2222 | 0 | } |
2223 | 0 |
|
2224 | 0 | case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: |
2225 | 0 | { |
2226 | 0 | // The bottom of the logical box is aligned with the |
2227 | 0 | // bottom of the parent elements text. |
2228 | 0 | nscoord parentDescent = |
2229 | 0 | lineWM.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent(); |
2230 | 0 | if (frameSpan) { |
2231 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent - |
2232 | 0 | pfd->mBounds.BSize(lineWM) + |
2233 | 0 | pfd->mBorderPadding.BEnd(lineWM) - |
2234 | 0 | frameSpan->mBEndLeading; |
2235 | 0 | } |
2236 | 0 | else { |
2237 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent - |
2238 | 0 | pfd->mBounds.BSize(lineWM) - |
2239 | 0 | pfd->mMargin.BEnd(lineWM); |
2240 | 0 | } |
2241 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2242 | 0 | break; |
2243 | 0 | } |
2244 | 0 |
|
2245 | 0 | case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: |
2246 | 0 | { |
2247 | 0 | // Align the midpoint of the frame with the baseline of the parent. |
2248 | 0 | if (frameSpan) { |
2249 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - |
2250 | 0 | pfd->mBounds.BSize(lineWM)/2; |
2251 | 0 | } |
2252 | 0 | else { |
2253 | 0 | pfd->mBounds.BStart(lineWM) = baselineBCoord - logicalBSize/2 + |
2254 | 0 | pfd->mMargin.BStart(lineWM); |
2255 | 0 | } |
2256 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2257 | 0 | break; |
2258 | 0 | } |
2259 | 0 | } |
2260 | 0 | } else { |
2261 | 0 | // We have either a coord, a percent, or a calc(). |
2262 | 0 | nscoord pctBasis = 0; |
2263 | 0 | if (verticalAlign.HasPercent()) { |
2264 | 0 | // Percentages are like lengths, except treated as a percentage |
2265 | 0 | // of the elements line block size value. |
2266 | 0 | float inflation = |
2267 | 0 | GetInflationForBlockDirAlignment(frame, mInflationMinFontSize); |
2268 | 0 | pctBasis = ReflowInput::CalcLineHeight( |
2269 | 0 | frame->GetContent(), |
2270 | 0 | frame->Style(), |
2271 | 0 | frame->PresContext(), |
2272 | 0 | mBlockReflowInput->ComputedBSize(), |
2273 | 0 | inflation); |
2274 | 0 | } |
2275 | 0 | nscoord offset = verticalAlign.ComputeCoordPercentCalc(pctBasis); |
2276 | 0 | // According to the CSS2 spec (10.8.1), a positive value |
2277 | 0 | // "raises" the box by the given distance while a negative value |
2278 | 0 | // "lowers" the box by the given distance (with zero being the |
2279 | 0 | // baseline). Since Y coordinates increase towards the bottom of |
2280 | 0 | // the screen we reverse the sign, unless the line orientation is |
2281 | 0 | // inverted relative to block direction. |
2282 | 0 | nscoord revisedBaselineBCoord = baselineBCoord - offset * |
2283 | 0 | lineWM.FlowRelativeToLineRelativeFactor(); |
2284 | 0 | if (lineWM.IsVertical() && !lineWM.IsSideways()) { |
2285 | 0 | // If we're using a dominant center baseline, we align with the center |
2286 | 0 | // of the frame being placed (bug 1133945). |
2287 | 0 | pfd->mBounds.BStart(lineWM) = |
2288 | 0 | revisedBaselineBCoord - pfd->mBounds.BSize(lineWM)/2; |
2289 | 0 | } else { |
2290 | 0 | pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; |
2291 | 0 | } |
2292 | 0 | pfd->mBlockDirAlign = VALIGN_OTHER; |
2293 | 0 | } |
2294 | 0 |
|
2295 | 0 | // Update minBCoord/maxBCoord for frames that we just placed. Do not factor |
2296 | 0 | // text into the equation. |
2297 | 0 | if (pfd->mBlockDirAlign == VALIGN_OTHER) { |
2298 | 0 | // Text frames do not contribute to the min/max Y values for the |
2299 | 0 | // line (instead their parent frame's font-size contributes). |
2300 | 0 | // XXXrbs -- relax this restriction because it causes text frames |
2301 | 0 | // to jam together when 'font-size-adjust' is enabled |
2302 | 0 | // and layout is using dynamic font heights (bug 20394) |
2303 | 0 | // -- Note #1: With this code enabled and with the fact that we are not |
2304 | 0 | // using Em[Ascent|Descent] as nsDimensions for text metrics in |
2305 | 0 | // GFX mean that the discussion in bug 13072 cannot hold. |
2306 | 0 | // -- Note #2: We still don't want empty-text frames to interfere. |
2307 | 0 | // For example in quirks mode, avoiding empty text frames prevents |
2308 | 0 | // "tall" lines around elements like <hr> since the rules of <hr> |
2309 | 0 | // in quirks.css have pseudo text contents with LF in them. |
2310 | 0 | bool canUpdate; |
2311 | 0 | if (pfd->mIsTextFrame) { |
2312 | 0 | // Only consider text frames if they're not empty and |
2313 | 0 | // line-height=normal. |
2314 | 0 | canUpdate = pfd->mIsNonWhitespaceTextFrame && |
2315 | 0 | frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal; |
2316 | 0 | } else { |
2317 | 0 | canUpdate = !pfd->mIsPlaceholder; |
2318 | 0 | } |
2319 | 0 |
|
2320 | 0 | if (canUpdate) { |
2321 | 0 | nscoord blockStart, blockEnd; |
2322 | 0 | if (frameSpan) { |
2323 | 0 | // For spans that were are now placing, use their position |
2324 | 0 | // plus their already computed min-Y and max-Y values for |
2325 | 0 | // computing blockStart and blockEnd. |
2326 | 0 | blockStart = pfd->mBounds.BStart(lineWM) + frameSpan->mMinBCoord; |
2327 | 0 | blockEnd = pfd->mBounds.BStart(lineWM) + frameSpan->mMaxBCoord; |
2328 | 0 | } |
2329 | 0 | else { |
2330 | 0 | blockStart = pfd->mBounds.BStart(lineWM) - |
2331 | 0 | pfd->mMargin.BStart(lineWM); |
2332 | 0 | blockEnd = blockStart + logicalBSize; |
2333 | 0 | } |
2334 | 0 | if (!preMode && |
2335 | 0 | mPresContext->CompatibilityMode() != eCompatibility_FullStandards && |
2336 | 0 | !logicalBSize) { |
2337 | 0 | // Check if it's a BR frame that is not alone on its line (it |
2338 | 0 | // is given a block size of zero to indicate this), and if so reset |
2339 | 0 | // blockStart and blockEnd so that BR frames don't influence the line. |
2340 | 0 | if (frame->IsBrFrame()) { |
2341 | 0 | blockStart = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
2342 | 0 | blockEnd = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
2343 | 0 | } |
2344 | 0 | } |
2345 | 0 | if (blockStart < minBCoord) minBCoord = blockStart; |
2346 | 0 | if (blockEnd > maxBCoord) maxBCoord = blockEnd; |
2347 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2348 | | printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n", |
2349 | | pfd->mAscent, pfd->mBounds.BSize(lineWM), |
2350 | | pfd->mBorderPadding.Top(lineWM), |
2351 | | pfd->mBorderPadding.Bottom(lineWM), |
2352 | | logicalBSize, |
2353 | | frameSpan ? frameSpan->mBStartLeading : 0, |
2354 | | pfd->mBounds.BStart(lineWM), minBCoord, maxBCoord); |
2355 | | #endif |
2356 | | } |
2357 | 0 | if (psd != mRootSpan) { |
2358 | 0 | frame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd)); |
2359 | 0 | } |
2360 | 0 | } |
2361 | 0 | pfd = pfd->mNext; |
2362 | 0 | } |
2363 | 0 |
|
2364 | 0 | // Factor in the minimum line block-size when handling the root-span for |
2365 | 0 | // the block. |
2366 | 0 | if (psd == mRootSpan) { |
2367 | 0 | // We should factor in the block element's minimum line-height (as |
2368 | 0 | // defined in section 10.8.1 of the css2 spec) assuming that |
2369 | 0 | // zeroEffectiveSpanBox is not set on the root span. This only happens |
2370 | 0 | // in some cases in quirks mode: |
2371 | 0 | // (1) if the root span contains non-whitespace text directly (this |
2372 | 0 | // is handled by zeroEffectiveSpanBox |
2373 | 0 | // (2) if this line has a bullet |
2374 | 0 | // (3) if this is the last line of an LI, DT, or DD element |
2375 | 0 | // (The last line before a block also counts, but not before a |
2376 | 0 | // BR) (NN4/IE5 quirk) |
2377 | 0 |
|
2378 | 0 | // (1) and (2) above |
2379 | 0 | bool applyMinLH = !zeroEffectiveSpanBox || mHasBullet; |
2380 | 0 | bool isLastLine = !mGotLineBox || |
2381 | 0 | (!mLineBox->IsLineWrapped() && !mLineEndsInBR); |
2382 | 0 | if (!applyMinLH && isLastLine) { |
2383 | 0 | nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent(); |
2384 | 0 | if (blockContent) { |
2385 | 0 | // (3) above, if the last line of LI, DT, or DD |
2386 | 0 | if (blockContent->IsAnyOfHTMLElements(nsGkAtoms::li, |
2387 | 0 | nsGkAtoms::dt, |
2388 | 0 | nsGkAtoms::dd)) { |
2389 | 0 | applyMinLH = true; |
2390 | 0 | } |
2391 | 0 | } |
2392 | 0 | } |
2393 | 0 | if (applyMinLH) { |
2394 | 0 | if (psd->mHasNonemptyContent || preMode || mHasBullet) { |
2395 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2396 | | printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord, maxBCoord); |
2397 | | #endif |
2398 | | nscoord minimumLineBSize = mMinLineBSize; |
2399 | 0 | nscoord blockStart = |
2400 | 0 | -nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize, |
2401 | 0 | lineWM.IsLineInverted()); |
2402 | 0 | nscoord blockEnd = blockStart + minimumLineBSize; |
2403 | 0 |
|
2404 | 0 | if (mStyleText->HasTextEmphasis()) { |
2405 | 0 | nscoord fontMaxHeight = fm->MaxHeight(); |
2406 | 0 | nscoord emphasisHeight = |
2407 | 0 | GetBSizeOfEmphasisMarks(spanFrame, inflation); |
2408 | 0 | nscoord delta = fontMaxHeight + emphasisHeight - minimumLineBSize; |
2409 | 0 | if (delta > 0) { |
2410 | 0 | if (minimumLineBSize < fontMaxHeight) { |
2411 | 0 | // If the leadings are negative, fill them first. |
2412 | 0 | nscoord ascent = fm->MaxAscent(); |
2413 | 0 | nscoord descent = fm->MaxDescent(); |
2414 | 0 | if (lineWM.IsLineInverted()) { |
2415 | 0 | Swap(ascent, descent); |
2416 | 0 | } |
2417 | 0 | blockStart = -ascent; |
2418 | 0 | blockEnd = descent; |
2419 | 0 | delta = emphasisHeight; |
2420 | 0 | } |
2421 | 0 | LogicalSide side = mStyleText->TextEmphasisSide(lineWM); |
2422 | 0 | if (side == eLogicalSideBStart) { |
2423 | 0 | blockStart -= delta; |
2424 | 0 | } else { |
2425 | 0 | blockEnd += delta; |
2426 | 0 | } |
2427 | 0 | } |
2428 | 0 | } |
2429 | 0 |
|
2430 | 0 | if (blockStart < minBCoord) minBCoord = blockStart; |
2431 | 0 | if (blockEnd > maxBCoord) maxBCoord = blockEnd; |
2432 | 0 |
|
2433 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2434 | | printf(" new values: %d,%d\n", minBCoord, maxBCoord); |
2435 | | #endif |
2436 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2437 | | printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize, blockStart, blockEnd); |
2438 | | #endif |
2439 | | } |
2440 | 0 | else { |
2441 | 0 | // XXX issues: |
2442 | 0 | // [1] BR's on empty lines stop working |
2443 | 0 | // [2] May not honor css2's notion of handling empty elements |
2444 | 0 | // [3] blank lines in a pre-section ("\n") (handled with preMode) |
2445 | 0 |
|
2446 | 0 | // XXX Are there other problems with this? |
2447 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2448 | | printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n", |
2449 | | minBCoord, maxBCoord); |
2450 | | #endif |
2451 | | minBCoord = maxBCoord = 0; |
2452 | 0 | } |
2453 | 0 | } |
2454 | 0 | } |
2455 | 0 |
|
2456 | 0 | if ((minBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM) || |
2457 | 0 | (maxBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM)) { |
2458 | 0 | minBCoord = maxBCoord = baselineBCoord; |
2459 | 0 | } |
2460 | 0 |
|
2461 | 0 | if (psd != mRootSpan && zeroEffectiveSpanBox) { |
2462 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2463 | | printf(" [span]adjusting for zeroEffectiveSpanBox\n"); |
2464 | | printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n", |
2465 | | minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM), |
2466 | | spanFramePFD->mAscent, |
2467 | | psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading); |
2468 | | #endif |
2469 | | nscoord goodMinBCoord = |
2470 | 0 | spanFramePFD->mBorderPadding.BStart(lineWM) - psd->mBStartLeading; |
2471 | 0 | nscoord goodMaxBCoord = goodMinBCoord + psd->mLogicalBSize; |
2472 | 0 |
|
2473 | 0 | // For cases like the one in bug 714519 (text-decoration placement |
2474 | 0 | // or making nsLineLayout::IsZeroBSize() handle |
2475 | 0 | // vertical-align:top/bottom on a descendant of the line that's not |
2476 | 0 | // a child of it), we want to treat elements that are |
2477 | 0 | // vertical-align: top or bottom somewhat like children for the |
2478 | 0 | // purposes of this quirk. To some extent, this is guessing, since |
2479 | 0 | // they might end up being aligned anywhere. However, we'll guess |
2480 | 0 | // that they'll be placed aligned with the top or bottom of this |
2481 | 0 | // frame (as though this frame is the only thing in the line). |
2482 | 0 | // (Guessing isn't crazy, since all we're doing is reducing the |
2483 | 0 | // scope of a quirk and making the behavior more standards-like.) |
2484 | 0 | if (maxStartBoxBSize > maxBCoord - minBCoord) { |
2485 | 0 | // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and |
2486 | 0 | // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or |
2487 | 0 | // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord. |
2488 | 0 | nscoord distribute = maxStartBoxBSize - (maxBCoord - minBCoord); |
2489 | 0 | nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0); |
2490 | 0 | if (distribute > ascentSpace) { |
2491 | 0 | distribute -= ascentSpace; |
2492 | 0 | minBCoord -= ascentSpace; |
2493 | 0 | nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0); |
2494 | 0 | if (distribute > descentSpace) { |
2495 | 0 | maxBCoord += descentSpace; |
2496 | 0 | } else { |
2497 | 0 | maxBCoord += distribute; |
2498 | 0 | } |
2499 | 0 | } else { |
2500 | 0 | minBCoord -= distribute; |
2501 | 0 | } |
2502 | 0 | } |
2503 | 0 | if (maxEndBoxBSize > maxBCoord - minBCoord) { |
2504 | 0 | // Likewise, but preferring descent to ascent. |
2505 | 0 | nscoord distribute = maxEndBoxBSize - (maxBCoord - minBCoord); |
2506 | 0 | nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0); |
2507 | 0 | if (distribute > descentSpace) { |
2508 | 0 | distribute -= descentSpace; |
2509 | 0 | maxBCoord += descentSpace; |
2510 | 0 | nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0); |
2511 | 0 | if (distribute > ascentSpace) { |
2512 | 0 | minBCoord -= ascentSpace; |
2513 | 0 | } else { |
2514 | 0 | minBCoord -= distribute; |
2515 | 0 | } |
2516 | 0 | } else { |
2517 | 0 | maxBCoord += distribute; |
2518 | 0 | } |
2519 | 0 | } |
2520 | 0 |
|
2521 | 0 | if (minBCoord > goodMinBCoord) { |
2522 | 0 | nscoord adjust = minBCoord - goodMinBCoord; // positive |
2523 | 0 |
|
2524 | 0 | // shrink the logical extents |
2525 | 0 | psd->mLogicalBSize -= adjust; |
2526 | 0 | psd->mBStartLeading -= adjust; |
2527 | 0 | } |
2528 | 0 | if (maxBCoord < goodMaxBCoord) { |
2529 | 0 | nscoord adjust = goodMaxBCoord - maxBCoord; |
2530 | 0 | psd->mLogicalBSize -= adjust; |
2531 | 0 | psd->mBEndLeading -= adjust; |
2532 | 0 | } |
2533 | 0 | if (minBCoord > 0) { |
2534 | 0 |
|
2535 | 0 | // shrink the content by moving its block start down. This is tricky, |
2536 | 0 | // since the block start is the 0 for many coordinates, so what we do is |
2537 | 0 | // move everything else up. |
2538 | 0 | spanFramePFD->mAscent -= minBCoord; // move the baseline up |
2539 | 0 | spanFramePFD->mBounds.BSize(lineWM) -= minBCoord; // move the block end up |
2540 | 0 | psd->mBStartLeading += minBCoord; |
2541 | 0 | *psd->mBaseline -= minBCoord; |
2542 | 0 |
|
2543 | 0 | pfd = psd->mFirstFrame; |
2544 | 0 | while (nullptr != pfd) { |
2545 | 0 | pfd->mBounds.BStart(lineWM) -= minBCoord; // move all the children |
2546 | 0 | // back up |
2547 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd)); |
2548 | 0 | pfd = pfd->mNext; |
2549 | 0 | } |
2550 | 0 | maxBCoord -= minBCoord; // since minBCoord is in the frame's own |
2551 | 0 | // coordinate system |
2552 | 0 | minBCoord = 0; |
2553 | 0 | } |
2554 | 0 | if (maxBCoord < spanFramePFD->mBounds.BSize(lineWM)) { |
2555 | 0 | nscoord adjust = spanFramePFD->mBounds.BSize(lineWM) - maxBCoord; |
2556 | 0 | spanFramePFD->mBounds.BSize(lineWM) -= adjust; // move the bottom up |
2557 | 0 | psd->mBEndLeading += adjust; |
2558 | 0 | } |
2559 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2560 | | printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n", |
2561 | | minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM), |
2562 | | spanFramePFD->mAscent, |
2563 | | psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading); |
2564 | | #endif |
2565 | | } |
2566 | 0 |
|
2567 | 0 | psd->mMinBCoord = minBCoord; |
2568 | 0 | psd->mMaxBCoord = maxBCoord; |
2569 | | #ifdef NOISY_BLOCKDIR_ALIGN |
2570 | | printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n", |
2571 | | minBCoord, maxBCoord, maxBCoord - minBCoord, maxStartBoxBSize, maxEndBoxBSize); |
2572 | | #endif |
2573 | 0 | if (maxStartBoxBSize > mMaxStartBoxBSize) { |
2574 | 0 | mMaxStartBoxBSize = maxStartBoxBSize; |
2575 | 0 | } |
2576 | 0 | if (maxEndBoxBSize > mMaxEndBoxBSize) { |
2577 | 0 | mMaxEndBoxBSize = maxEndBoxBSize; |
2578 | 0 | } |
2579 | 0 | } |
2580 | | |
2581 | | static void SlideSpanFrameRect(nsIFrame* aFrame, nscoord aDeltaWidth) |
2582 | 0 | { |
2583 | 0 | // This should not use nsIFrame::MovePositionBy because it happens |
2584 | 0 | // prior to relative positioning. In particular, because |
2585 | 0 | // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace() |
2586 | 0 | // prior to calling aLineLayout.RelativePositionFrames(). |
2587 | 0 | nsPoint p = aFrame->GetPosition(); |
2588 | 0 | p.x -= aDeltaWidth; |
2589 | 0 | aFrame->SetPosition(p); |
2590 | 0 | } |
2591 | | |
2592 | | bool |
2593 | | nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, |
2594 | | nscoord* aDeltaISize) |
2595 | 0 | { |
2596 | 0 | PerFrameData* pfd = psd->mFirstFrame; |
2597 | 0 | if (!pfd) { |
2598 | 0 | *aDeltaISize = 0; |
2599 | 0 | return false; |
2600 | 0 | } |
2601 | 0 | pfd = pfd->Last(); |
2602 | 0 | while (nullptr != pfd) { |
2603 | | #ifdef REALLY_NOISY_TRIM |
2604 | | nsFrame::ListTag(stdout, psd->mFrame->mFrame); |
2605 | | printf(": attempting trim of "); |
2606 | | nsFrame::ListTag(stdout, pfd->mFrame); |
2607 | | printf("\n"); |
2608 | | #endif |
2609 | | PerSpanData* childSpan = pfd->mSpan; |
2610 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
2611 | 0 | if (childSpan) { |
2612 | 0 | // Maybe the child span has the trailing white-space in it? |
2613 | 0 | if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaISize)) { |
2614 | 0 | nscoord deltaISize = *aDeltaISize; |
2615 | 0 | if (deltaISize) { |
2616 | 0 | // Adjust the child spans frame size |
2617 | 0 | pfd->mBounds.ISize(lineWM) -= deltaISize; |
2618 | 0 | if (psd != mRootSpan) { |
2619 | 0 | // When the child span is not a direct child of the block |
2620 | 0 | // we need to update the child spans frame rectangle |
2621 | 0 | // because it most likely will not be done again. Spans |
2622 | 0 | // that are direct children of the block will be updated |
2623 | 0 | // later, however, because the VerticalAlignFrames method |
2624 | 0 | // will be run after this method. |
2625 | 0 | nsSize containerSize = ContainerSizeForSpan(childSpan); |
2626 | 0 | nsIFrame* f = pfd->mFrame; |
2627 | 0 | LogicalRect r(lineWM, f->GetRect(), containerSize); |
2628 | 0 | r.ISize(lineWM) -= deltaISize; |
2629 | 0 | f->SetRect(lineWM, r, containerSize); |
2630 | 0 | } |
2631 | 0 |
|
2632 | 0 | // Adjust the inline end edge of the span that contains the child span |
2633 | 0 | psd->mICoord -= deltaISize; |
2634 | 0 |
|
2635 | 0 | // Slide any frames that follow the child span over by the |
2636 | 0 | // correct amount. The only thing that can follow the child |
2637 | 0 | // span is empty stuff, so we are just making things |
2638 | 0 | // sensible (keeping the combined area honest). |
2639 | 0 | while (pfd->mNext) { |
2640 | 0 | pfd = pfd->mNext; |
2641 | 0 | pfd->mBounds.IStart(lineWM) -= deltaISize; |
2642 | 0 | if (psd != mRootSpan) { |
2643 | 0 | // When the child span is not a direct child of the block |
2644 | 0 | // we need to update the child span's frame rectangle |
2645 | 0 | // because it most likely will not be done again. Spans |
2646 | 0 | // that are direct children of the block will be updated |
2647 | 0 | // later, however, because the VerticalAlignFrames method |
2648 | 0 | // will be run after this method. |
2649 | 0 | SlideSpanFrameRect(pfd->mFrame, deltaISize); |
2650 | 0 | } |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | return true; |
2654 | 0 | } |
2655 | 0 | } |
2656 | 0 | else if (!pfd->mIsTextFrame && !pfd->mSkipWhenTrimmingWhitespace) { |
2657 | 0 | // If we hit a frame on the end that's not text and not a placeholder, |
2658 | 0 | // then there is no trailing whitespace to trim. Stop the search. |
2659 | 0 | *aDeltaISize = 0; |
2660 | 0 | return true; |
2661 | 0 | } |
2662 | 0 | else if (pfd->mIsTextFrame) { |
2663 | 0 | // Call TrimTrailingWhiteSpace even on empty textframes because they |
2664 | 0 | // might have a soft hyphen which should now appear, changing the frame's |
2665 | 0 | // width |
2666 | 0 | nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)-> |
2667 | 0 | TrimTrailingWhiteSpace(mBlockReflowInput->mRenderingContext->GetDrawTarget()); |
2668 | | #ifdef NOISY_TRIM |
2669 | | nsFrame::ListTag(stdout, psd->mFrame->mFrame); |
2670 | | printf(": trim of "); |
2671 | | nsFrame::ListTag(stdout, pfd->mFrame); |
2672 | | printf(" returned %d\n", trimOutput.mDeltaWidth); |
2673 | | #endif |
2674 | |
|
2675 | 0 | if (trimOutput.mChanged) { |
2676 | 0 | pfd->mRecomputeOverflow = true; |
2677 | 0 | } |
2678 | 0 |
|
2679 | 0 | // Delta width not being zero means that |
2680 | 0 | // there is trimmed space in the frame. |
2681 | 0 | if (trimOutput.mDeltaWidth) { |
2682 | 0 | pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth; |
2683 | 0 |
|
2684 | 0 | // If any trailing space is trimmed, the justification opportunity |
2685 | 0 | // generated by the space should be removed as well. |
2686 | 0 | pfd->mJustificationInfo.CancelOpportunityForTrimmedSpace(); |
2687 | 0 |
|
2688 | 0 | // See if the text frame has already been placed in its parent |
2689 | 0 | if (psd != mRootSpan) { |
2690 | 0 | // The frame was already placed during psd's |
2691 | 0 | // reflow. Update the frames rectangle now. |
2692 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, |
2693 | 0 | ContainerSizeForSpan(psd)); |
2694 | 0 | } |
2695 | 0 |
|
2696 | 0 | // Adjust containing span's right edge |
2697 | 0 | psd->mICoord -= trimOutput.mDeltaWidth; |
2698 | 0 |
|
2699 | 0 | // Slide any frames that follow the text frame over by the |
2700 | 0 | // right amount. The only thing that can follow the text |
2701 | 0 | // frame is empty stuff, so we are just making things |
2702 | 0 | // sensible (keeping the combined area honest). |
2703 | 0 | while (pfd->mNext) { |
2704 | 0 | pfd = pfd->mNext; |
2705 | 0 | pfd->mBounds.IStart(lineWM) -= trimOutput.mDeltaWidth; |
2706 | 0 | if (psd != mRootSpan) { |
2707 | 0 | // When the child span is not a direct child of the block |
2708 | 0 | // we need to update the child spans frame rectangle |
2709 | 0 | // because it most likely will not be done again. Spans |
2710 | 0 | // that are direct children of the block will be updated |
2711 | 0 | // later, however, because the VerticalAlignFrames method |
2712 | 0 | // will be run after this method. |
2713 | 0 | SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth); |
2714 | 0 | } |
2715 | 0 | } |
2716 | 0 | } |
2717 | 0 |
|
2718 | 0 | if (pfd->mIsNonEmptyTextFrame || trimOutput.mChanged) { |
2719 | 0 | // Pass up to caller so they can shrink their span |
2720 | 0 | *aDeltaISize = trimOutput.mDeltaWidth; |
2721 | 0 | return true; |
2722 | 0 | } |
2723 | 0 | } |
2724 | 0 | pfd = pfd->mPrev; |
2725 | 0 | } |
2726 | 0 |
|
2727 | 0 | *aDeltaISize = 0; |
2728 | 0 | return false; |
2729 | 0 | } |
2730 | | |
2731 | | bool |
2732 | | nsLineLayout::TrimTrailingWhiteSpace() |
2733 | 0 | { |
2734 | 0 | PerSpanData* psd = mRootSpan; |
2735 | 0 | nscoord deltaISize; |
2736 | 0 | TrimTrailingWhiteSpaceIn(psd, &deltaISize); |
2737 | 0 | return 0 != deltaISize; |
2738 | 0 | } |
2739 | | |
2740 | | bool |
2741 | | nsLineLayout::PerFrameData::ParticipatesInJustification() const |
2742 | 0 | { |
2743 | 0 | if (mIsBullet || mIsEmpty || mSkipWhenTrimmingWhitespace) { |
2744 | 0 | // Skip bullets, empty frames, and placeholders |
2745 | 0 | return false; |
2746 | 0 | } |
2747 | 0 | if (mIsTextFrame && !mIsNonWhitespaceTextFrame && |
2748 | 0 | static_cast<nsTextFrame*>(mFrame)->IsAtEndOfLine()) { |
2749 | 0 | // Skip trimmed whitespaces |
2750 | 0 | return false; |
2751 | 0 | } |
2752 | 0 | return true; |
2753 | 0 | } |
2754 | | |
2755 | | struct nsLineLayout::JustificationComputationState |
2756 | | { |
2757 | | PerFrameData* mFirstParticipant; |
2758 | | PerFrameData* mLastParticipant; |
2759 | | // When we are going across a boundary of ruby base, i.e. entering |
2760 | | // one, leaving one, or both, the following fields will be set to |
2761 | | // the corresponding ruby base frame for handling ruby-align. |
2762 | | PerFrameData* mLastExitedRubyBase; |
2763 | | PerFrameData* mLastEnteredRubyBase; |
2764 | | |
2765 | | JustificationComputationState() |
2766 | | : mFirstParticipant(nullptr) |
2767 | | , mLastParticipant(nullptr) |
2768 | | , mLastExitedRubyBase(nullptr) |
2769 | 0 | , mLastEnteredRubyBase(nullptr) { } |
2770 | | }; |
2771 | | |
2772 | | static bool |
2773 | | IsRubyAlignSpaceAround(nsIFrame* aRubyBase) |
2774 | 0 | { |
2775 | 0 | return aRubyBase->StyleText()->mRubyAlign == NS_STYLE_RUBY_ALIGN_SPACE_AROUND; |
2776 | 0 | } |
2777 | | |
2778 | | /** |
2779 | | * Assign justification gaps for justification |
2780 | | * opportunities across two frames. |
2781 | | */ |
2782 | | /* static */ int |
2783 | | nsLineLayout::AssignInterframeJustificationGaps( |
2784 | | PerFrameData* aFrame, JustificationComputationState& aState) |
2785 | 0 | { |
2786 | 0 | PerFrameData* prev = aState.mLastParticipant; |
2787 | 0 | MOZ_ASSERT(prev); |
2788 | 0 |
|
2789 | 0 | auto& assign = aFrame->mJustificationAssignment; |
2790 | 0 | auto& prevAssign = prev->mJustificationAssignment; |
2791 | 0 |
|
2792 | 0 | if (aState.mLastExitedRubyBase || aState.mLastEnteredRubyBase) { |
2793 | 0 | PerFrameData* exitedRubyBase = aState.mLastExitedRubyBase; |
2794 | 0 | if (!exitedRubyBase || IsRubyAlignSpaceAround(exitedRubyBase->mFrame)) { |
2795 | 0 | prevAssign.mGapsAtEnd = 1; |
2796 | 0 | } else { |
2797 | 0 | exitedRubyBase->mJustificationAssignment.mGapsAtEnd = 1; |
2798 | 0 | } |
2799 | 0 |
|
2800 | 0 | PerFrameData* enteredRubyBase = aState.mLastEnteredRubyBase; |
2801 | 0 | if (!enteredRubyBase || IsRubyAlignSpaceAround(enteredRubyBase->mFrame)) { |
2802 | 0 | assign.mGapsAtStart = 1; |
2803 | 0 | } else { |
2804 | 0 | enteredRubyBase->mJustificationAssignment.mGapsAtStart = 1; |
2805 | 0 | } |
2806 | 0 |
|
2807 | 0 | // We are no longer going across a ruby base boundary. |
2808 | 0 | aState.mLastExitedRubyBase = nullptr; |
2809 | 0 | aState.mLastEnteredRubyBase = nullptr; |
2810 | 0 | return 1; |
2811 | 0 | } |
2812 | 0 |
|
2813 | 0 | const auto& info = aFrame->mJustificationInfo; |
2814 | 0 | const auto& prevInfo = prev->mJustificationInfo; |
2815 | 0 | if (!info.mIsStartJustifiable && !prevInfo.mIsEndJustifiable) { |
2816 | 0 | return 0; |
2817 | 0 | } |
2818 | 0 | |
2819 | 0 | if (!info.mIsStartJustifiable) { |
2820 | 0 | prevAssign.mGapsAtEnd = 2; |
2821 | 0 | assign.mGapsAtStart = 0; |
2822 | 0 | } else if (!prevInfo.mIsEndJustifiable) { |
2823 | 0 | prevAssign.mGapsAtEnd = 0; |
2824 | 0 | assign.mGapsAtStart = 2; |
2825 | 0 | } else { |
2826 | 0 | prevAssign.mGapsAtEnd = 1; |
2827 | 0 | assign.mGapsAtStart = 1; |
2828 | 0 | } |
2829 | 0 | return 1; |
2830 | 0 | } |
2831 | | |
2832 | | /** |
2833 | | * Compute the justification info of the given span, and store the |
2834 | | * number of inner opportunities into the frame's justification info. |
2835 | | * It returns the number of non-inner opportunities it detects. |
2836 | | */ |
2837 | | int32_t |
2838 | | nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD, |
2839 | | JustificationComputationState& aState) |
2840 | 0 | { |
2841 | 0 | NS_ASSERTION(aPSD, "null arg"); |
2842 | 0 | NS_ASSERTION(!aState.mLastParticipant || !aState.mLastParticipant->mSpan, |
2843 | 0 | "Last participant shall always be a leaf frame"); |
2844 | 0 | bool firstChild = true; |
2845 | 0 | int32_t& innerOpportunities = |
2846 | 0 | aPSD->mFrame->mJustificationInfo.mInnerOpportunities; |
2847 | 0 | MOZ_ASSERT(innerOpportunities == 0, |
2848 | 0 | "Justification info should not have been set yet."); |
2849 | 0 | int32_t outerOpportunities = 0; |
2850 | 0 |
|
2851 | 0 | for (PerFrameData* pfd = aPSD->mFirstFrame; pfd; pfd = pfd->mNext) { |
2852 | 0 | if (!pfd->ParticipatesInJustification()) { |
2853 | 0 | continue; |
2854 | 0 | } |
2855 | 0 | |
2856 | 0 | bool isRubyBase = pfd->mFrame->IsRubyBaseFrame(); |
2857 | 0 | PerFrameData* outerRubyBase = aState.mLastEnteredRubyBase; |
2858 | 0 | if (isRubyBase) { |
2859 | 0 | aState.mLastEnteredRubyBase = pfd; |
2860 | 0 | } |
2861 | 0 |
|
2862 | 0 | int extraOpportunities = 0; |
2863 | 0 | if (pfd->mSpan) { |
2864 | 0 | PerSpanData* span = pfd->mSpan; |
2865 | 0 | extraOpportunities = ComputeFrameJustification(span, aState); |
2866 | 0 | innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities; |
2867 | 0 | } else { |
2868 | 0 | if (pfd->mIsTextFrame) { |
2869 | 0 | innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities; |
2870 | 0 | } |
2871 | 0 |
|
2872 | 0 | if (!aState.mLastParticipant) { |
2873 | 0 | aState.mFirstParticipant = pfd; |
2874 | 0 | // It is not an empty ruby base, but we are not assigning gaps |
2875 | 0 | // to the content for now. Clear the last entered ruby base so |
2876 | 0 | // that we can correctly set the last exited ruby base. |
2877 | 0 | aState.mLastEnteredRubyBase = nullptr; |
2878 | 0 | } else { |
2879 | 0 | extraOpportunities = AssignInterframeJustificationGaps(pfd, aState); |
2880 | 0 | } |
2881 | 0 |
|
2882 | 0 | aState.mLastParticipant = pfd; |
2883 | 0 | } |
2884 | 0 |
|
2885 | 0 | if (isRubyBase) { |
2886 | 0 | if (aState.mLastEnteredRubyBase == pfd) { |
2887 | 0 | // There is no justification participant inside this ruby base. |
2888 | 0 | // Ignore this ruby base completely and restore the outer ruby |
2889 | 0 | // base here. |
2890 | 0 | aState.mLastEnteredRubyBase = outerRubyBase; |
2891 | 0 | } else { |
2892 | 0 | aState.mLastExitedRubyBase = pfd; |
2893 | 0 | } |
2894 | 0 | } |
2895 | 0 |
|
2896 | 0 | if (firstChild) { |
2897 | 0 | outerOpportunities = extraOpportunities; |
2898 | 0 | firstChild = false; |
2899 | 0 | } else { |
2900 | 0 | innerOpportunities += extraOpportunities; |
2901 | 0 | } |
2902 | 0 | } |
2903 | 0 |
|
2904 | 0 | return outerOpportunities; |
2905 | 0 | } |
2906 | | |
2907 | | void |
2908 | | nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD, |
2909 | | const nsSize& aContainerSize, |
2910 | | nscoord aDeltaICoord, |
2911 | | nscoord aDeltaISize) |
2912 | 0 | { |
2913 | 0 | nsIFrame* frame = aPFD->mFrame; |
2914 | 0 | LayoutFrameType frameType = frame->Type(); |
2915 | 0 | MOZ_ASSERT(frameType == LayoutFrameType::RubyText || |
2916 | 0 | frameType == LayoutFrameType::RubyTextContainer); |
2917 | 0 | MOZ_ASSERT(aPFD->mSpan, "rt and rtc should have span."); |
2918 | 0 |
|
2919 | 0 | PerSpanData* psd = aPFD->mSpan; |
2920 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
2921 | 0 | aPFD->mBounds.IStart(lineWM) += aDeltaICoord; |
2922 | 0 |
|
2923 | 0 | // Check whether this expansion should be counted into the reserved |
2924 | 0 | // isize or not. When it is a ruby text container, and it has some |
2925 | 0 | // children linked to the base, it must not have reserved isize, |
2926 | 0 | // or its children won't align with their bases. Otherwise, this |
2927 | 0 | // expansion should be reserved. There are two cases a ruby text |
2928 | 0 | // container does not have children linked to the base: |
2929 | 0 | // 1. it is a container for span; 2. its children are collapsed. |
2930 | 0 | // See bug 1055674 for the second case. |
2931 | 0 | if (frameType == LayoutFrameType::RubyText || |
2932 | 0 | // This ruby text container is a span. |
2933 | 0 | (psd->mFirstFrame == psd->mLastFrame && psd->mFirstFrame && |
2934 | 0 | !psd->mFirstFrame->mIsLinkedToBase)) { |
2935 | 0 | // For ruby text frames, only increase frames |
2936 | 0 | // which are not auto-hidden. |
2937 | 0 | if (frameType != LayoutFrameType::RubyText || |
2938 | 0 | !static_cast<nsRubyTextFrame*>(frame)->IsAutoHidden()) { |
2939 | 0 | nscoord reservedISize = RubyUtils::GetReservedISize(frame); |
2940 | 0 | RubyUtils::SetReservedISize(frame, reservedISize + aDeltaISize); |
2941 | 0 | } |
2942 | 0 | } else { |
2943 | 0 | // It is a normal ruby text container. Its children will expand |
2944 | 0 | // themselves properly. We only need to expand its own size here. |
2945 | 0 | aPFD->mBounds.ISize(lineWM) += aDeltaISize; |
2946 | 0 | } |
2947 | 0 | aPFD->mFrame->SetRect(lineWM, aPFD->mBounds, aContainerSize); |
2948 | 0 | } |
2949 | | |
2950 | | /** |
2951 | | * This function applies the changes of icoord and isize caused by |
2952 | | * justification to annotations of the given frame. |
2953 | | */ |
2954 | | void |
2955 | | nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData* aPFD, |
2956 | | nscoord aDeltaICoord, |
2957 | | nscoord aDeltaISize) |
2958 | 0 | { |
2959 | 0 | PerFrameData* pfd = aPFD->mNextAnnotation; |
2960 | 0 | while (pfd) { |
2961 | 0 | nsSize containerSize = pfd->mFrame->GetParent()->GetSize(); |
2962 | 0 | AdvanceAnnotationInlineBounds(pfd, containerSize, |
2963 | 0 | aDeltaICoord, aDeltaISize); |
2964 | 0 |
|
2965 | 0 | // There are two cases where an annotation frame has siblings which |
2966 | 0 | // do not attached to a ruby base-level frame: |
2967 | 0 | // 1. there's an intra-annotation whitespace which has no intra-base |
2968 | 0 | // white-space to pair with; |
2969 | 0 | // 2. there are not enough ruby bases to be paired with annotations. |
2970 | 0 | // In these cases, their size should not be affected, but we still |
2971 | 0 | // need to move them so that they won't overlap other frames. |
2972 | 0 | PerFrameData* sibling = pfd->mNext; |
2973 | 0 | while (sibling && !sibling->mIsLinkedToBase) { |
2974 | 0 | AdvanceAnnotationInlineBounds(sibling, containerSize, |
2975 | 0 | aDeltaICoord + aDeltaISize, 0); |
2976 | 0 | sibling = sibling->mNext; |
2977 | 0 | } |
2978 | 0 |
|
2979 | 0 | pfd = pfd->mNextAnnotation; |
2980 | 0 | } |
2981 | 0 | } |
2982 | | |
2983 | | nscoord |
2984 | | nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, |
2985 | | JustificationApplicationState& aState) |
2986 | 0 | { |
2987 | 0 | NS_ASSERTION(aPSD, "null arg"); |
2988 | 0 |
|
2989 | 0 | nscoord deltaICoord = 0; |
2990 | 0 | for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) { |
2991 | 0 | nscoord dw = 0; |
2992 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
2993 | 0 | const auto& assign = pfd->mJustificationAssignment; |
2994 | 0 | bool isInlineText = pfd->mIsTextFrame && |
2995 | 0 | !pfd->mWritingMode.IsOrthogonalTo(lineWM); |
2996 | 0 |
|
2997 | 0 | // Don't apply justification if the frame doesn't participate. Same |
2998 | 0 | // as the condition used in ComputeFrameJustification. Note that, |
2999 | 0 | // we still need to move the frame based on deltaICoord even if the |
3000 | 0 | // frame itself doesn't expand. |
3001 | 0 | if (pfd->ParticipatesInJustification()) { |
3002 | 0 | if (isInlineText) { |
3003 | 0 | if (aState.IsJustifiable()) { |
3004 | 0 | // Set corresponding justification gaps here, so that the |
3005 | 0 | // text frame knows how it should add gaps at its sides. |
3006 | 0 | const auto& info = pfd->mJustificationInfo; |
3007 | 0 | auto textFrame = static_cast<nsTextFrame*>(pfd->mFrame); |
3008 | 0 | textFrame->AssignJustificationGaps(assign); |
3009 | 0 | dw = aState.Consume(JustificationUtils::CountGaps(info, assign)); |
3010 | 0 | } |
3011 | 0 |
|
3012 | 0 | if (dw) { |
3013 | 0 | pfd->mRecomputeOverflow = true; |
3014 | 0 | } |
3015 | 0 | } else { |
3016 | 0 | if (nullptr != pfd->mSpan) { |
3017 | 0 | dw = ApplyFrameJustification(pfd->mSpan, aState); |
3018 | 0 | } |
3019 | 0 | } |
3020 | 0 | } else { |
3021 | 0 | MOZ_ASSERT(!assign.TotalGaps(), |
3022 | 0 | "Non-participants shouldn't have assigned gaps"); |
3023 | 0 | } |
3024 | 0 |
|
3025 | 0 | pfd->mBounds.ISize(lineWM) += dw; |
3026 | 0 | nscoord gapsAtEnd = 0; |
3027 | 0 | if (!isInlineText && assign.TotalGaps()) { |
3028 | 0 | // It is possible that we assign gaps to non-text frame or an |
3029 | 0 | // orthogonal text frame. Apply the gaps as margin for them. |
3030 | 0 | deltaICoord += aState.Consume(assign.mGapsAtStart); |
3031 | 0 | gapsAtEnd = aState.Consume(assign.mGapsAtEnd); |
3032 | 0 | dw += gapsAtEnd; |
3033 | 0 | } |
3034 | 0 | pfd->mBounds.IStart(lineWM) += deltaICoord; |
3035 | 0 |
|
3036 | 0 | // The gaps added to the end of the frame should also be |
3037 | 0 | // excluded from the isize added to the annotation. |
3038 | 0 | ApplyLineJustificationToAnnotations(pfd, deltaICoord, dw - gapsAtEnd); |
3039 | 0 | deltaICoord += dw; |
3040 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(aPSD)); |
3041 | 0 | } |
3042 | 0 | return deltaICoord; |
3043 | 0 | } |
3044 | | |
3045 | | static nsIFrame* |
3046 | | FindNearestRubyBaseAncestor(nsIFrame* aFrame) |
3047 | 0 | { |
3048 | 0 | MOZ_ASSERT(aFrame->Style()->ShouldSuppressLineBreak()); |
3049 | 0 | while (aFrame && !aFrame->IsRubyBaseFrame()) { |
3050 | 0 | aFrame = aFrame->GetParent(); |
3051 | 0 | } |
3052 | 0 | // XXX It is possible that no ruby base ancestor is found because of |
3053 | 0 | // some edge cases like form control or canvas inside ruby text. |
3054 | 0 | // See bug 1138092 comment 4. |
3055 | 0 | NS_WARNING_ASSERTION(aFrame, "no ruby base ancestor?"); |
3056 | 0 | return aFrame; |
3057 | 0 | } |
3058 | | |
3059 | | /** |
3060 | | * This method expands the given frame by the given reserved isize. |
3061 | | */ |
3062 | | void |
3063 | | nsLineLayout::ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize, |
3064 | | const nsSize& aContainerSize) |
3065 | 0 | { |
3066 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
3067 | 0 | auto rubyAlign = aFrame->mFrame->StyleText()->mRubyAlign; |
3068 | 0 | switch (rubyAlign) { |
3069 | 0 | case NS_STYLE_RUBY_ALIGN_START: |
3070 | 0 | // do nothing for start |
3071 | 0 | break; |
3072 | 0 | case NS_STYLE_RUBY_ALIGN_SPACE_BETWEEN: |
3073 | 0 | case NS_STYLE_RUBY_ALIGN_SPACE_AROUND: { |
3074 | 0 | int32_t opportunities = aFrame->mJustificationInfo.mInnerOpportunities; |
3075 | 0 | int32_t gaps = opportunities * 2; |
3076 | 0 | if (rubyAlign == NS_STYLE_RUBY_ALIGN_SPACE_AROUND) { |
3077 | 0 | // Each expandable ruby box with ruby-align space-around has a |
3078 | 0 | // gap at each of its sides. For rb/rbc, see comment in |
3079 | 0 | // AssignInterframeJustificationGaps; for rt/rtc, see comment |
3080 | 0 | // in ExpandRubyBoxWithAnnotations. |
3081 | 0 | gaps += 2; |
3082 | 0 | } |
3083 | 0 | if (gaps > 0) { |
3084 | 0 | JustificationApplicationState state(gaps, aReservedISize); |
3085 | 0 | ApplyFrameJustification(aFrame->mSpan, state); |
3086 | 0 | break; |
3087 | 0 | } |
3088 | 0 | // If there are no justification opportunities for space-between, |
3089 | 0 | // fall-through to center per spec. |
3090 | 0 | MOZ_FALLTHROUGH; |
3091 | 0 | } |
3092 | 0 | case NS_STYLE_RUBY_ALIGN_CENTER: |
3093 | 0 | // Indent all children by half of the reserved inline size. |
3094 | 0 | for (PerFrameData* child = aFrame->mSpan->mFirstFrame; |
3095 | 0 | child; child = child->mNext) { |
3096 | 0 | child->mBounds.IStart(lineWM) += aReservedISize / 2; |
3097 | 0 | child->mFrame->SetRect(lineWM, child->mBounds, aContainerSize); |
3098 | 0 | } |
3099 | 0 | break; |
3100 | 0 | default: |
3101 | 0 | MOZ_ASSERT_UNREACHABLE("Unknown ruby-align value"); |
3102 | 0 | } |
3103 | 0 |
|
3104 | 0 | aFrame->mBounds.ISize(lineWM) += aReservedISize; |
3105 | 0 | aFrame->mFrame->SetRect(lineWM, aFrame->mBounds, aContainerSize); |
3106 | 0 | } |
3107 | | |
3108 | | /** |
3109 | | * This method expands the given frame by the reserved inline size. |
3110 | | * It also expands its annotations if they are expandable and have |
3111 | | * reserved isize larger than zero. |
3112 | | */ |
3113 | | void |
3114 | | nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData* aFrame, |
3115 | | const nsSize& aContainerSize) |
3116 | 0 | { |
3117 | 0 | nscoord reservedISize = RubyUtils::GetReservedISize(aFrame->mFrame); |
3118 | 0 | if (reservedISize) { |
3119 | 0 | ExpandRubyBox(aFrame, reservedISize, aContainerSize); |
3120 | 0 | } |
3121 | 0 |
|
3122 | 0 | WritingMode lineWM = mRootSpan->mWritingMode; |
3123 | 0 | bool isLevelContainer = aFrame->mFrame->IsRubyBaseContainerFrame(); |
3124 | 0 | for (PerFrameData* annotation = aFrame->mNextAnnotation; |
3125 | 0 | annotation; annotation = annotation->mNextAnnotation) { |
3126 | 0 | if (lineWM.IsOrthogonalTo(annotation->mFrame->GetWritingMode())) { |
3127 | 0 | // Inter-character case: don't attempt to expand ruby annotations. |
3128 | 0 | continue; |
3129 | 0 | } |
3130 | 0 | if (isLevelContainer) { |
3131 | 0 | nsIFrame* rtcFrame = annotation->mFrame; |
3132 | 0 | MOZ_ASSERT(rtcFrame->IsRubyTextContainerFrame()); |
3133 | 0 | // It is necessary to set the rect again because the container |
3134 | 0 | // width was unknown, and zero was used instead when we reflow |
3135 | 0 | // them. The corresponding base containers were repositioned in |
3136 | 0 | // VerticalAlignFrames and PlaceTopBottomFrames. |
3137 | 0 | MOZ_ASSERT( |
3138 | 0 | rtcFrame->GetLogicalSize(lineWM) == annotation->mBounds.Size(lineWM)); |
3139 | 0 | rtcFrame->SetPosition(lineWM, annotation->mBounds.Origin(lineWM), |
3140 | 0 | aContainerSize); |
3141 | 0 | } |
3142 | 0 |
|
3143 | 0 | nscoord reservedISize = RubyUtils::GetReservedISize(annotation->mFrame); |
3144 | 0 | if (!reservedISize) { |
3145 | 0 | continue; |
3146 | 0 | } |
3147 | 0 | |
3148 | 0 | MOZ_ASSERT(annotation->mSpan); |
3149 | 0 | JustificationComputationState computeState; |
3150 | 0 | ComputeFrameJustification(annotation->mSpan, computeState); |
3151 | 0 | if (!computeState.mFirstParticipant) { |
3152 | 0 | continue; |
3153 | 0 | } |
3154 | 0 | if (IsRubyAlignSpaceAround(annotation->mFrame)) { |
3155 | 0 | // Add one gap at each side of this annotation. |
3156 | 0 | computeState.mFirstParticipant->mJustificationAssignment.mGapsAtStart = 1; |
3157 | 0 | computeState.mLastParticipant->mJustificationAssignment.mGapsAtEnd = 1; |
3158 | 0 | } |
3159 | 0 | nsIFrame* parentFrame = annotation->mFrame->GetParent(); |
3160 | 0 | nsSize containerSize = parentFrame->GetSize(); |
3161 | 0 | MOZ_ASSERT(containerSize == aContainerSize || |
3162 | 0 | parentFrame->IsRubyTextContainerFrame(), |
3163 | 0 | "Container width should only be different when the current " |
3164 | 0 | "annotation is a ruby text frame, whose parent is not same " |
3165 | 0 | "as its base frame."); |
3166 | 0 | ExpandRubyBox(annotation, reservedISize, containerSize); |
3167 | 0 | ExpandInlineRubyBoxes(annotation->mSpan); |
3168 | 0 | } |
3169 | 0 | } |
3170 | | |
3171 | | /** |
3172 | | * This method looks for all expandable ruby box in the given span, and |
3173 | | * calls ExpandRubyBox to expand them in depth-first preorder. |
3174 | | */ |
3175 | | void |
3176 | | nsLineLayout::ExpandInlineRubyBoxes(PerSpanData* aSpan) |
3177 | 0 | { |
3178 | 0 | nsSize containerSize = ContainerSizeForSpan(aSpan); |
3179 | 0 | for (PerFrameData* pfd = aSpan->mFirstFrame; pfd; pfd = pfd->mNext) { |
3180 | 0 | if (RubyUtils::IsExpandableRubyBox(pfd->mFrame)) { |
3181 | 0 | ExpandRubyBoxWithAnnotations(pfd, containerSize); |
3182 | 0 | } |
3183 | 0 | if (pfd->mSpan) { |
3184 | 0 | ExpandInlineRubyBoxes(pfd->mSpan); |
3185 | 0 | } |
3186 | 0 | } |
3187 | 0 | } |
3188 | | |
3189 | | // Align inline frames within the line according to the CSS text-align |
3190 | | // property. |
3191 | | void |
3192 | | nsLineLayout::TextAlignLine(nsLineBox* aLine, |
3193 | | bool aIsLastLine) |
3194 | 0 | { |
3195 | 0 | /** |
3196 | 0 | * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller |
3197 | 0 | * only in cases where the last line needs special handling. |
3198 | 0 | */ |
3199 | 0 | PerSpanData* psd = mRootSpan; |
3200 | 0 | WritingMode lineWM = psd->mWritingMode; |
3201 | 0 | LAYOUT_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE, |
3202 | 0 | "have unconstrained width; this should only result from " |
3203 | 0 | "very large sizes, not attempts at intrinsic width " |
3204 | 0 | "calculation"); |
3205 | 0 | nscoord availISize = psd->mIEnd - psd->mIStart; |
3206 | 0 | nscoord remainingISize = availISize - aLine->ISize(); |
3207 | | #ifdef NOISY_INLINEDIR_ALIGN |
3208 | | nsFrame::ListTag(stdout, mBlockReflowInput->mFrame); |
3209 | | printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n", |
3210 | | availISize, aLine->IStart(), aLine->ISize(), remainingISize); |
3211 | | #endif |
3212 | |
|
3213 | 0 | // 'text-align-last: auto' is equivalent to the value of the 'text-align' |
3214 | 0 | // property except when 'text-align' is set to 'justify', in which case it |
3215 | 0 | // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise. |
3216 | 0 | // |
3217 | 0 | // XXX: the code below will have to change when we implement text-justify |
3218 | 0 | // |
3219 | 0 | nscoord dx = 0; |
3220 | 0 | uint8_t textAlign = mStyleText->mTextAlign; |
3221 | 0 | bool textAlignTrue = mStyleText->mTextAlignTrue; |
3222 | 0 | if (aIsLastLine) { |
3223 | 0 | textAlignTrue = mStyleText->mTextAlignLastTrue; |
3224 | 0 | if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) { |
3225 | 0 | if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) { |
3226 | 0 | textAlign = NS_STYLE_TEXT_ALIGN_START; |
3227 | 0 | } |
3228 | 0 | } else { |
3229 | 0 | textAlign = mStyleText->mTextAlignLast; |
3230 | 0 | } |
3231 | 0 | } |
3232 | 0 |
|
3233 | 0 | bool isSVG = nsSVGUtils::IsInSVGTextSubtree(mBlockReflowInput->mFrame); |
3234 | 0 | bool doTextAlign = remainingISize > 0 || textAlignTrue; |
3235 | 0 |
|
3236 | 0 | int32_t additionalGaps = 0; |
3237 | 0 | if (!isSVG && (mHasRuby || (doTextAlign && |
3238 | 0 | textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY))) { |
3239 | 0 | JustificationComputationState computeState; |
3240 | 0 | ComputeFrameJustification(psd, computeState); |
3241 | 0 | if (mHasRuby && computeState.mFirstParticipant) { |
3242 | 0 | PerFrameData* firstFrame = computeState.mFirstParticipant; |
3243 | 0 | if (firstFrame->mFrame->Style()->ShouldSuppressLineBreak()) { |
3244 | 0 | MOZ_ASSERT(!firstFrame->mJustificationAssignment.mGapsAtStart); |
3245 | 0 | nsIFrame* rubyBase = FindNearestRubyBaseAncestor(firstFrame->mFrame); |
3246 | 0 | if (rubyBase && IsRubyAlignSpaceAround(rubyBase)) { |
3247 | 0 | firstFrame->mJustificationAssignment.mGapsAtStart = 1; |
3248 | 0 | additionalGaps++; |
3249 | 0 | } |
3250 | 0 | } |
3251 | 0 | PerFrameData* lastFrame = computeState.mLastParticipant; |
3252 | 0 | if (lastFrame->mFrame->Style()->ShouldSuppressLineBreak()) { |
3253 | 0 | MOZ_ASSERT(!lastFrame->mJustificationAssignment.mGapsAtEnd); |
3254 | 0 | nsIFrame* rubyBase = FindNearestRubyBaseAncestor(lastFrame->mFrame); |
3255 | 0 | if (rubyBase && IsRubyAlignSpaceAround(rubyBase)) { |
3256 | 0 | lastFrame->mJustificationAssignment.mGapsAtEnd = 1; |
3257 | 0 | additionalGaps++; |
3258 | 0 | } |
3259 | 0 | } |
3260 | 0 | } |
3261 | 0 | } |
3262 | 0 |
|
3263 | 0 | if (!isSVG && doTextAlign) { |
3264 | 0 | switch (textAlign) { |
3265 | 0 | case NS_STYLE_TEXT_ALIGN_JUSTIFY: { |
3266 | 0 | int32_t opportunities = |
3267 | 0 | psd->mFrame->mJustificationInfo.mInnerOpportunities; |
3268 | 0 | if (opportunities > 0) { |
3269 | 0 | int32_t gaps = opportunities * 2 + additionalGaps; |
3270 | 0 | JustificationApplicationState applyState(gaps, remainingISize); |
3271 | 0 |
|
3272 | 0 | // Apply the justification, and make sure to update our linebox |
3273 | 0 | // width to account for it. |
3274 | 0 | aLine->ExpandBy(ApplyFrameJustification(psd, applyState), |
3275 | 0 | ContainerSizeForSpan(psd)); |
3276 | 0 |
|
3277 | 0 | MOZ_ASSERT(applyState.mGaps.mHandled == applyState.mGaps.mCount, |
3278 | 0 | "Unprocessed justification gaps"); |
3279 | 0 | MOZ_ASSERT(applyState.mWidth.mConsumed == applyState.mWidth.mAvailable, |
3280 | 0 | "Unprocessed justification width"); |
3281 | 0 | break; |
3282 | 0 | } |
3283 | 0 | // Fall through to the default case if we could not justify to fill |
3284 | 0 | // the space. |
3285 | 0 | MOZ_FALLTHROUGH; |
3286 | 0 | } |
3287 | 0 |
|
3288 | 0 | case NS_STYLE_TEXT_ALIGN_START: |
3289 | 0 | // default alignment is to start edge so do nothing |
3290 | 0 | break; |
3291 | 0 |
|
3292 | 0 | case NS_STYLE_TEXT_ALIGN_LEFT: |
3293 | 0 | case NS_STYLE_TEXT_ALIGN_MOZ_LEFT: |
3294 | 0 | if (!lineWM.IsBidiLTR()) { |
3295 | 0 | dx = remainingISize; |
3296 | 0 | } |
3297 | 0 | break; |
3298 | 0 |
|
3299 | 0 | case NS_STYLE_TEXT_ALIGN_RIGHT: |
3300 | 0 | case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT: |
3301 | 0 | if (lineWM.IsBidiLTR()) { |
3302 | 0 | dx = remainingISize; |
3303 | 0 | } |
3304 | 0 | break; |
3305 | 0 |
|
3306 | 0 | case NS_STYLE_TEXT_ALIGN_END: |
3307 | 0 | dx = remainingISize; |
3308 | 0 | break; |
3309 | 0 |
|
3310 | 0 | case NS_STYLE_TEXT_ALIGN_CENTER: |
3311 | 0 | case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: |
3312 | 0 | dx = remainingISize / 2; |
3313 | 0 | break; |
3314 | 0 | } |
3315 | 0 | } |
3316 | 0 |
|
3317 | 0 | if (mHasRuby) { |
3318 | 0 | ExpandInlineRubyBoxes(mRootSpan); |
3319 | 0 | } |
3320 | 0 |
|
3321 | 0 | if (mPresContext->BidiEnabled() && |
3322 | 0 | (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) { |
3323 | 0 | PerFrameData* startFrame = psd->mFirstFrame; |
3324 | 0 | MOZ_ASSERT(startFrame, "empty line?"); |
3325 | 0 | if (startFrame->mIsBullet) { |
3326 | 0 | // Bullet shouldn't participate in bidi reordering. |
3327 | 0 | startFrame = startFrame->mNext; |
3328 | 0 | MOZ_ASSERT(startFrame, "no frame after bullet?"); |
3329 | 0 | MOZ_ASSERT(!startFrame->mIsBullet, "multiple bullets?"); |
3330 | 0 | } |
3331 | 0 | nsBidiPresUtils::ReorderFrames(startFrame->mFrame, |
3332 | 0 | aLine->GetChildCount(), |
3333 | 0 | lineWM, mContainerSize, |
3334 | 0 | psd->mIStart + mTextIndent + dx); |
3335 | 0 | if (dx) { |
3336 | 0 | aLine->IndentBy(dx, ContainerSize()); |
3337 | 0 | } |
3338 | 0 | } else if (dx) { |
3339 | 0 | for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
3340 | 0 | pfd->mBounds.IStart(lineWM) += dx; |
3341 | 0 | pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd)); |
3342 | 0 | } |
3343 | 0 | aLine->IndentBy(dx, ContainerSize()); |
3344 | 0 | } |
3345 | 0 | } |
3346 | | |
3347 | | // This method applies any relative positioning to the given frame. |
3348 | | void |
3349 | | nsLineLayout::ApplyRelativePositioning(PerFrameData* aPFD) |
3350 | 0 | { |
3351 | 0 | if (!aPFD->mRelativePos) { |
3352 | 0 | return; |
3353 | 0 | } |
3354 | 0 | |
3355 | 0 | nsIFrame* frame = aPFD->mFrame; |
3356 | 0 | WritingMode frameWM = aPFD->mWritingMode; |
3357 | 0 | LogicalPoint origin = frame->GetLogicalPosition(ContainerSize()); |
3358 | 0 | // right and bottom are handled by |
3359 | 0 | // ReflowInput::ComputeRelativeOffsets |
3360 | 0 | ReflowInput::ApplyRelativePositioning(frame, frameWM, |
3361 | 0 | aPFD->mOffsets, &origin, |
3362 | 0 | ContainerSize()); |
3363 | 0 | frame->SetPosition(frameWM, origin, ContainerSize()); |
3364 | 0 | } |
3365 | | |
3366 | | // This method do relative positioning for ruby annotations. |
3367 | | void |
3368 | | nsLineLayout::RelativePositionAnnotations(PerSpanData* aRubyPSD, |
3369 | | nsOverflowAreas& aOverflowAreas) |
3370 | 0 | { |
3371 | 0 | MOZ_ASSERT(aRubyPSD->mFrame->mFrame->IsRubyFrame()); |
3372 | 0 | for (PerFrameData* pfd = aRubyPSD->mFirstFrame; pfd; pfd = pfd->mNext) { |
3373 | 0 | MOZ_ASSERT(pfd->mFrame->IsRubyBaseContainerFrame()); |
3374 | 0 | for (PerFrameData* rtc = pfd->mNextAnnotation; |
3375 | 0 | rtc; rtc = rtc->mNextAnnotation) { |
3376 | 0 | nsIFrame* rtcFrame = rtc->mFrame; |
3377 | 0 | MOZ_ASSERT(rtcFrame->IsRubyTextContainerFrame()); |
3378 | 0 | ApplyRelativePositioning(rtc); |
3379 | 0 | nsOverflowAreas rtcOverflowAreas; |
3380 | 0 | RelativePositionFrames(rtc->mSpan, rtcOverflowAreas); |
3381 | 0 | aOverflowAreas.UnionWith(rtcOverflowAreas + rtcFrame->GetPosition()); |
3382 | 0 | } |
3383 | 0 | } |
3384 | 0 | } |
3385 | | |
3386 | | void |
3387 | | nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas) |
3388 | 0 | { |
3389 | 0 | nsOverflowAreas overflowAreas; |
3390 | 0 | WritingMode wm = psd->mWritingMode; |
3391 | 0 | if (psd != mRootSpan) { |
3392 | 0 | // The span's overflow areas come in three parts: |
3393 | 0 | // -- this frame's width and height |
3394 | 0 | // -- pfd->mOverflowAreas, which is the area of a bullet or the union |
3395 | 0 | // of a relatively positioned frame's absolute children |
3396 | 0 | // -- the bounds of all inline descendants |
3397 | 0 | // The former two parts are computed right here, we gather the descendants |
3398 | 0 | // below. |
3399 | 0 | // At this point psd->mFrame->mBounds might be out of date since |
3400 | 0 | // bidi reordering can move and resize the frames. So use the frame's |
3401 | 0 | // rect instead of mBounds. |
3402 | 0 | nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize()); |
3403 | 0 |
|
3404 | 0 | overflowAreas.ScrollableOverflow().UnionRect( |
3405 | 0 | psd->mFrame->mOverflowAreas.ScrollableOverflow(), adjustedBounds); |
3406 | 0 | overflowAreas.VisualOverflow().UnionRect( |
3407 | 0 | psd->mFrame->mOverflowAreas.VisualOverflow(), adjustedBounds); |
3408 | 0 | } |
3409 | 0 | else { |
3410 | 0 | LogicalRect rect(wm, psd->mIStart, mBStartEdge, |
3411 | 0 | psd->mICoord - psd->mIStart, mFinalLineBSize); |
3412 | 0 | // The minimum combined area for the frames that are direct |
3413 | 0 | // children of the block starts at the upper left corner of the |
3414 | 0 | // line and is sized to match the size of the line's bounding box |
3415 | 0 | // (the same size as the values returned from VerticalAlignFrames) |
3416 | 0 | overflowAreas.VisualOverflow() = rect.GetPhysicalRect(wm, ContainerSize()); |
3417 | 0 | overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow(); |
3418 | 0 | } |
3419 | 0 |
|
3420 | 0 | for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
3421 | 0 | nsIFrame* frame = pfd->mFrame; |
3422 | 0 |
|
3423 | 0 | // Adjust the origin of the frame |
3424 | 0 | ApplyRelativePositioning(pfd); |
3425 | 0 |
|
3426 | 0 | // We must position the view correctly before positioning its |
3427 | 0 | // descendants so that widgets are positioned properly (since only |
3428 | 0 | // some views have widgets). |
3429 | 0 | if (frame->HasView()) |
3430 | 0 | nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame, |
3431 | 0 | frame->GetView(), pfd->mOverflowAreas.VisualOverflow(), |
3432 | 0 | NS_FRAME_NO_SIZE_VIEW); |
3433 | 0 |
|
3434 | 0 | // Note: the combined area of a child is in its coordinate |
3435 | 0 | // system. We adjust the childs combined area into our coordinate |
3436 | 0 | // system before computing the aggregated value by adding in |
3437 | 0 | // <b>x</b> and <b>y</b> which were computed above. |
3438 | 0 | nsOverflowAreas r; |
3439 | 0 | if (pfd->mSpan) { |
3440 | 0 | // Compute a new combined area for the child span before |
3441 | 0 | // aggregating it into our combined area. |
3442 | 0 | RelativePositionFrames(pfd->mSpan, r); |
3443 | 0 | } else { |
3444 | 0 | r = pfd->mOverflowAreas; |
3445 | 0 | if (pfd->mIsTextFrame) { |
3446 | 0 | // We need to recompute overflow areas in four cases: |
3447 | 0 | // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming |
3448 | 0 | // (2) When there are text decorations, since we can't recompute the |
3449 | 0 | // overflow area until Reflow and VerticalAlignLine have finished |
3450 | 0 | // (3) When there are text emphasis marks, since the marks may be |
3451 | 0 | // put further away if the text is inside ruby. |
3452 | 0 | // (4) When there are text strokes |
3453 | 0 | if (pfd->mRecomputeOverflow || |
3454 | 0 | frame->Style()->HasTextDecorationLines() || |
3455 | 0 | frame->StyleText()->HasTextEmphasis() || |
3456 | 0 | frame->StyleText()->HasWebkitTextStroke()) { |
3457 | 0 | nsTextFrame* f = static_cast<nsTextFrame*>(frame); |
3458 | 0 | r = f->RecomputeOverflow(mBlockReflowInput->mFrame); |
3459 | 0 | } |
3460 | 0 | frame->FinishAndStoreOverflow(r, frame->GetSize()); |
3461 | 0 | } |
3462 | 0 |
|
3463 | 0 | // If we have something that's not an inline but with a complex frame |
3464 | 0 | // hierarchy inside that contains views, they need to be |
3465 | 0 | // positioned. |
3466 | 0 | // All descendant views must be repositioned even if this frame |
3467 | 0 | // does have a view in case this frame's view does not have a |
3468 | 0 | // widget and some of the descendant views do have widgets -- |
3469 | 0 | // otherwise the widgets won't be repositioned. |
3470 | 0 | nsContainerFrame::PositionChildViews(frame); |
3471 | 0 | } |
3472 | 0 |
|
3473 | 0 | // Do this here (rather than along with setting the overflow rect |
3474 | 0 | // below) so we get leaf frames as well. No need to worry |
3475 | 0 | // about the root span, since it doesn't have a frame. |
3476 | 0 | if (frame->HasView()) |
3477 | 0 | nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame, |
3478 | 0 | frame->GetView(), |
3479 | 0 | r.VisualOverflow(), |
3480 | 0 | NS_FRAME_NO_MOVE_VIEW); |
3481 | 0 |
|
3482 | 0 | overflowAreas.UnionWith(r + frame->GetPosition()); |
3483 | 0 | } |
3484 | 0 |
|
3485 | 0 | // Also compute relative position in the annotations. |
3486 | 0 | if (psd->mFrame->mFrame->IsRubyFrame()) { |
3487 | 0 | RelativePositionAnnotations(psd, overflowAreas); |
3488 | 0 | } |
3489 | 0 |
|
3490 | 0 | // If we just computed a spans combined area, we need to update its |
3491 | 0 | // overflow rect... |
3492 | 0 | if (psd != mRootSpan) { |
3493 | 0 | PerFrameData* spanPFD = psd->mFrame; |
3494 | 0 | nsIFrame* frame = spanPFD->mFrame; |
3495 | 0 | frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize()); |
3496 | 0 | } |
3497 | 0 | aOverflowAreas = overflowAreas; |
3498 | 0 | } |