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