Coverage Report

Created: 2018-09-25 14:53

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