|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* state and methods used while laying out a single line of a block frame */ |
|
7 |
|
8 // This has to be defined before nsLineLayout.h is included, because |
|
9 // nsLineLayout.h has a #include for plarena.h, which needs this defined: |
|
10 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1) |
|
11 #include "nsLineLayout.h" |
|
12 |
|
13 #include "SVGTextFrame.h" |
|
14 #include "nsBlockFrame.h" |
|
15 #include "nsStyleConsts.h" |
|
16 #include "nsContainerFrame.h" |
|
17 #include "nsFloatManager.h" |
|
18 #include "nsStyleContext.h" |
|
19 #include "nsPresContext.h" |
|
20 #include "nsRenderingContext.h" |
|
21 #include "nsGkAtoms.h" |
|
22 #include "nsIContent.h" |
|
23 #include "nsLayoutUtils.h" |
|
24 #include "nsTextFrame.h" |
|
25 #include "nsStyleStructInlines.h" |
|
26 #include "nsBidiPresUtils.h" |
|
27 #include <algorithm> |
|
28 |
|
29 #ifdef DEBUG |
|
30 #undef NOISY_INLINEDIR_ALIGN |
|
31 #undef NOISY_BLOCKDIR_ALIGN |
|
32 #undef REALLY_NOISY_BLOCKDIR_ALIGN |
|
33 #undef NOISY_REFLOW |
|
34 #undef REALLY_NOISY_REFLOW |
|
35 #undef NOISY_PUSHING |
|
36 #undef REALLY_NOISY_PUSHING |
|
37 #undef DEBUG_ADD_TEXT |
|
38 #undef NOISY_MAX_ELEMENT_SIZE |
|
39 #undef REALLY_NOISY_MAX_ELEMENT_SIZE |
|
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 #define FIX_BUG_50257 |
|
50 |
|
51 nsLineLayout::nsLineLayout(nsPresContext* aPresContext, |
|
52 nsFloatManager* aFloatManager, |
|
53 const nsHTMLReflowState* aOuterReflowState, |
|
54 const nsLineList::iterator* aLine) |
|
55 : mPresContext(aPresContext), |
|
56 mFloatManager(aFloatManager), |
|
57 mBlockReflowState(aOuterReflowState), |
|
58 mLastOptionalBreakContent(nullptr), |
|
59 mForceBreakContent(nullptr), |
|
60 mBlockRS(nullptr),/* XXX temporary */ |
|
61 mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak), |
|
62 mLastOptionalBreakContentOffset(-1), |
|
63 mForceBreakContentOffset(-1), |
|
64 mMinLineBSize(0), |
|
65 mTextIndent(0), |
|
66 mFirstLetterStyleOK(false), |
|
67 mIsTopOfPage(false), |
|
68 mImpactedByFloats(false), |
|
69 mLastFloatWasLetterFrame(false), |
|
70 mLineIsEmpty(false), |
|
71 mLineEndsInBR(false), |
|
72 mNeedBackup(false), |
|
73 mInFirstLine(false), |
|
74 mGotLineBox(false), |
|
75 mInFirstLetter(false), |
|
76 mHasBullet(false), |
|
77 mDirtyNextLine(false), |
|
78 mLineAtStart(false) |
|
79 { |
|
80 MOZ_ASSERT(aOuterReflowState, "aOuterReflowState must not be null"); |
|
81 NS_ASSERTION(aFloatManager || aOuterReflowState->frame->GetType() == |
|
82 nsGkAtoms::letterFrame, |
|
83 "float manager should be present"); |
|
84 MOZ_COUNT_CTOR(nsLineLayout); |
|
85 |
|
86 // Stash away some style data that we need |
|
87 nsBlockFrame* blockFrame = do_QueryFrame(aOuterReflowState->frame); |
|
88 if (blockFrame) |
|
89 mStyleText = blockFrame->StyleTextForLineLayout(); |
|
90 else |
|
91 mStyleText = aOuterReflowState->frame->StyleText(); |
|
92 |
|
93 mLineNumber = 0; |
|
94 mTotalPlacedFrames = 0; |
|
95 mBStartEdge = 0; |
|
96 mTrimmableWidth = 0; |
|
97 |
|
98 mInflationMinFontSize = |
|
99 nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame); |
|
100 |
|
101 // Instead of always pre-initializing the free-lists for frames and |
|
102 // spans, we do it on demand so that situations that only use a few |
|
103 // frames and spans won't waste a lot of time in unneeded |
|
104 // initialization. |
|
105 PL_INIT_ARENA_POOL(&mArena, "nsLineLayout", 1024); |
|
106 mFrameFreeList = nullptr; |
|
107 mSpanFreeList = nullptr; |
|
108 |
|
109 mCurrentSpan = mRootSpan = nullptr; |
|
110 mSpanDepth = 0; |
|
111 |
|
112 if (aLine) { |
|
113 mGotLineBox = true; |
|
114 mLineBox = *aLine; |
|
115 } |
|
116 } |
|
117 |
|
118 nsLineLayout::~nsLineLayout() |
|
119 { |
|
120 MOZ_COUNT_DTOR(nsLineLayout); |
|
121 |
|
122 NS_ASSERTION(nullptr == mRootSpan, "bad line-layout user"); |
|
123 |
|
124 PL_FinishArenaPool(&mArena); |
|
125 } |
|
126 |
|
127 // Find out if the frame has a non-null prev-in-flow, i.e., whether it |
|
128 // is a continuation. |
|
129 inline bool |
|
130 HasPrevInFlow(nsIFrame *aFrame) |
|
131 { |
|
132 nsIFrame *prevInFlow = aFrame->GetPrevInFlow(); |
|
133 return prevInFlow != nullptr; |
|
134 } |
|
135 |
|
136 void |
|
137 nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord, |
|
138 nscoord aISize, nscoord aBSize, |
|
139 bool aImpactedByFloats, |
|
140 bool aIsTopOfPage, |
|
141 WritingMode aWritingMode, |
|
142 nscoord aContainerWidth) |
|
143 { |
|
144 NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user"); |
|
145 NS_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE, |
|
146 "have unconstrained width; this should only result from " |
|
147 "very large sizes, not attempts at intrinsic width " |
|
148 "calculation"); |
|
149 #ifdef DEBUG |
|
150 if ((aISize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aISize)) { |
|
151 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
152 printf(": Init: bad caller: width WAS %d(0x%x)\n", |
|
153 aISize, aISize); |
|
154 } |
|
155 if ((aBSize != NS_UNCONSTRAINEDSIZE) && CRAZY_SIZE(aBSize)) { |
|
156 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
157 printf(": Init: bad caller: height WAS %d(0x%x)\n", |
|
158 aBSize, aBSize); |
|
159 } |
|
160 #endif |
|
161 #ifdef NOISY_REFLOW |
|
162 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
163 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n", |
|
164 aICoord, aBCoord, aISize, aBSize, |
|
165 aImpactedByFloats?"true":"false", |
|
166 aIsTopOfPage ? "top-of-page" : ""); |
|
167 #endif |
|
168 #ifdef DEBUG |
|
169 mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0; |
|
170 #endif |
|
171 |
|
172 mFirstLetterStyleOK = false; |
|
173 mIsTopOfPage = aIsTopOfPage; |
|
174 mImpactedByFloats = aImpactedByFloats; |
|
175 mTotalPlacedFrames = 0; |
|
176 mLineIsEmpty = true; |
|
177 mLineAtStart = true; |
|
178 mLineEndsInBR = false; |
|
179 mSpanDepth = 0; |
|
180 mMaxStartBoxBSize = mMaxEndBoxBSize = 0; |
|
181 |
|
182 if (mGotLineBox) { |
|
183 mLineBox->ClearHasBullet(); |
|
184 } |
|
185 |
|
186 PerSpanData* psd = NewPerSpanData(); |
|
187 mCurrentSpan = mRootSpan = psd; |
|
188 psd->mReflowState = mBlockReflowState; |
|
189 psd->mIStart = aICoord; |
|
190 psd->mICoord = aICoord; |
|
191 psd->mIEnd = aICoord + aISize; |
|
192 mContainerWidth = aContainerWidth; |
|
193 |
|
194 // If we're in a constrained height frame, then we don't allow a |
|
195 // max line box width to take effect. |
|
196 if (!(LineContainerFrame()->GetStateBits() & |
|
197 NS_FRAME_IN_CONSTRAINED_HEIGHT)) { |
|
198 |
|
199 // If the available size is greater than the maximum line box width (if |
|
200 // specified), then we need to adjust the line box width to be at the max |
|
201 // possible width. |
|
202 nscoord maxLineBoxWidth = |
|
203 LineContainerFrame()->PresContext()->PresShell()->MaxLineBoxWidth(); |
|
204 |
|
205 if (maxLineBoxWidth > 0 && |
|
206 psd->mIEnd - psd->mIStart > maxLineBoxWidth) { |
|
207 psd->mIEnd = psd->mIStart + maxLineBoxWidth; |
|
208 } |
|
209 } |
|
210 |
|
211 mBStartEdge = aBCoord; |
|
212 |
|
213 psd->mNoWrap = |
|
214 !mStyleText->WhiteSpaceCanWrapStyle() || LineContainerFrame()->IsSVGText(); |
|
215 psd->mWritingMode = aWritingMode; |
|
216 |
|
217 // If this is the first line of a block then see if the text-indent |
|
218 // property amounts to anything. |
|
219 |
|
220 if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) { |
|
221 const nsStyleCoord &textIndent = mStyleText->mTextIndent; |
|
222 nscoord pctBasis = 0; |
|
223 if (textIndent.HasPercent()) { |
|
224 pctBasis = |
|
225 nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState); |
|
226 |
|
227 if (mGotLineBox) { |
|
228 mLineBox->DisableResizeReflowOptimization(); |
|
229 } |
|
230 } |
|
231 nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis); |
|
232 |
|
233 mTextIndent = indent; |
|
234 |
|
235 psd->mICoord += indent; |
|
236 } |
|
237 } |
|
238 |
|
239 void |
|
240 nsLineLayout::EndLineReflow() |
|
241 { |
|
242 #ifdef NOISY_REFLOW |
|
243 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
244 printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart); |
|
245 #endif |
|
246 |
|
247 FreeSpan(mRootSpan); |
|
248 mCurrentSpan = mRootSpan = nullptr; |
|
249 |
|
250 NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak"); |
|
251 NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak"); |
|
252 |
|
253 #if 0 |
|
254 static int32_t maxSpansAllocated = NS_LINELAYOUT_NUM_SPANS; |
|
255 static int32_t maxFramesAllocated = NS_LINELAYOUT_NUM_FRAMES; |
|
256 if (mSpansAllocated > maxSpansAllocated) { |
|
257 printf("XXX: saw a line with %d spans\n", mSpansAllocated); |
|
258 maxSpansAllocated = mSpansAllocated; |
|
259 } |
|
260 if (mFramesAllocated > maxFramesAllocated) { |
|
261 printf("XXX: saw a line with %d frames\n", mFramesAllocated); |
|
262 maxFramesAllocated = mFramesAllocated; |
|
263 } |
|
264 #endif |
|
265 } |
|
266 |
|
267 // XXX swtich to a single mAvailLineWidth that we adjust as each frame |
|
268 // on the line is placed. Each span can still have a per-span mICoord that |
|
269 // tracks where a child frame is going in its span; they don't need a |
|
270 // per-span mIStart? |
|
271 |
|
272 void |
|
273 nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace, |
|
274 nsIFrame* aFloatFrame) |
|
275 { |
|
276 WritingMode lineWM = mRootSpan->mWritingMode; |
|
277 LogicalRect availSpace(lineWM, aNewAvailSpace, mContainerWidth); |
|
278 #ifdef REALLY_NOISY_REFLOW |
|
279 printf("nsLL::UpdateBand %d, %d, %d, %d, (logical %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n", |
|
280 aNewAvailSpace.x, aNewAvailSpace.y, |
|
281 aNewAvailSpace.width, aNewAvailSpace.height, |
|
282 availSpace.IStart(lineWM), availSpace.BStart(lineWM), |
|
283 availSpace.ISize(lineWM), availSpace.BSize(lineWM), |
|
284 aFloatFrame); |
|
285 #endif |
|
286 #ifdef DEBUG |
|
287 if ((availSpace.ISize(lineWM) != NS_UNCONSTRAINEDSIZE) && |
|
288 CRAZY_SIZE(availSpace.ISize(lineWM))) { |
|
289 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
290 printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n", |
|
291 availSpace.ISize(lineWM), availSpace.ISize(lineWM)); |
|
292 } |
|
293 if ((availSpace.BSize(lineWM) != NS_UNCONSTRAINEDSIZE) && |
|
294 CRAZY_SIZE(availSpace.BSize(lineWM))) { |
|
295 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
296 printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n", |
|
297 availSpace.BSize(lineWM), availSpace.BSize(lineWM)); |
|
298 } |
|
299 #endif |
|
300 |
|
301 // Compute the difference between last times width and the new width |
|
302 NS_WARN_IF_FALSE(mRootSpan->mIEnd != NS_UNCONSTRAINEDSIZE && |
|
303 aNewAvailSpace.width != NS_UNCONSTRAINEDSIZE, |
|
304 "have unconstrained width; this should only result from " |
|
305 "very large sizes, not attempts at intrinsic width " |
|
306 "calculation"); |
|
307 // The root span's mIStart moves to aICoord |
|
308 nscoord deltaICoord = availSpace.IStart(lineWM) - mRootSpan->mIStart; |
|
309 // The width of all spans changes by this much (the root span's |
|
310 // mIEnd moves to aICoord + aISize, its new width is aISize) |
|
311 nscoord deltaISize = availSpace.ISize(lineWM) - |
|
312 (mRootSpan->mIEnd - mRootSpan->mIStart); |
|
313 #ifdef NOISY_REFLOW |
|
314 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
315 printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n", |
|
316 aNewAvailSpace.x, aNewAvailSpace.y, |
|
317 aNewAvailSpace.width, aNewAvailSpace.height, deltaISize, deltaICoord); |
|
318 #endif |
|
319 |
|
320 // Update the root span position |
|
321 mRootSpan->mIStart += deltaICoord; |
|
322 mRootSpan->mIEnd += deltaICoord; |
|
323 mRootSpan->mICoord += deltaICoord; |
|
324 |
|
325 // Now update the right edges of the open spans to account for any |
|
326 // change in available space width |
|
327 for (PerSpanData* psd = mCurrentSpan; psd; psd = psd->mParent) { |
|
328 psd->mIEnd += deltaISize; |
|
329 psd->mContainsFloat = true; |
|
330 #ifdef NOISY_REFLOW |
|
331 printf(" span %p: oldIEnd=%d newIEnd=%d\n", |
|
332 psd, psd->mIEnd - deltaISize, psd->mIEnd); |
|
333 #endif |
|
334 } |
|
335 NS_ASSERTION(mRootSpan->mContainsFloat && |
|
336 mRootSpan->mIStart == availSpace.IStart(lineWM) && |
|
337 mRootSpan->mIEnd == availSpace.IEnd(lineWM), |
|
338 "root span was updated incorrectly?"); |
|
339 |
|
340 // Update frame bounds |
|
341 // Note: Only adjust the outermost frames (the ones that are direct |
|
342 // children of the block), not the ones in the child spans. The reason |
|
343 // is simple: the frames in the spans have coordinates local to their |
|
344 // parent therefore they are moved when their parent span is moved. |
|
345 if (deltaICoord != 0) { |
|
346 for (PerFrameData* pfd = mRootSpan->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
347 pfd->mBounds.IStart(lineWM) += deltaICoord; |
|
348 } |
|
349 } |
|
350 |
|
351 mBStartEdge = aNewAvailSpace.y; |
|
352 mImpactedByFloats = true; |
|
353 |
|
354 mLastFloatWasLetterFrame = nsGkAtoms::letterFrame == aFloatFrame->GetType(); |
|
355 } |
|
356 |
|
357 nsLineLayout::PerSpanData* |
|
358 nsLineLayout::NewPerSpanData() |
|
359 { |
|
360 PerSpanData* psd = mSpanFreeList; |
|
361 if (!psd) { |
|
362 void *mem; |
|
363 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerSpanData)); |
|
364 if (!mem) { |
|
365 NS_RUNTIMEABORT("OOM"); |
|
366 } |
|
367 psd = reinterpret_cast<PerSpanData*>(mem); |
|
368 } |
|
369 else { |
|
370 mSpanFreeList = psd->mNextFreeSpan; |
|
371 } |
|
372 psd->mParent = nullptr; |
|
373 psd->mFrame = nullptr; |
|
374 psd->mFirstFrame = nullptr; |
|
375 psd->mLastFrame = nullptr; |
|
376 psd->mContainsFloat = false; |
|
377 psd->mZeroEffectiveSpanBox = false; |
|
378 psd->mHasNonemptyContent = false; |
|
379 |
|
380 #ifdef DEBUG |
|
381 mSpansAllocated++; |
|
382 #endif |
|
383 return psd; |
|
384 } |
|
385 |
|
386 void |
|
387 nsLineLayout::BeginSpan(nsIFrame* aFrame, |
|
388 const nsHTMLReflowState* aSpanReflowState, |
|
389 nscoord aIStart, nscoord aIEnd, |
|
390 nscoord* aBaseline) |
|
391 { |
|
392 NS_ASSERTION(aIEnd != NS_UNCONSTRAINEDSIZE, |
|
393 "should no longer be using unconstrained sizes"); |
|
394 #ifdef NOISY_REFLOW |
|
395 nsFrame::IndentBy(stdout, mSpanDepth+1); |
|
396 nsFrame::ListTag(stdout, aFrame); |
|
397 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart, aIEnd); |
|
398 #endif |
|
399 |
|
400 PerSpanData* psd = NewPerSpanData(); |
|
401 // Link up span frame's pfd to point to its child span data |
|
402 PerFrameData* pfd = mCurrentSpan->mLastFrame; |
|
403 NS_ASSERTION(pfd->mFrame == aFrame, "huh?"); |
|
404 pfd->mSpan = psd; |
|
405 |
|
406 // Init new span |
|
407 psd->mFrame = pfd; |
|
408 psd->mParent = mCurrentSpan; |
|
409 psd->mReflowState = aSpanReflowState; |
|
410 psd->mIStart = aIStart; |
|
411 psd->mICoord = aIStart; |
|
412 psd->mIEnd = aIEnd; |
|
413 psd->mBaseline = aBaseline; |
|
414 |
|
415 nsIFrame* frame = aSpanReflowState->frame; |
|
416 psd->mNoWrap = !frame->StyleText()->WhiteSpaceCanWrap(frame); |
|
417 psd->mWritingMode = aSpanReflowState->GetWritingMode(); |
|
418 |
|
419 // Switch to new span |
|
420 mCurrentSpan = psd; |
|
421 mSpanDepth++; |
|
422 } |
|
423 |
|
424 nscoord |
|
425 nsLineLayout::EndSpan(nsIFrame* aFrame) |
|
426 { |
|
427 NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span"); |
|
428 #ifdef NOISY_REFLOW |
|
429 nsFrame::IndentBy(stdout, mSpanDepth); |
|
430 nsFrame::ListTag(stdout, aFrame); |
|
431 printf(": EndSpan width=%d\n", mCurrentSpan->mICoord - mCurrentSpan->mIStart); |
|
432 #endif |
|
433 PerSpanData* psd = mCurrentSpan; |
|
434 nscoord iSizeResult = psd->mLastFrame ? (psd->mICoord - psd->mIStart) : 0; |
|
435 |
|
436 mSpanDepth--; |
|
437 mCurrentSpan->mReflowState = nullptr; // no longer valid so null it out! |
|
438 mCurrentSpan = mCurrentSpan->mParent; |
|
439 return iSizeResult; |
|
440 } |
|
441 |
|
442 int32_t |
|
443 nsLineLayout::GetCurrentSpanCount() const |
|
444 { |
|
445 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
|
446 int32_t count = 0; |
|
447 PerFrameData* pfd = mRootSpan->mFirstFrame; |
|
448 while (nullptr != pfd) { |
|
449 count++; |
|
450 pfd = pfd->mNext; |
|
451 } |
|
452 return count; |
|
453 } |
|
454 |
|
455 void |
|
456 nsLineLayout::SplitLineTo(int32_t aNewCount) |
|
457 { |
|
458 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
|
459 |
|
460 #ifdef REALLY_NOISY_PUSHING |
|
461 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount, |
|
462 GetCurrentSpanCount()); |
|
463 DumpPerSpanData(mRootSpan, 1); |
|
464 #endif |
|
465 PerSpanData* psd = mRootSpan; |
|
466 PerFrameData* pfd = psd->mFirstFrame; |
|
467 while (nullptr != pfd) { |
|
468 if (--aNewCount == 0) { |
|
469 // Truncate list at pfd (we keep pfd, but anything following is freed) |
|
470 PerFrameData* next = pfd->mNext; |
|
471 pfd->mNext = nullptr; |
|
472 psd->mLastFrame = pfd; |
|
473 |
|
474 // Now release all of the frames following pfd |
|
475 pfd = next; |
|
476 while (nullptr != pfd) { |
|
477 next = pfd->mNext; |
|
478 pfd->mNext = mFrameFreeList; |
|
479 mFrameFreeList = pfd; |
|
480 #ifdef DEBUG |
|
481 mFramesFreed++; |
|
482 #endif |
|
483 if (nullptr != pfd->mSpan) { |
|
484 FreeSpan(pfd->mSpan); |
|
485 } |
|
486 pfd = next; |
|
487 } |
|
488 break; |
|
489 } |
|
490 pfd = pfd->mNext; |
|
491 } |
|
492 #ifdef NOISY_PUSHING |
|
493 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount, |
|
494 GetCurrentSpanCount()); |
|
495 DumpPerSpanData(mRootSpan, 1); |
|
496 #endif |
|
497 } |
|
498 |
|
499 void |
|
500 nsLineLayout::PushFrame(nsIFrame* aFrame) |
|
501 { |
|
502 PerSpanData* psd = mCurrentSpan; |
|
503 NS_ASSERTION(psd->mLastFrame->mFrame == aFrame, "pushing non-last frame"); |
|
504 |
|
505 #ifdef REALLY_NOISY_PUSHING |
|
506 nsFrame::IndentBy(stdout, mSpanDepth); |
|
507 printf("PushFrame %p, before:\n", psd); |
|
508 DumpPerSpanData(psd, 1); |
|
509 #endif |
|
510 |
|
511 // Take the last frame off of the span's frame list |
|
512 PerFrameData* pfd = psd->mLastFrame; |
|
513 if (pfd == psd->mFirstFrame) { |
|
514 // We are pushing away the only frame...empty the list |
|
515 psd->mFirstFrame = nullptr; |
|
516 psd->mLastFrame = nullptr; |
|
517 } |
|
518 else { |
|
519 PerFrameData* prevFrame = pfd->mPrev; |
|
520 prevFrame->mNext = nullptr; |
|
521 psd->mLastFrame = prevFrame; |
|
522 } |
|
523 |
|
524 // Now free it, and if it has a span, free that too |
|
525 pfd->mNext = mFrameFreeList; |
|
526 mFrameFreeList = pfd; |
|
527 #ifdef DEBUG |
|
528 mFramesFreed++; |
|
529 #endif |
|
530 if (nullptr != pfd->mSpan) { |
|
531 FreeSpan(pfd->mSpan); |
|
532 } |
|
533 #ifdef NOISY_PUSHING |
|
534 nsFrame::IndentBy(stdout, mSpanDepth); |
|
535 printf("PushFrame: %p after:\n", psd); |
|
536 DumpPerSpanData(psd, 1); |
|
537 #endif |
|
538 } |
|
539 |
|
540 void |
|
541 nsLineLayout::FreeSpan(PerSpanData* psd) |
|
542 { |
|
543 // Free its frames |
|
544 PerFrameData* pfd = psd->mFirstFrame; |
|
545 while (nullptr != pfd) { |
|
546 if (nullptr != pfd->mSpan) { |
|
547 FreeSpan(pfd->mSpan); |
|
548 } |
|
549 PerFrameData* next = pfd->mNext; |
|
550 pfd->mNext = mFrameFreeList; |
|
551 mFrameFreeList = pfd; |
|
552 #ifdef DEBUG |
|
553 mFramesFreed++; |
|
554 #endif |
|
555 pfd = next; |
|
556 } |
|
557 |
|
558 // Now put the span on the free list since it's free too |
|
559 psd->mNextFreeSpan = mSpanFreeList; |
|
560 mSpanFreeList = psd; |
|
561 #ifdef DEBUG |
|
562 mSpansFreed++; |
|
563 #endif |
|
564 } |
|
565 |
|
566 bool |
|
567 nsLineLayout::IsZeroBSize() |
|
568 { |
|
569 PerSpanData* psd = mCurrentSpan; |
|
570 PerFrameData* pfd = psd->mFirstFrame; |
|
571 while (nullptr != pfd) { |
|
572 if (0 != pfd->mBounds.BSize(psd->mWritingMode)) { |
|
573 return false; |
|
574 } |
|
575 pfd = pfd->mNext; |
|
576 } |
|
577 return true; |
|
578 } |
|
579 |
|
580 nsLineLayout::PerFrameData* |
|
581 nsLineLayout::NewPerFrameData(nsIFrame* aFrame) |
|
582 { |
|
583 PerFrameData* pfd = mFrameFreeList; |
|
584 if (!pfd) { |
|
585 void *mem; |
|
586 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerFrameData)); |
|
587 if (!mem) { |
|
588 NS_RUNTIMEABORT("OOM"); |
|
589 } |
|
590 pfd = reinterpret_cast<PerFrameData*>(mem); |
|
591 } |
|
592 else { |
|
593 mFrameFreeList = pfd->mNext; |
|
594 } |
|
595 pfd->mSpan = nullptr; |
|
596 pfd->mNext = nullptr; |
|
597 pfd->mPrev = nullptr; |
|
598 pfd->mFlags = 0; // all flags default to false |
|
599 pfd->mFrame = aFrame; |
|
600 |
|
601 WritingMode frameWM = aFrame->GetWritingMode(); |
|
602 WritingMode lineWM = mRootSpan->mWritingMode; |
|
603 pfd->mBounds = LogicalRect(lineWM); |
|
604 pfd->mMargin = LogicalMargin(frameWM); |
|
605 pfd->mBorderPadding = LogicalMargin(frameWM); |
|
606 pfd->mOffsets = LogicalMargin(frameWM); |
|
607 |
|
608 #ifdef DEBUG |
|
609 pfd->mBlockDirAlign = 0xFF; |
|
610 mFramesAllocated++; |
|
611 #endif |
|
612 return pfd; |
|
613 } |
|
614 |
|
615 bool |
|
616 nsLineLayout::LineIsBreakable() const |
|
617 { |
|
618 // XXX mTotalPlacedFrames should go away and we should just use |
|
619 // mLineIsEmpty here instead |
|
620 if ((0 != mTotalPlacedFrames) || mImpactedByFloats) { |
|
621 return true; |
|
622 } |
|
623 return false; |
|
624 } |
|
625 |
|
626 // Checks all four sides for percentage units. This means it should |
|
627 // only be used for things (margin, padding) where percentages on top |
|
628 // and bottom depend on the *width* just like percentages on left and |
|
629 // right. |
|
630 static bool |
|
631 HasPercentageUnitSide(const nsStyleSides& aSides) |
|
632 { |
|
633 NS_FOR_CSS_SIDES(side) { |
|
634 if (aSides.Get(side).HasPercent()) |
|
635 return true; |
|
636 } |
|
637 return false; |
|
638 } |
|
639 |
|
640 static bool |
|
641 IsPercentageAware(const nsIFrame* aFrame) |
|
642 { |
|
643 NS_ASSERTION(aFrame, "null frame is not allowed"); |
|
644 |
|
645 nsIAtom *fType = aFrame->GetType(); |
|
646 if (fType == nsGkAtoms::textFrame) { |
|
647 // None of these things can ever be true for text frames. |
|
648 return false; |
|
649 } |
|
650 |
|
651 // Some of these things don't apply to non-replaced inline frames |
|
652 // (that is, fType == nsGkAtoms::inlineFrame), but we won't bother making |
|
653 // things unnecessarily complicated, since they'll probably be set |
|
654 // quite rarely. |
|
655 |
|
656 const nsStyleMargin* margin = aFrame->StyleMargin(); |
|
657 if (HasPercentageUnitSide(margin->mMargin)) { |
|
658 return true; |
|
659 } |
|
660 |
|
661 const nsStylePadding* padding = aFrame->StylePadding(); |
|
662 if (HasPercentageUnitSide(padding->mPadding)) { |
|
663 return true; |
|
664 } |
|
665 |
|
666 // Note that borders can't be aware of percentages |
|
667 |
|
668 const nsStylePosition* pos = aFrame->StylePosition(); |
|
669 |
|
670 if ((pos->WidthDependsOnContainer() && |
|
671 pos->mWidth.GetUnit() != eStyleUnit_Auto) || |
|
672 pos->MaxWidthDependsOnContainer() || |
|
673 pos->MinWidthDependsOnContainer() || |
|
674 pos->OffsetHasPercent(NS_SIDE_RIGHT) || |
|
675 pos->OffsetHasPercent(NS_SIDE_LEFT)) { |
|
676 return true; |
|
677 } |
|
678 |
|
679 if (eStyleUnit_Auto == pos->mWidth.GetUnit()) { |
|
680 // We need to check for frames that shrink-wrap when they're auto |
|
681 // width. |
|
682 const nsStyleDisplay* disp = aFrame->StyleDisplay(); |
|
683 if (disp->mDisplay == NS_STYLE_DISPLAY_INLINE_BLOCK || |
|
684 disp->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE || |
|
685 fType == nsGkAtoms::HTMLButtonControlFrame || |
|
686 fType == nsGkAtoms::gfxButtonControlFrame || |
|
687 fType == nsGkAtoms::fieldSetFrame || |
|
688 fType == nsGkAtoms::comboboxDisplayFrame) { |
|
689 return true; |
|
690 } |
|
691 |
|
692 // Per CSS 2.1, section 10.3.2: |
|
693 // If 'height' and 'width' both have computed values of 'auto' and |
|
694 // the element has an intrinsic ratio but no intrinsic height or |
|
695 // width and the containing block's width does not itself depend |
|
696 // on the replaced element's width, then the used value of 'width' |
|
697 // is calculated from the constraint equation used for |
|
698 // block-level, non-replaced elements in normal flow. |
|
699 nsIFrame *f = const_cast<nsIFrame*>(aFrame); |
|
700 if (f->GetIntrinsicRatio() != nsSize(0, 0) && |
|
701 // Some percents are treated like 'auto', so check != coord |
|
702 pos->mHeight.GetUnit() != eStyleUnit_Coord) { |
|
703 const IntrinsicSize &intrinsicSize = f->GetIntrinsicSize(); |
|
704 if (intrinsicSize.width.GetUnit() == eStyleUnit_None && |
|
705 intrinsicSize.height.GetUnit() == eStyleUnit_None) { |
|
706 return true; |
|
707 } |
|
708 } |
|
709 } |
|
710 |
|
711 return false; |
|
712 } |
|
713 |
|
714 nsresult |
|
715 nsLineLayout::ReflowFrame(nsIFrame* aFrame, |
|
716 nsReflowStatus& aReflowStatus, |
|
717 nsHTMLReflowMetrics* aMetrics, |
|
718 bool& aPushedFrame) |
|
719 { |
|
720 // Initialize OUT parameter |
|
721 aPushedFrame = false; |
|
722 |
|
723 PerFrameData* pfd = NewPerFrameData(aFrame); |
|
724 PerSpanData* psd = mCurrentSpan; |
|
725 psd->AppendFrame(pfd); |
|
726 |
|
727 #ifdef REALLY_NOISY_REFLOW |
|
728 nsFrame::IndentBy(stdout, mSpanDepth); |
|
729 printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd); |
|
730 nsFrame::ListTag(stdout, aFrame); |
|
731 printf("\n"); |
|
732 #endif |
|
733 |
|
734 mTextJustificationNumSpaces = 0; |
|
735 mTextJustificationNumLetters = 0; |
|
736 |
|
737 // Stash copies of some of the computed state away for later |
|
738 // (block-direction alignment, for example) |
|
739 WritingMode frameWM = aFrame->GetWritingMode(); |
|
740 WritingMode lineWM = mRootSpan->mWritingMode; |
|
741 |
|
742 // NOTE: While the x coordinate remains relative to the parent span, |
|
743 // the y coordinate is fixed at the top edge for the line. During |
|
744 // BlockDirAlignFrames we will repair this so that the y coordinate |
|
745 // is properly set and relative to the appropriate span. |
|
746 pfd->mBounds.IStart(lineWM) = psd->mICoord; |
|
747 pfd->mBounds.BStart(lineWM) = mBStartEdge; |
|
748 |
|
749 // We want to guarantee that we always make progress when |
|
750 // formatting. Therefore, if the object being placed on the line is |
|
751 // too big for the line, but it is the only thing on the line and is not |
|
752 // impacted by a float, then we go ahead and place it anyway. (If the line |
|
753 // is impacted by one or more floats, then it is safe to break because |
|
754 // we can move the line down below float(s).) |
|
755 // |
|
756 // Capture this state *before* we reflow the frame in case it clears |
|
757 // the state out. We need to know how to treat the current frame |
|
758 // when breaking. |
|
759 bool notSafeToBreak = LineIsEmpty() && !mImpactedByFloats; |
|
760 |
|
761 // Figure out whether we're talking about a textframe here |
|
762 nsIAtom* frameType = aFrame->GetType(); |
|
763 bool isText = frameType == nsGkAtoms::textFrame; |
|
764 |
|
765 // Compute the available size for the frame. This available width |
|
766 // includes room for the side margins. |
|
767 // For now, set the available height to unconstrained always. |
|
768 nsSize availSize(mBlockReflowState->ComputedWidth(), NS_UNCONSTRAINEDSIZE); |
|
769 |
|
770 // Inline-ish and text-ish things don't compute their width; |
|
771 // everything else does. We need to give them an available width that |
|
772 // reflects the space left on the line. |
|
773 NS_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE, |
|
774 "have unconstrained width; this should only result from " |
|
775 "very large sizes, not attempts at intrinsic width " |
|
776 "calculation"); |
|
777 nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord; |
|
778 |
|
779 // Setup reflow state for reflowing the frame |
|
780 Maybe<nsHTMLReflowState> reflowStateHolder; |
|
781 if (!isText) { |
|
782 reflowStateHolder.construct(mPresContext, *psd->mReflowState, |
|
783 aFrame, availSize); |
|
784 nsHTMLReflowState& reflowState = reflowStateHolder.ref(); |
|
785 reflowState.mLineLayout = this; |
|
786 reflowState.mFlags.mIsTopOfPage = mIsTopOfPage; |
|
787 if (reflowState.ComputedWidth() == NS_UNCONSTRAINEDSIZE) |
|
788 reflowState.AvailableWidth() = availableSpaceOnLine; |
|
789 WritingMode stateWM = reflowState.GetWritingMode(); |
|
790 pfd->mMargin = |
|
791 reflowState.ComputedLogicalMargin().ConvertTo(frameWM, stateWM); |
|
792 pfd->mBorderPadding = |
|
793 reflowState.ComputedLogicalBorderPadding().ConvertTo(frameWM, stateWM); |
|
794 pfd->SetFlag(PFD_RELATIVEPOS, |
|
795 reflowState.mStyleDisplay->IsRelativelyPositionedStyle()); |
|
796 if (pfd->GetFlag(PFD_RELATIVEPOS)) { |
|
797 pfd->mOffsets = |
|
798 reflowState.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM); |
|
799 } |
|
800 |
|
801 // Calculate whether the the frame should have a start margin and |
|
802 // subtract the margin from the available width if necessary. |
|
803 // The margin will be applied to the starting inline coordinates of |
|
804 // the frame in CanPlaceFrame() after reflowing the frame. |
|
805 AllowForStartMargin(pfd, reflowState); |
|
806 } |
|
807 // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent, |
|
808 // because reflow doesn't look at the dirty bits on the frame being reflowed. |
|
809 |
|
810 // See if this frame depends on the width of its containing block. If |
|
811 // so, disable resize reflow optimizations for the line. (Note that, |
|
812 // to be conservative, we do this if we *try* to fit a frame on a |
|
813 // line, even if we don't succeed.) (Note also that we can only make |
|
814 // this IsPercentageAware check *after* we've constructed our |
|
815 // nsHTMLReflowState, because that construction may be what forces aFrame |
|
816 // to lazily initialize its (possibly-percent-valued) intrinsic size.) |
|
817 if (mGotLineBox && IsPercentageAware(aFrame)) { |
|
818 mLineBox->DisableResizeReflowOptimization(); |
|
819 } |
|
820 |
|
821 // Let frame know that are reflowing it. Note that we don't bother |
|
822 // positioning the frame yet, because we're probably going to end up |
|
823 // moving it when we do the block-direction alignment |
|
824 aFrame->WillReflow(mPresContext); |
|
825 |
|
826 // Adjust spacemanager coordinate system for the frame. |
|
827 nsHTMLReflowMetrics metrics(lineWM); |
|
828 #ifdef DEBUG |
|
829 metrics.Width() = nscoord(0xdeadbeef); |
|
830 metrics.Height() = nscoord(0xdeadbeef); |
|
831 #endif |
|
832 nsRect physicalBounds = pfd->mBounds.GetPhysicalRect(lineWM, mContainerWidth); |
|
833 nscoord tx = physicalBounds.x; |
|
834 nscoord ty = physicalBounds.y; |
|
835 mFloatManager->Translate(tx, ty); |
|
836 |
|
837 int32_t savedOptionalBreakOffset; |
|
838 gfxBreakPriority savedOptionalBreakPriority; |
|
839 nsIContent* savedOptionalBreakContent = |
|
840 GetLastOptionalBreakPosition(&savedOptionalBreakOffset, |
|
841 &savedOptionalBreakPriority); |
|
842 |
|
843 if (!isText) { |
|
844 nsresult rv = aFrame->Reflow(mPresContext, metrics, reflowStateHolder.ref(), |
|
845 aReflowStatus); |
|
846 if (NS_FAILED(rv)) { |
|
847 NS_WARNING( "Reflow of frame failed in nsLineLayout" ); |
|
848 return rv; |
|
849 } |
|
850 } else { |
|
851 static_cast<nsTextFrame*>(aFrame)-> |
|
852 ReflowText(*this, availableSpaceOnLine, psd->mReflowState->rendContext, |
|
853 metrics, aReflowStatus); |
|
854 } |
|
855 |
|
856 pfd->mJustificationNumSpaces = mTextJustificationNumSpaces; |
|
857 pfd->mJustificationNumLetters = mTextJustificationNumLetters; |
|
858 |
|
859 // See if the frame is a placeholderFrame and if it is process |
|
860 // the float. At the same time, check if the frame has any non-collapsed-away |
|
861 // content. |
|
862 bool placedFloat = false; |
|
863 bool isEmpty; |
|
864 if (!frameType) { |
|
865 isEmpty = pfd->mFrame->IsEmpty(); |
|
866 } else { |
|
867 if (nsGkAtoms::placeholderFrame == frameType) { |
|
868 isEmpty = true; |
|
869 pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true); |
|
870 nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); |
|
871 if (outOfFlowFrame) { |
|
872 // Add mTrimmableWidth to the available width since if the line ends |
|
873 // here, the width of the inline content will be reduced by |
|
874 // mTrimmableWidth. |
|
875 nscoord availableWidth = psd->mIEnd - (psd->mICoord - mTrimmableWidth); |
|
876 if (psd->mNoWrap) { |
|
877 // If we place floats after inline content where there's |
|
878 // no break opportunity, we don't know how much additional |
|
879 // width is required for the non-breaking content after the float, |
|
880 // so we can't know whether the float plus that content will fit |
|
881 // on the line. So for now, don't place floats after inline |
|
882 // content where there's no break opportunity. This is incorrect |
|
883 // but hopefully rare. Fixing it will require significant |
|
884 // restructuring of line layout. |
|
885 // We might as well allow zero-width floats to be placed, though. |
|
886 availableWidth = 0; |
|
887 } |
|
888 placedFloat = AddFloat(outOfFlowFrame, availableWidth); |
|
889 NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame && |
|
890 GetFirstLetterStyleOK()), |
|
891 "FirstLetterStyle set on line with floating first letter"); |
|
892 } |
|
893 } |
|
894 else if (isText) { |
|
895 // Note non-empty text-frames for inline frame compatibility hackery |
|
896 pfd->SetFlag(PFD_ISTEXTFRAME, true); |
|
897 nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame); |
|
898 isEmpty = !textFrame->HasNoncollapsedCharacters(); |
|
899 if (!isEmpty) { |
|
900 pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, true); |
|
901 nsIContent* content = textFrame->GetContent(); |
|
902 |
|
903 const nsTextFragment* frag = content->GetText(); |
|
904 if (frag) { |
|
905 pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, |
|
906 !content->TextIsOnlyWhitespace()); |
|
907 } |
|
908 } |
|
909 } |
|
910 else if (nsGkAtoms::brFrame == frameType) { |
|
911 pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true); |
|
912 isEmpty = false; |
|
913 } else { |
|
914 if (nsGkAtoms::letterFrame==frameType) { |
|
915 pfd->SetFlag(PFD_ISLETTERFRAME, true); |
|
916 } |
|
917 if (pfd->mSpan) { |
|
918 isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty(); |
|
919 } else { |
|
920 isEmpty = pfd->mFrame->IsEmpty(); |
|
921 } |
|
922 } |
|
923 } |
|
924 |
|
925 mFloatManager->Translate(-tx, -ty); |
|
926 |
|
927 NS_ASSERTION(metrics.Width() >= 0, "bad width"); |
|
928 NS_ASSERTION(metrics.Height() >= 0,"bad height"); |
|
929 if (metrics.Width() < 0) metrics.Width() = 0; |
|
930 if (metrics.Height() < 0) metrics.Height() = 0; |
|
931 |
|
932 #ifdef DEBUG |
|
933 // Note: break-before means ignore the reflow metrics since the |
|
934 // frame will be reflowed another time. |
|
935 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { |
|
936 if (CRAZY_SIZE(metrics.Width()) || CRAZY_SIZE(metrics.Height())) { |
|
937 printf("nsLineLayout: "); |
|
938 nsFrame::ListTag(stdout, aFrame); |
|
939 printf(" metrics=%d,%d!\n", metrics.Width(), metrics.Height()); |
|
940 } |
|
941 if ((metrics.Width() == nscoord(0xdeadbeef)) || |
|
942 (metrics.Height() == nscoord(0xdeadbeef))) { |
|
943 printf("nsLineLayout: "); |
|
944 nsFrame::ListTag(stdout, aFrame); |
|
945 printf(" didn't set w/h %d,%d!\n", metrics.Width(), metrics.Height()); |
|
946 } |
|
947 } |
|
948 #endif |
|
949 |
|
950 // Unlike with non-inline reflow, the overflow area here does *not* |
|
951 // include the accumulation of the frame's bounds and its inline |
|
952 // descendants' bounds. Nor does it include the outline area; it's |
|
953 // just the union of the bounds of any absolute children. That is |
|
954 // added in later by nsLineLayout::ReflowInlineFrames. |
|
955 pfd->mOverflowAreas = metrics.mOverflowAreas; |
|
956 |
|
957 pfd->mBounds.ISize(lineWM) = metrics.ISize(); |
|
958 pfd->mBounds.BSize(lineWM) = metrics.BSize(); |
|
959 |
|
960 // Size the frame, but |RelativePositionFrames| will size the view. |
|
961 aFrame->SetSize(nsSize(metrics.Width(), metrics.Height())); |
|
962 |
|
963 // Tell the frame that we're done reflowing it |
|
964 aFrame->DidReflow(mPresContext, |
|
965 isText ? nullptr : reflowStateHolder.addr(), |
|
966 nsDidReflowStatus::FINISHED); |
|
967 |
|
968 if (aMetrics) { |
|
969 *aMetrics = metrics; |
|
970 } |
|
971 |
|
972 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { |
|
973 // If frame is complete and has a next-in-flow, we need to delete |
|
974 // them now. Do not do this when a break-before is signaled because |
|
975 // the frame is going to get reflowed again (and may end up wanting |
|
976 // a next-in-flow where it ends up). |
|
977 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { |
|
978 nsIFrame* kidNextInFlow = aFrame->GetNextInFlow(); |
|
979 if (nullptr != kidNextInFlow) { |
|
980 // Remove all of the childs next-in-flows. Make sure that we ask |
|
981 // the right parent to do the removal (it's possible that the |
|
982 // parent is not this because we are executing pullup code) |
|
983 nsContainerFrame* parent = static_cast<nsContainerFrame*> |
|
984 (kidNextInFlow->GetParent()); |
|
985 parent->DeleteNextInFlowChild(kidNextInFlow, true); |
|
986 } |
|
987 } |
|
988 |
|
989 // Check whether this frame breaks up text runs. All frames break up text |
|
990 // runs (hence return false here) except for text frames and inline containers. |
|
991 bool continuingTextRun = aFrame->CanContinueTextRun(); |
|
992 |
|
993 // Clear any residual mTrimmableWidth if this isn't a text frame |
|
994 if (!continuingTextRun && !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) { |
|
995 mTrimmableWidth = 0; |
|
996 } |
|
997 |
|
998 // See if we can place the frame. If we can't fit it, then we |
|
999 // return now. |
|
1000 bool optionalBreakAfterFits; |
|
1001 NS_ASSERTION(isText || |
|
1002 !reflowStateHolder.ref().IsFloating(), |
|
1003 "How'd we get a floated inline frame? " |
|
1004 "The frame ctor should've dealt with this."); |
|
1005 if (CanPlaceFrame(pfd, notSafeToBreak, continuingTextRun, |
|
1006 savedOptionalBreakContent != nullptr, metrics, |
|
1007 aReflowStatus, &optionalBreakAfterFits)) { |
|
1008 if (!isEmpty) { |
|
1009 psd->mHasNonemptyContent = true; |
|
1010 mLineIsEmpty = false; |
|
1011 if (!pfd->mSpan) { |
|
1012 // nonempty leaf content has been placed |
|
1013 mLineAtStart = false; |
|
1014 } |
|
1015 } |
|
1016 |
|
1017 // Place the frame, updating aBounds with the final size and |
|
1018 // location. Then apply the bottom+right margins (as |
|
1019 // appropriate) to the frame. |
|
1020 PlaceFrame(pfd, metrics); |
|
1021 PerSpanData* span = pfd->mSpan; |
|
1022 if (span) { |
|
1023 // The frame we just finished reflowing is an inline |
|
1024 // container. It needs its child frames aligned in the block direction, |
|
1025 // so do most of it now. |
|
1026 BlockDirAlignFrames(span); |
|
1027 } |
|
1028 |
|
1029 if (!continuingTextRun) { |
|
1030 if (!psd->mNoWrap && (!LineIsEmpty() || placedFloat)) { |
|
1031 // record soft break opportunity after this content that can't be |
|
1032 // part of a text run. This is not a text frame so we know |
|
1033 // that offset INT32_MAX means "after the content". |
|
1034 if (NotifyOptionalBreakPosition(aFrame->GetContent(), INT32_MAX, optionalBreakAfterFits, gfxBreakPriority::eNormalBreak)) { |
|
1035 // If this returns true then we are being told to actually break here. |
|
1036 aReflowStatus = NS_INLINE_LINE_BREAK_AFTER(aReflowStatus); |
|
1037 } |
|
1038 } |
|
1039 } |
|
1040 } |
|
1041 else { |
|
1042 PushFrame(aFrame); |
|
1043 aPushedFrame = true; |
|
1044 // Undo any saved break positions that the frame might have told us about, |
|
1045 // since we didn't end up placing it |
|
1046 RestoreSavedBreakPosition(savedOptionalBreakContent, |
|
1047 savedOptionalBreakOffset, |
|
1048 savedOptionalBreakPriority); |
|
1049 } |
|
1050 } |
|
1051 else { |
|
1052 PushFrame(aFrame); |
|
1053 } |
|
1054 |
|
1055 #ifdef REALLY_NOISY_REFLOW |
|
1056 nsFrame::IndentBy(stdout, mSpanDepth); |
|
1057 printf("End ReflowFrame "); |
|
1058 nsFrame::ListTag(stdout, aFrame); |
|
1059 printf(" status=%x\n", aReflowStatus); |
|
1060 #endif |
|
1061 |
|
1062 return NS_OK; |
|
1063 } |
|
1064 |
|
1065 void |
|
1066 nsLineLayout::AllowForStartMargin(PerFrameData* pfd, |
|
1067 nsHTMLReflowState& aReflowState) |
|
1068 { |
|
1069 NS_ASSERTION(!aReflowState.IsFloating(), |
|
1070 "How'd we get a floated inline frame? " |
|
1071 "The frame ctor should've dealt with this."); |
|
1072 |
|
1073 WritingMode frameWM = pfd->mFrame->GetWritingMode(); |
|
1074 |
|
1075 // Only apply start-margin on the first-in flow for inline frames, |
|
1076 // and make sure to not apply it to any inline other than the first |
|
1077 // in an ib split. Note that the ib sibling (block-in-inline |
|
1078 // sibling) annotations only live on the first continuation, but we |
|
1079 // don't want to apply the start margin for later continuations |
|
1080 // anyway. |
|
1081 if (pfd->mFrame->GetPrevContinuation() || |
|
1082 pfd->mFrame->FrameIsNonFirstInIBSplit()) { |
|
1083 // Zero this out so that when we compute the max-element-width of |
|
1084 // the frame we will properly avoid adding in the starting margin. |
|
1085 pfd->mMargin.IStart(frameWM) = 0; |
|
1086 } else { |
|
1087 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth(), |
|
1088 "have unconstrained width; this should only result from " |
|
1089 "very large sizes, not attempts at intrinsic width " |
|
1090 "calculation"); |
|
1091 if (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth()) { |
|
1092 // For inline-ish and text-ish things (which don't compute widths |
|
1093 // in the reflow state), adjust available width to account for the |
|
1094 // start margin. The end margin will be accounted for when we |
|
1095 // finish flowing the frame. |
|
1096 aReflowState.AvailableWidth() -= pfd->mMargin.IStart(frameWM); |
|
1097 } |
|
1098 } |
|
1099 } |
|
1100 |
|
1101 nscoord |
|
1102 nsLineLayout::GetCurrentFrameInlineDistanceFromBlock() |
|
1103 { |
|
1104 PerSpanData* psd; |
|
1105 nscoord x = 0; |
|
1106 for (psd = mCurrentSpan; psd; psd = psd->mParent) { |
|
1107 x += psd->mICoord; |
|
1108 } |
|
1109 return x; |
|
1110 } |
|
1111 |
|
1112 /** |
|
1113 * See if the frame can be placed now that we know it's desired size. |
|
1114 * We can always place the frame if the line is empty. Note that we |
|
1115 * know that the reflow-status is not a break-before because if it was |
|
1116 * ReflowFrame above would have returned false, preventing this method |
|
1117 * from being called. The logic in this method assumes that. |
|
1118 * |
|
1119 * Note that there is no check against the Y coordinate because we |
|
1120 * assume that the caller will take care of that. |
|
1121 */ |
|
1122 bool |
|
1123 nsLineLayout::CanPlaceFrame(PerFrameData* pfd, |
|
1124 bool aNotSafeToBreak, |
|
1125 bool aFrameCanContinueTextRun, |
|
1126 bool aCanRollBackBeforeFrame, |
|
1127 nsHTMLReflowMetrics& aMetrics, |
|
1128 nsReflowStatus& aStatus, |
|
1129 bool* aOptionalBreakAfterFits) |
|
1130 { |
|
1131 NS_PRECONDITION(pfd && pfd->mFrame, "bad args, null pointers for frame data"); |
|
1132 |
|
1133 *aOptionalBreakAfterFits = true; |
|
1134 |
|
1135 WritingMode frameWM = pfd->mFrame->GetWritingMode(); |
|
1136 WritingMode lineWM = mRootSpan->mWritingMode; |
|
1137 /* |
|
1138 * We want to only apply the end margin if we're the last continuation and |
|
1139 * either not in an {ib} split or the last inline in it. In all other |
|
1140 * cases we want to zero it out. That means zeroing it out if any of these |
|
1141 * conditions hold: |
|
1142 * 1) The frame is not complete (in this case it will get a next-in-flow) |
|
1143 * 2) The frame is complete but has a non-fluid continuation on its |
|
1144 * continuation chain. Note that if it has a fluid continuation, that |
|
1145 * continuation will get destroyed later, so we don't want to drop the |
|
1146 * end-margin in that case. |
|
1147 * 3) The frame is in an {ib} split and is not the last part. |
|
1148 * |
|
1149 * However, none of that applies if this is a letter frame (XXXbz why?) |
|
1150 */ |
|
1151 if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) || |
|
1152 pfd->mFrame->LastInFlow()->GetNextContinuation() || |
|
1153 pfd->mFrame->FrameIsNonLastInIBSplit()) |
|
1154 && !pfd->GetFlag(PFD_ISLETTERFRAME)) { |
|
1155 pfd->mMargin.IEnd(frameWM) = 0; |
|
1156 } |
|
1157 |
|
1158 // Convert the frame's margins to the line's writing mode and apply |
|
1159 // the start margin to the frame bounds. |
|
1160 LogicalMargin usedMargins = pfd->mMargin.ConvertTo(lineWM, frameWM); |
|
1161 nscoord startMargin = usedMargins.IStart(lineWM); |
|
1162 nscoord endMargin = usedMargins.IEnd(lineWM); |
|
1163 |
|
1164 pfd->mBounds.IStart(lineWM) += startMargin; |
|
1165 |
|
1166 PerSpanData* psd = mCurrentSpan; |
|
1167 if (psd->mNoWrap) { |
|
1168 // When wrapping is off, everything fits. |
|
1169 return true; |
|
1170 } |
|
1171 |
|
1172 #ifdef NOISY_CAN_PLACE_FRAME |
|
1173 if (nullptr != psd->mFrame) { |
|
1174 nsFrame::ListTag(stdout, psd->mFrame->mFrame); |
|
1175 } |
|
1176 else { |
|
1177 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
1178 } |
|
1179 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false"); |
|
1180 nsFrame::ListTag(stdout, pfd->mFrame); |
|
1181 printf(" frameWidth=%d, margins=%d,%d\n", |
|
1182 pfd->mBounds.IEnd(lineWM) + endMargin - psd->mICoord, |
|
1183 startMargin, endMargin); |
|
1184 #endif |
|
1185 |
|
1186 // Set outside to true if the result of the reflow leads to the |
|
1187 // frame sticking outside of our available area. |
|
1188 bool outside = pfd->mBounds.IEnd(lineWM) - mTrimmableWidth + endMargin > |
|
1189 psd->mIEnd; |
|
1190 if (!outside) { |
|
1191 // If it fits, it fits |
|
1192 #ifdef NOISY_CAN_PLACE_FRAME |
|
1193 printf(" ==> inside\n"); |
|
1194 #endif |
|
1195 return true; |
|
1196 } |
|
1197 *aOptionalBreakAfterFits = false; |
|
1198 |
|
1199 // When it doesn't fit, check for a few special conditions where we |
|
1200 // allow it to fit anyway. |
|
1201 if (0 == startMargin + pfd->mBounds.ISize(lineWM) + endMargin) { |
|
1202 // Empty frames always fit right where they are |
|
1203 #ifdef NOISY_CAN_PLACE_FRAME |
|
1204 printf(" ==> empty frame fits\n"); |
|
1205 #endif |
|
1206 return true; |
|
1207 } |
|
1208 |
|
1209 #ifdef FIX_BUG_50257 |
|
1210 // another special case: always place a BR |
|
1211 if (nsGkAtoms::brFrame == pfd->mFrame->GetType()) { |
|
1212 #ifdef NOISY_CAN_PLACE_FRAME |
|
1213 printf(" ==> BR frame fits\n"); |
|
1214 #endif |
|
1215 return true; |
|
1216 } |
|
1217 #endif |
|
1218 |
|
1219 if (aNotSafeToBreak) { |
|
1220 // There are no frames on the line that take up width and the line is |
|
1221 // not impacted by floats, so we must allow the current frame to be |
|
1222 // placed on the line |
|
1223 #ifdef NOISY_CAN_PLACE_FRAME |
|
1224 printf(" ==> not-safe and not-impacted fits: "); |
|
1225 while (nullptr != psd) { |
|
1226 printf("<psd=%p x=%d left=%d> ", psd, psd->mICoord, psd->mIStart); |
|
1227 psd = psd->mParent; |
|
1228 } |
|
1229 printf("\n"); |
|
1230 #endif |
|
1231 return true; |
|
1232 } |
|
1233 |
|
1234 // Special check for span frames |
|
1235 if (pfd->mSpan && pfd->mSpan->mContainsFloat) { |
|
1236 // If the span either directly or indirectly contains a float then |
|
1237 // it fits. Why? It's kind of complicated, but here goes: |
|
1238 // |
|
1239 // 1. CanPlaceFrame is used for all frame placements on a line, |
|
1240 // and in a span. This includes recursively placement of frames |
|
1241 // inside of spans, and the span itself. Because the logic always |
|
1242 // checks for room before proceeding (the code above here), the |
|
1243 // only things on a line will be those things that "fit". |
|
1244 // |
|
1245 // 2. Before a float is placed on a line, the line has to be empty |
|
1246 // (otherwise it's a "below current line" float and will be placed |
|
1247 // after the line). |
|
1248 // |
|
1249 // Therefore, if the span directly or indirectly has a float |
|
1250 // then it means that at the time of the placement of the float |
|
1251 // the line was empty. Because of #1, only the frames that fit can |
|
1252 // be added after that point, therefore we can assume that the |
|
1253 // current span being placed has fit. |
|
1254 // |
|
1255 // So how do we get here and have a span that should already fit |
|
1256 // and yet doesn't: Simple: span's that have the no-wrap attribute |
|
1257 // set on them and contain a float and are placed where they |
|
1258 // don't naturally fit. |
|
1259 return true; |
|
1260 } |
|
1261 |
|
1262 if (aFrameCanContinueTextRun) { |
|
1263 // Let it fit, but we reserve the right to roll back. |
|
1264 // Note that we usually won't get here because a text frame will break |
|
1265 // itself to avoid exceeding the available width. |
|
1266 // We'll only get here for text frames that couldn't break early enough. |
|
1267 #ifdef NOISY_CAN_PLACE_FRAME |
|
1268 printf(" ==> placing overflowing textrun, requesting backup\n"); |
|
1269 #endif |
|
1270 |
|
1271 // We will want to try backup. |
|
1272 mNeedBackup = true; |
|
1273 return true; |
|
1274 } |
|
1275 |
|
1276 #ifdef NOISY_CAN_PLACE_FRAME |
|
1277 printf(" ==> didn't fit\n"); |
|
1278 #endif |
|
1279 aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
|
1280 return false; |
|
1281 } |
|
1282 |
|
1283 /** |
|
1284 * Place the frame. Update running counters. |
|
1285 */ |
|
1286 void |
|
1287 nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics) |
|
1288 { |
|
1289 // Record ascent and update max-ascent and max-descent values |
|
1290 if (aMetrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) |
|
1291 pfd->mAscent = pfd->mFrame->GetBaseline(); |
|
1292 else |
|
1293 pfd->mAscent = aMetrics.TopAscent(); |
|
1294 |
|
1295 // Advance to next inline coordinate |
|
1296 WritingMode frameWM = pfd->mFrame->GetWritingMode(); |
|
1297 WritingMode lineWM = mRootSpan->mWritingMode; |
|
1298 mCurrentSpan->mICoord = pfd->mBounds.IEnd(lineWM) + |
|
1299 pfd->mMargin.ConvertTo(lineWM, frameWM).IEnd(lineWM); |
|
1300 |
|
1301 // Count the number of non-placeholder frames on the line... |
|
1302 if (pfd->mFrame->GetType() == nsGkAtoms::placeholderFrame) { |
|
1303 NS_ASSERTION(pfd->mBounds.ISize(lineWM) == 0 && |
|
1304 pfd->mBounds.BSize(lineWM) == 0, |
|
1305 "placeholders should have 0 width/height (checking " |
|
1306 "placeholders were never counted by the old code in " |
|
1307 "this function)"); |
|
1308 } else { |
|
1309 mTotalPlacedFrames++; |
|
1310 } |
|
1311 } |
|
1312 |
|
1313 void |
|
1314 nsLineLayout::AddBulletFrame(nsIFrame* aFrame, |
|
1315 const nsHTMLReflowMetrics& aMetrics) |
|
1316 { |
|
1317 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); |
|
1318 NS_ASSERTION(mGotLineBox, "must have line box"); |
|
1319 |
|
1320 nsIFrame *blockFrame = mBlockReflowState->frame; |
|
1321 NS_ASSERTION(blockFrame->IsFrameOfType(nsIFrame::eBlockFrame), |
|
1322 "must be for block"); |
|
1323 if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) { |
|
1324 mHasBullet = true; |
|
1325 mLineBox->SetHasBullet(); |
|
1326 } |
|
1327 |
|
1328 PerFrameData* pfd = NewPerFrameData(aFrame); |
|
1329 mRootSpan->AppendFrame(pfd); |
|
1330 pfd->SetFlag(PFD_ISBULLET, true); |
|
1331 if (aMetrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) |
|
1332 pfd->mAscent = aFrame->GetBaseline(); |
|
1333 else |
|
1334 pfd->mAscent = aMetrics.TopAscent(); |
|
1335 |
|
1336 // Note: block-coord value will be updated during block-direction alignment |
|
1337 pfd->mBounds = LogicalRect(mRootSpan->mWritingMode, |
|
1338 aFrame->GetRect(), mContainerWidth); |
|
1339 pfd->mOverflowAreas = aMetrics.mOverflowAreas; |
|
1340 } |
|
1341 |
|
1342 #ifdef DEBUG |
|
1343 void |
|
1344 nsLineLayout::DumpPerSpanData(PerSpanData* psd, int32_t aIndent) |
|
1345 { |
|
1346 nsFrame::IndentBy(stdout, aIndent); |
|
1347 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd), |
|
1348 psd->mIStart, psd->mICoord, psd->mIEnd); |
|
1349 PerFrameData* pfd = psd->mFirstFrame; |
|
1350 while (nullptr != pfd) { |
|
1351 nsFrame::IndentBy(stdout, aIndent+1); |
|
1352 nsFrame::ListTag(stdout, pfd->mFrame); |
|
1353 nsRect rect = pfd->mBounds.GetPhysicalRect(psd->mWritingMode, |
|
1354 mContainerWidth); |
|
1355 printf(" %d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); |
|
1356 if (pfd->mSpan) { |
|
1357 DumpPerSpanData(pfd->mSpan, aIndent + 1); |
|
1358 } |
|
1359 pfd = pfd->mNext; |
|
1360 } |
|
1361 } |
|
1362 #endif |
|
1363 |
|
1364 #define VALIGN_OTHER 0 |
|
1365 #define VALIGN_TOP 1 |
|
1366 #define VALIGN_BOTTOM 2 |
|
1367 |
|
1368 void |
|
1369 nsLineLayout::BlockDirAlignLine() |
|
1370 { |
|
1371 // Synthesize a PerFrameData for the block frame |
|
1372 PerFrameData rootPFD(mBlockReflowState->frame->GetWritingMode()); |
|
1373 rootPFD.mFrame = mBlockReflowState->frame; |
|
1374 rootPFD.mAscent = 0; |
|
1375 mRootSpan->mFrame = &rootPFD; |
|
1376 |
|
1377 // Partially place the children of the block frame. The baseline for |
|
1378 // this operation is set to zero so that the y coordinates for all |
|
1379 // of the placed children will be relative to there. |
|
1380 PerSpanData* psd = mRootSpan; |
|
1381 BlockDirAlignFrames(psd); |
|
1382 |
|
1383 // *** Note that comments here still use the anachronistic term "line-height" |
|
1384 // when we really mean "size of the line in the block direction". This is |
|
1385 // partly for brevity and partly to retain the association with the CSS |
|
1386 // line-height property |
|
1387 // |
|
1388 // Compute the line-height. The line-height will be the larger of: |
|
1389 // |
|
1390 // [1] maxBCoord - minBCoord (the distance between the first child's |
|
1391 // block-start edge and the last child's block-end edge) |
|
1392 // |
|
1393 // [2] the maximum logical box block size (since not every frame may have |
|
1394 // participated in #1; for example: block-start/end aligned frames) |
|
1395 // |
|
1396 // [3] the minimum line height ("line-height" property set on the |
|
1397 // block frame) |
|
1398 nscoord lineBSize = psd->mMaxBCoord - psd->mMinBCoord; |
|
1399 |
|
1400 // Now that the line-height is computed, we need to know where the |
|
1401 // baseline is in the line. Position baseline so that mMinBCoord is just |
|
1402 // inside the start of the line box. |
|
1403 nscoord baselineBCoord; |
|
1404 if (psd->mMinBCoord < 0) { |
|
1405 baselineBCoord = mBStartEdge - psd->mMinBCoord; |
|
1406 } |
|
1407 else { |
|
1408 baselineBCoord = mBStartEdge; |
|
1409 } |
|
1410 |
|
1411 // It's also possible that the line block-size isn't tall enough because |
|
1412 // of block start/end aligned elements that were not accounted for in |
|
1413 // min/max BCoord. |
|
1414 // |
|
1415 // The CSS2 spec doesn't really say what happens when to the |
|
1416 // baseline in this situations. What we do is if the largest start |
|
1417 // aligned box block size is greater than the line block-size then we leave |
|
1418 // the baseline alone. If the largest end aligned box is greater |
|
1419 // than the line block-size then we slide the baseline forward by the extra |
|
1420 // amount. |
|
1421 // |
|
1422 // Navigator 4 gives precedence to the first top/bottom aligned |
|
1423 // object. We just let block end aligned objects win. |
|
1424 if (lineBSize < mMaxEndBoxBSize) { |
|
1425 // When the line is shorter than the maximum block start aligned box |
|
1426 nscoord extra = mMaxEndBoxBSize - lineBSize; |
|
1427 baselineBCoord += extra; |
|
1428 lineBSize = mMaxEndBoxBSize; |
|
1429 } |
|
1430 if (lineBSize < mMaxStartBoxBSize) { |
|
1431 lineBSize = mMaxStartBoxBSize; |
|
1432 } |
|
1433 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1434 printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize, baselineBCoord); |
|
1435 #endif |
|
1436 |
|
1437 // Now position all of the frames in the root span. We will also |
|
1438 // recurse over the child spans and place any block start/end aligned |
|
1439 // frames we find. |
|
1440 // XXX PERFORMANCE: set a bit per-span to avoid the extra work |
|
1441 // (propagate it upward too) |
|
1442 WritingMode lineWM = psd->mWritingMode; |
|
1443 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
1444 if (pfd->mBlockDirAlign == VALIGN_OTHER) { |
|
1445 pfd->mBounds.BStart(lineWM) += baselineBCoord; |
|
1446 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
1447 } |
|
1448 } |
|
1449 PlaceStartEndFrames(psd, -mBStartEdge, lineBSize); |
|
1450 |
|
1451 // If the frame being reflowed has text decorations, we simulate the |
|
1452 // propagation of those decorations to a line-level element by storing the |
|
1453 // offset in a frame property on any child frames that are aligned in the |
|
1454 // block direction somewhere other than the baseline. This property is then |
|
1455 // used by nsTextFrame::GetTextDecorations when the same conditions are met. |
|
1456 if (rootPFD.mFrame->StyleContext()->HasTextDecorationLines()) { |
|
1457 for (const PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
1458 const nsIFrame *const f = pfd->mFrame; |
|
1459 if (f->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) { |
|
1460 const nscoord offset = baselineBCoord - pfd->mBounds.BStart(lineWM); |
|
1461 f->Properties().Set(nsIFrame::LineBaselineOffset(), |
|
1462 NS_INT32_TO_PTR(offset)); |
|
1463 } |
|
1464 } |
|
1465 } |
|
1466 |
|
1467 // Fill in returned line-box and max-element-width data |
|
1468 mLineBox->SetBounds(lineWM, |
|
1469 psd->mIStart, mBStartEdge, |
|
1470 psd->mICoord - psd->mIStart, lineBSize, |
|
1471 mContainerWidth); |
|
1472 |
|
1473 mFinalLineBSize = lineBSize; |
|
1474 mLineBox->SetAscent(baselineBCoord - mBStartEdge); |
|
1475 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1476 printf( |
|
1477 " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n", |
|
1478 mLineBox->mBounds.IStart(lineWM), mLineBox->mBounds.BStart(lineWM), |
|
1479 mLineBox->mBounds.ISize(lineWM), mLineBox->mBounds.BSize(lineWM), |
|
1480 mFinalLineBSize, mLineBox->GetAscent()); |
|
1481 #endif |
|
1482 |
|
1483 // Undo root-span mFrame pointer to prevent brane damage later on... |
|
1484 mRootSpan->mFrame = nullptr; |
|
1485 } |
|
1486 |
|
1487 void |
|
1488 nsLineLayout::PlaceStartEndFrames(PerSpanData* psd, |
|
1489 nscoord aDistanceFromStart, |
|
1490 nscoord aLineBSize) |
|
1491 { |
|
1492 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
1493 PerSpanData* span = pfd->mSpan; |
|
1494 #ifdef DEBUG |
|
1495 NS_ASSERTION(0xFF != pfd->mBlockDirAlign, "umr"); |
|
1496 #endif |
|
1497 WritingMode frameWM = pfd->mFrame->GetWritingMode(); |
|
1498 WritingMode lineWM = mRootSpan->mWritingMode; |
|
1499 switch (pfd->mBlockDirAlign) { |
|
1500 case VALIGN_TOP: |
|
1501 if (span) { |
|
1502 pfd->mBounds.BStart(lineWM) = -aDistanceFromStart - span->mMinBCoord; |
|
1503 } |
|
1504 else { |
|
1505 pfd->mBounds.BStart(lineWM) = |
|
1506 -aDistanceFromStart + pfd->mMargin.BStart(frameWM); |
|
1507 } |
|
1508 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
1509 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1510 printf(" "); |
|
1511 nsFrame::ListTag(stdout, pfd->mFrame); |
|
1512 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n", |
|
1513 pfd->mBounds.BStart(lineWM), aDistanceFromStart, |
|
1514 span ? pfd->mBorderPadding.BStart(frameWM) : 0, |
|
1515 span ? span->mBStartLeading : 0); |
|
1516 #endif |
|
1517 break; |
|
1518 case VALIGN_BOTTOM: |
|
1519 if (span) { |
|
1520 // Compute bottom leading |
|
1521 pfd->mBounds.BStart(lineWM) = |
|
1522 -aDistanceFromStart + aLineBSize - span->mMaxBCoord; |
|
1523 } |
|
1524 else { |
|
1525 pfd->mBounds.BStart(lineWM) = -aDistanceFromStart + aLineBSize - |
|
1526 pfd->mMargin.BEnd(frameWM) - pfd->mBounds.BSize(lineWM); |
|
1527 } |
|
1528 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
1529 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1530 printf(" "); |
|
1531 nsFrame::ListTag(stdout, pfd->mFrame); |
|
1532 printf(": y=%d\n", pfd->mBounds.BStart(lineWM)); |
|
1533 #endif |
|
1534 break; |
|
1535 } |
|
1536 if (span) { |
|
1537 nscoord fromStart = aDistanceFromStart + pfd->mBounds.BStart(lineWM); |
|
1538 PlaceStartEndFrames(span, fromStart, aLineBSize); |
|
1539 } |
|
1540 } |
|
1541 } |
|
1542 |
|
1543 static float |
|
1544 GetInflationForBlockDirAlignment(nsIFrame* aFrame, |
|
1545 nscoord aInflationMinFontSize) |
|
1546 { |
|
1547 if (aFrame->IsSVGText()) { |
|
1548 const nsIFrame* container = |
|
1549 nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame); |
|
1550 NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame"); |
|
1551 return |
|
1552 static_cast<const SVGTextFrame*>(container)->GetFontSizeScaleFactor(); |
|
1553 } |
|
1554 return nsLayoutUtils::FontSizeInflationInner(aFrame, aInflationMinFontSize); |
|
1555 } |
|
1556 |
|
1557 #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX |
|
1558 #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN |
|
1559 |
|
1560 // Place frames in the block direction within a given span. Note: this doesn't |
|
1561 // place block start/end aligned frames as those have to wait until the |
|
1562 // entire line box block size is known. This is called after the span |
|
1563 // frame has finished being reflowed so that we know its block size. |
|
1564 void |
|
1565 nsLineLayout::BlockDirAlignFrames(PerSpanData* psd) |
|
1566 { |
|
1567 // Get parent frame info |
|
1568 PerFrameData* spanFramePFD = psd->mFrame; |
|
1569 nsIFrame* spanFrame = spanFramePFD->mFrame; |
|
1570 |
|
1571 // Get the parent frame's font for all of the frames in this span |
|
1572 nsRefPtr<nsFontMetrics> fm; |
|
1573 float inflation = |
|
1574 GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize); |
|
1575 nsLayoutUtils::GetFontMetricsForFrame(spanFrame, getter_AddRefs(fm), |
|
1576 inflation); |
|
1577 mBlockReflowState->rendContext->SetFont(fm); |
|
1578 |
|
1579 bool preMode = mStyleText->WhiteSpaceIsSignificant(); |
|
1580 |
|
1581 // See if the span is an empty continuation. It's an empty continuation iff: |
|
1582 // - it has a prev-in-flow |
|
1583 // - it has no next in flow |
|
1584 // - it's zero sized |
|
1585 WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode(); |
|
1586 WritingMode lineWM = mRootSpan->mWritingMode; |
|
1587 bool emptyContinuation = psd != mRootSpan && |
|
1588 spanFrame->GetPrevInFlow() && !spanFrame->GetNextInFlow() && |
|
1589 spanFramePFD->mBounds.IsZeroSize(); |
|
1590 |
|
1591 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1592 printf("[%sSpan]", (psd == mRootSpan)?"Root":""); |
|
1593 nsFrame::ListTag(stdout, spanFrame); |
|
1594 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s", |
|
1595 preMode ? "yes" : "no", |
|
1596 mPresContext->CompatibilityMode() != eCompatibility_NavQuirks ? "yes" : "no", |
|
1597 spanFramePFD->mBounds.ISize(lineWM), |
|
1598 spanFramePFD->mBounds.BSize(lineWM), |
|
1599 emptyContinuation ? "yes" : "no"); |
|
1600 if (psd != mRootSpan) { |
|
1601 WritingMode frameWM = spanFramePFD->mFrame->GetWritingMode(); |
|
1602 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d", |
|
1603 spanFramePFD->mBorderPadding.Top(frameWM), |
|
1604 spanFramePFD->mBorderPadding.Right(frameWM), |
|
1605 spanFramePFD->mBorderPadding.Bottom(frameWM), |
|
1606 spanFramePFD->mBorderPadding.Left(frameWM), |
|
1607 spanFramePFD->mMargin.Top(frameWM), |
|
1608 spanFramePFD->mMargin.Right(frameWM), |
|
1609 spanFramePFD->mMargin.Bottom(frameWM), |
|
1610 spanFramePFD->mMargin.Left(frameWM)); |
|
1611 } |
|
1612 printf("\n"); |
|
1613 #endif |
|
1614 |
|
1615 // Compute the span's mZeroEffectiveSpanBox flag. What we are trying |
|
1616 // to determine is how we should treat the span: should it act |
|
1617 // "normally" according to css2 or should it effectively |
|
1618 // "disappear". |
|
1619 // |
|
1620 // In general, if the document being processed is in full standards |
|
1621 // mode then it should act normally (with one exception). The |
|
1622 // exception case is when a span is continued and yet the span is |
|
1623 // empty (e.g. compressed whitespace). For this kind of span we treat |
|
1624 // it as if it were not there so that it doesn't impact the |
|
1625 // line block-size. |
|
1626 // |
|
1627 // In almost standards mode or quirks mode, we should sometimes make |
|
1628 // it disappear. The cases that matter are those where the span |
|
1629 // contains no real text elements that would provide an ascent and |
|
1630 // descent and height. However, if css style elements have been |
|
1631 // applied to the span (border/padding/margin) so that it's clear the |
|
1632 // document author is intending css2 behavior then we act as if strict |
|
1633 // mode is set. |
|
1634 // |
|
1635 // This code works correctly for preMode, because a blank line |
|
1636 // in PRE mode is encoded as a text node with a LF in it, since |
|
1637 // text nodes with only whitespace are considered in preMode. |
|
1638 // |
|
1639 // Much of this logic is shared with the various implementations of |
|
1640 // nsIFrame::IsEmpty since they need to duplicate the way it makes |
|
1641 // some lines empty. However, nsIFrame::IsEmpty can't be reused here |
|
1642 // since this code sets zeroEffectiveSpanBox even when there are |
|
1643 // non-empty children. |
|
1644 bool zeroEffectiveSpanBox = false; |
|
1645 // XXXldb If we really have empty continuations, then all these other |
|
1646 // checks don't make sense for them. |
|
1647 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that |
|
1648 // it agrees with this code. (If it doesn't agree, it probably should.) |
|
1649 if ((emptyContinuation || |
|
1650 mPresContext->CompatibilityMode() != eCompatibility_FullStandards) && |
|
1651 ((psd == mRootSpan) || |
|
1652 (spanFramePFD->mBorderPadding.IsEmpty() && |
|
1653 spanFramePFD->mMargin.IsEmpty()))) { |
|
1654 // This code handles an issue with compatibility with non-css |
|
1655 // conformant browsers. In particular, there are some cases |
|
1656 // where the font-size and line-height for a span must be |
|
1657 // ignored and instead the span must *act* as if it were zero |
|
1658 // sized. In general, if the span contains any non-compressed |
|
1659 // text then we don't use this logic. |
|
1660 // However, this is not propagated outwards, since (in compatibility |
|
1661 // mode) we don't want big line heights for things like |
|
1662 // <p><font size="-1">Text</font></p> |
|
1663 |
|
1664 // We shouldn't include any whitespace that collapses, unless we're |
|
1665 // preformatted (in which case it shouldn't, but the width=0 test is |
|
1666 // perhaps incorrect). This includes whitespace at the beginning of |
|
1667 // a line and whitespace preceded (?) by other whitespace. |
|
1668 // See bug 134580 and bug 155333. |
|
1669 zeroEffectiveSpanBox = true; |
|
1670 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
1671 if (pfd->GetFlag(PFD_ISTEXTFRAME) && |
|
1672 (pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) || preMode || |
|
1673 pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) { |
|
1674 zeroEffectiveSpanBox = false; |
|
1675 break; |
|
1676 } |
|
1677 } |
|
1678 } |
|
1679 psd->mZeroEffectiveSpanBox = zeroEffectiveSpanBox; |
|
1680 |
|
1681 // Setup baselineBCoord, minBCoord, and maxBCoord |
|
1682 nscoord baselineBCoord, minBCoord, maxBCoord; |
|
1683 if (psd == mRootSpan) { |
|
1684 // Use a zero baselineBCoord since we don't yet know where the baseline |
|
1685 // will be (until we know how tall the line is; then we will |
|
1686 // know). In addition, use extreme values for the minBCoord and maxBCoord |
|
1687 // values so that only the child frames will impact their values |
|
1688 // (since these are children of the block, there is no span box to |
|
1689 // provide initial values). |
|
1690 baselineBCoord = 0; |
|
1691 minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
|
1692 maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
|
1693 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1694 printf("[RootSpan]"); |
|
1695 nsFrame::ListTag(stdout, spanFrame); |
|
1696 printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n", |
|
1697 mBStartEdge, mMinLineBSize, |
|
1698 zeroEffectiveSpanBox ? "yes" : "no"); |
|
1699 #endif |
|
1700 } |
|
1701 else { |
|
1702 // Compute the logical block size for this span. The logical block size |
|
1703 // is based on the "line-height" value, not the font-size. Also |
|
1704 // compute the top leading. |
|
1705 float inflation = |
|
1706 GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize); |
|
1707 nscoord logicalBSize = nsHTMLReflowState:: |
|
1708 CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(), |
|
1709 mBlockReflowState->ComputedHeight(), |
|
1710 inflation); |
|
1711 nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) - |
|
1712 spanFramePFD->mBorderPadding.BStart(frameWM) - |
|
1713 spanFramePFD->mBorderPadding.BEnd(frameWM); |
|
1714 |
|
1715 // Special-case for a ::first-letter frame, set the line height to |
|
1716 // the frame block size if the user has left line-height == normal |
|
1717 if (spanFramePFD->GetFlag(PFD_ISLETTERFRAME) && |
|
1718 !spanFrame->GetPrevInFlow() && |
|
1719 spanFrame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) { |
|
1720 logicalBSize = spanFramePFD->mBounds.BSize(lineWM); |
|
1721 } |
|
1722 |
|
1723 nscoord leading = logicalBSize - contentBSize; |
|
1724 psd->mBStartLeading = leading / 2; |
|
1725 psd->mBEndLeading = leading - psd->mBStartLeading; |
|
1726 psd->mLogicalBSize = logicalBSize; |
|
1727 |
|
1728 if (zeroEffectiveSpanBox) { |
|
1729 // When the span-box is to be ignored, zero out the initial |
|
1730 // values so that the span doesn't impact the final line |
|
1731 // height. The contents of the span can impact the final line |
|
1732 // height. |
|
1733 |
|
1734 // Note that things are readjusted for this span after its children |
|
1735 // are reflowed |
|
1736 minBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
|
1737 maxBCoord = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
|
1738 } |
|
1739 else { |
|
1740 |
|
1741 // The initial values for the min and max block coord values are in the |
|
1742 // span's coordinate space, and cover the logical block size of the span. |
|
1743 // If there are child frames in this span that stick out of this area |
|
1744 // then the minBCoord and maxBCoord are updated by the amount of logical |
|
1745 // blockSize that is outside this range. |
|
1746 minBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) - |
|
1747 psd->mBStartLeading; |
|
1748 maxBCoord = minBCoord + psd->mLogicalBSize; |
|
1749 } |
|
1750 |
|
1751 // This is the distance from the top edge of the parents visual |
|
1752 // box to the baseline. The span already computed this for us, |
|
1753 // so just use it. |
|
1754 *psd->mBaseline = baselineBCoord = spanFramePFD->mAscent; |
|
1755 |
|
1756 |
|
1757 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1758 printf("[%sSpan]", (psd == mRootSpan)?"Root":""); |
|
1759 nsFrame::ListTag(stdout, spanFrame); |
|
1760 printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n", |
|
1761 baselineBCoord, psd->mLogicalBSize, psd->mBStartLeading, |
|
1762 spanFramePFD->mBounds.BSize(lineWM), |
|
1763 spanFramePFD->mBorderPadding.Top(frameWM), |
|
1764 spanFramePFD->mBorderPadding.Bottom(frameWM), |
|
1765 zeroEffectiveSpanBox ? "yes" : "no"); |
|
1766 #endif |
|
1767 } |
|
1768 |
|
1769 nscoord maxStartBoxBSize = 0; |
|
1770 nscoord maxEndBoxBSize = 0; |
|
1771 PerFrameData* pfd = psd->mFirstFrame; |
|
1772 while (nullptr != pfd) { |
|
1773 nsIFrame* frame = pfd->mFrame; |
|
1774 WritingMode frameWM = frame->GetWritingMode(); |
|
1775 |
|
1776 // sanity check (see bug 105168, non-reproducible crashes from null frame) |
|
1777 NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad"); |
|
1778 if (!frame) { |
|
1779 return; |
|
1780 } |
|
1781 |
|
1782 // Compute the logical block size of the frame |
|
1783 nscoord logicalBSize; |
|
1784 PerSpanData* frameSpan = pfd->mSpan; |
|
1785 if (frameSpan) { |
|
1786 // For span frames the logical-block-size and start-leading were |
|
1787 // pre-computed when the span was reflowed. |
|
1788 logicalBSize = frameSpan->mLogicalBSize; |
|
1789 } |
|
1790 else { |
|
1791 // For other elements the logical block size is the same as the |
|
1792 // frame's block size plus its margins. |
|
1793 logicalBSize = pfd->mBounds.BSize(lineWM) + |
|
1794 pfd->mMargin.BStartEnd(frameWM); |
|
1795 if (logicalBSize < 0 && |
|
1796 mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) { |
|
1797 pfd->mAscent -= logicalBSize; |
|
1798 logicalBSize = 0; |
|
1799 } |
|
1800 } |
|
1801 |
|
1802 // Get vertical-align property ("vertical-align" is the CSS name for |
|
1803 // block-direction align) |
|
1804 const nsStyleCoord& verticalAlign = |
|
1805 frame->StyleTextReset()->mVerticalAlign; |
|
1806 uint8_t verticalAlignEnum = frame->VerticalAlignEnum(); |
|
1807 #ifdef NOISY_BLOCKDIR_ALIGN |
|
1808 printf(" [frame]"); |
|
1809 nsFrame::ListTag(stdout, frame); |
|
1810 printf(": verticalAlignUnit=%d (enum == %d", |
|
1811 verticalAlign.GetUnit(), |
|
1812 ((eStyleUnit_Enumerated == verticalAlign.GetUnit()) |
|
1813 ? verticalAlign.GetIntValue() |
|
1814 : -1)); |
|
1815 if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) { |
|
1816 printf(", after SVG dominant-baseline conversion == %d", |
|
1817 verticalAlignEnum); |
|
1818 } |
|
1819 printf(")\n"); |
|
1820 #endif |
|
1821 |
|
1822 if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) { |
|
1823 switch (verticalAlignEnum) { |
|
1824 default: |
|
1825 case NS_STYLE_VERTICAL_ALIGN_BASELINE: |
|
1826 { |
|
1827 // The element's baseline is aligned with the baseline of |
|
1828 // the parent. |
|
1829 pfd->mBounds.BStart(lineWM) = baselineBCoord - pfd->mAscent; |
|
1830 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1831 break; |
|
1832 } |
|
1833 |
|
1834 case NS_STYLE_VERTICAL_ALIGN_SUB: |
|
1835 { |
|
1836 // Lower the baseline of the box to the subscript offset |
|
1837 // of the parent's box. This is identical to the baseline |
|
1838 // alignment except for the addition of the subscript |
|
1839 // offset to the baseline BCoord. |
|
1840 nscoord parentSubscript = fm->SubscriptOffset(); |
|
1841 nscoord revisedBaselineBCoord = baselineBCoord + parentSubscript; |
|
1842 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; |
|
1843 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1844 break; |
|
1845 } |
|
1846 |
|
1847 case NS_STYLE_VERTICAL_ALIGN_SUPER: |
|
1848 { |
|
1849 // Raise the baseline of the box to the superscript offset |
|
1850 // of the parent's box. This is identical to the baseline |
|
1851 // alignment except for the subtraction of the superscript |
|
1852 // offset to the baseline BCoord. |
|
1853 nscoord parentSuperscript = fm->SuperscriptOffset(); |
|
1854 nscoord revisedBaselineBCoord = baselineBCoord - parentSuperscript; |
|
1855 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; |
|
1856 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1857 break; |
|
1858 } |
|
1859 |
|
1860 case NS_STYLE_VERTICAL_ALIGN_TOP: |
|
1861 { |
|
1862 pfd->mBlockDirAlign = VALIGN_TOP; |
|
1863 nscoord subtreeBSize = logicalBSize; |
|
1864 if (frameSpan) { |
|
1865 subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord; |
|
1866 NS_ASSERTION(subtreeBSize >= logicalBSize, |
|
1867 "unexpected subtree block size"); |
|
1868 } |
|
1869 if (subtreeBSize > maxStartBoxBSize) { |
|
1870 maxStartBoxBSize = subtreeBSize; |
|
1871 } |
|
1872 break; |
|
1873 } |
|
1874 |
|
1875 case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
|
1876 { |
|
1877 pfd->mBlockDirAlign = VALIGN_BOTTOM; |
|
1878 nscoord subtreeBSize = logicalBSize; |
|
1879 if (frameSpan) { |
|
1880 subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord; |
|
1881 NS_ASSERTION(subtreeBSize >= logicalBSize, |
|
1882 "unexpected subtree block size"); |
|
1883 } |
|
1884 if (subtreeBSize > maxEndBoxBSize) { |
|
1885 maxEndBoxBSize = subtreeBSize; |
|
1886 } |
|
1887 break; |
|
1888 } |
|
1889 |
|
1890 case NS_STYLE_VERTICAL_ALIGN_MIDDLE: |
|
1891 { |
|
1892 // Align the midpoint of the frame with 1/2 the parents |
|
1893 // x-height above the baseline. |
|
1894 nscoord parentXHeight = fm->XHeight(); |
|
1895 if (frameSpan) { |
|
1896 pfd->mBounds.BStart(lineWM) = baselineBCoord - |
|
1897 (parentXHeight + pfd->mBounds.BSize(lineWM))/2; |
|
1898 } |
|
1899 else { |
|
1900 pfd->mBounds.BStart(lineWM) = baselineBCoord - |
|
1901 (parentXHeight + logicalBSize)/2 + |
|
1902 pfd->mMargin.BStart(frameWM); |
|
1903 } |
|
1904 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1905 break; |
|
1906 } |
|
1907 |
|
1908 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: |
|
1909 { |
|
1910 // The top of the logical box is aligned with the top of |
|
1911 // the parent element's text. |
|
1912 nscoord parentAscent = fm->MaxAscent(); |
|
1913 if (frameSpan) { |
|
1914 pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent - |
|
1915 pfd->mBorderPadding.BStart(frameWM) + frameSpan->mBStartLeading; |
|
1916 } |
|
1917 else { |
|
1918 pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent + |
|
1919 pfd->mMargin.BStart(frameWM); |
|
1920 } |
|
1921 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1922 break; |
|
1923 } |
|
1924 |
|
1925 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: |
|
1926 { |
|
1927 // The bottom of the logical box is aligned with the |
|
1928 // bottom of the parent elements text. |
|
1929 nscoord parentDescent = fm->MaxDescent(); |
|
1930 if (frameSpan) { |
|
1931 pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent - |
|
1932 pfd->mBounds.BSize(lineWM) + |
|
1933 pfd->mBorderPadding.BEnd(frameWM) - |
|
1934 frameSpan->mBEndLeading; |
|
1935 } |
|
1936 else { |
|
1937 pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent - |
|
1938 pfd->mBounds.BSize(lineWM) - |
|
1939 pfd->mMargin.BEnd(frameWM); |
|
1940 } |
|
1941 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1942 break; |
|
1943 } |
|
1944 |
|
1945 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: |
|
1946 { |
|
1947 // Align the midpoint of the frame with the baseline of the parent. |
|
1948 if (frameSpan) { |
|
1949 pfd->mBounds.BStart(lineWM) = baselineBCoord - |
|
1950 pfd->mBounds.BSize(lineWM)/2; |
|
1951 } |
|
1952 else { |
|
1953 pfd->mBounds.BStart(lineWM) = baselineBCoord - logicalBSize/2 + |
|
1954 pfd->mMargin.BStart(frameWM); |
|
1955 } |
|
1956 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1957 break; |
|
1958 } |
|
1959 } |
|
1960 } else { |
|
1961 // We have either a coord, a percent, or a calc(). |
|
1962 nscoord pctBasis = 0; |
|
1963 if (verticalAlign.HasPercent()) { |
|
1964 // Percentages are like lengths, except treated as a percentage |
|
1965 // of the elements line block size value. |
|
1966 float inflation = |
|
1967 GetInflationForBlockDirAlignment(frame, mInflationMinFontSize); |
|
1968 pctBasis = nsHTMLReflowState::CalcLineHeight(frame->GetContent(), |
|
1969 frame->StyleContext(), mBlockReflowState->ComputedBSize(), |
|
1970 inflation); |
|
1971 } |
|
1972 nscoord offset = |
|
1973 nsRuleNode::ComputeCoordPercentCalc(verticalAlign, pctBasis); |
|
1974 // According to the CSS2 spec (10.8.1), a positive value |
|
1975 // "raises" the box by the given distance while a negative value |
|
1976 // "lowers" the box by the given distance (with zero being the |
|
1977 // baseline). Since Y coordinates increase towards the bottom of |
|
1978 // the screen we reverse the sign. |
|
1979 nscoord revisedBaselineBCoord = baselineBCoord - offset; |
|
1980 pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent; |
|
1981 pfd->mBlockDirAlign = VALIGN_OTHER; |
|
1982 } |
|
1983 |
|
1984 // Update minBCoord/maxBCoord for frames that we just placed. Do not factor |
|
1985 // text into the equation. |
|
1986 if (pfd->mBlockDirAlign == VALIGN_OTHER) { |
|
1987 // Text frames do not contribute to the min/max Y values for the |
|
1988 // line (instead their parent frame's font-size contributes). |
|
1989 // XXXrbs -- relax this restriction because it causes text frames |
|
1990 // to jam together when 'font-size-adjust' is enabled |
|
1991 // and layout is using dynamic font heights (bug 20394) |
|
1992 // -- Note #1: With this code enabled and with the fact that we are not |
|
1993 // using Em[Ascent|Descent] as nsDimensions for text metrics in |
|
1994 // GFX mean that the discussion in bug 13072 cannot hold. |
|
1995 // -- Note #2: We still don't want empty-text frames to interfere. |
|
1996 // For example in quirks mode, avoiding empty text frames prevents |
|
1997 // "tall" lines around elements like <hr> since the rules of <hr> |
|
1998 // in quirks.css have pseudo text contents with LF in them. |
|
1999 #if 0 |
|
2000 if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { |
|
2001 #else |
|
2002 // Only consider non empty text frames when line-height=normal |
|
2003 bool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME); |
|
2004 if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) { |
|
2005 canUpdate = |
|
2006 frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal; |
|
2007 } |
|
2008 if (canUpdate) { |
|
2009 #endif |
|
2010 nscoord blockStart, blockEnd; |
|
2011 if (frameSpan) { |
|
2012 // For spans that were are now placing, use their position |
|
2013 // plus their already computed min-Y and max-Y values for |
|
2014 // computing blockStart and blockEnd. |
|
2015 blockStart = pfd->mBounds.BStart(lineWM) + frameSpan->mMinBCoord; |
|
2016 blockEnd = pfd->mBounds.BStart(lineWM) + frameSpan->mMaxBCoord; |
|
2017 } |
|
2018 else { |
|
2019 blockStart = pfd->mBounds.BStart(lineWM) - |
|
2020 pfd->mMargin.BStart(frameWM); |
|
2021 blockEnd = blockStart + logicalBSize; |
|
2022 } |
|
2023 if (!preMode && |
|
2024 mPresContext->CompatibilityMode() != eCompatibility_FullStandards && |
|
2025 !logicalBSize) { |
|
2026 // Check if it's a BR frame that is not alone on its line (it |
|
2027 // is given a block size of zero to indicate this), and if so reset |
|
2028 // blockStart and blockEnd so that BR frames don't influence the line. |
|
2029 if (nsGkAtoms::brFrame == frame->GetType()) { |
|
2030 blockStart = BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM; |
|
2031 blockEnd = BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM; |
|
2032 } |
|
2033 } |
|
2034 if (blockStart < minBCoord) minBCoord = blockStart; |
|
2035 if (blockEnd > maxBCoord) maxBCoord = blockEnd; |
|
2036 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2037 printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n", |
|
2038 pfd->mAscent, pfd->mBounds.BSize(lineWM), |
|
2039 pfd->mBorderPadding.Top(frameWM), |
|
2040 pfd->mBorderPadding.Bottom(frameWM), |
|
2041 logicalBSize, |
|
2042 frameSpan ? frameSpan->mBStartLeading : 0, |
|
2043 pfd->mBounds.BStart(lineWM), minBCoord, maxBCoord); |
|
2044 #endif |
|
2045 } |
|
2046 if (psd != mRootSpan) { |
|
2047 frame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
2048 } |
|
2049 } |
|
2050 pfd = pfd->mNext; |
|
2051 } |
|
2052 |
|
2053 // Factor in the minimum line block-size when handling the root-span for |
|
2054 // the block. |
|
2055 if (psd == mRootSpan) { |
|
2056 // We should factor in the block element's minimum line-height (as |
|
2057 // defined in section 10.8.1 of the css2 spec) assuming that |
|
2058 // mZeroEffectiveSpanBox is not set on the root span. This only happens |
|
2059 // in some cases in quirks mode: |
|
2060 // (1) if the root span contains non-whitespace text directly (this |
|
2061 // is handled by mZeroEffectiveSpanBox |
|
2062 // (2) if this line has a bullet |
|
2063 // (3) if this is the last line of an LI, DT, or DD element |
|
2064 // (The last line before a block also counts, but not before a |
|
2065 // BR) (NN4/IE5 quirk) |
|
2066 |
|
2067 // (1) and (2) above |
|
2068 bool applyMinLH = !psd->mZeroEffectiveSpanBox || mHasBullet; |
|
2069 bool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR); |
|
2070 if (!applyMinLH && isLastLine) { |
|
2071 nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent(); |
|
2072 if (blockContent) { |
|
2073 nsIAtom *blockTagAtom = blockContent->Tag(); |
|
2074 // (3) above, if the last line of LI, DT, or DD |
|
2075 if (blockTagAtom == nsGkAtoms::li || |
|
2076 blockTagAtom == nsGkAtoms::dt || |
|
2077 blockTagAtom == nsGkAtoms::dd) { |
|
2078 applyMinLH = true; |
|
2079 } |
|
2080 } |
|
2081 } |
|
2082 if (applyMinLH) { |
|
2083 if (psd->mHasNonemptyContent || preMode || mHasBullet) { |
|
2084 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2085 printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord, maxBCoord); |
|
2086 #endif |
|
2087 nscoord minimumLineBSize = mMinLineBSize; |
|
2088 nscoord blockStart = |
|
2089 -nsLayoutUtils::GetCenteredFontBaseline(fm, minimumLineBSize); |
|
2090 nscoord blockEnd = blockStart + minimumLineBSize; |
|
2091 |
|
2092 if (blockStart < minBCoord) minBCoord = blockStart; |
|
2093 if (blockEnd > maxBCoord) maxBCoord = blockEnd; |
|
2094 |
|
2095 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2096 printf(" new values: %d,%d\n", minBCoord, maxBCoord); |
|
2097 #endif |
|
2098 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2099 printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize, blockStart, blockEnd); |
|
2100 #endif |
|
2101 } |
|
2102 else { |
|
2103 // XXX issues: |
|
2104 // [1] BR's on empty lines stop working |
|
2105 // [2] May not honor css2's notion of handling empty elements |
|
2106 // [3] blank lines in a pre-section ("\n") (handled with preMode) |
|
2107 |
|
2108 // XXX Are there other problems with this? |
|
2109 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2110 printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n", |
|
2111 minBCoord, maxBCoord); |
|
2112 #endif |
|
2113 minBCoord = maxBCoord = 0; |
|
2114 } |
|
2115 } |
|
2116 } |
|
2117 |
|
2118 if ((minBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM) || |
|
2119 (maxBCoord == BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM)) { |
|
2120 minBCoord = maxBCoord = baselineBCoord; |
|
2121 } |
|
2122 |
|
2123 if ((psd != mRootSpan) && (psd->mZeroEffectiveSpanBox)) { |
|
2124 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2125 printf(" [span]adjusting for zeroEffectiveSpanBox\n"); |
|
2126 printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n", |
|
2127 minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(frameWM), |
|
2128 spanFramePFD->mAscent, |
|
2129 psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading); |
|
2130 #endif |
|
2131 nscoord goodMinBCoord = spanFramePFD->mBorderPadding.BStart(frameWM) - |
|
2132 psd->mBStartLeading; |
|
2133 nscoord goodMaxBCoord = goodMinBCoord + psd->mLogicalBSize; |
|
2134 |
|
2135 // For cases like the one in bug 714519 (text-decoration placement |
|
2136 // or making nsLineLayout::IsZeroBSize() handle |
|
2137 // vertical-align:top/bottom on a descendant of the line that's not |
|
2138 // a child of it), we want to treat elements that are |
|
2139 // vertical-align: top or bottom somewhat like children for the |
|
2140 // purposes of this quirk. To some extent, this is guessing, since |
|
2141 // they might end up being aligned anywhere. However, we'll guess |
|
2142 // that they'll be placed aligned with the top or bottom of this |
|
2143 // frame (as though this frame is the only thing in the line). |
|
2144 // (Guessing isn't crazy, since all we're doing is reducing the |
|
2145 // scope of a quirk and making the behavior more standards-like.) |
|
2146 if (maxStartBoxBSize > maxBCoord - minBCoord) { |
|
2147 // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and |
|
2148 // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or |
|
2149 // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord. |
|
2150 nscoord distribute = maxStartBoxBSize - (maxBCoord - minBCoord); |
|
2151 nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0); |
|
2152 if (distribute > ascentSpace) { |
|
2153 distribute -= ascentSpace; |
|
2154 minBCoord -= ascentSpace; |
|
2155 nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0); |
|
2156 if (distribute > descentSpace) { |
|
2157 maxBCoord += descentSpace; |
|
2158 } else { |
|
2159 maxBCoord += distribute; |
|
2160 } |
|
2161 } else { |
|
2162 minBCoord -= distribute; |
|
2163 } |
|
2164 } |
|
2165 if (maxEndBoxBSize > maxBCoord - minBCoord) { |
|
2166 // Likewise, but preferring descent to ascent. |
|
2167 nscoord distribute = maxEndBoxBSize - (maxBCoord - minBCoord); |
|
2168 nscoord descentSpace = std::max(goodMaxBCoord - maxBCoord, 0); |
|
2169 if (distribute > descentSpace) { |
|
2170 distribute -= descentSpace; |
|
2171 maxBCoord += descentSpace; |
|
2172 nscoord ascentSpace = std::max(minBCoord - goodMinBCoord, 0); |
|
2173 if (distribute > ascentSpace) { |
|
2174 minBCoord -= ascentSpace; |
|
2175 } else { |
|
2176 minBCoord -= distribute; |
|
2177 } |
|
2178 } else { |
|
2179 maxBCoord += distribute; |
|
2180 } |
|
2181 } |
|
2182 |
|
2183 if (minBCoord > goodMinBCoord) { |
|
2184 nscoord adjust = minBCoord - goodMinBCoord; // positive |
|
2185 |
|
2186 // shrink the logical extents |
|
2187 psd->mLogicalBSize -= adjust; |
|
2188 psd->mBStartLeading -= adjust; |
|
2189 } |
|
2190 if (maxBCoord < goodMaxBCoord) { |
|
2191 nscoord adjust = goodMaxBCoord - maxBCoord; |
|
2192 psd->mLogicalBSize -= adjust; |
|
2193 psd->mBEndLeading -= adjust; |
|
2194 } |
|
2195 if (minBCoord > 0) { |
|
2196 |
|
2197 // shrink the content by moving its block start down. This is tricky, |
|
2198 // since the block start is the 0 for many coordinates, so what we do is |
|
2199 // move everything else up. |
|
2200 spanFramePFD->mAscent -= minBCoord; // move the baseline up |
|
2201 spanFramePFD->mBounds.BSize(lineWM) -= minBCoord; // move the block end up |
|
2202 psd->mBStartLeading += minBCoord; |
|
2203 *psd->mBaseline -= minBCoord; |
|
2204 |
|
2205 pfd = psd->mFirstFrame; |
|
2206 while (nullptr != pfd) { |
|
2207 pfd->mBounds.BStart(lineWM) -= minBCoord; // move all the children |
|
2208 // back up |
|
2209 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
2210 pfd = pfd->mNext; |
|
2211 } |
|
2212 maxBCoord -= minBCoord; // since minBCoord is in the frame's own |
|
2213 // coordinate system |
|
2214 minBCoord = 0; |
|
2215 } |
|
2216 if (maxBCoord < spanFramePFD->mBounds.BSize(lineWM)) { |
|
2217 nscoord adjust = spanFramePFD->mBounds.BSize(lineWM) - maxBCoord; |
|
2218 spanFramePFD->mBounds.BSize(lineWM) -= adjust; // move the bottom up |
|
2219 psd->mBEndLeading += adjust; |
|
2220 } |
|
2221 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2222 printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n", |
|
2223 minBCoord, maxBCoord, spanFramePFD->mBounds.BSize(lineWM), |
|
2224 spanFramePFD->mAscent, |
|
2225 psd->mLogicalBSize, psd->mBStartLeading, psd->mBEndLeading); |
|
2226 #endif |
|
2227 } |
|
2228 |
|
2229 psd->mMinBCoord = minBCoord; |
|
2230 psd->mMaxBCoord = maxBCoord; |
|
2231 #ifdef NOISY_BLOCKDIR_ALIGN |
|
2232 printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n", |
|
2233 minBCoord, maxBCoord, maxBCoord - minBCoord, maxStartBoxBSize, maxEndBoxBSize); |
|
2234 #endif |
|
2235 if (maxStartBoxBSize > mMaxStartBoxBSize) { |
|
2236 mMaxStartBoxBSize = maxStartBoxBSize; |
|
2237 } |
|
2238 if (maxEndBoxBSize > mMaxEndBoxBSize) { |
|
2239 mMaxEndBoxBSize = maxEndBoxBSize; |
|
2240 } |
|
2241 } |
|
2242 |
|
2243 static void SlideSpanFrameRect(nsIFrame* aFrame, nscoord aDeltaWidth) |
|
2244 { |
|
2245 // This should not use nsIFrame::MovePositionBy because it happens |
|
2246 // prior to relative positioning. In particular, because |
|
2247 // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace() |
|
2248 // prior to calling aLineLayout.RelativePositionFrames(). |
|
2249 nsPoint p = aFrame->GetPosition(); |
|
2250 p.x -= aDeltaWidth; |
|
2251 aFrame->SetPosition(p); |
|
2252 } |
|
2253 |
|
2254 bool |
|
2255 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, |
|
2256 nscoord* aDeltaISize) |
|
2257 { |
|
2258 PerFrameData* pfd = psd->mFirstFrame; |
|
2259 if (!pfd) { |
|
2260 *aDeltaISize = 0; |
|
2261 return false; |
|
2262 } |
|
2263 pfd = pfd->Last(); |
|
2264 while (nullptr != pfd) { |
|
2265 #ifdef REALLY_NOISY_TRIM |
|
2266 nsFrame::ListTag(stdout, (psd == mRootSpan |
|
2267 ? mBlockReflowState->frame |
|
2268 : psd->mFrame->mFrame)); |
|
2269 printf(": attempting trim of "); |
|
2270 nsFrame::ListTag(stdout, pfd->mFrame); |
|
2271 printf("\n"); |
|
2272 #endif |
|
2273 PerSpanData* childSpan = pfd->mSpan; |
|
2274 WritingMode lineWM = mRootSpan->mWritingMode; |
|
2275 if (childSpan) { |
|
2276 // Maybe the child span has the trailing white-space in it? |
|
2277 if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaISize)) { |
|
2278 nscoord deltaISize = *aDeltaISize; |
|
2279 if (deltaISize) { |
|
2280 // Adjust the child spans frame size |
|
2281 pfd->mBounds.ISize(lineWM) -= deltaISize; |
|
2282 if (psd != mRootSpan) { |
|
2283 // When the child span is not a direct child of the block |
|
2284 // we need to update the child spans frame rectangle |
|
2285 // because it most likely will not be done again. Spans |
|
2286 // that are direct children of the block will be updated |
|
2287 // later, however, because the BlockDirAlignFrames method |
|
2288 // will be run after this method. |
|
2289 nsIFrame* f = pfd->mFrame; |
|
2290 LogicalRect r(lineWM, f->GetRect(), mContainerWidth); |
|
2291 r.ISize(lineWM) -= deltaISize; |
|
2292 f->SetRect(lineWM, r, mContainerWidth); |
|
2293 } |
|
2294 |
|
2295 // Adjust the inline end edge of the span that contains the child span |
|
2296 psd->mICoord -= deltaISize; |
|
2297 |
|
2298 // Slide any frames that follow the child span over by the |
|
2299 // correct amount. The only thing that can follow the child |
|
2300 // span is empty stuff, so we are just making things |
|
2301 // sensible (keeping the combined area honest). |
|
2302 while (pfd->mNext) { |
|
2303 pfd = pfd->mNext; |
|
2304 pfd->mBounds.IStart(lineWM) -= deltaISize; |
|
2305 if (psd != mRootSpan) { |
|
2306 // When the child span is not a direct child of the block |
|
2307 // we need to update the child span's frame rectangle |
|
2308 // because it most likely will not be done again. Spans |
|
2309 // that are direct children of the block will be updated |
|
2310 // later, however, because the BlockDirAlignFrames method |
|
2311 // will be run after this method. |
|
2312 SlideSpanFrameRect(pfd->mFrame, deltaISize); |
|
2313 } |
|
2314 } |
|
2315 } |
|
2316 return true; |
|
2317 } |
|
2318 } |
|
2319 else if (!pfd->GetFlag(PFD_ISTEXTFRAME) && |
|
2320 !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) { |
|
2321 // If we hit a frame on the end that's not text and not a placeholder, |
|
2322 // then there is no trailing whitespace to trim. Stop the search. |
|
2323 *aDeltaISize = 0; |
|
2324 return true; |
|
2325 } |
|
2326 else if (pfd->GetFlag(PFD_ISTEXTFRAME)) { |
|
2327 // Call TrimTrailingWhiteSpace even on empty textframes because they |
|
2328 // might have a soft hyphen which should now appear, changing the frame's |
|
2329 // width |
|
2330 nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)-> |
|
2331 TrimTrailingWhiteSpace(mBlockReflowState->rendContext); |
|
2332 #ifdef NOISY_TRIM |
|
2333 nsFrame::ListTag(stdout, (psd == mRootSpan |
|
2334 ? mBlockReflowState->frame |
|
2335 : psd->mFrame->mFrame)); |
|
2336 printf(": trim of "); |
|
2337 nsFrame::ListTag(stdout, pfd->mFrame); |
|
2338 printf(" returned %d\n", trimOutput.mDeltaWidth); |
|
2339 #endif |
|
2340 if (trimOutput.mLastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) { |
|
2341 pfd->mJustificationNumSpaces--; |
|
2342 } |
|
2343 |
|
2344 if (trimOutput.mChanged) { |
|
2345 pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true); |
|
2346 } |
|
2347 |
|
2348 if (trimOutput.mDeltaWidth) { |
|
2349 pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth; |
|
2350 |
|
2351 // See if the text frame has already been placed in its parent |
|
2352 if (psd != mRootSpan) { |
|
2353 // The frame was already placed during psd's |
|
2354 // reflow. Update the frames rectangle now. |
|
2355 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
2356 } |
|
2357 |
|
2358 // Adjust containing span's right edge |
|
2359 psd->mICoord -= trimOutput.mDeltaWidth; |
|
2360 |
|
2361 // Slide any frames that follow the text frame over by the |
|
2362 // right amount. The only thing that can follow the text |
|
2363 // frame is empty stuff, so we are just making things |
|
2364 // sensible (keeping the combined area honest). |
|
2365 while (pfd->mNext) { |
|
2366 pfd = pfd->mNext; |
|
2367 pfd->mBounds.IStart(lineWM) -= trimOutput.mDeltaWidth; |
|
2368 if (psd != mRootSpan) { |
|
2369 // When the child span is not a direct child of the block |
|
2370 // we need to update the child spans frame rectangle |
|
2371 // because it most likely will not be done again. Spans |
|
2372 // that are direct children of the block will be updated |
|
2373 // later, however, because the BlockDirAlignFrames method |
|
2374 // will be run after this method. |
|
2375 SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth); |
|
2376 } |
|
2377 } |
|
2378 } |
|
2379 |
|
2380 if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) { |
|
2381 // Pass up to caller so they can shrink their span |
|
2382 *aDeltaISize = trimOutput.mDeltaWidth; |
|
2383 return true; |
|
2384 } |
|
2385 } |
|
2386 pfd = pfd->mPrev; |
|
2387 } |
|
2388 |
|
2389 *aDeltaISize = 0; |
|
2390 return false; |
|
2391 } |
|
2392 |
|
2393 bool |
|
2394 nsLineLayout::TrimTrailingWhiteSpace() |
|
2395 { |
|
2396 PerSpanData* psd = mRootSpan; |
|
2397 nscoord deltaISize; |
|
2398 TrimTrailingWhiteSpaceIn(psd, &deltaISize); |
|
2399 return 0 != deltaISize; |
|
2400 } |
|
2401 |
|
2402 void |
|
2403 nsLineLayout::ComputeJustificationWeights(PerSpanData* aPSD, |
|
2404 int32_t* aNumSpaces, |
|
2405 int32_t* aNumLetters) |
|
2406 { |
|
2407 NS_ASSERTION(aPSD, "null arg"); |
|
2408 NS_ASSERTION(aNumSpaces, "null arg"); |
|
2409 NS_ASSERTION(aNumLetters, "null arg"); |
|
2410 int32_t numSpaces = 0; |
|
2411 int32_t numLetters = 0; |
|
2412 |
|
2413 for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) { |
|
2414 |
|
2415 if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) { |
|
2416 numSpaces += pfd->mJustificationNumSpaces; |
|
2417 numLetters += pfd->mJustificationNumLetters; |
|
2418 } |
|
2419 else if (pfd->mSpan != nullptr) { |
|
2420 int32_t spanSpaces; |
|
2421 int32_t spanLetters; |
|
2422 |
|
2423 ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters); |
|
2424 |
|
2425 numSpaces += spanSpaces; |
|
2426 numLetters += spanLetters; |
|
2427 } |
|
2428 } |
|
2429 |
|
2430 *aNumSpaces = numSpaces; |
|
2431 *aNumLetters = numLetters; |
|
2432 } |
|
2433 |
|
2434 nscoord |
|
2435 nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState) |
|
2436 { |
|
2437 NS_ASSERTION(aPSD, "null arg"); |
|
2438 NS_ASSERTION(aState, "null arg"); |
|
2439 |
|
2440 nscoord deltaICoord = 0; |
|
2441 for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) { |
|
2442 // Don't reposition bullets (and other frames that occur out of X-order?) |
|
2443 if (!pfd->GetFlag(PFD_ISBULLET)) { |
|
2444 nscoord dw = 0; |
|
2445 WritingMode lineWM = mRootSpan->mWritingMode; |
|
2446 |
|
2447 pfd->mBounds.IStart(lineWM) += deltaICoord; |
|
2448 |
|
2449 if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) { |
|
2450 if (aState->mTotalWidthForSpaces > 0 && |
|
2451 aState->mTotalNumSpaces > 0) { |
|
2452 aState->mNumSpacesProcessed += pfd->mJustificationNumSpaces; |
|
2453 |
|
2454 nscoord newAllocatedWidthForSpaces = |
|
2455 (aState->mTotalWidthForSpaces*aState->mNumSpacesProcessed) |
|
2456 /aState->mTotalNumSpaces; |
|
2457 |
|
2458 dw += newAllocatedWidthForSpaces - aState->mWidthForSpacesProcessed; |
|
2459 |
|
2460 aState->mWidthForSpacesProcessed = newAllocatedWidthForSpaces; |
|
2461 } |
|
2462 |
|
2463 if (aState->mTotalWidthForLetters > 0 && |
|
2464 aState->mTotalNumLetters > 0) { |
|
2465 aState->mNumLettersProcessed += pfd->mJustificationNumLetters; |
|
2466 |
|
2467 nscoord newAllocatedWidthForLetters = |
|
2468 (aState->mTotalWidthForLetters*aState->mNumLettersProcessed) |
|
2469 /aState->mTotalNumLetters; |
|
2470 |
|
2471 dw += newAllocatedWidthForLetters - aState->mWidthForLettersProcessed; |
|
2472 |
|
2473 aState->mWidthForLettersProcessed = newAllocatedWidthForLetters; |
|
2474 } |
|
2475 |
|
2476 if (dw) { |
|
2477 pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true); |
|
2478 } |
|
2479 } |
|
2480 else { |
|
2481 if (nullptr != pfd->mSpan) { |
|
2482 dw += ApplyFrameJustification(pfd->mSpan, aState); |
|
2483 } |
|
2484 } |
|
2485 |
|
2486 pfd->mBounds.ISize(lineWM) += dw; |
|
2487 |
|
2488 deltaICoord += dw; |
|
2489 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
2490 } |
|
2491 } |
|
2492 return deltaICoord; |
|
2493 } |
|
2494 |
|
2495 void |
|
2496 nsLineLayout::InlineDirAlignFrames(nsLineBox* aLine, |
|
2497 bool aIsLastLine) |
|
2498 { |
|
2499 /** |
|
2500 * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller |
|
2501 * only in cases where the last line needs special handling. |
|
2502 */ |
|
2503 PerSpanData* psd = mRootSpan; |
|
2504 WritingMode lineWM = psd->mWritingMode; |
|
2505 NS_WARN_IF_FALSE(psd->mIEnd != NS_UNCONSTRAINEDSIZE, |
|
2506 "have unconstrained width; this should only result from " |
|
2507 "very large sizes, not attempts at intrinsic width " |
|
2508 "calculation"); |
|
2509 nscoord availISize = psd->mIEnd - psd->mIStart; |
|
2510 nscoord remainingISize = availISize - aLine->ISize(); |
|
2511 #ifdef NOISY_INLINEDIR_ALIGN |
|
2512 nsFrame::ListTag(stdout, mBlockReflowState->frame); |
|
2513 printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n", |
|
2514 availISize, aLine->IStart(), aLine->ISize(), remainingISize); |
|
2515 #endif |
|
2516 |
|
2517 // 'text-align-last: auto' is equivalent to the value of the 'text-align' |
|
2518 // property except when 'text-align' is set to 'justify', in which case it |
|
2519 // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise. |
|
2520 // |
|
2521 // XXX: the code below will have to change when we implement text-justify |
|
2522 // |
|
2523 nscoord dx = 0; |
|
2524 uint8_t textAlign = mStyleText->mTextAlign; |
|
2525 bool textAlignTrue = mStyleText->mTextAlignTrue; |
|
2526 if (aIsLastLine) { |
|
2527 textAlignTrue = mStyleText->mTextAlignLastTrue; |
|
2528 if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) { |
|
2529 if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) { |
|
2530 textAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; |
|
2531 } |
|
2532 } else { |
|
2533 textAlign = mStyleText->mTextAlignLast; |
|
2534 } |
|
2535 } |
|
2536 |
|
2537 if ((remainingISize > 0 || textAlignTrue) && |
|
2538 !(mBlockReflowState->frame->IsSVGText())) { |
|
2539 |
|
2540 switch (textAlign) { |
|
2541 case NS_STYLE_TEXT_ALIGN_JUSTIFY: |
|
2542 int32_t numSpaces; |
|
2543 int32_t numLetters; |
|
2544 |
|
2545 ComputeJustificationWeights(psd, &numSpaces, &numLetters); |
|
2546 |
|
2547 if (numSpaces > 0) { |
|
2548 FrameJustificationState state = |
|
2549 { numSpaces, numLetters, remainingISize, 0, 0, 0, 0, 0 }; |
|
2550 |
|
2551 // Apply the justification, and make sure to update our linebox |
|
2552 // width to account for it. |
|
2553 aLine->ExpandBy(ApplyFrameJustification(psd, &state), |
|
2554 mContainerWidth); |
|
2555 remainingISize = availISize - aLine->ISize(); |
|
2556 break; |
|
2557 } |
|
2558 // Fall through to the default case if we could not justify to fill |
|
2559 // the space. |
|
2560 |
|
2561 case NS_STYLE_TEXT_ALIGN_DEFAULT: |
|
2562 // default alignment is to start edge so do nothing |
|
2563 break; |
|
2564 |
|
2565 case NS_STYLE_TEXT_ALIGN_LEFT: |
|
2566 case NS_STYLE_TEXT_ALIGN_MOZ_LEFT: |
|
2567 if (!lineWM.IsBidiLTR()) { |
|
2568 dx = remainingISize; |
|
2569 } |
|
2570 break; |
|
2571 |
|
2572 case NS_STYLE_TEXT_ALIGN_RIGHT: |
|
2573 case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT: |
|
2574 if (lineWM.IsBidiLTR()) { |
|
2575 dx = remainingISize; |
|
2576 } |
|
2577 break; |
|
2578 |
|
2579 case NS_STYLE_TEXT_ALIGN_END: |
|
2580 dx = remainingISize; |
|
2581 break; |
|
2582 |
|
2583 |
|
2584 case NS_STYLE_TEXT_ALIGN_CENTER: |
|
2585 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: |
|
2586 dx = remainingISize / 2; |
|
2587 break; |
|
2588 } |
|
2589 } |
|
2590 |
|
2591 if (dx) { |
|
2592 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
2593 pfd->mBounds.IStart(lineWM) += dx; |
|
2594 pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); |
|
2595 } |
|
2596 aLine->IndentBy(dx, mContainerWidth); |
|
2597 } |
|
2598 |
|
2599 if (mPresContext->BidiEnabled() && |
|
2600 (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) { |
|
2601 nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame, |
|
2602 aLine->GetChildCount(), |
|
2603 lineWM, mContainerWidth); |
|
2604 } |
|
2605 } |
|
2606 |
|
2607 void |
|
2608 nsLineLayout::RelativePositionFrames(nsOverflowAreas& aOverflowAreas) |
|
2609 { |
|
2610 RelativePositionFrames(mRootSpan, aOverflowAreas); |
|
2611 } |
|
2612 |
|
2613 void |
|
2614 nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflowAreas) |
|
2615 { |
|
2616 nsOverflowAreas overflowAreas; |
|
2617 WritingMode wm = psd->mWritingMode; |
|
2618 if (nullptr != psd->mFrame) { |
|
2619 // The span's overflow areas come in three parts: |
|
2620 // -- this frame's width and height |
|
2621 // -- pfd->mOverflowAreas, which is the area of a bullet or the union |
|
2622 // of a relatively positioned frame's absolute children |
|
2623 // -- the bounds of all inline descendants |
|
2624 // The former two parts are computed right here, we gather the descendants |
|
2625 // below. |
|
2626 // At this point psd->mFrame->mBounds might be out of date since |
|
2627 // bidi reordering can move and resize the frames. So use the frame's |
|
2628 // rect instead of mBounds. |
|
2629 nsRect adjustedBounds(nsPoint(0, 0), psd->mFrame->mFrame->GetSize()); |
|
2630 |
|
2631 overflowAreas.ScrollableOverflow().UnionRect( |
|
2632 psd->mFrame->mOverflowAreas.ScrollableOverflow(), adjustedBounds); |
|
2633 overflowAreas.VisualOverflow().UnionRect( |
|
2634 psd->mFrame->mOverflowAreas.VisualOverflow(), adjustedBounds); |
|
2635 } |
|
2636 else { |
|
2637 LogicalRect rect(wm, psd->mIStart, mBStartEdge, |
|
2638 psd->mICoord - psd->mIStart, mFinalLineBSize); |
|
2639 // The minimum combined area for the frames that are direct |
|
2640 // children of the block starts at the upper left corner of the |
|
2641 // line and is sized to match the size of the line's bounding box |
|
2642 // (the same size as the values returned from BlockDirAlignFrames) |
|
2643 overflowAreas.VisualOverflow() = rect.GetPhysicalRect(wm, mContainerWidth); |
|
2644 overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow(); |
|
2645 } |
|
2646 |
|
2647 for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { |
|
2648 nsIFrame* frame = pfd->mFrame; |
|
2649 nsPoint origin = frame->GetPosition(); |
|
2650 |
|
2651 // Adjust the origin of the frame |
|
2652 if (pfd->GetFlag(PFD_RELATIVEPOS)) { |
|
2653 //XXX temporary until ApplyRelativePositioning can handle logical offsets |
|
2654 nsMargin physicalOffsets = |
|
2655 pfd->mOffsets.GetPhysicalMargin(pfd->mFrame->GetWritingMode()); |
|
2656 // right and bottom are handled by |
|
2657 // nsHTMLReflowState::ComputeRelativeOffsets |
|
2658 nsHTMLReflowState::ApplyRelativePositioning(pfd->mFrame, |
|
2659 physicalOffsets, |
|
2660 &origin); |
|
2661 frame->SetPosition(origin); |
|
2662 } |
|
2663 |
|
2664 // We must position the view correctly before positioning its |
|
2665 // descendants so that widgets are positioned properly (since only |
|
2666 // some views have widgets). |
|
2667 if (frame->HasView()) |
|
2668 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame, |
|
2669 frame->GetView(), pfd->mOverflowAreas.VisualOverflow(), |
|
2670 NS_FRAME_NO_SIZE_VIEW); |
|
2671 |
|
2672 // Note: the combined area of a child is in its coordinate |
|
2673 // system. We adjust the childs combined area into our coordinate |
|
2674 // system before computing the aggregated value by adding in |
|
2675 // <b>x</b> and <b>y</b> which were computed above. |
|
2676 nsOverflowAreas r; |
|
2677 if (pfd->mSpan) { |
|
2678 // Compute a new combined area for the child span before |
|
2679 // aggregating it into our combined area. |
|
2680 RelativePositionFrames(pfd->mSpan, r); |
|
2681 } else { |
|
2682 r = pfd->mOverflowAreas; |
|
2683 if (pfd->GetFlag(PFD_ISTEXTFRAME)) { |
|
2684 // We need to recompute overflow areas in two cases: |
|
2685 // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming |
|
2686 // (2) When there are text decorations, since we can't recompute the |
|
2687 // overflow area until Reflow and BlockDirAlignLine have finished |
|
2688 if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW) || |
|
2689 frame->StyleContext()->HasTextDecorationLines()) { |
|
2690 nsTextFrame* f = static_cast<nsTextFrame*>(frame); |
|
2691 r = f->RecomputeOverflow(*mBlockReflowState); |
|
2692 } |
|
2693 frame->FinishAndStoreOverflow(r, frame->GetSize()); |
|
2694 } |
|
2695 |
|
2696 // If we have something that's not an inline but with a complex frame |
|
2697 // hierarchy inside that contains views, they need to be |
|
2698 // positioned. |
|
2699 // All descendant views must be repositioned even if this frame |
|
2700 // does have a view in case this frame's view does not have a |
|
2701 // widget and some of the descendant views do have widgets -- |
|
2702 // otherwise the widgets won't be repositioned. |
|
2703 nsContainerFrame::PositionChildViews(frame); |
|
2704 } |
|
2705 |
|
2706 // Do this here (rather than along with setting the overflow rect |
|
2707 // below) so we get leaf frames as well. No need to worry |
|
2708 // about the root span, since it doesn't have a frame. |
|
2709 if (frame->HasView()) |
|
2710 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame, |
|
2711 frame->GetView(), |
|
2712 r.VisualOverflow(), |
|
2713 NS_FRAME_NO_MOVE_VIEW); |
|
2714 |
|
2715 overflowAreas.UnionWith(r + origin); |
|
2716 } |
|
2717 |
|
2718 // If we just computed a spans combined area, we need to update its |
|
2719 // overflow rect... |
|
2720 if (psd->mFrame) { |
|
2721 PerFrameData* spanPFD = psd->mFrame; |
|
2722 nsIFrame* frame = spanPFD->mFrame; |
|
2723 frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize()); |
|
2724 } |
|
2725 aOverflowAreas = overflowAreas; |
|
2726 } |