layout/generic/nsLineLayout.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial