layout/generic/nsBlockFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 // vim:cindent:ts=2:et:sw=2:
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * rendering object for CSS display:block, inline-block, and list-item
     9  * boxes, also used for various anonymous boxes
    10  */
    12 #include "nsBlockFrame.h"
    14 #include "mozilla/DebugOnly.h"
    16 #include "nsCOMPtr.h"
    17 #include "nsAbsoluteContainingBlock.h"
    18 #include "nsBlockReflowContext.h"
    19 #include "nsBlockReflowState.h"
    20 #include "nsBulletFrame.h"
    21 #include "nsLineBox.h"
    22 #include "nsLineLayout.h"
    23 #include "nsPlaceholderFrame.h"
    24 #include "nsStyleConsts.h"
    25 #include "nsFrameManager.h"
    26 #include "nsPresContext.h"
    27 #include "nsIPresShell.h"
    28 #include "nsStyleContext.h"
    29 #include "nsHTMLParts.h"
    30 #include "nsGkAtoms.h"
    31 #include "nsGenericHTMLElement.h"
    32 #include "nsAttrValueInlines.h"
    33 #include "prprf.h"
    34 #include "nsFloatManager.h"
    35 #include "prenv.h"
    36 #include "plstr.h"
    37 #include "nsError.h"
    38 #include "nsAutoPtr.h"
    39 #include "nsIScrollableFrame.h"
    40 #include <algorithm>
    41 #ifdef ACCESSIBILITY
    42 #include "nsIDOMHTMLDocument.h"
    43 #endif
    44 #include "nsLayoutUtils.h"
    45 #include "nsDisplayList.h"
    46 #include "nsCSSAnonBoxes.h"
    47 #include "nsCSSFrameConstructor.h"
    48 #include "nsRenderingContext.h"
    49 #include "TextOverflow.h"
    50 #include "nsIFrameInlines.h"
    52 #include "nsBidiPresUtils.h"
    54 static const int MIN_LINES_NEEDING_CURSOR = 20;
    56 static const char16_t kDiscCharacter = 0x2022;
    57 static const char16_t kCircleCharacter = 0x25e6;
    58 static const char16_t kSquareCharacter = 0x25aa;
    60 #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
    62 using namespace mozilla;
    63 using namespace mozilla::css;
    64 using namespace mozilla::layout;
    66 #ifdef DEBUG
    67 #include "nsBlockDebugFlags.h"
    69 bool nsBlockFrame::gLamePaintMetrics;
    70 bool nsBlockFrame::gLameReflowMetrics;
    71 bool nsBlockFrame::gNoisy;
    72 bool nsBlockFrame::gNoisyDamageRepair;
    73 bool nsBlockFrame::gNoisyIntrinsic;
    74 bool nsBlockFrame::gNoisyReflow;
    75 bool nsBlockFrame::gReallyNoisyReflow;
    76 bool nsBlockFrame::gNoisyFloatManager;
    77 bool nsBlockFrame::gVerifyLines;
    78 bool nsBlockFrame::gDisableResizeOpt;
    80 int32_t nsBlockFrame::gNoiseIndent;
    82 struct BlockDebugFlags {
    83   const char* name;
    84   bool* on;
    85 };
    87 static const BlockDebugFlags gFlags[] = {
    88   { "reflow", &nsBlockFrame::gNoisyReflow },
    89   { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
    90   { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
    91   { "float-manager", &nsBlockFrame::gNoisyFloatManager },
    92   { "verify-lines", &nsBlockFrame::gVerifyLines },
    93   { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
    94   { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
    95   { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
    96   { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
    97 };
    98 #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
   100 static void
   101 ShowDebugFlags()
   102 {
   103   printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
   104   const BlockDebugFlags* bdf = gFlags;
   105   const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
   106   for (; bdf < end; bdf++) {
   107     printf("  %s\n", bdf->name);
   108   }
   109   printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
   110   printf("names (no whitespace)\n");
   111 }
   113 void
   114 nsBlockFrame::InitDebugFlags()
   115 {
   116   static bool firstTime = true;
   117   if (firstTime) {
   118     firstTime = false;
   119     char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
   120     if (flags) {
   121       bool error = false;
   122       for (;;) {
   123         char* cm = PL_strchr(flags, ',');
   124         if (cm) *cm = '\0';
   126         bool found = false;
   127         const BlockDebugFlags* bdf = gFlags;
   128         const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
   129         for (; bdf < end; bdf++) {
   130           if (PL_strcasecmp(bdf->name, flags) == 0) {
   131             *(bdf->on) = true;
   132             printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
   133             gNoisy = true;
   134             found = true;
   135             break;
   136           }
   137         }
   138         if (!found) {
   139           error = true;
   140         }
   142         if (!cm) break;
   143         *cm = ',';
   144         flags = cm + 1;
   145       }
   146       if (error) {
   147         ShowDebugFlags();
   148       }
   149     }
   150   }
   151 }
   153 #endif
   155 // add in a sanity check for absurdly deep frame trees.  See bug 42138
   156 // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
   157 #define MAX_DEPTH_FOR_LIST_RENUMBERING 200  // 200 open displayable tags is pretty unrealistic
   159 //----------------------------------------------------------------------
   161 // Debugging support code
   163 #ifdef DEBUG
   164 const char* nsBlockFrame::kReflowCommandType[] = {
   165   "ContentChanged",
   166   "StyleChanged",
   167   "ReflowDirty",
   168   "Timeout",
   169   "UserDefined",
   170 };
   171 #endif
   173 #ifdef REALLY_NOISY_FIRST_LINE
   174 static void
   175 DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
   176 {
   177   fputs(gap, stdout);
   178   nsFrame::ListTag(stdout, aFrame);
   179   printf(": ");
   180   nsStyleContext* sc = aFrame->StyleContext();
   181   while (nullptr != sc) {
   182     nsStyleContext* psc;
   183     printf("%p ", sc);
   184     psc = sc->GetParent();
   185     sc = psc;
   186   }
   187   printf("\n");
   188 }
   189 #endif
   191 #ifdef REFLOW_STATUS_COVERAGE
   192 static void
   193 RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
   194 {
   195   static uint32_t record[2];
   197   // 0: child-is-block
   198   // 1: child-is-inline
   199   int index = 0;
   200   if (!aChildIsBlock) index |= 1;
   202   // Compute new status
   203   uint32_t newS = record[index];
   204   if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
   205     if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
   206       newS |= 1;
   207     }
   208     else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
   209       newS |= 2;
   210     }
   211     else {
   212       newS |= 4;
   213     }
   214   }
   215   else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
   216     newS |= 8;
   217   }
   218   else {
   219     newS |= 16;
   220   }
   222   // Log updates to the status that yield different values
   223   if (record[index] != newS) {
   224     record[index] = newS;
   225     printf("record(%d): %02x %02x\n", index, record[0], record[1]);
   226   }
   227 }
   228 #endif
   230 // Destructor function for the overflowLines frame property
   231 static void
   232 DestroyOverflowLines(void* aPropertyValue)
   233 {
   234   NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
   235 }
   237 NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
   238 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
   239 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
   240 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
   241 NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr)
   242 NS_DECLARE_FRAME_PROPERTY(BottomEdgeOfChildrenProperty, nullptr)
   244 //----------------------------------------------------------------------
   246 nsIFrame*
   247 NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags)
   248 {
   249   nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
   250   it->SetFlags(aFlags);
   251   return it;
   252 }
   254 NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
   256 nsBlockFrame::~nsBlockFrame()
   257 {
   258 }
   260 void
   261 nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
   262 {
   263   ClearLineCursor();
   264   DestroyAbsoluteFrames(aDestructRoot);
   265   mFloats.DestroyFramesFrom(aDestructRoot);
   266   nsPresContext* presContext = PresContext();
   267   nsIPresShell* shell = presContext->PresShell();
   268   nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
   269                             &mFrames);
   271   FramePropertyTable* props = presContext->PropertyTable();
   273   if (HasPushedFloats()) {
   274     SafelyDestroyFrameListProp(aDestructRoot, shell, props,
   275                                PushedFloatProperty());
   276     RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
   277   }
   279   // destroy overflow lines now
   280   FrameLines* overflowLines = RemoveOverflowLines();
   281   if (overflowLines) {
   282     nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
   283                               aDestructRoot, &overflowLines->mFrames);
   284     delete overflowLines;
   285   }
   287   if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
   288     SafelyDestroyFrameListProp(aDestructRoot, shell, props,
   289                                OverflowOutOfFlowsProperty());
   290     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
   291   }
   293   if (HasOutsideBullet()) {
   294     SafelyDestroyFrameListProp(aDestructRoot, shell, props,
   295                                OutsideBulletProperty());
   296     RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
   297   }
   299   nsBlockFrameSuper::DestroyFrom(aDestructRoot);
   300 }
   302 /* virtual */ nsILineIterator*
   303 nsBlockFrame::GetLineIterator()
   304 {
   305   nsLineIterator* it = new nsLineIterator;
   306   if (!it)
   307     return nullptr;
   309   const nsStyleVisibility* visibility = StyleVisibility();
   310   nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL);
   311   if (NS_FAILED(rv)) {
   312     delete it;
   313     return nullptr;
   314   }
   315   return it;
   316 }
   318 NS_QUERYFRAME_HEAD(nsBlockFrame)
   319   NS_QUERYFRAME_ENTRY(nsBlockFrame)
   320 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper)
   322 nsSplittableType
   323 nsBlockFrame::GetSplittableType() const
   324 {
   325   return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
   326 }
   328 #ifdef DEBUG_FRAME_DUMP
   329 void
   330 nsBlockFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
   331 {
   332   nsCString str;
   333   ListGeneric(str, aPrefix, aFlags);
   335   fprintf_stderr(out, "%s<\n", str.get());
   337   nsCString pfx(aPrefix);
   338   pfx += "  ";
   340   // Output the lines
   341   if (!mLines.empty()) {
   342     const_line_iterator line = begin_lines(), line_end = end_lines();
   343     for ( ; line != line_end; ++line) {
   344       line->List(out, pfx.get(), aFlags);
   345     }
   346   }
   348   // Output the overflow lines.
   349   const FrameLines* overflowLines = GetOverflowLines();
   350   if (overflowLines && !overflowLines->mLines.empty()) {
   351     fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines, &overflowLines->mFrames);
   352     nsCString nestedPfx(pfx);
   353     nestedPfx += "  ";
   354     const_line_iterator line = overflowLines->mLines.begin(),
   355                         line_end = overflowLines->mLines.end();
   356     for ( ; line != line_end; ++line) {
   357       line->List(out, nestedPfx.get(), aFlags);
   358     }
   359     fprintf_stderr(out, "%s>\n", pfx.get());
   360   }
   362   // skip the principal list - we printed the lines above
   363   // skip the overflow list - we printed the overflow lines above
   364   ChildListIterator lists(this);
   365   ChildListIDs skip(kPrincipalList | kOverflowList);
   366   for (; !lists.IsDone(); lists.Next()) {
   367     if (skip.Contains(lists.CurrentID())) {
   368       continue;
   369     }
   370     fprintf_stderr(out, "%s%s %p <\n", pfx.get(),
   371       mozilla::layout::ChildListName(lists.CurrentID()),
   372       &GetChildList(lists.CurrentID()));
   373     nsCString nestedPfx(pfx);
   374     nestedPfx += "  ";
   375     nsFrameList::Enumerator childFrames(lists.CurrentList());
   376     for (; !childFrames.AtEnd(); childFrames.Next()) {
   377       nsIFrame* kid = childFrames.get();
   378       kid->List(out, nestedPfx.get(), aFlags);
   379     }
   380     fprintf_stderr(out, "%s>\n", pfx.get());
   381   }
   383   fprintf_stderr(out, "%s>\n", aPrefix);
   384 }
   386 nsresult
   387 nsBlockFrame::GetFrameName(nsAString& aResult) const
   388 {
   389   return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
   390 }
   391 #endif
   393 #ifdef DEBUG
   394 nsFrameState
   395 nsBlockFrame::GetDebugStateBits() const
   396 {
   397   // We don't want to include our cursor flag in the bits the
   398   // regression tester looks at
   399   return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
   400 }
   401 #endif
   403 nsIAtom*
   404 nsBlockFrame::GetType() const
   405 {
   406   return nsGkAtoms::blockFrame;
   407 }
   409 void
   410 nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey)
   411 {
   412   if (IsSVGText()) {
   413     NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
   414                  "unexpected block frame in SVG text");
   415     GetParent()->InvalidateFrame();
   416     return;
   417   }
   418   nsBlockFrameSuper::InvalidateFrame(aDisplayItemKey);
   419 }
   421 void
   422 nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
   423 {
   424   if (IsSVGText()) {
   425     NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame,
   426                  "unexpected block frame in SVG text");
   427     GetParent()->InvalidateFrame();
   428     return;
   429   }
   430   nsBlockFrameSuper::InvalidateFrameWithRect(aRect, aDisplayItemKey);
   431 }
   433 nscoord
   434 nsBlockFrame::GetBaseline() const
   435 {
   436   nscoord result;
   437   if (nsLayoutUtils::GetLastLineBaseline(this, &result))
   438     return result;
   439   return nsFrame::GetBaseline();
   440 }
   442 nscoord
   443 nsBlockFrame::GetCaretBaseline() const
   444 {
   445   nsRect contentRect = GetContentRect();
   446   nsMargin bp = GetUsedBorderAndPadding();
   448   if (!mLines.empty()) {
   449     const_line_iterator line = begin_lines();
   450     const nsLineBox* firstLine = line;
   451     if (firstLine->GetChildCount()) {
   452       return bp.top + firstLine->mFirstChild->GetCaretBaseline();
   453     }
   454   }
   455   nsRefPtr<nsFontMetrics> fm;
   456   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   457   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
   458   nscoord lineHeight =
   459     nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
   460                                       contentRect.height, inflation);
   461   return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight) + bp.top;
   462 }
   464 /////////////////////////////////////////////////////////////////////////////
   465 // Child frame enumeration
   467 const nsFrameList&
   468 nsBlockFrame::GetChildList(ChildListID aListID) const
   469 {
   470   switch (aListID) {
   471     case kPrincipalList:
   472       return mFrames;
   473     case kOverflowList: {
   474       FrameLines* overflowLines = GetOverflowLines();
   475       return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
   476     }
   477     case kFloatList:
   478       return mFloats;
   479     case kOverflowOutOfFlowList: {
   480       const nsFrameList* list = GetOverflowOutOfFlows();
   481       return list ? *list : nsFrameList::EmptyList();
   482     }
   483     case kPushedFloatsList: {
   484       const nsFrameList* list = GetPushedFloats();
   485       return list ? *list : nsFrameList::EmptyList();
   486     }
   487     case kBulletList: {
   488       const nsFrameList* list = GetOutsideBulletList();
   489       return list ? *list : nsFrameList::EmptyList();
   490     }
   491     default:
   492       return nsContainerFrame::GetChildList(aListID);
   493   }
   494 }
   496 void
   497 nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const
   498 {
   499   nsContainerFrame::GetChildLists(aLists);
   500   FrameLines* overflowLines = GetOverflowLines();
   501   if (overflowLines) {
   502     overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
   503   }
   504   const nsFrameList* list = GetOverflowOutOfFlows();
   505   if (list) {
   506     list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
   507   }
   508   mFloats.AppendIfNonempty(aLists, kFloatList);
   509   list = GetOutsideBulletList();
   510   if (list) {
   511     list->AppendIfNonempty(aLists, kBulletList);
   512   }
   513   list = GetPushedFloats();
   514   if (list) {
   515     list->AppendIfNonempty(aLists, kPushedFloatsList);
   516   }
   517 }
   519 /* virtual */ bool
   520 nsBlockFrame::IsFloatContainingBlock() const
   521 {
   522   return true;
   523 }
   525 static void
   526 ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent)
   527 {
   528   NS_ASSERTION(aOldParent == aFrame->GetParent(),
   529                "Parent not consistent with expectations");
   531   aFrame->SetParent(aNewParent);
   533   // When pushing and pulling frames we need to check for whether any
   534   // views need to be reparented
   535   nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent);
   536 }
   538 static void
   539 ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent,
   540                nsIFrame* aNewParent)
   541 {
   542   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
   543     ReparentFrame(e.get(), aOldParent, aNewParent);
   544   }
   545 }
   547 /**
   548  * Remove the first line from aFromLines and adjust the associated frame list
   549  * aFromFrames accordingly.  The removed line is assigned to *aOutLine and
   550  * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
   551  * that were extracted from the head of aFromFrames.
   552  * aFromLines must contain at least one line, the line may be empty.
   553  * @return true if aFromLines becomes empty
   554  */
   555 static bool
   556 RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
   557                 nsLineBox** aOutLine, nsFrameList* aOutFrames)
   558 {
   559   nsLineList_iterator removedLine = aFromLines.begin();
   560   *aOutLine = removedLine;
   561   nsLineList_iterator next = aFromLines.erase(removedLine);
   562   bool isLastLine = next == aFromLines.end();
   563   nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
   564                                    : next->mFirstChild->GetPrevSibling();
   565   nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
   566   *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
   567   return isLastLine;
   568 }
   570 //////////////////////////////////////////////////////////////////////
   571 // Reflow methods
   573 /* virtual */ void
   574 nsBlockFrame::MarkIntrinsicWidthsDirty()
   575 {
   576   nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation());
   577   dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
   578   dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
   579   if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
   580     for (nsIFrame* frame = dirtyBlock; frame; 
   581          frame = frame->GetNextContinuation()) {
   582       frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
   583     }
   584   }
   586   nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
   587 }
   589 void
   590 nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState()
   591 {
   592   nsPresContext *presContext = PresContext();
   593   if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) {
   594     return;
   595   }
   596   bool inflationEnabled =
   597     !presContext->mInflationDisabledForShrinkWrap;
   598   if (inflationEnabled !=
   599       !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED)) {
   600     mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
   601     mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
   602     if (inflationEnabled) {
   603       AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
   604     } else {
   605       RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED);
   606     }
   607   }
   608 }
   610 /* virtual */ nscoord
   611 nsBlockFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   612 {
   613   nsIFrame* firstInFlow = FirstContinuation();
   614   if (firstInFlow != this)
   615     return firstInFlow->GetMinWidth(aRenderingContext);
   617   DISPLAY_MIN_WIDTH(this, mMinWidth);
   619   CheckIntrinsicCacheAgainstShrinkWrapState();
   621   if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
   622     return mMinWidth;
   624 #ifdef DEBUG
   625   if (gNoisyIntrinsic) {
   626     IndentBy(stdout, gNoiseIndent);
   627     ListTag(stdout);
   628     printf(": GetMinWidth\n");
   629   }
   630   AutoNoisyIndenter indenter(gNoisyIntrinsic);
   631 #endif
   633   for (nsBlockFrame* curFrame = this; curFrame;
   634        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   635     curFrame->LazyMarkLinesDirty();
   636   }
   638   if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
   639     ResolveBidi();
   640   InlineMinWidthData data;
   641   for (nsBlockFrame* curFrame = this; curFrame;
   642        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   643     for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
   644       line != line_end; ++line)
   645     {
   646 #ifdef DEBUG
   647       if (gNoisyIntrinsic) {
   648         IndentBy(stdout, gNoiseIndent);
   649         printf("line (%s%s)\n",
   650                line->IsBlock() ? "block" : "inline",
   651                line->IsEmpty() ? ", empty" : "");
   652       }
   653       AutoNoisyIndenter lineindent(gNoisyIntrinsic);
   654 #endif
   655       if (line->IsBlock()) {
   656         data.ForceBreak(aRenderingContext);
   657         data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   658                         line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
   659         data.ForceBreak(aRenderingContext);
   660       } else {
   661         if (!curFrame->GetPrevContinuation() &&
   662             line == curFrame->begin_lines()) {
   663           // Only add text-indent if it has no percentages; using a
   664           // percentage basis of 0 unconditionally would give strange
   665           // behavior for calc(10%-3px).
   666           const nsStyleCoord &indent = StyleText()->mTextIndent;
   667           if (indent.ConvertsToLength())
   668             data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
   669         }
   670         // XXX Bug NNNNNN Should probably handle percentage text-indent.
   672         data.line = &line;
   673         data.lineContainer = curFrame;
   674         nsIFrame *kid = line->mFirstChild;
   675         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
   676              ++i, kid = kid->GetNextSibling()) {
   677           kid->AddInlineMinWidth(aRenderingContext, &data);
   678         }
   679       }
   680 #ifdef DEBUG
   681       if (gNoisyIntrinsic) {
   682         IndentBy(stdout, gNoiseIndent);
   683         printf("min: [prevLines=%d currentLine=%d]\n",
   684                data.prevLines, data.currentLine);
   685       }
   686 #endif
   687     }
   688   }
   689   data.ForceBreak(aRenderingContext);
   691   mMinWidth = data.prevLines;
   692   return mMinWidth;
   693 }
   695 /* virtual */ nscoord
   696 nsBlockFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   697 {
   698   nsIFrame* firstInFlow = FirstContinuation();
   699   if (firstInFlow != this)
   700     return firstInFlow->GetPrefWidth(aRenderingContext);
   702   DISPLAY_PREF_WIDTH(this, mPrefWidth);
   704   CheckIntrinsicCacheAgainstShrinkWrapState();
   706   if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
   707     return mPrefWidth;
   709 #ifdef DEBUG
   710   if (gNoisyIntrinsic) {
   711     IndentBy(stdout, gNoiseIndent);
   712     ListTag(stdout);
   713     printf(": GetPrefWidth\n");
   714   }
   715   AutoNoisyIndenter indenter(gNoisyIntrinsic);
   716 #endif
   718   for (nsBlockFrame* curFrame = this; curFrame;
   719        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   720     curFrame->LazyMarkLinesDirty();
   721   }
   723   if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
   724     ResolveBidi();
   725   InlinePrefWidthData data;
   726   for (nsBlockFrame* curFrame = this; curFrame;
   727        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   728     for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
   729          line != line_end; ++line)
   730     {
   731 #ifdef DEBUG
   732       if (gNoisyIntrinsic) {
   733         IndentBy(stdout, gNoiseIndent);
   734         printf("line (%s%s)\n",
   735                line->IsBlock() ? "block" : "inline",
   736                line->IsEmpty() ? ", empty" : "");
   737       }
   738       AutoNoisyIndenter lineindent(gNoisyIntrinsic);
   739 #endif
   740       if (line->IsBlock()) {
   741         data.ForceBreak(aRenderingContext);
   742         data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
   743                         line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
   744         data.ForceBreak(aRenderingContext);
   745       } else {
   746         if (!curFrame->GetPrevContinuation() &&
   747             line == curFrame->begin_lines()) {
   748           // Only add text-indent if it has no percentages; using a
   749           // percentage basis of 0 unconditionally would give strange
   750           // behavior for calc(10%-3px).
   751           const nsStyleCoord &indent = StyleText()->mTextIndent;
   752           if (indent.ConvertsToLength())
   753             data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
   754         }
   755         // XXX Bug NNNNNN Should probably handle percentage text-indent.
   757         data.line = &line;
   758         data.lineContainer = curFrame;
   759         nsIFrame *kid = line->mFirstChild;
   760         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
   761              ++i, kid = kid->GetNextSibling()) {
   762           kid->AddInlinePrefWidth(aRenderingContext, &data);
   763         }
   764       }
   765 #ifdef DEBUG
   766       if (gNoisyIntrinsic) {
   767         IndentBy(stdout, gNoiseIndent);
   768         printf("pref: [prevLines=%d currentLine=%d]\n",
   769                data.prevLines, data.currentLine);
   770       }
   771 #endif
   772     }
   773   }
   774   data.ForceBreak(aRenderingContext);
   776   mPrefWidth = data.prevLines;
   777   return mPrefWidth;
   778 }
   780 nsRect
   781 nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const
   782 {
   783   // be conservative
   784   if (StyleContext()->HasTextDecorationLines()) {
   785     return GetVisualOverflowRect();
   786   }
   787   return ComputeSimpleTightBounds(aContext);
   788 }
   790 /* virtual */ nsresult
   791 nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext,
   792                                       nscoord* aX,
   793                                       nscoord* aXMost)
   794 {
   795   nsIFrame* firstInFlow = FirstContinuation();
   796   if (firstInFlow != this) {
   797     return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost);
   798   }
   800   *aX = 0;
   801   *aXMost = 0;
   803   nsresult rv;
   804   InlinePrefWidthData data;
   805   for (nsBlockFrame* curFrame = this; curFrame;
   806        curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
   807     for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
   808          line != line_end; ++line)
   809     {
   810       nscoord childX, childXMost;
   811       if (line->IsBlock()) {
   812         data.ForceBreak(aRenderingContext);
   813         rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext,
   814                                                         &childX, &childXMost);
   815         NS_ENSURE_SUCCESS(rv, rv);
   816         *aX = std::min(*aX, childX);
   817         *aXMost = std::max(*aXMost, childXMost);
   818       } else {
   819         if (!curFrame->GetPrevContinuation() &&
   820             line == curFrame->begin_lines()) {
   821           // Only add text-indent if it has no percentages; using a
   822           // percentage basis of 0 unconditionally would give strange
   823           // behavior for calc(10%-3px).
   824           const nsStyleCoord &indent = StyleText()->mTextIndent;
   825           if (indent.ConvertsToLength()) {
   826             data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
   827           }
   828         }
   829         // XXX Bug NNNNNN Should probably handle percentage text-indent.
   831         data.line = &line;
   832         data.lineContainer = curFrame;
   833         nsIFrame *kid = line->mFirstChild;
   834         for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end;
   835              ++i, kid = kid->GetNextSibling()) {
   836           rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX,
   837                                             &childXMost);
   838           NS_ENSURE_SUCCESS(rv, rv);
   839           *aX = std::min(*aX, data.currentLine + childX);
   840           *aXMost = std::max(*aXMost, data.currentLine + childXMost);
   841           kid->AddInlinePrefWidth(aRenderingContext, &data);
   842         }
   843       }
   844     }
   845   }
   846   data.ForceBreak(aRenderingContext);
   848   return NS_OK;
   849 }
   851 static bool
   852 AvailableSpaceShrunk(const nsRect& aOldAvailableSpace,
   853                      const nsRect& aNewAvailableSpace)
   854 {
   855   if (aNewAvailableSpace.width == 0) {
   856     // Positions are not significant if the width is zero.
   857     return aOldAvailableSpace.width != 0;
   858   }
   859   NS_ASSERTION(aOldAvailableSpace.x <= aNewAvailableSpace.x &&
   860                aOldAvailableSpace.XMost() >= aNewAvailableSpace.XMost(),
   861                "available space should never grow");
   862   return aOldAvailableSpace.width != aNewAvailableSpace.width;
   863 }
   865 static nsSize
   866 CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
   867                                          nsSize aFrameSize)
   868 {
   869   // The issue here is that for a 'height' of 'auto' the reflow state
   870   // code won't know how to calculate the containing block height
   871   // because it's calculated bottom up. So we use our own computed
   872   // size as the dimensions.
   873   nsIFrame* frame = aReflowState.frame;
   875   nsSize cbSize(aFrameSize);
   876     // Containing block is relative to the padding edge
   877   const nsMargin& border =
   878     aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding();
   879   cbSize.width -= border.LeftRight();
   880   cbSize.height -= border.TopBottom();
   882   if (frame->GetParent()->GetContent() == frame->GetContent() &&
   883       frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) {
   884     // We are a wrapped frame for the content (and the wrapper is not the
   885     // canvas frame, whose size is not meaningful here).
   886     // Use the container's dimensions, if they have been precomputed.
   887     // XXX This is a hack! We really should be waiting until the outermost
   888     // frame is fully reflowed and using the resulting dimensions, even
   889     // if they're intrinsic.
   890     // In fact we should be attaching absolute children to the outermost
   891     // frame and not always sticking them in block frames.
   893     // First, find the reflow state for the outermost frame for this
   894     // content, except for fieldsets where the inner anonymous frame has
   895     // the correct padding area with the legend taken into account.
   896     const nsHTMLReflowState* aLastRS = &aReflowState;
   897     const nsHTMLReflowState* lastButOneRS = &aReflowState;
   898     while (aLastRS->parentReflowState &&
   899            aLastRS->parentReflowState->frame->GetContent() == frame->GetContent() &&
   900            aLastRS->parentReflowState->frame->GetType() != nsGkAtoms::fieldSetFrame) {
   901       lastButOneRS = aLastRS;
   902       aLastRS = aLastRS->parentReflowState;
   903     }
   904     if (aLastRS != &aReflowState) {
   905       // Scrollbars need to be specifically excluded, if present, because they are outside the
   906       // padding-edge. We need better APIs for getting the various boxes from a frame.
   907       nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRS->frame);
   908       nsMargin scrollbars(0,0,0,0);
   909       if (scrollFrame) {
   910         scrollbars =
   911           scrollFrame->GetDesiredScrollbarSizes(aLastRS->frame->PresContext(),
   912                                                 aLastRS->rendContext);
   913         if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
   914           scrollbars.top = scrollbars.bottom = 0;
   915         }
   916         if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
   917           scrollbars.left = scrollbars.right = 0;
   918         }
   919       }
   920       // We found a reflow state for the outermost wrapping frame, so use
   921       // its computed metrics if available
   922       if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
   923         cbSize.width = std::max(0,
   924           aLastRS->ComputedWidth() + aLastRS->ComputedPhysicalPadding().LeftRight() - scrollbars.LeftRight());
   925       }
   926       if (aLastRS->ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
   927         cbSize.height = std::max(0,
   928           aLastRS->ComputedHeight() + aLastRS->ComputedPhysicalPadding().TopBottom() - scrollbars.TopBottom());
   929       }
   930     }
   931   }
   933   return cbSize;
   934 }
   936 nsresult
   937 nsBlockFrame::Reflow(nsPresContext*           aPresContext,
   938                      nsHTMLReflowMetrics&     aMetrics,
   939                      const nsHTMLReflowState& aReflowState,
   940                      nsReflowStatus&          aStatus)
   941 {
   942   DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
   943   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   944 #ifdef DEBUG
   945   if (gNoisyReflow) {
   946     IndentBy(stdout, gNoiseIndent);
   947     ListTag(stdout);
   948     printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
   949            aReflowState.AvailableWidth(), aReflowState.AvailableHeight(),
   950            aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
   951   }
   952   AutoNoisyIndenter indent(gNoisy);
   953   PRTime start = 0; // Initialize these variablies to silence the compiler.
   954   int32_t ctc = 0;        // We only use these if they are set (gLameReflowMetrics).
   955   if (gLameReflowMetrics) {
   956     start = PR_Now();
   957     ctc = nsLineBox::GetCtorCount();
   958   }
   959 #endif
   961   const nsHTMLReflowState *reflowState = &aReflowState;
   962   nscoord consumedHeight = GetConsumedHeight();
   963   nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState,
   964                                                                consumedHeight);
   965   Maybe<nsHTMLReflowState> mutableReflowState;
   966   // If we have non-auto height, we're clipping our kids and we fit,
   967   // make sure our kids fit too.
   968   if (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
   969       aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
   970       ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
   971     LogicalMargin blockDirExtras = aReflowState.ComputedLogicalBorderPadding();
   972     WritingMode wm = aReflowState.GetWritingMode();
   973     if (GetLogicalSkipSides() & (LOGICAL_SIDE_B_START)) {
   974       blockDirExtras.BStart(wm) = 0;
   975     } else {
   976       // Bottom margin never causes us to create continuations, so we
   977       // don't need to worry about whether it fits in its entirety.
   978       blockDirExtras.BStart(wm) +=
   979         aReflowState.ComputedLogicalMargin().BStart(wm);
   980     }
   982     if (effectiveComputedHeight + blockDirExtras.BStartEnd(wm) <=
   983         aReflowState.AvailableBSize()) {
   984       mutableReflowState.construct(aReflowState);
   985       mutableReflowState.ref().AvailableBSize() = NS_UNCONSTRAINEDSIZE;
   986       reflowState = mutableReflowState.addr();
   987     }
   988   }
   990   // See comment below about oldSize. Use *only* for the
   991   // abs-pos-containing-block-size-change optimization!
   992   nsSize oldSize = GetSize();
   994   // Should we create a float manager?
   995   nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState));
   997   // XXXldb If we start storing the float manager in the frame rather
   998   // than keeping it around only during reflow then we should create it
   999   // only when there are actually floats to manage.  Otherwise things
  1000   // like tables will gain significant bloat.
  1001   bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
  1002   if (needFloatManager)
  1003     autoFloatManager.CreateFloatManager(aPresContext);
  1005   // OK, some lines may be reflowed. Blow away any saved line cursor
  1006   // because we may invalidate the nondecreasing
  1007   // overflowArea.VisualOverflow().y/yMost invariant, and we may even
  1008   // delete the line with the line cursor.
  1009   ClearLineCursor();
  1011   if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
  1012     return NS_OK;
  1015   bool topMarginRoot, bottomMarginRoot;
  1016   IsMarginRoot(&topMarginRoot, &bottomMarginRoot);
  1018   // Cache the consumed height in the block reflow state so that we don't have
  1019   // to continually recompute it.
  1020   nsBlockReflowState state(*reflowState, aPresContext, this,
  1021                            topMarginRoot, bottomMarginRoot, needFloatManager,
  1022                            consumedHeight);
  1024   if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
  1025     static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
  1027   if (RenumberLists(aPresContext)) {
  1028     AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  1031   nsresult rv = NS_OK;
  1033   // ALWAYS drain overflow. We never want to leave the previnflow's
  1034   // overflow lines hanging around; block reflow depends on the
  1035   // overflow line lists being cleared out between reflow passes.
  1036   DrainOverflowLines();
  1038   // Handle paginated overflow (see nsContainerFrame.h)
  1039   nsOverflowAreas ocBounds;
  1040   nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
  1041   if (GetPrevInFlow()) {
  1042     ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0,
  1043                                     ocStatus);
  1046   // Now that we're done cleaning up our overflow container lists, we can
  1047   // give |state| its nsOverflowContinuationTracker.
  1048   nsOverflowContinuationTracker tracker(this, false);
  1049   state.mOverflowTracker = &tracker;
  1051   // Drain & handle pushed floats
  1052   DrainPushedFloats(state);
  1053   nsOverflowAreas fcBounds;
  1054   nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
  1055   ReflowPushedFloats(state, fcBounds, fcStatus);
  1057   // If we're not dirty (which means we'll mark everything dirty later)
  1058   // and our width has changed, mark the lines dirty that we need to
  1059   // mark dirty for a resize reflow.
  1060   if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowState->mFlags.mHResize) {
  1061     PrepareResizeReflow(state);
  1064   LazyMarkLinesDirty();
  1066   mState &= ~NS_FRAME_FIRST_REFLOW;
  1068   // Now reflow...
  1069   rv = ReflowDirtyLines(state);
  1071   // If we have a next-in-flow, and that next-in-flow has pushed floats from
  1072   // this frame from a previous iteration of reflow, then we should not return
  1073   // a status of NS_FRAME_COMPLETE, since we actually have overflow, it's just
  1074   // already been handled.
  1076   // NOTE: This really shouldn't happen, since we _should_ pull back our floats
  1077   // and reflow them, but just in case it does, this is a safety precaution so
  1078   // we don't end up with a placeholder pointing to frames that have already
  1079   // been deleted as part of removing our next-in-flow.
  1080   if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
  1081     nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow());
  1082     while (nif) {
  1083       if (nif->HasPushedFloatsFromPrevContinuation()) {
  1084         NS_MergeReflowStatusInto(&state.mReflowStatus, NS_FRAME_NOT_COMPLETE);
  1087       nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow());
  1091   NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
  1092   if (NS_FAILED(rv)) return rv;
  1094   NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
  1095   NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
  1097   // If we end in a BR with clear and affected floats continue,
  1098   // we need to continue, too.
  1099   if (NS_UNCONSTRAINEDSIZE != reflowState->AvailableHeight() &&
  1100       NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
  1101       state.mFloatManager->ClearContinues(FindTrailingClear())) {
  1102     NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
  1105   if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
  1106     if (HasOverflowLines() || HasPushedFloats()) {
  1107       state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  1110 #ifdef DEBUG_kipp
  1111     ListTag(stdout); printf(": block is not fully complete\n");
  1112 #endif
  1115   // Place the "marker" (bullet) frame if it is placed next to a block
  1116   // child.
  1117   //
  1118   // According to the CSS2 spec, section 12.6.1, the "marker" box
  1119   // participates in the height calculation of the list-item box's
  1120   // first line box.
  1121   //
  1122   // There are exactly two places a bullet can be placed: near the
  1123   // first or second line. It's only placed on the second line in a
  1124   // rare case: an empty first line followed by a second line that
  1125   // contains a block (example: <LI>\n<P>... ). This is where
  1126   // the second case can happen.
  1127   if (HasOutsideBullet() && !mLines.empty() &&
  1128       (mLines.front()->IsBlock() ||
  1129        (0 == mLines.front()->BSize() &&
  1130         mLines.front() != mLines.back() &&
  1131         mLines.begin().next()->IsBlock()))) {
  1132     // Reflow the bullet
  1133     nsHTMLReflowMetrics metrics(aReflowState);
  1134     // XXX Use the entire line when we fix bug 25888.
  1135     nsLayoutUtils::LinePosition position;
  1136     bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
  1137     nscoord lineTop = havePosition ? position.mTop
  1138                                    : reflowState->ComputedPhysicalBorderPadding().top;
  1139     nsIFrame* bullet = GetOutsideBullet();
  1140     ReflowBullet(bullet, state, metrics, lineTop);
  1141     NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
  1142                  "empty bullet took up space");
  1144     if (havePosition && !BulletIsEmpty()) {
  1145       // We have some lines to align the bullet with.  
  1147       // Doing the alignment using the baseline will also cater for
  1148       // bullets that are placed next to a child block (bug 92896)
  1150       // Tall bullets won't look particularly nice here...
  1151       nsRect bbox = bullet->GetRect();
  1152       bbox.y = position.mBaseline - metrics.TopAscent();
  1153       bullet->SetRect(bbox);
  1155     // Otherwise just leave the bullet where it is, up against our top padding.
  1158   CheckFloats(state);
  1160   // Compute our final size
  1161   nscoord bottomEdgeOfChildren;
  1162   ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren);
  1163   nsRect areaBounds = nsRect(0, 0, aMetrics.Width(), aMetrics.Height());
  1164   ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay,
  1165                        bottomEdgeOfChildren, aMetrics.mOverflowAreas);
  1166   // Factor overflow container child bounds into the overflow area
  1167   aMetrics.mOverflowAreas.UnionWith(ocBounds);
  1168   // Factor pushed float child bounds into the overflow area
  1169   aMetrics.mOverflowAreas.UnionWith(fcBounds);
  1171   // Let the absolutely positioned container reflow any absolutely positioned
  1172   // child frames that need to be reflowed, e.g., elements with a percentage
  1173   // based width/height
  1174   // We want to do this under either of two conditions:
  1175   //  1. If we didn't do the incremental reflow above.
  1176   //  2. If our size changed.
  1177   // Even though it's the padding edge that's the containing block, we
  1178   // can use our rect (the border edge) since if the border style
  1179   // changed, the reflow would have been targeted at us so we'd satisfy
  1180   // condition 1.
  1181   // XXX checking oldSize is bogus, there are various reasons we might have
  1182   // reflowed but our size might not have been changed to what we
  1183   // asked for (e.g., we ended up being pushed to a new page)
  1184   // When WillReflowAgainForClearance is true, we will reflow again without
  1185   // resetting the size. Because of this, we must not reflow our abs-pos children
  1186   // in that situation --- what we think is our "new size"
  1187   // will not be our real new size. This also happens to be more efficient.
  1188   if (HasAbsolutelyPositionedChildren()) {
  1189     nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
  1190     bool haveInterrupt = aPresContext->HasPendingInterrupt();
  1191     if (reflowState->WillReflowAgainForClearance() ||
  1192         haveInterrupt) {
  1193       // Make sure that when we reflow again we'll actually reflow all the abs
  1194       // pos frames that might conceivably depend on our size (or all of them,
  1195       // if we're dirty right now and interrupted; in that case we also need
  1196       // to mark them all with NS_FRAME_IS_DIRTY).  Sadly, we can't do much
  1197       // better than that, because we don't really know what our size will be,
  1198       // and it might in fact not change on the followup reflow!
  1199       if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
  1200         absoluteContainer->MarkAllFramesDirty();
  1201       } else {
  1202         absoluteContainer->MarkSizeDependentFramesDirty();
  1204     } else {
  1205       nsSize containingBlockSize =
  1206         CalculateContainingBlockSizeForAbsolutes(*reflowState,
  1207                                                  nsSize(aMetrics.Width(),
  1208                                                         aMetrics.Height()));
  1210       // Mark frames that depend on changes we just made to this frame as dirty:
  1211       // Now we can assume that the padding edge hasn't moved.
  1212       // We need to reflow the absolutes if one of them depends on
  1213       // its placeholder position, or the containing block size in a
  1214       // direction in which the containing block size might have
  1215       // changed.
  1216       bool cbWidthChanged = aMetrics.Width() != oldSize.width;
  1217       bool isRoot = !GetContent()->GetParent();
  1218       // If isRoot and we have auto height, then we are the initial
  1219       // containing block and the containing block height is the
  1220       // viewport height, which can't change during incremental
  1221       // reflow.
  1222       bool cbHeightChanged =
  1223         !(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
  1224         aMetrics.Height() != oldSize.height;
  1226       nsRect containingBlock(nsPoint(0, 0), containingBlockSize);
  1227       absoluteContainer->Reflow(this, aPresContext, *reflowState,
  1228                                 state.mReflowStatus,
  1229                                 containingBlock, true,
  1230                                 cbWidthChanged, cbHeightChanged,
  1231                                 &aMetrics.mOverflowAreas);
  1233       //XXXfr Why isn't this rv (and others in this file) checked/returned?
  1237   FinishAndStoreOverflow(&aMetrics);
  1239   // Clear the float manager pointer in the block reflow state so we
  1240   // don't waste time translating the coordinate system back on a dead
  1241   // float manager.
  1242   if (needFloatManager)
  1243     state.mFloatManager = nullptr;
  1245   aStatus = state.mReflowStatus;
  1247 #ifdef DEBUG
  1248   // Between when we drain pushed floats and when we complete reflow,
  1249   // we're allowed to have multiple continuations of the same float on
  1250   // our floats list, since a first-in-flow might get pushed to a later
  1251   // continuation of its containing block.  But it's not permitted
  1252   // outside that time.
  1253   nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
  1255   if (gNoisyReflow) {
  1256     IndentBy(stdout, gNoiseIndent);
  1257     ListTag(stdout);
  1258     printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
  1259            aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
  1260            aMetrics.Width(), aMetrics.Height(),
  1261            aMetrics.mCarriedOutBottomMargin.get());
  1262     if (HasOverflowAreas()) {
  1263       printf(" overflow-vis={%d,%d,%d,%d}",
  1264              aMetrics.VisualOverflow().x,
  1265              aMetrics.VisualOverflow().y,
  1266              aMetrics.VisualOverflow().width,
  1267              aMetrics.VisualOverflow().height);
  1268       printf(" overflow-scr={%d,%d,%d,%d}",
  1269              aMetrics.ScrollableOverflow().x,
  1270              aMetrics.ScrollableOverflow().y,
  1271              aMetrics.ScrollableOverflow().width,
  1272              aMetrics.ScrollableOverflow().height);
  1274     printf("\n");
  1277   if (gLameReflowMetrics) {
  1278     PRTime end = PR_Now();
  1280     int32_t ectc = nsLineBox::GetCtorCount();
  1281     int32_t numLines = mLines.size();
  1282     if (!numLines) numLines = 1;
  1283     PRTime delta, perLineDelta, lines;
  1284     lines = int64_t(numLines);
  1285     delta = end - start;
  1286     perLineDelta = delta / lines;
  1288     ListTag(stdout);
  1289     char buf[400];
  1290     PR_snprintf(buf, sizeof(buf),
  1291                 ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
  1292                 delta, perLineDelta, numLines, ectc - ctc);
  1293     printf("%s\n", buf);
  1295 #endif
  1297   NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics);
  1298   return rv;
  1301 bool
  1302 nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
  1304   line_iterator begin = begin_lines();
  1305   line_iterator line = end_lines();
  1307   while (true) {
  1308     if (begin == line) {
  1309       return false;
  1311     --line;
  1312     if (line->BSize() != 0 || !line->CachedIsEmpty()) {
  1313       return false;
  1315     if (line->HasClearance()) {
  1316       return true;
  1319   // not reached
  1322 void
  1323 nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
  1324                                nsBlockReflowState&      aState,
  1325                                nsHTMLReflowMetrics&     aMetrics,
  1326                                nscoord*                 aBottomEdgeOfChildren)
  1328   const nsMargin& borderPadding = aState.BorderPadding();
  1329 #ifdef NOISY_FINAL_SIZE
  1330   ListTag(stdout);
  1331   printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
  1332          aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
  1333          aState.mPrevBottomMargin,
  1334          borderPadding.top, borderPadding.bottom);
  1335 #endif
  1337   // Compute final width
  1338   aMetrics.Width() =
  1339     NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.left,
  1340                                               aReflowState.ComputedWidth()), 
  1341                          borderPadding.right);
  1343   // Return bottom margin information
  1344   // rbs says he hit this assertion occasionally (see bug 86947), so
  1345   // just set the margin to zero and we'll figure out why later
  1346   //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
  1347   //             "someone else set the margin");
  1348   nscoord nonCarriedOutVerticalMargin = 0;
  1349   if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
  1350     // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
  1351     // line with clearance and a non-zero top margin and all
  1352     // subsequent lines are empty, then we do not allow our children's
  1353     // carried out bottom margin to be carried out of us and collapse
  1354     // with our own bottom margin.
  1355     if (CheckForCollapsedBottomMarginFromClearanceLine()) {
  1356       // Convert the children's carried out margin to something that
  1357       // we will include in our height
  1358       nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
  1359       aState.mPrevBottomMargin.Zero();
  1361     aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
  1362   } else {
  1363     aMetrics.mCarriedOutBottomMargin.Zero();
  1366   nscoord bottomEdgeOfChildren = aState.mY + nonCarriedOutVerticalMargin;
  1367   // Shrink wrap our height around our contents.
  1368   if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ||
  1369       NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
  1370     // When we are a bottom-margin root make sure that our last
  1371     // childs bottom margin is fully applied. We also do this when
  1372     // we have a computed height, since in that case the carried out
  1373     // margin is not going to be applied anywhere, so we should note it
  1374     // here to be included in the overflow area.
  1375     // Apply the margin only if there's space for it.
  1376     if (bottomEdgeOfChildren < aState.mReflowState.AvailableHeight())
  1378       // Truncate bottom margin if it doesn't fit to our available height.
  1379       bottomEdgeOfChildren =
  1380         std::min(bottomEdgeOfChildren + aState.mPrevBottomMargin.get(),
  1381                aState.mReflowState.AvailableHeight());
  1384   if (aState.GetFlag(BRS_FLOAT_MGR)) {
  1385     // Include the float manager's state to properly account for the
  1386     // bottom margin of any floated elements; e.g., inside a table cell.
  1387     nscoord floatHeight =
  1388       aState.ClearFloats(bottomEdgeOfChildren, NS_STYLE_CLEAR_BOTH,
  1389                          nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
  1390     bottomEdgeOfChildren = std::max(bottomEdgeOfChildren, floatHeight);
  1393   if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()
  1394       && (mParent->GetType() != nsGkAtoms::columnSetFrame ||
  1395           aReflowState.parentReflowState->AvailableHeight() == NS_UNCONSTRAINEDSIZE)) {
  1396     ComputeFinalHeight(aReflowState, &aState.mReflowStatus,
  1397                        aState.mY + nonCarriedOutVerticalMargin,
  1398                        borderPadding, aMetrics, aState.mConsumedHeight);
  1399     if (!NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
  1400       // Use the current height; continuations will take up the rest.
  1401       // Do extend the height to at least consume the available
  1402       // height, otherwise our left/right borders (for example) won't
  1403       // extend all the way to the break.
  1404       aMetrics.Height() = std::max(aReflowState.AvailableHeight(),
  1405                                aState.mY + nonCarriedOutVerticalMargin);
  1406       // ... but don't take up more height than is available
  1407       nscoord effectiveComputedHeight =
  1408         GetEffectiveComputedHeight(aReflowState, aState.GetConsumedHeight());
  1409       aMetrics.Height() = std::min(aMetrics.Height(),
  1410                                borderPadding.top + effectiveComputedHeight);
  1411       // XXX It's pretty wrong that our bottom border still gets drawn on
  1412       // on its own on the last-in-flow, even if we ran out of height
  1413       // here. We need GetSkipSides to check whether we ran out of content
  1414       // height in the current frame, not whether it's last-in-flow.
  1417     // Don't carry out a bottom margin when our height is fixed.
  1418     aMetrics.mCarriedOutBottomMargin.Zero();
  1420   else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
  1421     nscoord contentHeight = bottomEdgeOfChildren - borderPadding.top;
  1422     nscoord autoHeight = aReflowState.ApplyMinMaxHeight(contentHeight);
  1423     if (autoHeight != contentHeight) {
  1424       // Our min-height or max-height made our height change.  Don't carry out
  1425       // our kids' bottom margins.
  1426       aMetrics.mCarriedOutBottomMargin.Zero();
  1428     autoHeight += borderPadding.top + borderPadding.bottom;
  1429     aMetrics.Height() = autoHeight;
  1431   else {
  1432     NS_ASSERTION(aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE,
  1433       "Shouldn't be incomplete if availableHeight is UNCONSTRAINED.");
  1434     aMetrics.Height() = std::max(aState.mY, aReflowState.AvailableHeight());
  1435     if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE)
  1436       // This should never happen, but it does. See bug 414255
  1437       aMetrics.Height() = aState.mY;
  1440   if (IS_TRUE_OVERFLOW_CONTAINER(this) &&
  1441       NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
  1442     // Overflow containers can only be overflow complete.
  1443     // Note that auto height overflow containers have no normal children
  1444     NS_ASSERTION(aMetrics.Height() == 0, "overflow containers must be zero-height");
  1445     NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
  1448   // Screen out negative heights --- can happen due to integer overflows :-(
  1449   aMetrics.Height() = std::max(0, aMetrics.Height());
  1450   *aBottomEdgeOfChildren = bottomEdgeOfChildren;
  1452   FrameProperties properties = Properties();
  1453   if (bottomEdgeOfChildren != aMetrics.Height() - borderPadding.bottom) {
  1454     properties.Set(BottomEdgeOfChildrenProperty(),
  1455                    NS_INT32_TO_PTR(bottomEdgeOfChildren));
  1456   } else {
  1457     properties.Delete(BottomEdgeOfChildrenProperty());
  1460 #ifdef DEBUG_blocks
  1461   if (CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) {
  1462     ListTag(stdout);
  1463     printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
  1465 #endif
  1468 static void
  1469 ConsiderBottomEdgeOfChildren(nscoord aBottomEdgeOfChildren,
  1470                              nsOverflowAreas& aOverflowAreas)
  1472   // Factor in the bottom edge of the children.  Child frames will be added
  1473   // to the overflow area as we iterate through the lines, but their margins
  1474   // won't, so we need to account for bottom margins here.
  1475   // REVIEW: For now, we do this for both visual and scrollable area,
  1476   // although when we make scrollable overflow area not be a subset of
  1477   // visual, we can change this.
  1478   NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
  1479     nsRect& o = aOverflowAreas.Overflow(otype);
  1480     o.height = std::max(o.YMost(), aBottomEdgeOfChildren) - o.y;
  1484 void
  1485 nsBlockFrame::ComputeOverflowAreas(const nsRect&         aBounds,
  1486                                    const nsStyleDisplay* aDisplay,
  1487                                    nscoord               aBottomEdgeOfChildren,
  1488                                    nsOverflowAreas&      aOverflowAreas)
  1490   // Compute the overflow areas of our children
  1491   // XXX_perf: This can be done incrementally.  It is currently one of
  1492   // the things that makes incremental reflow O(N^2).
  1493   nsOverflowAreas areas(aBounds, aBounds);
  1494   if (!ShouldApplyOverflowClipping(this, aDisplay)) {
  1495     for (line_iterator line = begin_lines(), line_end = end_lines();
  1496          line != line_end;
  1497          ++line) {
  1498       areas.UnionWith(line->GetOverflowAreas());
  1501     // Factor an outside bullet in; normally the bullet will be factored into
  1502     // the line-box's overflow areas. However, if the line is a block
  1503     // line then it won't; if there are no lines, it won't. So just
  1504     // factor it in anyway (it can't hurt if it was already done).
  1505     // XXXldb Can we just fix GetOverflowArea instead?
  1506     nsIFrame* outsideBullet = GetOutsideBullet();
  1507     if (outsideBullet) {
  1508       areas.UnionAllWith(outsideBullet->GetRect());
  1511     ConsiderBottomEdgeOfChildren(aBottomEdgeOfChildren, areas);
  1514 #ifdef NOISY_COMBINED_AREA
  1515   ListTag(stdout);
  1516   printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height);
  1517 #endif
  1519   aOverflowAreas = areas;
  1522 bool
  1523 nsBlockFrame::UpdateOverflow()
  1525   nsRect rect(nsPoint(0, 0), GetSize());
  1526   nsOverflowAreas overflowAreas(rect, rect);
  1528   // We need to update the overflow areas of lines manually, as they
  1529   // get cached and re-used otherwise. Lines aren't exposed as normal
  1530   // frame children, so calling UnionChildOverflow alone will end up
  1531   // using the old cached values.
  1532   for (line_iterator line = begin_lines(), line_end = end_lines();
  1533        line != line_end;
  1534        ++line) {
  1535     nsRect bounds = line->GetPhysicalBounds();
  1536     nsOverflowAreas lineAreas(bounds, bounds);
  1538     int32_t n = line->GetChildCount();
  1539     for (nsIFrame* lineFrame = line->mFirstChild;
  1540          n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
  1541       ConsiderChildOverflow(lineAreas, lineFrame);
  1544     // Consider the overflow areas of the floats attached to the line as well
  1545     if (line->HasFloats()) {
  1546       for (nsFloatCache* fc = line->GetFirstFloat(); fc; fc = fc->Next()) {
  1547         ConsiderChildOverflow(lineAreas, fc->mFloat);
  1551     line->SetOverflowAreas(lineAreas);
  1552     overflowAreas.UnionWith(lineAreas);
  1555   // Line cursor invariants depend on the overflow areas of the lines, so
  1556   // we must clear the line cursor since those areas may have changed.
  1557   ClearLineCursor();
  1559   // Union with child frames, skipping the principal and float lists
  1560   // since we already handled those using the line boxes.
  1561   nsLayoutUtils::UnionChildOverflow(this, overflowAreas,
  1562                                     kPrincipalList | kFloatList);
  1564   bool found;
  1565   nscoord bottomEdgeOfChildren = NS_PTR_TO_INT32(
  1566     Properties().Get(BottomEdgeOfChildrenProperty(), &found));
  1567   if (found) {
  1568     ConsiderBottomEdgeOfChildren(bottomEdgeOfChildren, overflowAreas);
  1571   return FinishAndStoreOverflow(overflowAreas, GetSize());
  1574 void
  1575 nsBlockFrame::LazyMarkLinesDirty()
  1577   if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) {
  1578     for (line_iterator line = begin_lines(), line_end = end_lines();
  1579          line != line_end; ++line) {
  1580       int32_t n = line->GetChildCount();
  1581       for (nsIFrame* lineFrame = line->mFirstChild;
  1582            n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
  1583         if (NS_SUBTREE_DIRTY(lineFrame)) {
  1584           // NOTE:  MarkLineDirty does more than just marking the line dirty.
  1585           MarkLineDirty(line, &mLines);
  1586           break;
  1590     RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
  1594 void
  1595 nsBlockFrame::MarkLineDirty(line_iterator aLine, const nsLineList* aLineList)
  1597   // Mark aLine dirty
  1598   aLine->MarkDirty();
  1599   aLine->SetInvalidateTextRuns(true);
  1600 #ifdef DEBUG
  1601   if (gNoisyReflow) {
  1602     IndentBy(stdout, gNoiseIndent);
  1603     ListTag(stdout);
  1604     printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
  1606 #endif
  1608   // Mark previous line dirty if it's an inline line so that it can
  1609   // maybe pullup something from the line just affected.
  1610   // XXX We don't need to do this if aPrevLine ends in a break-after...
  1611   if (aLine != aLineList->front() && aLine->IsInline() &&
  1612       aLine.prev()->IsInline()) {
  1613     aLine.prev()->MarkDirty();
  1614     aLine.prev()->SetInvalidateTextRuns(true);
  1615 #ifdef DEBUG
  1616     if (gNoisyReflow) {
  1617       IndentBy(stdout, gNoiseIndent);
  1618       ListTag(stdout);
  1619       printf(": mark prev-line %p dirty\n",
  1620              static_cast<void*>(aLine.prev().get()));
  1622 #endif
  1626 /**
  1627  * Test whether lines are certain to be aligned left so that we can make
  1628  * resizing optimizations
  1629  */
  1630 static inline bool
  1631 IsAlignedLeft(uint8_t aAlignment,
  1632               uint8_t aDirection,
  1633               uint8_t aUnicodeBidi,
  1634               nsIFrame* aFrame)
  1636   return aFrame->IsSVGText() ||
  1637          NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
  1638          (((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment &&
  1639            NS_STYLE_DIRECTION_LTR == aDirection) ||
  1640           (NS_STYLE_TEXT_ALIGN_END == aAlignment &&
  1641            NS_STYLE_DIRECTION_RTL == aDirection)) &&
  1642          !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
  1645 void
  1646 nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
  1648   // See if we can try and avoid marking all the lines as dirty
  1649   bool tryAndSkipLines =
  1650     // The left content-edge must be a constant distance from the left
  1651     // border-edge.
  1652     !StylePadding()->mPadding.GetLeft().HasPercent();
  1654 #ifdef DEBUG
  1655   if (gDisableResizeOpt) {
  1656     tryAndSkipLines = false;
  1658   if (gNoisyReflow) {
  1659     if (!tryAndSkipLines) {
  1660       IndentBy(stdout, gNoiseIndent);
  1661       ListTag(stdout);
  1662       printf(": marking all lines dirty: availWidth=%d\n",
  1663              aState.mReflowState.AvailableWidth());
  1666 #endif
  1668   if (tryAndSkipLines) {
  1669     nscoord newAvailWidth = aState.mReflowState.ComputedPhysicalBorderPadding().left +
  1670                             aState.mReflowState.ComputedWidth();
  1671     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedPhysicalBorderPadding().left &&
  1672                  NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(),
  1673                  "math on NS_UNCONSTRAINEDSIZE");
  1675 #ifdef DEBUG
  1676     if (gNoisyReflow) {
  1677       IndentBy(stdout, gNoiseIndent);
  1678       ListTag(stdout);
  1679       printf(": trying to avoid marking all lines dirty\n");
  1681 #endif
  1683     for (line_iterator line = begin_lines(), line_end = end_lines();
  1684          line != line_end;
  1685          ++line)
  1687       // We let child blocks make their own decisions the same
  1688       // way we are here.
  1689       bool isLastLine = line == mLines.back() && !GetNextInFlow();
  1690       if (line->IsBlock() ||
  1691           line->HasFloats() ||
  1692           (!isLastLine && !line->HasBreakAfter()) ||
  1693           ((isLastLine || !line->IsLineWrapped())) ||
  1694           line->ResizeReflowOptimizationDisabled() ||
  1695           line->IsImpactedByFloat() ||
  1696           (line->IEnd() > newAvailWidth)) {
  1697         line->MarkDirty();
  1700 #ifdef REALLY_NOISY_REFLOW
  1701       if (!line->IsBlock()) {
  1702         printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n", 
  1703                line.get(), line->IsImpactedByFloat() ? "" : "not ");
  1705 #endif
  1706 #ifdef DEBUG
  1707       if (gNoisyReflow && !line->IsDirty()) {
  1708         IndentBy(stdout, gNoiseIndent + 1);
  1709         printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
  1710            static_cast<void*>(line.get()),
  1711            static_cast<void*>((line.next() != end_lines() ? line.next().get() : nullptr)),
  1712            line->IsBlock() ? "block" : "inline",
  1713            line->HasBreakAfter() ? "has-break-after " : "",
  1714            line->HasFloats() ? "has-floats " : "",
  1715            line->IsImpactedByFloat() ? "impacted " : "",
  1716            line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
  1717            line->IEnd());
  1719 #endif
  1722   else {
  1723     // Mark everything dirty
  1724     for (line_iterator line = begin_lines(), line_end = end_lines();
  1725          line != line_end;
  1726          ++line)
  1728       line->MarkDirty();
  1733 //----------------------------------------
  1735 /**
  1736  * Propagate reflow "damage" from from earlier lines to the current
  1737  * line.  The reflow damage comes from the following sources:
  1738  *  1. The regions of float damage remembered during reflow.
  1739  *  2. The combination of nonzero |aDeltaY| and any impact by a float,
  1740  *     either the previous reflow or now.
  1742  * When entering this function, |aLine| is still at its old position and
  1743  * |aDeltaY| indicates how much it will later be slid (assuming it
  1744  * doesn't get marked dirty and reflowed entirely).
  1745  */
  1746 void
  1747 nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
  1748                                    nsLineBox* aLine,
  1749                                    nscoord aDeltaY)
  1751   nsFloatManager *floatManager = aState.mReflowState.mFloatManager;
  1752   NS_ASSERTION((aState.mReflowState.parentReflowState &&
  1753                 aState.mReflowState.parentReflowState->mFloatManager == floatManager) ||
  1754                 aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in");
  1756   // Check to see if there are any floats; if there aren't, there can't
  1757   // be any float damage
  1758   if (!floatManager->HasAnyFloats())
  1759     return;
  1761   // Check the damage region recorded in the float damage.
  1762   if (floatManager->HasFloatDamage()) {
  1763     // Need to check mBounds *and* mCombinedArea to find intersections 
  1764     // with aLine's floats
  1765     nscoord lineYA = aLine->BStart() + aDeltaY;
  1766     nscoord lineYB = lineYA + aLine->BSize();
  1767     // Scrollable overflow should be sufficient for things that affect
  1768     // layout.
  1769     nsRect overflow = aLine->GetOverflowArea(eScrollableOverflow);
  1770     nscoord lineYCombinedA = overflow.y + aDeltaY;
  1771     nscoord lineYCombinedB = lineYCombinedA + overflow.height;
  1772     if (floatManager->IntersectsDamage(lineYA, lineYB) ||
  1773         floatManager->IntersectsDamage(lineYCombinedA, lineYCombinedB)) {
  1774       aLine->MarkDirty();
  1775       return;
  1779   // Check if the line is moving relative to the float manager
  1780   if (aDeltaY + aState.mReflowState.mBlockDelta != 0) {
  1781     if (aLine->IsBlock()) {
  1782       // Unconditionally reflow sliding blocks; we only really need to reflow
  1783       // if there's a float impacting this block, but the current float manager
  1784       // makes it difficult to check that.  Therefore, we let the child block
  1785       // decide what it needs to reflow.
  1786       aLine->MarkDirty();
  1787     } else {
  1788       bool wasImpactedByFloat = aLine->IsImpactedByFloat();
  1789       nsFlowAreaRect floatAvailableSpace =
  1790         aState.GetFloatAvailableSpaceForHeight(aLine->BStart() + aDeltaY,
  1791                                                aLine->BSize(),
  1792                                                nullptr);
  1794 #ifdef REALLY_NOISY_REFLOW
  1795     printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", 
  1796            this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
  1797 #endif
  1799       // Mark the line dirty if it was or is affected by a float
  1800       // We actually only really need to reflow if the amount of impact
  1801       // changes, but that's not straightforward to check
  1802       if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
  1803         aLine->MarkDirty();
  1809 static bool LineHasClear(nsLineBox* aLine) {
  1810   return aLine->IsBlock()
  1811     ? (aLine->GetBreakTypeBefore() ||
  1812        (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
  1813        !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
  1814     : aLine->HasFloatBreakAfter();
  1818 /**
  1819  * Reparent a whole list of floats from aOldParent to this block.  The
  1820  * floats might be taken from aOldParent's overflow list. They will be
  1821  * removed from the list. They end up appended to our mFloats list.
  1822  */
  1823 void
  1824 nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent,
  1825                              bool aReparentSiblings) {
  1826   nsFrameList list;
  1827   aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings);
  1828   if (list.NotEmpty()) {
  1829     for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) {
  1830       ReparentFrame(f, aOldParent, this);
  1832     mFloats.AppendFrames(nullptr, list);
  1836 static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine,
  1837                      nscoord aDeltaY, int32_t aDeltaIndent) {
  1838 #ifdef DEBUG
  1839   if (nsBlockFrame::gNoisyReflow) {
  1840     nsRect ovis(aLine->GetVisualOverflowArea());
  1841     nsRect oscr(aLine->GetScrollableOverflowArea());
  1842     nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
  1843     printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
  1844            static_cast<void*>(aLine), aState.mY,
  1845            aLine->IsDirty() ? "yes" : "no",
  1846            aLine->IStart(), aLine->BStart(),
  1847            aLine->ISize(), aLine->BSize(),
  1848            ovis.x, ovis.y, ovis.width, ovis.height,
  1849            oscr.x, oscr.y, oscr.width, oscr.height,
  1850            aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount());
  1852 #endif
  1855 /**
  1856  * Reflow the dirty lines
  1857  */
  1858 nsresult
  1859 nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
  1861   nsresult rv = NS_OK;
  1862   bool keepGoing = true;
  1863   bool repositionViews = false; // should we really need this?
  1864   bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE;
  1865   bool willReflowAgain = false;
  1867 #ifdef DEBUG
  1868   if (gNoisyReflow) {
  1869     IndentBy(stdout, gNoiseIndent);
  1870     ListTag(stdout);
  1871     printf(": reflowing dirty lines");
  1872     printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth());
  1874   AutoNoisyIndenter indent(gNoisyReflow);
  1875 #endif
  1877   bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
  1878                      (aState.mReflowState.mFlags.mVResize &&
  1879                       (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT));
  1881   // Reflow our last line if our availableHeight has increased
  1882   // so that we (and our last child) pull up content as necessary
  1883   if (aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE
  1884       && GetNextInFlow() && aState.mReflowState.AvailableHeight() > mRect.height) {
  1885     line_iterator lastLine = end_lines();
  1886     if (lastLine != begin_lines()) {
  1887       --lastLine;
  1888       lastLine->MarkDirty();
  1891     // the amount by which we will slide the current line if it is not
  1892     // dirty
  1893   nscoord deltaY = 0;
  1895     // whether we did NOT reflow the previous line and thus we need to
  1896     // recompute the carried out margin before the line if we want to
  1897     // reflow it or if its previous margin is dirty
  1898   bool needToRecoverState = false;
  1899     // Float continuations were reflowed in ReflowPushedFloats
  1900   bool reflowedFloat = mFloats.NotEmpty() &&
  1901     (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
  1902   bool lastLineMovedUp = false;
  1903   // We save up information about BR-clearance here
  1904   uint8_t inlineFloatBreakType = aState.mFloatBreakType;
  1906   line_iterator line = begin_lines(), line_end = end_lines();
  1908   // Reflow the lines that are already ours
  1909   for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
  1910     DumpLine(aState, line, deltaY, 0);
  1911 #ifdef DEBUG
  1912     AutoNoisyIndenter indent2(gNoisyReflow);
  1913 #endif
  1915     if (selfDirty)
  1916       line->MarkDirty();
  1918     // This really sucks, but we have to look inside any blocks that have clear
  1919     // elements inside them.
  1920     // XXX what can we do smarter here?
  1921     if (!line->IsDirty() && line->IsBlock() &&
  1922         (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
  1923       line->MarkDirty();
  1926     nsIFrame *replacedBlock = nullptr;
  1927     if (line->IsBlock() &&
  1928         !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
  1929       replacedBlock = line->mFirstChild;
  1932     // We have to reflow the line if it's a block whose clearance
  1933     // might have changed, so detect that.
  1934     if (!line->IsDirty() &&
  1935         (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE ||
  1936          replacedBlock)) {
  1937       nscoord curY = aState.mY;
  1938       // See where we would be after applying any clearance due to
  1939       // BRs.
  1940       if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
  1941         curY = aState.ClearFloats(curY, inlineFloatBreakType);
  1944       nscoord newY =
  1945         aState.ClearFloats(curY, line->GetBreakTypeBefore(), replacedBlock);
  1947       if (line->HasClearance()) {
  1948         // Reflow the line if it might not have clearance anymore.
  1949         if (newY == curY
  1950             // aState.mY is the clearance point which should be the
  1951             // top border-edge of the block frame. If sliding the
  1952             // block by deltaY isn't going to put it in the predicted
  1953             // position, then we'd better reflow the line.
  1954             || newY != line->BStart() + deltaY) {
  1955           line->MarkDirty();
  1957       } else {
  1958         // Reflow the line if the line might have clearance now.
  1959         if (curY != newY) {
  1960           line->MarkDirty();
  1965     // We might have to reflow a line that is after a clearing BR.
  1966     if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
  1967       aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
  1968       if (aState.mY != line->BStart() + deltaY) {
  1969         // SlideLine is not going to put the line where the clearance
  1970         // put it. Reflow the line to be sure.
  1971         line->MarkDirty();
  1973       inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
  1976     bool previousMarginWasDirty = line->IsPreviousMarginDirty();
  1977     if (previousMarginWasDirty) {
  1978       // If the previous margin is dirty, reflow the current line
  1979       line->MarkDirty();
  1980       line->ClearPreviousMarginDirty();
  1981     } else if (line->BEnd() + deltaY > aState.mBottomEdge) {
  1982       // Lines that aren't dirty but get slid past our height constraint must
  1983       // be reflowed.
  1984       line->MarkDirty();
  1987     // If we have a constrained height (i.e., breaking columns/pages),
  1988     // and the distance to the bottom might have changed, then we need
  1989     // to reflow any line that might have floats in it, both because the
  1990     // breakpoints within those floats may have changed and because we
  1991     // might have to push/pull the floats in their entirety.
  1992     // FIXME: What about a deltaY or height change that forces us to
  1993     // push lines?  Why does that work?
  1994     if (!line->IsDirty() &&
  1995         aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
  1996         (deltaY != 0 || aState.mReflowState.mFlags.mVResize ||
  1997          aState.mReflowState.mFlags.mMustReflowPlaceholders) &&
  1998         (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) {
  1999       line->MarkDirty();
  2002     if (!line->IsDirty()) {
  2003       // See if there's any reflow damage that requires that we mark the
  2004       // line dirty.
  2005       PropagateFloatDamage(aState, line, deltaY);
  2008     // If the container width has changed reset the container width. If the
  2009     // line's writing mode is not ltr, or if the line is not left-aligned, also
  2010     // mark the line dirty.
  2011     if (aState.mContainerWidth != line->mContainerWidth) {
  2012       line->mContainerWidth = aState.mContainerWidth;
  2014       bool isLastLine = line == mLines.back() &&
  2015                         !GetNextInFlow() &&
  2016                         NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast;
  2017       uint8_t align = isLastLine ?
  2018         StyleText()->mTextAlign : StyleText()->mTextAlignLast;
  2020       if (line->mWritingMode.IsVertical() ||
  2021           !line->mWritingMode.IsBidiLTR() ||
  2022           !IsAlignedLeft(align,
  2023                          aState.mReflowState.mStyleVisibility->mDirection,
  2024                          StyleTextReset()->mUnicodeBidi, this)) {
  2025         line->MarkDirty();
  2029     if (needToRecoverState && line->IsDirty()) {
  2030       // We need to reconstruct the bottom margin only if we didn't
  2031       // reflow the previous line and we do need to reflow (or repair
  2032       // the top position of) the next line.
  2033       aState.ReconstructMarginAbove(line);
  2036     bool reflowedPrevLine = !needToRecoverState;
  2037     if (needToRecoverState) {
  2038       needToRecoverState = false;
  2040       // Update aState.mPrevChild as if we had reflowed all of the frames in
  2041       // this line.
  2042       if (line->IsDirty()) {
  2043         NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
  2044                      line.prev()->LastChild(), "unexpected line frames");
  2045         aState.mPrevChild = line->mFirstChild->GetPrevSibling();
  2049     // Now repair the line and update |aState.mY| by calling
  2050     // |ReflowLine| or |SlideLine|.
  2051     // If we're going to reflow everything again, then no need to reflow
  2052     // the dirty line ... unless the line has floats, in which case we'd
  2053     // better reflow it now to refresh its float cache, which may contain
  2054     // dangling frame pointers! Ugh! This reflow of the line may be
  2055     // incorrect because we skipped reflowing previous lines (e.g., floats
  2056     // may be placed incorrectly), but that's OK because we'll mark the
  2057     // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..."
  2058     if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
  2059       lastLineMovedUp = true;
  2061       bool maybeReflowingForFirstTime =
  2062         line->IStart() == 0 && line->BStart() == 0 &&
  2063         line->ISize() == 0 && line->BSize() == 0;
  2065       // Compute the dirty lines "before" BEnd, after factoring in
  2066       // the running deltaY value - the running value is implicit in
  2067       // aState.mY.
  2068       nscoord oldY = line->BStart();
  2069       nscoord oldYMost = line->BEnd();
  2071       NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
  2072                    "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
  2074       // Reflow the dirty line. If it's an incremental reflow, then force
  2075       // it to invalidate the dirty area if necessary
  2076       rv = ReflowLine(aState, line, &keepGoing);
  2077       NS_ENSURE_SUCCESS(rv, rv);
  2079       if (aState.mReflowState.WillReflowAgainForClearance()) {
  2080         line->MarkDirty();
  2081         willReflowAgain = true;
  2082         // Note that once we've entered this state, every line that gets here
  2083         // (e.g. because it has floats) gets marked dirty and reflowed again.
  2084         // in the next pass. This is important, see above.
  2087       if (line->HasFloats()) {
  2088         reflowedFloat = true;
  2091       if (!keepGoing) {
  2092         DumpLine(aState, line, deltaY, -1);
  2093         if (0 == line->GetChildCount()) {
  2094           DeleteLine(aState, line, line_end);
  2096         break;
  2099       // Test to see whether the margin that should be carried out
  2100       // to the next line (NL) might have changed. In ReflowBlockFrame
  2101       // we call nextLine->MarkPreviousMarginDirty if the block's
  2102       // actual carried-out bottom margin changed. So here we only
  2103       // need to worry about the following effects:
  2104       // 1) the line was just created, and it might now be blocking
  2105       // a carried-out bottom margin from previous lines that
  2106       // used to reach NL from reaching NL
  2107       // 2) the line used to be empty, and is now not empty,
  2108       // thus blocking a carried-out bottom margin from previous lines
  2109       // that used to reach NL from reaching NL
  2110       // 3) the line wasn't empty, but now is, so a carried-out
  2111       // bottom margin from previous lines that didn't used to reach NL
  2112       // now does
  2113       // 4) the line might have changed in a way that affects NL's
  2114       // ShouldApplyTopMargin decision. The three things that matter
  2115       // are the line's emptiness, its adjacency to the top of the block,
  2116       // and whether it has clearance (the latter only matters if the block
  2117       // was and is adjacent to the top and empty).
  2118       //
  2119       // If the line is empty now, we can't reliably tell if the line was empty
  2120       // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
  2121       // This means the checks in 4) are redundant; if the line is empty now
  2122       // we don't need to check 4), but if the line is not empty now and we're sure
  2123       // it wasn't empty before, any adjacency and clearance changes are irrelevant
  2124       // to the result of nextLine->ShouldApplyTopMargin.
  2125       if (line.next() != end_lines()) {
  2126         bool maybeWasEmpty = oldY == line.next()->BStart();
  2127         bool isEmpty = line->CachedIsEmpty();
  2128         if (maybeReflowingForFirstTime /*1*/ ||
  2129             (isEmpty || maybeWasEmpty) /*2/3/4*/) {
  2130           line.next()->MarkPreviousMarginDirty();
  2131           // since it's marked dirty, nobody will care about |deltaY|
  2135       // If the line was just reflowed for the first time, then its
  2136       // old mBounds cannot be trusted so this deltaY computation is
  2137       // bogus. But that's OK because we just did
  2138       // MarkPreviousMarginDirty on the next line which will force it
  2139       // to be reflowed, so this computation of deltaY will not be
  2140       // used.
  2141       deltaY = line->BEnd() - oldYMost;
  2143       // Now do an interrupt check. We want to do this only in the case when we
  2144       // actually reflow the line, so that if we get back in here we'll get
  2145       // further on the reflow before interrupting.
  2146       aState.mPresContext->CheckForInterrupt(this);
  2147     } else {
  2148       aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
  2149         // Nop except for blocks (we don't create overflow container
  2150         // continuations for any inlines atm), so only checking mFirstChild
  2151         // is enough
  2153       lastLineMovedUp = deltaY < 0;
  2155       if (deltaY != 0)
  2156         SlideLine(aState, line, deltaY);
  2157       else
  2158         repositionViews = true;
  2160       NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
  2161                    "Possibly stale float cache here!");
  2162       if (willReflowAgain && line->IsBlock()) {
  2163         // If we're going to reflow everything again, and this line is a block,
  2164         // then there is no need to recover float state. The line may contain
  2165         // other lines with floats, but in that case RecoverStateFrom would only
  2166         // add floats to the float manager. We don't need to do that because
  2167         // everything's going to get reflowed again "for real". Calling
  2168         // RecoverStateFrom in this situation could be lethal because the
  2169         // block's descendant lines may have float caches containing dangling
  2170         // frame pointers. Ugh!
  2171         // If this line is inline, then we need to recover its state now
  2172         // to make sure that we don't forget to move its floats by deltaY.
  2173       } else {
  2174         // XXX EVIL O(N^2) EVIL
  2175         aState.RecoverStateFrom(line, deltaY);
  2178       // Keep mY up to date in case we're propagating reflow damage
  2179       // and also because our final height may depend on it. If the
  2180       // line is inlines, then only update mY if the line is not
  2181       // empty, because that's what PlaceLine does. (Empty blocks may
  2182       // want to update mY, e.g. if they have clearance.)
  2183       if (line->IsBlock() || !line->CachedIsEmpty()) {
  2184         aState.mY = line->BEnd();
  2187       needToRecoverState = true;
  2189       if (reflowedPrevLine && !line->IsBlock() &&
  2190           aState.mPresContext->HasPendingInterrupt()) {
  2191         // Need to make sure to pull overflows from any prev-in-flows
  2192         for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
  2193              inlineKid = inlineKid->GetFirstPrincipalChild()) {
  2194           inlineKid->PullOverflowsFromPrevInFlow();
  2199     // Record if we need to clear floats before reflowing the next
  2200     // line. Note that inlineFloatBreakType will be handled and
  2201     // cleared before the next line is processed, so there is no
  2202     // need to combine break types here.
  2203     if (line->HasFloatBreakAfter()) {
  2204       inlineFloatBreakType = line->GetBreakTypeAfter();
  2207     if (LineHasClear(line.get())) {
  2208       foundAnyClears = true;
  2211     DumpLine(aState, line, deltaY, -1);
  2213     if (aState.mPresContext->HasPendingInterrupt()) {
  2214       willReflowAgain = true;
  2215       // Another option here might be to leave |line| clean if
  2216       // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
  2217       // that case the line really did reflow as it should have.  Not sure
  2218       // whether that would be safe, so doing this for now instead.  Also not
  2219       // sure whether we really want to mark all lines dirty after an
  2220       // interrupt, but until we get better at propagating float damage we
  2221       // really do need to do it this way; see comments inside MarkLineDirty.
  2222       MarkLineDirtyForInterrupt(line);
  2226   // Handle BR-clearance from the last line of the block
  2227   if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
  2228     aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
  2231   if (needToRecoverState) {
  2232     // Is this expensive?
  2233     aState.ReconstructMarginAbove(line);
  2235     // Update aState.mPrevChild as if we had reflowed all of the frames in
  2236     // the last line.
  2237     NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
  2238                  line.prev()->LastChild(), "unexpected line frames");
  2239     aState.mPrevChild =
  2240       line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
  2243   // Should we really have to do this?
  2244   if (repositionViews)
  2245     nsContainerFrame::PlaceFrameView(this);
  2247   // We can skip trying to pull up the next line if our height is constrained
  2248   // (so we can report being incomplete) and there is no next in flow or we
  2249   // were told not to or we know it will be futile, i.e.,
  2250   // -- the next in flow is not changing
  2251   // -- and we cannot have added more space for its first line to be
  2252   // pulled up into,
  2253   // -- it's an incremental reflow of a descendant
  2254   // -- and we didn't reflow any floats (so the available space
  2255   // didn't change)
  2256   // -- my chain of next-in-flows either has no first line, or its first
  2257   // line isn't dirty.
  2258   bool heightConstrained =
  2259     aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE;
  2260   bool skipPull = willReflowAgain && heightConstrained;
  2261   if (!skipPull && heightConstrained && aState.mNextInFlow &&
  2262       (aState.mReflowState.mFlags.mNextInFlowUntouched &&
  2263        !lastLineMovedUp && 
  2264        !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
  2265        !reflowedFloat)) {
  2266     // We'll place lineIter at the last line of this block, so that 
  2267     // nsBlockInFlowLineIterator::Next() will take us to the first
  2268     // line of my next-in-flow-chain.  (But first, check that I 
  2269     // have any lines -- if I don't, just bail out of this
  2270     // optimization.) 
  2271     line_iterator lineIter = this->end_lines();
  2272     if (lineIter != this->begin_lines()) {
  2273       lineIter--; // I have lines; step back from dummy iterator to last line.
  2274       nsBlockInFlowLineIterator bifLineIter(this, lineIter);
  2276       // Check for next-in-flow-chain's first line.
  2277       // (First, see if there is such a line, and second, see if it's clean)
  2278       if (!bifLineIter.Next() ||                
  2279           !bifLineIter.GetLine()->IsDirty()) {
  2280         skipPull=true;
  2285   if (skipPull && aState.mNextInFlow) {
  2286     NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
  2287     if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
  2288       NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
  2289     else
  2290       NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  2293   if (!skipPull && aState.mNextInFlow) {
  2294     // Pull data from a next-in-flow if there's still room for more
  2295     // content here.
  2296     while (keepGoing && aState.mNextInFlow) {
  2297       // Grab first line from our next-in-flow
  2298       nsBlockFrame* nextInFlow = aState.mNextInFlow;
  2299       nsLineBox* pulledLine;
  2300       nsFrameList pulledFrames;
  2301       if (!nextInFlow->mLines.empty()) {
  2302         RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames,
  2303                         &pulledLine, &pulledFrames);
  2304       } else {
  2305         // Grab an overflow line if there are any
  2306         FrameLines* overflowLines = nextInFlow->GetOverflowLines();
  2307         if (!overflowLines) {
  2308           aState.mNextInFlow =
  2309             static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
  2310           continue;
  2312         bool last =
  2313           RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
  2314                           &pulledLine, &pulledFrames);
  2315         if (last) {
  2316           nextInFlow->DestroyOverflowLines();
  2320       if (pulledFrames.IsEmpty()) {
  2321         // The line is empty. Try the next one.
  2322         NS_ASSERTION(pulledLine->GetChildCount() == 0 &&
  2323                      !pulledLine->mFirstChild, "bad empty line");
  2324         nextInFlow->FreeLineBox(pulledLine);
  2325         continue;
  2328       if (pulledLine == nextInFlow->GetLineCursor()) {
  2329         nextInFlow->ClearLineCursor();
  2331       ReparentFrames(pulledFrames, nextInFlow, this);
  2333       NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
  2334                    "Unexpected last frame");
  2335       NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
  2336       NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
  2337                    "Incorrect aState.mPrevChild before inserting line at end");
  2339       // Shift pulledLine's frames into our mFrames list.
  2340       mFrames.AppendFrames(nullptr, pulledFrames);
  2342       // Add line to our line list, and set its last child as our new prev-child
  2343       line = mLines.before_insert(end_lines(), pulledLine);
  2344       aState.mPrevChild = mFrames.LastChild();
  2346       // Reparent floats whose placeholders are in the line.
  2347       ReparentFloats(pulledLine->mFirstChild, nextInFlow, true);
  2349       DumpLine(aState, pulledLine, deltaY, 0);
  2350 #ifdef DEBUG
  2351       AutoNoisyIndenter indent2(gNoisyReflow);
  2352 #endif
  2354       if (aState.mPresContext->HasPendingInterrupt()) {
  2355         MarkLineDirtyForInterrupt(line);
  2356       } else {
  2357         // Now reflow it and any lines that it makes during it's reflow
  2358         // (we have to loop here because reflowing the line may cause a new
  2359         // line to be created; see SplitLine's callers for examples of
  2360         // when this happens).
  2361         while (line != end_lines()) {
  2362           rv = ReflowLine(aState, line, &keepGoing);
  2363           NS_ENSURE_SUCCESS(rv, rv);
  2365           if (aState.mReflowState.WillReflowAgainForClearance()) {
  2366             line->MarkDirty();
  2367             keepGoing = false;
  2368             NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  2369             break;
  2372           DumpLine(aState, line, deltaY, -1);
  2373           if (!keepGoing) {
  2374             if (0 == line->GetChildCount()) {
  2375               DeleteLine(aState, line, line_end);
  2377             break;
  2380           if (LineHasClear(line.get())) {
  2381             foundAnyClears = true;
  2384           if (aState.mPresContext->CheckForInterrupt(this)) {
  2385             MarkLineDirtyForInterrupt(line);
  2386             break;
  2389           // If this is an inline frame then its time to stop
  2390           ++line;
  2391           aState.AdvanceToNextLine();
  2396     if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
  2397       aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  2398     } //XXXfr shouldn't set this flag when nextinflow has no lines
  2401   // Handle an odd-ball case: a list-item with no lines
  2402   if (HasOutsideBullet() && mLines.empty()) {
  2403     nsHTMLReflowMetrics metrics(aState.mReflowState);
  2404     nsIFrame* bullet = GetOutsideBullet();
  2405     ReflowBullet(bullet, aState, metrics,
  2406                  aState.mReflowState.ComputedPhysicalBorderPadding().top);
  2407     NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
  2408                  "empty bullet took up space");
  2410     if (!BulletIsEmpty()) {
  2411       // There are no lines so we have to fake up some y motion so that
  2412       // we end up with *some* height.
  2414       if (metrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
  2415         nscoord ascent;
  2416         if (nsLayoutUtils::GetFirstLineBaseline(bullet, &ascent)) {
  2417           metrics.SetTopAscent(ascent);
  2418         } else {
  2419           metrics.SetTopAscent(metrics.Height());
  2423       nsRefPtr<nsFontMetrics> fm;
  2424       nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
  2425         nsLayoutUtils::FontSizeInflationFor(this));
  2426       aState.mReflowState.rendContext->SetFont(fm); // FIXME: needed?
  2428       nscoord minAscent =
  2429         nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight);
  2430       nscoord minDescent = aState.mMinLineHeight - minAscent;
  2432       aState.mY += std::max(minAscent, metrics.TopAscent()) +
  2433                    std::max(minDescent, metrics.Height() - metrics.TopAscent());
  2435       nscoord offset = minAscent - metrics.TopAscent();
  2436       if (offset > 0) {
  2437         bullet->SetRect(bullet->GetRect() + nsPoint(0, offset));
  2442   if (foundAnyClears) {
  2443     AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
  2444   } else {
  2445     RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
  2448 #ifdef DEBUG
  2449   VerifyLines(true);
  2450   VerifyOverflowSituation();
  2451   if (gNoisyReflow) {
  2452     IndentBy(stdout, gNoiseIndent - 1);
  2453     ListTag(stdout);
  2454     printf(": done reflowing dirty lines (status=%x)\n",
  2455            aState.mReflowStatus);
  2457 #endif
  2459   return rv;
  2462 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
  2464   nsLineList::iterator line = aBlock->begin_lines();
  2465   nsLineList::iterator endLine = aBlock->end_lines();
  2466   while (line != endLine) {
  2467     if (line->IsBlock()) {
  2468       nsIFrame* f = line->mFirstChild;
  2469       nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
  2470       if (bf) {
  2471         MarkAllDescendantLinesDirty(bf);
  2474     line->MarkDirty();
  2475     ++line;
  2479 void
  2480 nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
  2482   aLine->MarkDirty();
  2484   // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
  2485   // marked the lines that need to be marked dirty based on our
  2486   // vertical resize stuff.  So we'll definitely reflow all those kids;
  2487   // the only question is how they should behave.
  2488   if (GetStateBits() & NS_FRAME_IS_DIRTY) {
  2489     // Mark all our child frames dirty so we make sure to reflow them
  2490     // later.
  2491     int32_t n = aLine->GetChildCount();
  2492     for (nsIFrame* f = aLine->mFirstChild; n > 0;
  2493          f = f->GetNextSibling(), --n) {
  2494       f->AddStateBits(NS_FRAME_IS_DIRTY);
  2496     // And mark all the floats whose reflows we might be skipping dirty too.
  2497     if (aLine->HasFloats()) {
  2498       for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
  2499         fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY);
  2502   } else {
  2503     // Dirty all the descendant lines of block kids to handle float damage,
  2504     // since our nsFloatManager will go away by the next time we're reflowing.
  2505     // XXXbz Can we do something more like what PropagateFloatDamage does?
  2506     // Would need to sort out the exact business with mBlockDelta for that....
  2507     // This marks way too much dirty.  If we ever make this better, revisit
  2508     // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
  2509     nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
  2510     if (bf) {
  2511       MarkAllDescendantLinesDirty(bf);
  2516 void
  2517 nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
  2518                          nsLineList::iterator aLine,
  2519                          nsLineList::iterator aLineEnd)
  2521   NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
  2522   if (0 == aLine->GetChildCount()) {
  2523     NS_ASSERTION(aState.mCurrentLine == aLine,
  2524                  "using function more generally than designed, "
  2525                  "but perhaps OK now");
  2526     nsLineBox* line = aLine;
  2527     aLine = mLines.erase(aLine);
  2528     FreeLineBox(line);
  2529     // Mark the previous margin of the next line dirty since we need to
  2530     // recompute its top position.
  2531     if (aLine != aLineEnd)
  2532       aLine->MarkPreviousMarginDirty();
  2536 /**
  2537  * Reflow a line. The line will either contain a single block frame
  2538  * or contain 1 or more inline frames. aKeepReflowGoing indicates
  2539  * whether or not the caller should continue to reflow more lines.
  2540  */
  2541 nsresult
  2542 nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
  2543                          line_iterator aLine,
  2544                          bool* aKeepReflowGoing)
  2546   nsresult rv = NS_OK;
  2548   NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
  2550   // Setup the line-layout for the new line
  2551   aState.mCurrentLine = aLine;
  2552   aLine->ClearDirty();
  2553   aLine->InvalidateCachedIsEmpty();
  2554   aLine->ClearHadFloatPushed();
  2556   // Now that we know what kind of line we have, reflow it
  2557   if (aLine->IsBlock()) {
  2558     rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
  2559   } else {
  2560     aLine->SetLineWrapped(false);
  2561     rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
  2564   return rv;
  2567 nsIFrame*
  2568 nsBlockFrame::PullFrame(nsBlockReflowState& aState,
  2569                         line_iterator       aLine)
  2571   // First check our remaining lines.
  2572   if (end_lines() != aLine.next()) {
  2573     return PullFrameFrom(aLine, this, aLine.next());
  2576   NS_ASSERTION(!GetOverflowLines(),
  2577     "Our overflow lines should have been removed at the start of reflow");
  2579   // Try each next-in-flow.
  2580   nsBlockFrame* nextInFlow = aState.mNextInFlow;
  2581   while (nextInFlow) {
  2582     if (nextInFlow->mLines.empty()) {
  2583       nextInFlow->DrainSelfOverflowList();
  2585     if (!nextInFlow->mLines.empty()) {
  2586       return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin());
  2588     nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
  2589     aState.mNextInFlow = nextInFlow;
  2592   return nullptr;
  2595 nsIFrame*
  2596 nsBlockFrame::PullFrameFrom(nsLineBox*           aLine,
  2597                             nsBlockFrame*        aFromContainer,
  2598                             nsLineList::iterator aFromLine)
  2600   nsLineBox* fromLine = aFromLine;
  2601   NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
  2602   NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
  2603   NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
  2605   NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(),
  2606                "Disagreement about whether it's a block or not");
  2608   if (fromLine->IsBlock()) {
  2609     // If our line is not empty and the child in aFromLine is a block
  2610     // then we cannot pull up the frame into this line. In this case
  2611     // we stop pulling.
  2612     return nullptr;
  2614   // Take frame from fromLine
  2615   nsIFrame* frame = fromLine->mFirstChild;
  2616   nsIFrame* newFirstChild = frame->GetNextSibling();
  2618   if (aFromContainer != this) {
  2619     // The frame is being pulled from a next-in-flow; therefore we
  2620     // need to add it to our sibling list.
  2621     MOZ_ASSERT(aLine == mLines.back());
  2622     MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(),
  2623                "should only pull from first line");
  2624     aFromContainer->mFrames.RemoveFrame(frame);
  2626     // When pushing and pulling frames we need to check for whether any
  2627     // views need to be reparented.
  2628     ReparentFrame(frame, aFromContainer, this);
  2629     mFrames.AppendFrame(nullptr, frame);
  2631     // The frame might have (or contain) floats that need to be brought
  2632     // over too. (pass 'false' since there are no siblings to check)
  2633     ReparentFloats(frame, aFromContainer, false);
  2634   } else {
  2635     MOZ_ASSERT(aLine == aFromLine.prev());
  2638   aLine->NoteFrameAdded(frame);
  2639   fromLine->NoteFrameRemoved(frame);
  2641   if (fromLine->GetChildCount() > 0) {
  2642     // Mark line dirty now that we pulled a child
  2643     fromLine->MarkDirty();
  2644     fromLine->mFirstChild = newFirstChild;
  2645   } else {
  2646     // Free up the fromLine now that it's empty.
  2647     // Its bounds might need to be redrawn, though.
  2648     if (aFromLine.next() != aFromContainer->mLines.end()) {
  2649       aFromLine.next()->MarkPreviousMarginDirty();
  2651     aFromContainer->mLines.erase(aFromLine);
  2652     // aFromLine is now invalid
  2653     aFromContainer->FreeLineBox(fromLine);
  2656 #ifdef DEBUG
  2657   VerifyLines(true);
  2658   VerifyOverflowSituation();
  2659 #endif
  2661   return frame;
  2664 void
  2665 nsBlockFrame::SlideLine(nsBlockReflowState& aState,
  2666                         nsLineBox* aLine, nscoord aDY)
  2668   NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
  2670   // Adjust line state
  2671   aLine->SlideBy(aDY, aState.mContainerWidth);
  2673   // Adjust the frames in the line
  2674   nsIFrame* kid = aLine->mFirstChild;
  2675   if (!kid) {
  2676     return;
  2679   if (aLine->IsBlock()) {
  2680     if (aDY) {
  2681       kid->MovePositionBy(nsPoint(0, aDY));
  2684     // Make sure the frame's view and any child views are updated
  2685     nsContainerFrame::PlaceFrameView(kid);
  2687   else {
  2688     // Adjust the Y coordinate of the frames in the line.
  2689     // Note: we need to re-position views even if aDY is 0, because
  2690     // one of our parent frames may have moved and so the view's position
  2691     // relative to its parent may have changed
  2692     int32_t n = aLine->GetChildCount();
  2693     while (--n >= 0) {
  2694       if (aDY) {
  2695         kid->MovePositionBy(nsPoint(0, aDY));
  2697       // Make sure the frame's view and any child views are updated
  2698       nsContainerFrame::PlaceFrameView(kid);
  2699       kid = kid->GetNextSibling();
  2704 nsresult 
  2705 nsBlockFrame::AttributeChanged(int32_t         aNameSpaceID,
  2706                                nsIAtom*        aAttribute,
  2707                                int32_t         aModType)
  2709   nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID,
  2710                                                     aAttribute, aModType);
  2712   if (NS_FAILED(rv)) {
  2713     return rv;
  2715   if (nsGkAtoms::start == aAttribute ||
  2716       (nsGkAtoms::reversed == aAttribute && mContent->IsHTML(nsGkAtoms::ol))) {
  2717     nsPresContext* presContext = PresContext();
  2719     // XXX Not sure if this is necessary anymore
  2720     if (RenumberLists(presContext)) {
  2721       presContext->PresShell()->
  2722         FrameNeedsReflow(this, nsIPresShell::eStyleChange,
  2723                          NS_FRAME_HAS_DIRTY_CHILDREN);
  2726   else if (nsGkAtoms::value == aAttribute) {
  2727     const nsStyleDisplay* styleDisplay = StyleDisplay();
  2728     if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
  2729       // Search for the closest ancestor that's a block frame. We
  2730       // make the assumption that all related list items share a
  2731       // common block parent.
  2732       // XXXldb I think that's a bad assumption.
  2733       nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
  2735       // Tell the enclosing block frame to renumber list items within
  2736       // itself
  2737       if (nullptr != blockParent) {
  2738         nsPresContext* presContext = PresContext();
  2739         // XXX Not sure if this is necessary anymore
  2740         if (blockParent->RenumberLists(presContext)) {
  2741           presContext->PresShell()->
  2742             FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange,
  2743                              NS_FRAME_HAS_DIRTY_CHILDREN);
  2749   return rv;
  2752 static inline bool
  2753 IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
  2755   if (aCoord.GetUnit() == eStyleUnit_Auto)
  2756     return false;
  2757   if (aCoord.IsCoordPercentCalcUnit()) {
  2758     // If we evaluate the length/percent/calc at a percentage basis of
  2759     // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
  2760     // length, percent, or combination thereof.  Test > 0 so we clamp
  2761     // negative calc() results to 0.
  2762     return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
  2763            nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
  2765   NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height");
  2766   return true;
  2769 /* virtual */ bool
  2770 nsBlockFrame::IsSelfEmpty()
  2772   // Blocks which are margin-roots (including inline-blocks) cannot be treated
  2773   // as empty for margin-collapsing and other purposes. They're more like
  2774   // replaced elements.
  2775   if (GetStateBits() & NS_BLOCK_MARGIN_ROOT)
  2776     return false;
  2778   const nsStylePosition* position = StylePosition();
  2780   if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
  2781       IsNonAutoNonZeroHeight(position->mHeight))
  2782     return false;
  2784   const nsStyleBorder* border = StyleBorder();
  2785   const nsStylePadding* padding = StylePadding();
  2786   if (border->GetComputedBorderWidth(NS_SIDE_TOP) != 0 ||
  2787       border->GetComputedBorderWidth(NS_SIDE_BOTTOM) != 0 ||
  2788       !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
  2789       !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom())) {
  2790     return false;
  2793   if (HasOutsideBullet() && !BulletIsEmpty()) {
  2794     return false;
  2797   return true;
  2800 bool
  2801 nsBlockFrame::CachedIsEmpty()
  2803   if (!IsSelfEmpty()) {
  2804     return false;
  2807   for (line_iterator line = begin_lines(), line_end = end_lines();
  2808        line != line_end;
  2809        ++line)
  2811     if (!line->CachedIsEmpty())
  2812       return false;
  2815   return true;
  2818 bool
  2819 nsBlockFrame::IsEmpty()
  2821   if (!IsSelfEmpty()) {
  2822     return false;
  2825   for (line_iterator line = begin_lines(), line_end = end_lines();
  2826        line != line_end;
  2827        ++line)
  2829     if (!line->IsEmpty())
  2830       return false;
  2833   return true;
  2836 bool
  2837 nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
  2838                                    nsLineBox* aLine)
  2840   if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
  2841     // Apply short-circuit check to avoid searching the line list
  2842     return true;
  2845   if (!aState.IsAdjacentWithTop()) {
  2846     // If we aren't at the top Y coordinate then something of non-zero
  2847     // height must have been placed. Therefore the childs top-margin
  2848     // applies.
  2849     aState.SetFlag(BRS_APPLYTOPMARGIN, true);
  2850     return true;
  2853   // Determine if this line is "essentially" the first line
  2854   line_iterator line = begin_lines();
  2855   if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
  2856     line = aState.mLineAdjacentToTop;
  2858   while (line != aLine) {
  2859     if (!line->CachedIsEmpty() || line->HasClearance()) {
  2860       // A line which precedes aLine is non-empty, or has clearance,
  2861       // so therefore the top margin applies.
  2862       aState.SetFlag(BRS_APPLYTOPMARGIN, true);
  2863       return true;
  2865     // No need to apply the top margin if the line has floats.  We
  2866     // should collapse anyway (bug 44419)
  2867     ++line;
  2868     aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, true);
  2869     aState.mLineAdjacentToTop = line;
  2872   // The line being reflowed is "essentially" the first line in the
  2873   // block. Therefore its top-margin will be collapsed by the
  2874   // generational collapsing logic with its parent (us).
  2875   return false;
  2878 nsresult
  2879 nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
  2880                                line_iterator aLine,
  2881                                bool* aKeepReflowGoing)
  2883   NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
  2885   nsresult rv = NS_OK;
  2887   nsIFrame* frame = aLine->mFirstChild;
  2888   if (!frame) {
  2889     NS_ASSERTION(false, "program error - unexpected empty line"); 
  2890     return NS_ERROR_NULL_POINTER; 
  2893   // Prepare the block reflow engine
  2894   const nsStyleDisplay* display = frame->StyleDisplay();
  2895   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
  2897   uint8_t breakType = display->mBreakType;
  2898   if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
  2899     breakType = nsLayoutUtils::CombineBreakType(breakType,
  2900                                                 aState.mFloatBreakType);
  2901     aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
  2904   // Clear past floats before the block if the clear style is not none
  2905   aLine->SetBreakTypeBefore(breakType);
  2907   // See if we should apply the top margin. If the block frame being
  2908   // reflowed is a continuation (non-null prev-in-flow) then we don't
  2909   // apply its top margin because it's not significant. Otherwise, dig
  2910   // deeper.
  2911   bool applyTopMargin =
  2912     !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
  2914   if (applyTopMargin) {
  2915     // The HasClearance setting is only valid if ShouldApplyTopMargin
  2916     // returned false (in which case the top-margin-root set our
  2917     // clearance flag). Otherwise clear it now. We'll set it later on
  2918     // ourselves if necessary.
  2919     aLine->ClearHasClearance();
  2921   bool treatWithClearance = aLine->HasClearance();
  2923   bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE;
  2924   nsIFrame *replacedBlock = nullptr;
  2925   if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
  2926     mightClearFloats = true;
  2927     replacedBlock = frame;
  2930   // If our top margin was counted as part of some parents top-margin
  2931   // collapse and we are being speculatively reflowed assuming this
  2932   // frame DID NOT need clearance, then we need to check that
  2933   // assumption.
  2934   if (!treatWithClearance && !applyTopMargin && mightClearFloats &&
  2935       aState.mReflowState.mDiscoveredClearance) {
  2936     nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
  2937     nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
  2938     if (clearY != curY) {
  2939       // Looks like that assumption was invalid, we do need
  2940       // clearance. Tell our ancestor so it can reflow again. It is
  2941       // responsible for actually setting our clearance flag before
  2942       // the next reflow.
  2943       treatWithClearance = true;
  2944       // Only record the first frame that requires clearance
  2945       if (!*aState.mReflowState.mDiscoveredClearance) {
  2946         *aState.mReflowState.mDiscoveredClearance = frame;
  2948       aState.mPrevChild = frame;
  2949       // Exactly what we do now is flexible since we'll definitely be
  2950       // reflowed.
  2951       return NS_OK;
  2954   if (treatWithClearance) {
  2955     applyTopMargin = true;
  2958   nsIFrame* clearanceFrame = nullptr;
  2959   nscoord startingY = aState.mY;
  2960   nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
  2961   nscoord clearance;
  2962   // Save the original position of the frame so that we can reposition
  2963   // its view as needed.
  2964   nsPoint originalPosition = frame->GetPosition();
  2965   while (true) {
  2966     clearance = 0;
  2967     nscoord topMargin = 0;
  2968     bool mayNeedRetry = false;
  2969     bool clearedFloats = false;
  2970     if (applyTopMargin) {
  2971       // Precompute the blocks top margin value so that we can get the
  2972       // correct available space (there might be a float that's
  2973       // already been placed below the aState.mPrevBottomMargin
  2975       // Setup a reflowState to get the style computed margin-top
  2976       // value. We'll use a reason of `resize' so that we don't fudge
  2977       // any incremental reflow state.
  2979       // The availSpace here is irrelevant to our needs - all we want
  2980       // out if this setup is the margin-top value which doesn't depend
  2981       // on the childs available space.
  2982       // XXX building a complete nsHTMLReflowState just to get the margin-top
  2983       // seems like a waste. And we do this for almost every block!
  2984       nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
  2985       nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
  2986                                     frame, availSpace);
  2988       if (treatWithClearance) {
  2989         aState.mY += aState.mPrevBottomMargin.get();
  2990         aState.mPrevBottomMargin.Zero();
  2993       // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
  2994       // that all child margins collapse down to clearanceFrame.
  2995       nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
  2996                                                       &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
  2998       // XXX optimization; we could check the collapsing children to see if they are sure
  2999       // to require clearance, and so avoid retrying them
  3001       if (clearanceFrame) {
  3002         // Don't allow retries on the second pass. The clearance decisions for the
  3003         // blocks whose top-margins collapse with ours are now fixed.
  3004         mayNeedRetry = false;
  3007       if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
  3008         // We don't know if we need clearance and this is the first,
  3009         // optimistic pass.  So determine whether *this block* needs
  3010         // clearance. Note that we do not allow the decision for whether
  3011         // this block has clearance to change on the second pass; that
  3012         // decision is only allowed to be made under the optimistic
  3013         // first pass.
  3014         nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
  3015         nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
  3016         if (clearY != curY) {
  3017           // Looks like we need clearance and we didn't know about it already. So
  3018           // recompute collapsed margin
  3019           treatWithClearance = true;
  3020           // Remember this decision, needed for incremental reflow
  3021           aLine->SetHasClearance();
  3023           // Apply incoming margins
  3024           aState.mY += aState.mPrevBottomMargin.get();
  3025           aState.mPrevBottomMargin.Zero();
  3027           // Compute the collapsed margin again, ignoring the incoming margin this time
  3028           mayNeedRetry = false;
  3029           nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
  3030                                                           &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
  3034       // Temporarily advance the running Y value so that the
  3035       // GetAvailableSpace method will return the right available
  3036       // space. This undone as soon as the horizontal margins are
  3037       // computed.
  3038       topMargin = aState.mPrevBottomMargin.get();
  3040       if (treatWithClearance) {
  3041         nscoord currentY = aState.mY;
  3042         // advance mY to the clear position.
  3043         aState.mY = aState.ClearFloats(aState.mY, breakType, replacedBlock);
  3045         clearedFloats = aState.mY != currentY;
  3047         // Compute clearance. It's the amount we need to add to the top
  3048         // border-edge of the frame, after applying collapsed margins
  3049         // from the frame and its children, to get it to line up with
  3050         // the bottom of the floats. The former is currentY + topMargin,
  3051         // the latter is the current aState.mY.
  3052         // Note that negative clearance is possible
  3053         clearance = aState.mY - (currentY + topMargin);
  3055         // Add clearance to our top margin while we compute available
  3056         // space for the frame
  3057         topMargin += clearance;
  3059         // Note that aState.mY should stay where it is: at the top
  3060         // border-edge of the frame
  3061       } else {
  3062         // Advance aState.mY to the top border-edge of the frame.
  3063         aState.mY += topMargin;
  3067     // Here aState.mY is the top border-edge of the block.
  3068     // Compute the available space for the block
  3069     nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
  3070 #ifdef REALLY_NOISY_REFLOW
  3071     printf("setting line %p isImpacted to %s\n",
  3072            aLine.get(), floatAvailableSpace.mHasFloats?"true":"false");
  3073 #endif
  3074     aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats);
  3075     nsRect availSpace;
  3076     aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
  3077                                   replacedBlock != nullptr, availSpace);
  3079     // The check for
  3080     //   (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats)
  3081     // is to some degree out of paranoia:  if we reliably eat up top
  3082     // margins at the top of the page as we ought to, it wouldn't be
  3083     // needed.
  3084     if ((!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) &&
  3085         availSpace.height < 0) {
  3086       // We know already that this child block won't fit on this
  3087       // page/column due to the top margin or the clearance.  So we need
  3088       // to get out of here now.  (If we don't, most blocks will handle
  3089       // things fine, and report break-before, but zero-height blocks
  3090       // won't, and will thus make their parent overly-large and force
  3091       // *it* to be pushed in its entirety.)
  3092       // Doing this means that we also don't need to worry about the
  3093       // |availSpace.height += topMargin| below interacting with pushed
  3094       // floats (which force nscoord_MAX clearance) to cause a
  3095       // constrained height to turn into an unconstrained one.
  3096       aState.mY = startingY;
  3097       aState.mPrevBottomMargin = incomingMargin;
  3098       *aKeepReflowGoing = false;
  3099       if (ShouldAvoidBreakInside(aState.mReflowState)) {
  3100         aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  3101       } else {
  3102         PushLines(aState, aLine.prev());
  3103         NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  3105       return NS_OK;
  3108     // Now put the Y coordinate back to the top of the top-margin +
  3109     // clearance, and flow the block.
  3110     aState.mY -= topMargin;
  3111     availSpace.y -= topMargin;
  3112     if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
  3113       availSpace.height += topMargin;
  3116     // Reflow the block into the available space
  3117     // construct the html reflow state for the block. ReflowBlock 
  3118     // will initialize it
  3119     nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame, 
  3120                                   availSpace.Size());
  3121     blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
  3123     nsFloatManager::SavedState floatManagerState;
  3124     if (mayNeedRetry) {
  3125       blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
  3126       aState.mFloatManager->PushState(&floatManagerState);
  3127     } else if (!applyTopMargin) {
  3128       blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
  3131     nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
  3132     rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
  3133                          clearance, aState.IsAdjacentWithTop(),
  3134                          aLine.get(), blockHtmlRS, frameReflowStatus, aState);
  3136     NS_ENSURE_SUCCESS(rv, rv);
  3138     if (mayNeedRetry && clearanceFrame) {
  3139       aState.mFloatManager->PopState(&floatManagerState);
  3140       aState.mY = startingY;
  3141       aState.mPrevBottomMargin = incomingMargin;
  3142       continue;
  3145     aState.mPrevChild = frame;
  3147     if (blockHtmlRS.WillReflowAgainForClearance()) {
  3148       // If an ancestor of ours is going to reflow for clearance, we
  3149       // need to avoid calling PlaceBlock, because it unsets dirty bits
  3150       // on the child block (both itself, and through its call to
  3151       // nsFrame::DidReflow), and those dirty bits imply dirtiness for
  3152       // all of the child block, including the lines it didn't reflow.
  3153       NS_ASSERTION(originalPosition == frame->GetPosition(),
  3154                    "we need to call PositionChildViews");
  3155       return NS_OK;
  3158 #if defined(REFLOW_STATUS_COVERAGE)
  3159     RecordReflowStatus(true, frameReflowStatus);
  3160 #endif
  3162     if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
  3163       // None of the child block fits.
  3164       *aKeepReflowGoing = false;
  3165       if (ShouldAvoidBreakInside(aState.mReflowState)) {
  3166         aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  3167       } else {
  3168         PushLines(aState, aLine.prev());
  3169         NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  3172     else {
  3173       // Note: line-break-after a block is a nop
  3175       // Try to place the child block.
  3176       // Don't force the block to fit if we have positive clearance, because
  3177       // pushing it to the next page would give it more room.
  3178       // Don't force the block to fit if it's impacted by a float. If it is,
  3179       // then pushing it to the next page would give it more room. Note that
  3180       // isImpacted doesn't include impact from the block's own floats.
  3181       bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
  3182         !floatAvailableSpace.mHasFloats;
  3183       nsCollapsingMargin collapsedBottomMargin;
  3184       nsOverflowAreas overflowAreas;
  3185       *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
  3186                                          collapsedBottomMargin,
  3187                                          overflowAreas,
  3188                                          frameReflowStatus,
  3189                                          aState.mContainerWidth);
  3190       if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) &&
  3191           ShouldAvoidBreakInside(aState.mReflowState)) {
  3192         *aKeepReflowGoing = false;
  3195       if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
  3196         line_iterator nextLine = aLine;
  3197         ++nextLine;
  3198         if (nextLine != end_lines()) {
  3199           nextLine->MarkPreviousMarginDirty();
  3203       aLine->SetOverflowAreas(overflowAreas);
  3204       if (*aKeepReflowGoing) {
  3205         // Some of the child block fit
  3207         // Advance to new Y position
  3208         nscoord newY = aLine->BEnd();
  3209         aState.mY = newY;
  3211         // Continue the block frame now if it didn't completely fit in
  3212         // the available space.
  3213         if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
  3214           bool madeContinuation =
  3215             CreateContinuationFor(aState, nullptr, frame);
  3217           nsIFrame* nextFrame = frame->GetNextInFlow();
  3218           NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
  3220           if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
  3221             // If nextFrame used to be an overflow container, make it a normal block
  3222             if (!madeContinuation &&
  3223                 (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
  3224               nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
  3225               nsContainerFrame* parent =
  3226                 static_cast<nsContainerFrame*>(nextFrame->GetParent());
  3227               rv = parent->StealFrame(nextFrame);
  3228               NS_ENSURE_SUCCESS(rv, rv);
  3229               if (parent != this)
  3230                 ReparentFrame(nextFrame, parent, this);
  3231               mFrames.InsertFrame(nullptr, frame, nextFrame);
  3232               madeContinuation = true; // needs to be added to mLines
  3233               nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
  3234               frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  3237             // Push continuation to a new line, but only if we actually made one.
  3238             if (madeContinuation) {
  3239               nsLineBox* line = NewLineBox(nextFrame, true);
  3240               mLines.after_insert(aLine, line);
  3243             PushLines(aState, aLine);
  3244             NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  3246             // If we need to reflow the continuation of the block child,
  3247             // then we'd better reflow our continuation
  3248             if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
  3249               aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  3250               // We also need to make that continuation's line dirty so
  3251               // it gets reflowed when we reflow our next in flow. The
  3252               // nif's line must always be either a line of the nif's
  3253               // parent block (only if we didn't make a continuation) or
  3254               // else one of our own overflow lines. In the latter case
  3255               // the line is already marked dirty, so just handle the
  3256               // first case.
  3257               if (!madeContinuation) {
  3258                 nsBlockFrame* nifBlock =
  3259                   nsLayoutUtils::GetAsBlock(nextFrame->GetParent());
  3260                 NS_ASSERTION(nifBlock,
  3261                              "A block's child's next in flow's parent must be a block!");
  3262                 for (line_iterator line = nifBlock->begin_lines(),
  3263                      line_end = nifBlock->end_lines(); line != line_end; ++line) {
  3264                   if (line->Contains(nextFrame)) {
  3265                     line->MarkDirty();
  3266                     break;
  3271             *aKeepReflowGoing = false;
  3273             // The bottom margin for a block is only applied on the last
  3274             // flow block. Since we just continued the child block frame,
  3275             // we know that line->mFirstChild is not the last flow block
  3276             // therefore zero out the running margin value.
  3277 #ifdef NOISY_VERTICAL_MARGINS
  3278             ListTag(stdout);
  3279             printf(": reflow incomplete, frame=");
  3280             nsFrame::ListTag(stdout, frame);
  3281             printf(" prevBottomMargin=%d, setting to zero\n",
  3282                    aState.mPrevBottomMargin);
  3283 #endif
  3284             aState.mPrevBottomMargin.Zero();
  3286           else { // frame is complete but its overflow is not complete
  3287             // Disconnect the next-in-flow and put it in our overflow tracker
  3288             if (!madeContinuation &&
  3289                 !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
  3290               // It already exists, but as a normal next-in-flow, so we need
  3291               // to dig it out of the child lists.
  3292               nsContainerFrame* parent = static_cast<nsContainerFrame*>
  3293                                            (nextFrame->GetParent());
  3294               rv = parent->StealFrame(nextFrame);
  3295               NS_ENSURE_SUCCESS(rv, rv);
  3297             else if (madeContinuation) {
  3298               mFrames.RemoveFrame(nextFrame);
  3301             // Put it in our overflow list
  3302             aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
  3303             NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
  3305 #ifdef NOISY_VERTICAL_MARGINS
  3306             ListTag(stdout);
  3307             printf(": reflow complete but overflow incomplete for ");
  3308             nsFrame::ListTag(stdout, frame);
  3309             printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
  3310                    aState.mPrevBottomMargin, collapsedBottomMargin.get());
  3311 #endif
  3312             aState.mPrevBottomMargin = collapsedBottomMargin;
  3315         else { // frame is fully complete
  3316 #ifdef NOISY_VERTICAL_MARGINS
  3317           ListTag(stdout);
  3318           printf(": reflow complete for ");
  3319           nsFrame::ListTag(stdout, frame);
  3320           printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
  3321                  aState.mPrevBottomMargin, collapsedBottomMargin.get());
  3322 #endif
  3323           aState.mPrevBottomMargin = collapsedBottomMargin;
  3325 #ifdef NOISY_VERTICAL_MARGINS
  3326         ListTag(stdout);
  3327         printf(": frame=");
  3328         nsFrame::ListTag(stdout, frame);
  3329         printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
  3330                brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
  3331                aState.mPrevBottomMargin);
  3332 #endif
  3333       } else {
  3334         if ((aLine == mLines.front() && !GetPrevInFlow()) ||
  3335             ShouldAvoidBreakInside(aState.mReflowState)) {
  3336           // If it's our very first line *or* we're not at the top of the page
  3337           // and we have page-break-inside:avoid, then we need to be pushed to
  3338           // our parent's next-in-flow.
  3339           aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  3340         } else {
  3341           // Push the line that didn't fit and any lines that follow it
  3342           // to our next-in-flow.
  3343           PushLines(aState, aLine.prev());
  3344           NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  3348     break; // out of the reflow retry loop
  3351   // Now that we've got its final position all figured out, position any child
  3352   // views it may have.  Note that the case when frame has a view got handled
  3353   // by FinishReflowChild, but that function didn't have the coordinates needed
  3354   // to correctly decide whether to reposition child views.
  3355   if (originalPosition != frame->GetPosition() && !frame->HasView()) {
  3356     nsContainerFrame::PositionChildViews(frame);
  3359 #ifdef DEBUG
  3360   VerifyLines(true);
  3361 #endif
  3362   return rv;
  3365 nsresult
  3366 nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
  3367                                  line_iterator aLine,
  3368                                  bool* aKeepReflowGoing)
  3370   nsresult rv = NS_OK;
  3371   *aKeepReflowGoing = true;
  3373   aLine->SetLineIsImpactedByFloat(false);
  3375   // Setup initial coordinate system for reflowing the inline frames
  3376   // into. Apply a previous block frame's bottom margin first.
  3377   if (ShouldApplyTopMargin(aState, aLine)) {
  3378     aState.mY += aState.mPrevBottomMargin.get();
  3380   nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
  3382   LineReflowStatus lineReflowStatus;
  3383   do {
  3384     nscoord availableSpaceHeight = 0;
  3385     do {
  3386       bool allowPullUp = true;
  3387       nsIContent* forceBreakInContent = nullptr;
  3388       int32_t forceBreakOffset = -1;
  3389       gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak;
  3390       do {
  3391         nsFloatManager::SavedState floatManagerState;
  3392         aState.mReflowState.mFloatManager->PushState(&floatManagerState);
  3394         // Once upon a time we allocated the first 30 nsLineLayout objects
  3395         // on the stack, and then we switched to the heap.  At that time
  3396         // these objects were large (1100 bytes on a 32 bit system).
  3397         // Then the nsLineLayout object was shrunk to 156 bytes by
  3398         // removing some internal buffers.  Given that it is so much
  3399         // smaller, the complexity of 2 different ways of allocating
  3400         // no longer makes sense.  Now we always allocate on the stack.
  3401         nsLineLayout lineLayout(aState.mPresContext,
  3402                                 aState.mReflowState.mFloatManager,
  3403                                 &aState.mReflowState, &aLine);
  3404         lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
  3405         if (forceBreakInContent) {
  3406           lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
  3408         rv = DoReflowInlineFrames(aState, lineLayout, aLine,
  3409                                   floatAvailableSpace, availableSpaceHeight,
  3410                                   &floatManagerState, aKeepReflowGoing,
  3411                                   &lineReflowStatus, allowPullUp);
  3412         lineLayout.EndLineReflow();
  3414         if (NS_FAILED(rv)) {
  3415           return rv;
  3418         if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
  3419             LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus ||
  3420             LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
  3421           if (lineLayout.NeedsBackup()) {
  3422             NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary");
  3423             // If there is no saved break position, then this will set
  3424             // set forceBreakInContent to null and we won't back up, which is
  3425             // correct.
  3426             forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority);
  3427           } else {
  3428             forceBreakInContent = nullptr;
  3430           // restore the float manager state
  3431           aState.mReflowState.mFloatManager->PopState(&floatManagerState);
  3432           // Clear out float lists
  3433           aState.mCurrentLineFloats.DeleteAll();
  3434           aState.mBelowCurrentLineFloats.DeleteAll();
  3437         // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
  3438         allowPullUp = false;
  3439       } while (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
  3440     } while (LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus);
  3441   } while (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
  3443   return rv;
  3446 void
  3447 nsBlockFrame::PushTruncatedLine(nsBlockReflowState& aState,
  3448                                 line_iterator       aLine,
  3449                                 bool*               aKeepReflowGoing)
  3451   PushLines(aState, aLine.prev());
  3452   *aKeepReflowGoing = false;
  3453   NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
  3456 #ifdef DEBUG
  3457 static const char* LineReflowStatusNames[] = {
  3458   "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
  3459   "LINE_REFLOW_REDO_MORE_FLOATS",
  3460   "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
  3461 };
  3462 #endif
  3464 nsresult
  3465 nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
  3466                                    nsLineLayout& aLineLayout,
  3467                                    line_iterator aLine,
  3468                                    nsFlowAreaRect& aFloatAvailableSpace,
  3469                                    nscoord& aAvailableSpaceHeight,
  3470                                    nsFloatManager::SavedState*
  3471                                      aFloatStateBeforeLine,
  3472                                    bool* aKeepReflowGoing,
  3473                                    LineReflowStatus* aLineReflowStatus,
  3474                                    bool aAllowPullUp)
  3476   // Forget all of the floats on the line
  3477   aLine->FreeFloats(aState.mFloatCacheFreeList);
  3478   aState.mFloatOverflowAreas.Clear();
  3480   // We need to set this flag on the line if any of our reflow passes
  3481   // are impacted by floats.
  3482   if (aFloatAvailableSpace.mHasFloats)
  3483     aLine->SetLineIsImpactedByFloat(true);
  3484 #ifdef REALLY_NOISY_REFLOW
  3485   printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
  3486          this, aFloatAvailableSpace.mHasFloats);
  3487 #endif
  3489   WritingMode wm = GetWritingMode(aLine->mFirstChild);
  3490   LogicalRect lineRect(wm, aFloatAvailableSpace.mRect, aState.mContainerWidth);
  3492   nscoord iStart = lineRect.IStart(wm);
  3494   nscoord availISize = lineRect.ISize(wm);
  3495   nscoord availBSize;
  3496   if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
  3497     availBSize = NS_UNCONSTRAINEDSIZE;
  3499   else {
  3500     /* XXX get the height right! */
  3501     availBSize = lineRect.BSize(wm);
  3504   // Make sure to enable resize optimization before we call BeginLineReflow
  3505   // because it might get disabled there
  3506   aLine->EnableResizeReflowOptimization();
  3508   aLineLayout.BeginLineReflow(iStart, aState.mY,
  3509                               availISize, availBSize,
  3510                               aFloatAvailableSpace.mHasFloats,
  3511                               false, /*XXX isTopOfPage*/
  3512                               wm, aState.mContainerWidth);
  3514   aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false);
  3516   // XXX Unfortunately we need to know this before reflowing the first
  3517   // inline frame in the line. FIX ME.
  3518   if ((0 == aLineLayout.GetLineNumber()) &&
  3519       (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
  3520       (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
  3521     aLineLayout.SetFirstLetterStyleOK(true);
  3523   NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
  3524                  GetPrevContinuation()),
  3525                "first letter child bit should only be on first continuation");
  3527   // Reflow the frames that are already on the line first
  3528   nsresult rv = NS_OK;
  3529   LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
  3530   int32_t i;
  3531   nsIFrame* frame = aLine->mFirstChild;
  3533   if (aFloatAvailableSpace.mHasFloats) {
  3534     // There is a soft break opportunity at the start of the line, because
  3535     // we can always move this line down below float(s).
  3536     if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, true, gfxBreakPriority::eNormalBreak)) {
  3537       lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
  3541   // need to repeatedly call GetChildCount here, because the child
  3542   // count can change during the loop!
  3543   for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
  3544        i++, frame = frame->GetNextSibling()) {
  3545     rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
  3546                            &lineReflowStatus);
  3547     NS_ENSURE_SUCCESS(rv, rv);
  3548     if (LINE_REFLOW_OK != lineReflowStatus) {
  3549       // It is possible that one or more of next lines are empty
  3550       // (because of DeleteNextInFlowChild). If so, delete them now
  3551       // in case we are finished.
  3552       ++aLine;
  3553       while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
  3554         // XXX Is this still necessary now that DeleteNextInFlowChild
  3555         // uses DoRemoveFrame?
  3556         nsLineBox *toremove = aLine;
  3557         aLine = mLines.erase(aLine);
  3558         NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line");
  3559         FreeLineBox(toremove);
  3561       --aLine;
  3563       NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED,
  3564                    "ReflowInlineFrame should never determine that a line "
  3565                    "needs to go to the next page/column");
  3569   // Don't pull up new frames into lines with continuation placeholders
  3570   if (aAllowPullUp) {
  3571     // Pull frames and reflow them until we can't
  3572     while (LINE_REFLOW_OK == lineReflowStatus) {
  3573       frame = PullFrame(aState, aLine);
  3574       if (!frame) {
  3575         break;
  3578       while (LINE_REFLOW_OK == lineReflowStatus) {
  3579         int32_t oldCount = aLine->GetChildCount();
  3580         rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
  3581                                &lineReflowStatus);
  3582         NS_ENSURE_SUCCESS(rv, rv);
  3583         if (aLine->GetChildCount() != oldCount) {
  3584           // We just created a continuation for aFrame AND its going
  3585           // to end up on this line (e.g. :first-letter
  3586           // situation). Therefore we have to loop here before trying
  3587           // to pull another frame.
  3588           frame = frame->GetNextSibling();
  3590         else {
  3591           break;
  3597   aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, aLineLayout.LineIsEmpty());
  3599   // We only need to backup if the line isn't going to be reflowed again anyway
  3600   bool needsBackup = aLineLayout.NeedsBackup() &&
  3601     (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK);
  3602   if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
  3603   	NS_WARNING("We shouldn't be backing up more than once! "
  3604                "Someone must have set a break opportunity beyond the available width, "
  3605                "even though there were better break opportunities before it");
  3606     needsBackup = false;
  3608   if (needsBackup) {
  3609     // We need to try backing up to before a text run
  3610     int32_t offset;
  3611     gfxBreakPriority breakPriority;
  3612     nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset, &breakPriority);
  3613     // XXX It's possible, in fact not unusual, for the break opportunity to already
  3614     // be the end of the line. We should detect that and optimize to not
  3615     // re-do the line.
  3616     if (breakContent) {
  3617       // We can back up!
  3618       lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
  3620   } else {
  3621     // In case we reflow this line again, remember that we don't
  3622     // need to force any breaking
  3623     aLineLayout.ClearOptionalBreakPosition();
  3626   if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
  3627     // This happens only when we have a line that is impacted by
  3628     // floats and the first element in the line doesn't fit with
  3629     // the floats.
  3630     //
  3631     // What we do is to advance past the first float we find and
  3632     // then reflow the line all over again.
  3633     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.height,
  3634                  "unconstrained height on totally empty line");
  3636     // See the analogous code for blocks in nsBlockReflowState::ClearFloats.
  3637     if (aFloatAvailableSpace.mRect.height > 0) {
  3638       NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
  3639                    "redo line on totally empty line with non-empty band...");
  3640       // We should never hit this case if we've placed floats on the
  3641       // line; if we have, then the GetFloatAvailableSpace call is wrong
  3642       // and needs to happen after the caller pops the space manager
  3643       // state.
  3644       aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
  3645       aState.mY += aFloatAvailableSpace.mRect.height;
  3646       aFloatAvailableSpace = aState.GetFloatAvailableSpace();
  3647     } else {
  3648       NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.AvailableHeight(),
  3649                    "We shouldn't be running out of height here");
  3650       if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.AvailableHeight()) {
  3651         // just move it down a bit to try to get out of this mess
  3652         aState.mY += 1;
  3653         // We should never hit this case if we've placed floats on the
  3654         // line; if we have, then the GetFloatAvailableSpace call is wrong
  3655         // and needs to happen after the caller pops the space manager
  3656         // state.
  3657         aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
  3658         aFloatAvailableSpace = aState.GetFloatAvailableSpace();
  3659       } else {
  3660         // There's nowhere to retry placing the line, so we want to push
  3661         // it to the next page/column where its contents can fit not
  3662         // next to a float.
  3663         lineReflowStatus = LINE_REFLOW_TRUNCATED;
  3664         PushTruncatedLine(aState, aLine, aKeepReflowGoing);
  3668     // XXX: a small optimization can be done here when paginating:
  3669     // if the new Y coordinate is past the end of the block then
  3670     // push the line and return now instead of later on after we are
  3671     // past the float.
  3673   else if (LINE_REFLOW_TRUNCATED != lineReflowStatus &&
  3674            LINE_REFLOW_REDO_NO_PULL != lineReflowStatus) {
  3675     // If we are propagating out a break-before status then there is
  3676     // no point in placing the line.
  3677     if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
  3678       if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
  3679                      aFloatAvailableSpace.mRect, aAvailableSpaceHeight,
  3680                      aKeepReflowGoing)) {
  3681         lineReflowStatus = LINE_REFLOW_REDO_MORE_FLOATS;
  3682         // PlaceLine already called GetAvailableSpaceForHeight for us.
  3686 #ifdef DEBUG
  3687   if (gNoisyReflow) {
  3688     printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
  3690 #endif
  3692   if (aLineLayout.GetDirtyNextLine()) {
  3693     // aLine may have been pushed to the overflow lines.
  3694     FrameLines* overflowLines = GetOverflowLines();
  3695     // We can't just compare iterators front() to aLine here, since they may be in
  3696     // different lists.
  3697     bool pushedToOverflowLines = overflowLines &&
  3698       overflowLines->mLines.front() == aLine.get();
  3699     if (pushedToOverflowLines) {
  3700       // aLine is stale, it's associated with the main line list but it should
  3701       // be associated with the overflow line list now
  3702       aLine = overflowLines->mLines.begin();
  3704     nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
  3705     if (iter.Next() && iter.GetLine()->IsInline()) {
  3706       iter.GetLine()->MarkDirty();
  3707       if (iter.GetContainer() != this) {
  3708         aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  3713   *aLineReflowStatus = lineReflowStatus;
  3715   return rv;
  3718 /**
  3719  * Reflow an inline frame. The reflow status is mapped from the frames
  3720  * reflow status to the lines reflow status (not to our reflow status).
  3721  * The line reflow status is simple: true means keep placing frames
  3722  * on the line; false means don't (the line is done). If the line
  3723  * has some sort of breaking affect then aLine's break-type will be set
  3724  * to something other than NS_STYLE_CLEAR_NONE.
  3725  */
  3726 nsresult
  3727 nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
  3728                                 nsLineLayout& aLineLayout,
  3729                                 line_iterator aLine,
  3730                                 nsIFrame* aFrame,
  3731                                 LineReflowStatus* aLineReflowStatus)
  3733   NS_ENSURE_ARG_POINTER(aFrame);
  3735   *aLineReflowStatus = LINE_REFLOW_OK;
  3737 #ifdef NOISY_FIRST_LETTER
  3738   ListTag(stdout);
  3739   printf(": reflowing ");
  3740   nsFrame::ListTag(stdout, aFrame);
  3741   printf(" reflowingFirstLetter=%s\n",
  3742          aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
  3743 #endif
  3745   // Reflow the inline frame
  3746   nsReflowStatus frameReflowStatus;
  3747   bool           pushedFrame;
  3748   nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
  3749                                         nullptr, pushedFrame);
  3750   NS_ENSURE_SUCCESS(rv, rv);
  3752   if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
  3753     aLineLayout.SetDirtyNextLine();
  3756   NS_ENSURE_SUCCESS(rv, rv);
  3757 #ifdef REALLY_NOISY_REFLOW_CHILD
  3758   nsFrame::ListTag(stdout, aFrame);
  3759   printf(": status=%x\n", frameReflowStatus);
  3760 #endif
  3762 #if defined(REFLOW_STATUS_COVERAGE)
  3763   RecordReflowStatus(false, frameReflowStatus);
  3764 #endif
  3766   // Send post-reflow notification
  3767   aState.mPrevChild = aFrame;
  3769    /* XXX
  3770       This is where we need to add logic to handle some odd behavior.
  3771       For one thing, we should usually place at least one thing next
  3772       to a left float, even when that float takes up all the width on a line.
  3773       see bug 22496
  3774    */
  3776   // Process the child frames reflow status. There are 5 cases:
  3777   // complete, not-complete, break-before, break-after-complete,
  3778   // break-after-not-complete. There are two situations: we are a
  3779   // block or we are an inline. This makes a total of 10 cases
  3780   // (fortunately, there is some overlap).
  3781   aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
  3782   if (NS_INLINE_IS_BREAK(frameReflowStatus) || 
  3783       (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
  3784     // Always abort the line reflow (because a line break is the
  3785     // minimal amount of break we do).
  3786     *aLineReflowStatus = LINE_REFLOW_STOP;
  3788     // XXX what should aLine's break-type be set to in all these cases?
  3789     uint8_t breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
  3790     NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) || 
  3791                  (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
  3792     NS_ASSERTION(NS_STYLE_CLEAR_MAX >= breakType, "invalid break type");
  3794     if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
  3795       // Break-before cases.
  3796       if (aFrame == aLine->mFirstChild) {
  3797         // If we break before the first frame on the line then we must
  3798         // be trying to place content where there's no room (e.g. on a
  3799         // line with wide floats). Inform the caller to reflow the
  3800         // line after skipping past a float.
  3801         *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
  3803       else {
  3804         // It's not the first child on this line so go ahead and split
  3805         // the line. We will see the frame again on the next-line.
  3806         SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
  3808         // If we're splitting the line because the frame didn't fit and it
  3809         // was pushed, then mark the line as having word wrapped. We need to
  3810         // know that if we're shrink wrapping our width
  3811         if (pushedFrame) {
  3812           aLine->SetLineWrapped(true);
  3816     else {
  3817       // If a float split and its prev-in-flow was followed by a <BR>, then combine 
  3818       // the <BR>'s break type with the inline's break type (the inline will be the very 
  3819       // next frame after the split float).
  3820       if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
  3821         breakType = nsLayoutUtils::CombineBreakType(breakType,
  3822                                                     aState.mFloatBreakType);
  3823         aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
  3825       // Break-after cases
  3826       if (breakType == NS_STYLE_CLEAR_LINE) {
  3827         if (!aLineLayout.GetLineEndsInBR()) {
  3828           breakType = NS_STYLE_CLEAR_NONE;
  3831       aLine->SetBreakTypeAfter(breakType);
  3832       if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
  3833         // Split line, but after the frame just reflowed
  3834         SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
  3836         if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
  3837             !aLineLayout.GetLineEndsInBR()) {
  3838           aLineLayout.SetDirtyNextLine();
  3844   if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
  3845     // Create a continuation for the incomplete frame. Note that the
  3846     // frame may already have a continuation.
  3847     CreateContinuationFor(aState, aLine, aFrame);
  3849     // Remember that the line has wrapped
  3850     if (!aLineLayout.GetLineEndsInBR()) {
  3851       aLine->SetLineWrapped(true);
  3854     // If we just ended a first-letter frame or reflowed a placeholder then 
  3855     // don't split the line and don't stop the line reflow...
  3856     // But if we are going to stop anyways we'd better split the line.
  3857     if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) && 
  3858          nsGkAtoms::placeholderFrame != aFrame->GetType()) ||
  3859         *aLineReflowStatus == LINE_REFLOW_STOP) {
  3860       // Split line after the current frame
  3861       *aLineReflowStatus = LINE_REFLOW_STOP;
  3862       SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
  3866   return NS_OK;
  3869 bool
  3870 nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
  3871                                     nsLineBox*          aLine,
  3872                                     nsIFrame*           aFrame)
  3874   nsIFrame* newFrame = nullptr;
  3876   if (!aFrame->GetNextInFlow()) {
  3877     newFrame = aState.mPresContext->PresShell()->FrameConstructor()->
  3878       CreateContinuingFrame(aState.mPresContext, aFrame, this);
  3880     mFrames.InsertFrame(nullptr, aFrame, newFrame);
  3882     if (aLine) {
  3883       aLine->NoteFrameAdded(newFrame);
  3886 #ifdef DEBUG
  3887   VerifyLines(false);
  3888 #endif
  3889   return !!newFrame;
  3892 nsresult
  3893 nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
  3894                          nsIFrame*           aFloat,
  3895                          nsReflowStatus      aFloatStatus)
  3897   nsIFrame* nextInFlow = aFloat->GetNextInFlow();
  3898   if (nextInFlow) {
  3899     nsContainerFrame *oldParent =
  3900       static_cast<nsContainerFrame*>(nextInFlow->GetParent());
  3901     DebugOnly<nsresult> rv = oldParent->StealFrame(nextInFlow);
  3902     NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
  3903     if (oldParent != this) {
  3904       ReparentFrame(nextInFlow, oldParent, this);
  3906   } else {
  3907     nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
  3908       CreateContinuingFrame(aState.mPresContext, aFloat, this);
  3910   if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
  3911     aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
  3913   // The containing block is now overflow-incomplete.
  3914   NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
  3916   if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
  3917     aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
  3918   } else {
  3919     NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats ==
  3920                         NS_STYLE_FLOAT_RIGHT, "unexpected float side");
  3921     aState.mFloatManager->SetSplitRightFloatAcrossBreak();
  3924   aState.AppendPushedFloat(nextInFlow);
  3925   return NS_OK;
  3928 static nsFloatCache*
  3929 GetLastFloat(nsLineBox* aLine)
  3931   nsFloatCache* fc = aLine->GetFirstFloat();
  3932   while (fc && fc->Next()) {
  3933     fc = fc->Next();
  3935   return fc;
  3938 static bool
  3939 CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
  3941   if (!aFC)
  3942     return true;
  3943   NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
  3944                "float in a line should never be a continuation");
  3945   NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
  3946                "float in a line should never be a pushed float");
  3947   nsIFrame* ph = aBlock->PresContext()->FrameManager()->
  3948                    GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow());
  3949   for (nsIFrame* f = ph; f; f = f->GetParent()) {
  3950     if (f->GetParent() == aBlock)
  3951       return aLine->Contains(f);
  3953   NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
  3954   return true;
  3957 void
  3958 nsBlockFrame::SplitLine(nsBlockReflowState& aState,
  3959                         nsLineLayout& aLineLayout,
  3960                         line_iterator aLine,
  3961                         nsIFrame* aFrame,
  3962                         LineReflowStatus* aLineReflowStatus)
  3964   NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
  3966   int32_t pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
  3967   NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count"); 
  3969 #ifdef DEBUG
  3970   if (gNoisyReflow) {
  3971     nsFrame::IndentBy(stdout, gNoiseIndent);
  3972     printf("split line: from line=%p pushCount=%d aFrame=",
  3973            static_cast<void*>(aLine.get()), pushCount);
  3974     if (aFrame) {
  3975       nsFrame::ListTag(stdout, aFrame);
  3977     else {
  3978       printf("(null)");
  3980     printf("\n");
  3981     if (gReallyNoisyReflow) {
  3982       aLine->List(stdout, gNoiseIndent+1);
  3985 #endif
  3987   if (0 != pushCount) {
  3988     NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
  3989     NS_ABORT_IF_FALSE(nullptr != aFrame, "whoops");
  3990 #ifdef DEBUG
  3992       nsIFrame *f = aFrame;
  3993       int32_t count = pushCount;
  3994       while (f && count > 0) {
  3995         f = f->GetNextSibling();
  3996         --count;
  3998       NS_ASSERTION(count == 0, "Not enough frames to push");
  4000 #endif
  4002     // Put frames being split out into their own line
  4003     nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
  4004     mLines.after_insert(aLine, newLine);
  4005 #ifdef DEBUG
  4006     if (gReallyNoisyReflow) {
  4007       newLine->List(stdout, gNoiseIndent+1);
  4009 #endif
  4011     // Let line layout know that some frames are no longer part of its
  4012     // state.
  4013     aLineLayout.SplitLineTo(aLine->GetChildCount());
  4015     // If floats have been placed whose placeholders have been pushed to the new
  4016     // line, we need to reflow the old line again. We don't want to look at the
  4017     // frames in the new line, because as a large paragraph is laid out the 
  4018     // we'd get O(N^2) performance. So instead we just check that the last
  4019     // float and the last below-current-line float are still in aLine.
  4020     if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
  4021         !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
  4022       *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
  4025 #ifdef DEBUG
  4026     VerifyLines(true);
  4027 #endif
  4031 bool
  4032 nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
  4033                          line_iterator aLine)
  4035   while (++aLine != end_lines()) {
  4036     // There is another line
  4037     if (0 != aLine->GetChildCount()) {
  4038       // If the next line is a block line then this line is the last in a
  4039       // group of inline lines.
  4040       return aLine->IsBlock();
  4042     // The next line is empty, try the next one
  4045   // XXX Not sure about this part
  4046   // Try our next-in-flows lines to answer the question
  4047   nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
  4048   while (nullptr != nextInFlow) {
  4049     for (line_iterator line = nextInFlow->begin_lines(),
  4050                    line_end = nextInFlow->end_lines();
  4051          line != line_end;
  4052          ++line)
  4054       if (0 != line->GetChildCount())
  4055         return line->IsBlock();
  4057     nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
  4060   // This is the last line - so don't allow justification
  4061   return true;
  4064 bool
  4065 nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
  4066                         nsLineLayout&       aLineLayout,
  4067                         line_iterator       aLine,
  4068                         nsFloatManager::SavedState *aFloatStateBeforeLine,
  4069                         nsRect&             aFloatAvailableSpace,
  4070                         nscoord&            aAvailableSpaceHeight,
  4071                         bool*             aKeepReflowGoing)
  4073   // Trim extra white-space from the line before placing the frames
  4074   aLineLayout.TrimTrailingWhiteSpace();
  4076   // Vertically align the frames on this line.
  4077   //
  4078   // According to the CSS2 spec, section 12.6.1, the "marker" box
  4079   // participates in the height calculation of the list-item box's
  4080   // first line box.
  4081   //
  4082   // There are exactly two places a bullet can be placed: near the
  4083   // first or second line. It's only placed on the second line in a
  4084   // rare case: when the first line is empty.
  4085   bool addedBullet = false;
  4086   if (HasOutsideBullet() &&
  4087       ((aLine == mLines.front() &&
  4088         (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) ||
  4089        (mLines.front() != mLines.back() &&
  4090         0 == mLines.front()->BSize() &&
  4091         aLine == mLines.begin().next()))) {
  4092     nsHTMLReflowMetrics metrics(aState.mReflowState);
  4093     nsIFrame* bullet = GetOutsideBullet();
  4094     ReflowBullet(bullet, aState, metrics, aState.mY);
  4095     NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0,
  4096                  "empty bullet took up space");
  4097     aLineLayout.AddBulletFrame(bullet, metrics);
  4098     addedBullet = true;
  4100   aLineLayout.BlockDirAlignLine();
  4102   // We want to compare to the available space that we would have had in
  4103   // the line's height *before* we placed any floats in the line itself.
  4104   // Floats that are in the line are handled during line reflow (and may
  4105   // result in floats being pushed to below the line or (I HOPE???) in a
  4106   // reflow with a forced break position).
  4107   nsRect oldFloatAvailableSpace(aFloatAvailableSpace);
  4108   // As we redo for floats, we can't reduce the amount of height we're
  4109   // checking.
  4110   aAvailableSpaceHeight = std::max(aAvailableSpaceHeight, aLine->BSize());
  4111   aFloatAvailableSpace = 
  4112     aState.GetFloatAvailableSpaceForHeight(aLine->BStart(),
  4113                                            aAvailableSpaceHeight,
  4114                                            aFloatStateBeforeLine).mRect;
  4115   NS_ASSERTION(aFloatAvailableSpace.y == oldFloatAvailableSpace.y, "yikes");
  4116   // Restore the height to the position of the next band.
  4117   aFloatAvailableSpace.height = oldFloatAvailableSpace.height;
  4118   // If the available space between the floats is smaller now that we
  4119   // know the height, return false (and cause another pass with
  4120   // LINE_REFLOW_REDO_MORE_FLOATS).
  4121   if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) {
  4122     return false;
  4125 #ifdef DEBUG
  4127     static nscoord lastHeight = 0;
  4128     if (CRAZY_SIZE(aLine->BStart())) {
  4129       lastHeight = aLine->BStart();
  4130       if (abs(aLine->BStart() - lastHeight) > CRAZY_COORD/10) {
  4131         nsFrame::ListTag(stdout);
  4132         printf(": line=%p y=%d line.bounds.height=%d\n",
  4133                static_cast<void*>(aLine.get()),
  4134                aLine->BStart(), aLine->BSize());
  4137     else {
  4138       lastHeight = 0;
  4141 #endif
  4143   // Only block frames horizontally align their children because
  4144   // inline frames "shrink-wrap" around their children (therefore
  4145   // there is no extra horizontal space).
  4146   const nsStyleText* styleText = StyleText();
  4148   /**
  4149    * text-align-last defaults to the same value as text-align when
  4150    * text-align-last is set to auto (except when text-align is set to justify),
  4151    * so in that case we don't need to set isLastLine.
  4153    * In other words, isLastLine really means isLastLineAndWeCare.
  4154    */
  4155   bool isLastLine =
  4156     !IsSVGText() &&
  4157     ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
  4158       NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
  4159      (aLineLayout.GetLineEndsInBR() ||
  4160       IsLastLine(aState, aLine)));
  4162   aLineLayout.InlineDirAlignFrames(aLine, isLastLine);
  4164   // From here on, pfd->mBounds rectangles are incorrect because bidi
  4165   // might have moved frames around!
  4166   nsOverflowAreas overflowAreas;
  4167   aLineLayout.RelativePositionFrames(overflowAreas);
  4168   aLine->SetOverflowAreas(overflowAreas);
  4169   if (addedBullet) {
  4170     aLineLayout.RemoveBulletFrame(GetOutsideBullet());
  4173   // Inline lines do not have margins themselves; however they are
  4174   // impacted by prior block margins. If this line ends up having some
  4175   // height then we zero out the previous bottom margin value that was
  4176   // already applied to the line's starting Y coordinate. Otherwise we
  4177   // leave it be so that the previous blocks bottom margin can be
  4178   // collapsed with a block that follows.
  4179   nscoord newY;
  4181   if (!aLine->CachedIsEmpty()) {
  4182     // This line has some height. Therefore the application of the
  4183     // previous-bottom-margin should stick.
  4184     aState.mPrevBottomMargin.Zero();
  4185     newY = aLine->BEnd();
  4187   else {
  4188     // Don't let the previous-bottom-margin value affect the newY
  4189     // coordinate (it was applied in ReflowInlineFrames speculatively)
  4190     // since the line is empty.
  4191     // We already called |ShouldApplyTopMargin|, and if we applied it
  4192     // then BRS_APPLYTOPMARGIN is set.
  4193     nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
  4194                    ? -aState.mPrevBottomMargin.get() : 0;
  4195     newY = aState.mY + dy;
  4198   if (!NS_FRAME_IS_FULLY_COMPLETE(aState.mReflowStatus) &&
  4199       ShouldAvoidBreakInside(aState.mReflowState)) {
  4200     aLine->AppendFloats(aState.mCurrentLineFloats);
  4201     aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  4202     return true;
  4205   // See if the line fit (our first line always does).
  4206   if (mLines.front() != aLine &&
  4207       newY > aState.mBottomEdge &&
  4208       aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
  4209     NS_ASSERTION(aState.mCurrentLine == aLine, "oops");
  4210     if (ShouldAvoidBreakInside(aState.mReflowState)) {
  4211       // All our content doesn't fit, start on the next page.
  4212       aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  4213     } else {
  4214       // Push aLine and all of its children and anything else that
  4215       // follows to our next-in-flow.
  4216       PushTruncatedLine(aState, aLine, aKeepReflowGoing);
  4218     return true;
  4221   aState.mY = newY;
  4223   // Add the already placed current-line floats to the line
  4224   aLine->AppendFloats(aState.mCurrentLineFloats);
  4226   // Any below current line floats to place?
  4227   if (aState.mBelowCurrentLineFloats.NotEmpty()) {
  4228     // Reflow the below-current-line floats, which places on the line's
  4229     // float list.
  4230     aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine);
  4231     aLine->AppendFloats(aState.mBelowCurrentLineFloats);
  4234   // When a line has floats, factor them into the combined-area
  4235   // computations.
  4236   if (aLine->HasFloats()) {
  4237     // Combine the float combined area (stored in aState) and the
  4238     // value computed by the line layout code.
  4239     nsOverflowAreas lineOverflowAreas;
  4240     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
  4241       nsRect &o = lineOverflowAreas.Overflow(otype);
  4242       o = aLine->GetOverflowArea(otype);
  4243 #ifdef NOISY_COMBINED_AREA
  4244       ListTag(stdout);
  4245       printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
  4246              otype,
  4247              o.x, o.y, o.width, o.height,
  4248              aState.mFloatOverflowAreas.Overflow(otype).x,
  4249              aState.mFloatOverflowAreas.Overflow(otype).y,
  4250              aState.mFloatOverflowAreas.Overflow(otype).width,
  4251              aState.mFloatOverflowAreas.Overflow(otype).height);
  4252 #endif
  4253       o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o);
  4255 #ifdef NOISY_COMBINED_AREA
  4256       printf("  ==> final lineCA=%d,%d,%d,%d\n",
  4257              o.x, o.y, o.width, o.height);
  4258 #endif
  4260     aLine->SetOverflowAreas(lineOverflowAreas);
  4263   // Apply break-after clearing if necessary
  4264   // This must stay in sync with |ReflowDirtyLines|.
  4265   if (aLine->HasFloatBreakAfter()) {
  4266     aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
  4268   return true;
  4271 void
  4272 nsBlockFrame::PushLines(nsBlockReflowState&  aState,
  4273                         nsLineList::iterator aLineBefore)
  4275   // NOTE: aLineBefore is always a normal line, not an overflow line.
  4276   // The following expression will assert otherwise.
  4277   DebugOnly<bool> check = aLineBefore == mLines.begin();
  4279   nsLineList::iterator overBegin(aLineBefore.next());
  4281   // PushTruncatedPlaceholderLine sometimes pushes the first line.  Ugh.
  4282   bool firstLine = overBegin == begin_lines();
  4284   if (overBegin != end_lines()) {
  4285     // Remove floats in the lines from mFloats
  4286     nsFrameList floats;
  4287     CollectFloats(overBegin->mFirstChild, floats, true);
  4289     if (floats.NotEmpty()) {
  4290       // Push the floats onto the front of the overflow out-of-flows list
  4291       nsAutoOOFFrameList oofs(this);
  4292       oofs.mList.InsertFrames(nullptr, nullptr, floats);
  4295     // overflow lines can already exist in some cases, in particular,
  4296     // when shrinkwrapping and we discover that the shrinkwap causes
  4297     // the height of some child block to grow which creates additional
  4298     // overflowing content. In such cases we must prepend the new
  4299     // overflow to the existing overflow.
  4300     FrameLines* overflowLines = RemoveOverflowLines();
  4301     if (!overflowLines) {
  4302       // XXXldb use presshell arena!
  4303       overflowLines = new FrameLines();
  4305     if (overflowLines) {
  4306       nsIFrame* lineBeforeLastFrame;
  4307       if (firstLine) {
  4308         lineBeforeLastFrame = nullptr; // removes all frames
  4309       } else {
  4310         nsIFrame* f = overBegin->mFirstChild;
  4311         lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
  4312         NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
  4313                      "unexpected line frames");
  4315       nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
  4316       overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames);
  4318       overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
  4319                                     overBegin, end_lines());
  4320       NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
  4321       // this takes ownership but it won't delete it immediately so we
  4322       // can keep using it.
  4323       SetOverflowLines(overflowLines);
  4325       // Mark all the overflow lines dirty so that they get reflowed when
  4326       // they are pulled up by our next-in-flow.
  4328       // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
  4329       for (line_iterator line = overflowLines->mLines.begin(),
  4330              line_end = overflowLines->mLines.end();
  4331            line != line_end;
  4332            ++line)
  4334         line->MarkDirty();
  4335         line->MarkPreviousMarginDirty();
  4336         line->SetBoundsEmpty();
  4337         if (line->HasFloats()) {
  4338           line->FreeFloats(aState.mFloatCacheFreeList);
  4344 #ifdef DEBUG
  4345   VerifyOverflowSituation();
  4346 #endif
  4349 // The overflowLines property is stored as a pointer to a line list,
  4350 // which must be deleted.  However, the following functions all maintain
  4351 // the invariant that the property is never set if the list is empty.
  4353 bool
  4354 nsBlockFrame::DrainOverflowLines()
  4356 #ifdef DEBUG
  4357   VerifyOverflowSituation();
  4358 #endif
  4360   // Steal the prev-in-flow's overflow lines and prepend them.
  4361   bool didFindOverflow = false;
  4362   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
  4363   if (prevBlock) {
  4364     prevBlock->ClearLineCursor();
  4365     FrameLines* overflowLines = prevBlock->RemoveOverflowLines();
  4366     if (overflowLines) {
  4367       // Make all the frames on the overflow line list mine.
  4368       ReparentFrames(overflowLines->mFrames, prevBlock, this);
  4370       // Make the overflow out-of-flow frames mine too.
  4371       nsAutoOOFFrameList oofs(prevBlock);
  4372       if (oofs.mList.NotEmpty()) {
  4373         ReparentFrames(oofs.mList, prevBlock, this);
  4374         mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
  4377       if (!mLines.empty()) {
  4378         // Remember to recompute the margins on the first line. This will
  4379         // also recompute the correct deltaY if necessary.
  4380         mLines.front()->MarkPreviousMarginDirty();
  4382       // The overflow lines have already been marked dirty and their previous
  4383       // margins marked dirty also.
  4385       // Prepend the overflow frames/lines to our principal list.
  4386       mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames);
  4387       mLines.splice(mLines.begin(), overflowLines->mLines);
  4388       NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
  4389       delete overflowLines;
  4390       didFindOverflow = true;
  4394   // Now append our own overflow lines.
  4395   return DrainSelfOverflowList() || didFindOverflow;
  4398 bool
  4399 nsBlockFrame::DrainSelfOverflowList()
  4401   nsAutoPtr<FrameLines> ourOverflowLines(RemoveOverflowLines());
  4402   if (!ourOverflowLines) {
  4403     return false;
  4406   // No need to reparent frames in our own overflow lines/oofs, because they're
  4407   // already ours. But we should put overflow floats back in mFloats.
  4408   nsAutoOOFFrameList oofs(this);
  4409   if (oofs.mList.NotEmpty()) {
  4410     // The overflow floats go after our regular floats.
  4411     mFloats.AppendFrames(nullptr, oofs.mList);
  4414   if (!ourOverflowLines->mLines.empty()) {
  4415     mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames);
  4416     mLines.splice(mLines.end(), ourOverflowLines->mLines);
  4418   return true;
  4421 /**
  4422  * Pushed floats are floats whose placeholders are in a previous
  4423  * continuation.  They might themselves be next-continuations of a float
  4424  * that partially fit in an earlier continuation, or they might be the
  4425  * first continuation of a float that couldn't be placed at all.
  4427  * Pushed floats live permanently at the beginning of a block's float
  4428  * list, where they must live *before* any floats whose placeholders are
  4429  * in that block.
  4431  * Temporarily, during reflow, they also live on the pushed floats list,
  4432  * which only holds them between (a) when one continuation pushes them to
  4433  * its pushed floats list because they don't fit and (b) when the next
  4434  * continuation pulls them onto the beginning of its float list.
  4436  * DrainPushedFloats sets up pushed floats the way we need them at the
  4437  * start of reflow; they are then reflowed by ReflowPushedFloats (which
  4438  * might push some of them on).  Floats with placeholders in this block
  4439  * are reflowed by (nsBlockReflowState/nsLineLayout)::AddFloat, which
  4440  * also maintains these invariants.
  4441  */
  4442 void
  4443 nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState)
  4445 #ifdef DEBUG
  4446   // Between when we drain pushed floats and when we complete reflow,
  4447   // we're allowed to have multiple continuations of the same float on
  4448   // our floats list, since a first-in-flow might get pushed to a later
  4449   // continuation of its containing block.  But it's not permitted
  4450   // outside that time.
  4451   nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
  4452 #endif
  4454   // If we're getting reflowed multiple times without our
  4455   // next-continuation being reflowed, we might need to pull back floats
  4456   // that we just put in the list to be pushed to our next-in-flow.
  4457   // We don't want to pull back any next-in-flows of floats on our own
  4458   // float list, and we only need to pull back first-in-flows whose
  4459   // placeholders were in earlier blocks (since first-in-flows whose
  4460   // placeholders are in this block will get pulled appropriately by
  4461   // AddFloat, and will then be more likely to be in the correct order).
  4462   // FIXME: What if there's a continuation in our pushed floats list
  4463   // whose prev-in-flow is in a previous continuation of this block
  4464   // rather than this block?  Might we need to pull it back so we don't
  4465   // report ourselves complete?
  4466   // FIXME: Maybe we should just pull all of them back?
  4467   nsPresContext* presContext = PresContext();
  4468   nsFrameList* ourPushedFloats = GetPushedFloats();
  4469   if (ourPushedFloats) {
  4470     // When we pull back floats, we want to put them with the pushed
  4471     // floats, which must live at the start of our float list, but we
  4472     // want them at the end of those pushed floats.
  4473     // FIXME: This isn't quite right!  What if they're all pushed floats?
  4474     nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
  4475     for (nsIFrame* f = mFloats.FirstChild();
  4476          f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
  4477          f = f->GetNextSibling()) {
  4478       insertionPrevSibling = f;
  4481     for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
  4482       next = f->GetPrevSibling();
  4484       if (f->GetPrevContinuation()) {
  4485         // FIXME
  4486       } else {
  4487         nsPlaceholderFrame *placeholder =
  4488           presContext->FrameManager()->GetPlaceholderFrameFor(f);
  4489         nsIFrame *floatOriginalParent = presContext->PresShell()->
  4490           FrameConstructor()->GetFloatContainingBlock(placeholder);
  4491         if (floatOriginalParent != this) {
  4492           // This is a first continuation that was pushed from one of our
  4493           // previous continuations.  Take it out of the pushed floats
  4494           // list and put it in our floats list, before any of our
  4495           // floats, but after other pushed floats.
  4496           ourPushedFloats->RemoveFrame(f);
  4497           mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
  4502     if (ourPushedFloats->IsEmpty()) {
  4503       RemovePushedFloats()->Delete(presContext->PresShell());
  4507   // After our prev-in-flow has completed reflow, it may have a pushed
  4508   // floats list, containing floats that we need to own.  Take these.
  4509   nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
  4510   if (prevBlock) {
  4511     AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats());
  4512     if (list && list->NotEmpty()) {
  4513       mFloats.InsertFrames(this, nullptr, *list);
  4518 nsBlockFrame::FrameLines*
  4519 nsBlockFrame::GetOverflowLines() const
  4521   if (!HasOverflowLines()) {
  4522     return nullptr;
  4524   FrameLines* prop =
  4525     static_cast<FrameLines*>(Properties().Get(OverflowLinesProperty()));
  4526   NS_ASSERTION(prop && !prop->mLines.empty() &&
  4527                prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
  4528                  prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
  4529                "value should always be stored and non-empty when state set");
  4530   return prop;
  4533 nsBlockFrame::FrameLines*
  4534 nsBlockFrame::RemoveOverflowLines()
  4536   if (!HasOverflowLines()) {
  4537     return nullptr;
  4539   FrameLines* prop =
  4540     static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
  4541   NS_ASSERTION(prop && !prop->mLines.empty() &&
  4542                prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() :
  4543                  prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
  4544                "value should always be stored and non-empty when state set");
  4545   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
  4546   return prop;
  4549 void
  4550 nsBlockFrame::DestroyOverflowLines()
  4552   NS_ASSERTION(HasOverflowLines(), "huh?");
  4553   FrameLines* prop =
  4554     static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
  4555   NS_ASSERTION(prop && prop->mLines.empty(),
  4556                "value should always be stored but empty when destroying");
  4557   RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
  4558   delete prop;
  4561 // This takes ownership of aOverflowLines.
  4562 // XXX We should allocate overflowLines from presShell arena!
  4563 void
  4564 nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines)
  4566   NS_ASSERTION(aOverflowLines, "null lines");
  4567   NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
  4568   NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
  4569                aOverflowLines->mFrames.FirstChild(),
  4570                "invalid overflow lines / frames");
  4571   NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
  4572                "Overwriting existing overflow lines");
  4574   FrameProperties props = Properties();
  4575   // Verify that we won't overwrite an existing overflow list
  4576   NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
  4577   props.Set(OverflowLinesProperty(), aOverflowLines);
  4578   AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
  4581 nsFrameList*
  4582 nsBlockFrame::GetOverflowOutOfFlows() const
  4584   if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
  4585     return nullptr;
  4587   nsFrameList* result =
  4588     GetPropTableFrames(OverflowOutOfFlowsProperty());
  4589   NS_ASSERTION(result, "value should always be non-empty when state set");
  4590   return result;
  4593 // This takes ownership of the frames
  4594 void
  4595 nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
  4596                                     nsFrameList* aPropValue)
  4598   NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
  4599                   !!aPropValue, "state does not match value");
  4601   if (aList.IsEmpty()) {
  4602     if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
  4603       return;
  4605     nsFrameList* list = RemovePropTableFrames(OverflowOutOfFlowsProperty());
  4606     NS_ASSERTION(aPropValue == list, "prop value mismatch");
  4607     list->Clear();
  4608     list->Delete(PresContext()->PresShell());
  4609     RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
  4611   else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
  4612     NS_ASSERTION(aPropValue == GetPropTableFrames(OverflowOutOfFlowsProperty()),
  4613                  "prop value mismatch");
  4614     *aPropValue = aList;
  4616   else {
  4617     SetPropTableFrames(new (PresContext()->PresShell()) nsFrameList(aList),
  4618                        OverflowOutOfFlowsProperty());
  4619     AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
  4623 nsBulletFrame*
  4624 nsBlockFrame::GetInsideBullet() const
  4626   if (!HasInsideBullet()) {
  4627     return nullptr;
  4629   NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
  4630   nsBulletFrame* frame =
  4631     static_cast<nsBulletFrame*>(Properties().Get(InsideBulletProperty()));
  4632   NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame,
  4633                "bogus inside bullet frame");
  4634   return frame;
  4637 nsBulletFrame*
  4638 nsBlockFrame::GetOutsideBullet() const
  4640   nsFrameList* list = GetOutsideBulletList();
  4641   return list ? static_cast<nsBulletFrame*>(list->FirstChild())
  4642               : nullptr;
  4645 nsFrameList*
  4646 nsBlockFrame::GetOutsideBulletList() const
  4648   if (!HasOutsideBullet()) {
  4649     return nullptr;
  4651   NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
  4652   nsFrameList* list =
  4653     static_cast<nsFrameList*>(Properties().Get(OutsideBulletProperty()));
  4654   NS_ASSERTION(list && list->GetLength() == 1 &&
  4655                list->FirstChild()->GetType() == nsGkAtoms::bulletFrame,
  4656                "bogus outside bullet list");
  4657   return list;
  4660 nsFrameList*
  4661 nsBlockFrame::GetPushedFloats() const
  4663   if (!HasPushedFloats()) {
  4664     return nullptr;
  4666   nsFrameList* result =
  4667     static_cast<nsFrameList*>(Properties().Get(PushedFloatProperty()));
  4668   NS_ASSERTION(result, "value should always be non-empty when state set");
  4669   return result;
  4672 nsFrameList*
  4673 nsBlockFrame::EnsurePushedFloats()
  4675   nsFrameList *result = GetPushedFloats();
  4676   if (result)
  4677     return result;
  4679   result = new (PresContext()->PresShell()) nsFrameList;
  4680   Properties().Set(PushedFloatProperty(), result);
  4681   AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
  4683   return result;
  4686 nsFrameList*
  4687 nsBlockFrame::RemovePushedFloats()
  4689   if (!HasPushedFloats()) {
  4690     return nullptr;
  4692   nsFrameList *result =
  4693     static_cast<nsFrameList*>(Properties().Remove(PushedFloatProperty()));
  4694   RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
  4695   NS_ASSERTION(result, "value should always be non-empty when state set");
  4696   return result;
  4699 //////////////////////////////////////////////////////////////////////
  4700 // Frame list manipulation routines
  4702 nsresult
  4703 nsBlockFrame::AppendFrames(ChildListID  aListID,
  4704                            nsFrameList& aFrameList)
  4706   if (aFrameList.IsEmpty()) {
  4707     return NS_OK;
  4709   if (aListID != kPrincipalList) {
  4710     if (kAbsoluteList == aListID) {
  4711       return nsContainerFrame::AppendFrames(aListID, aFrameList);
  4713     else if (kFloatList == aListID) {
  4714       mFloats.AppendFrames(nullptr, aFrameList);
  4715       return NS_OK;
  4717     else {
  4718       NS_ERROR("unexpected child list");
  4719       return NS_ERROR_INVALID_ARG;
  4723   // Find the proper last-child for where the append should go
  4724   nsIFrame* lastKid = mFrames.LastChild();
  4725   NS_ASSERTION((mLines.empty() ? nullptr : mLines.back()->LastChild()) ==
  4726                lastKid, "out-of-sync mLines / mFrames");
  4728   // Add frames after the last child
  4729 #ifdef NOISY_REFLOW_REASON
  4730   ListTag(stdout);
  4731   printf(": append ");
  4732   nsFrame::ListTag(stdout, aFrameList);
  4733   if (lastKid) {
  4734     printf(" after ");
  4735     nsFrame::ListTag(stdout, lastKid);
  4737   printf("\n");
  4738 #endif
  4740   AddFrames(aFrameList, lastKid);
  4741   PresContext()->PresShell()->
  4742     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  4743                      NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
  4744   return NS_OK;
  4747 nsresult
  4748 nsBlockFrame::InsertFrames(ChildListID aListID,
  4749                            nsIFrame* aPrevFrame,
  4750                            nsFrameList& aFrameList)
  4752   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
  4753                "inserting after sibling frame with different parent");
  4755   if (aListID != kPrincipalList) {
  4756     if (kAbsoluteList == aListID) {
  4757       return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
  4759     else if (kFloatList == aListID) {
  4760       mFloats.InsertFrames(this, aPrevFrame, aFrameList);
  4761       return NS_OK;
  4763     else if (kNoReflowPrincipalList != aListID) {
  4764       NS_ERROR("unexpected child list");
  4765       return NS_ERROR_INVALID_ARG;
  4769 #ifdef NOISY_REFLOW_REASON
  4770   ListTag(stdout);
  4771   printf(": insert ");
  4772   nsFrame::ListTag(stdout, aFrameList);
  4773   if (aPrevFrame) {
  4774     printf(" after ");
  4775     nsFrame::ListTag(stdout, aPrevFrame);
  4777   printf("\n");
  4778 #endif
  4780   AddFrames(aFrameList, aPrevFrame);
  4782   if (aListID != kNoReflowPrincipalList)
  4783     PresContext()->PresShell()->
  4784       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  4785                        NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
  4786   return NS_OK;
  4789 static bool
  4790 ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
  4792   nsIAtom* type = aLastFrame->GetType();
  4793   if (type == nsGkAtoms::brFrame) {
  4794     return true;
  4796   // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910.
  4797   if (type == nsGkAtoms::textFrame &&
  4798       !(aLastFrame->GetStateBits() & TEXT_OFFSETS_NEED_FIXING)) {
  4799     return aLastFrame->HasSignificantTerminalNewline();
  4801   return false;
  4804 void
  4805 nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
  4807   // Clear our line cursor, since our lines may change.
  4808   ClearLineCursor();
  4810   if (aFrameList.IsEmpty()) {
  4811     return;
  4814   // If we're inserting at the beginning of our list and we have an
  4815   // inside bullet, insert after that bullet.
  4816   if (!aPrevSibling && HasInsideBullet()) {
  4817     aPrevSibling = GetInsideBullet();
  4820   // Attempt to find the line that contains the previous sibling
  4821   FrameLines* overflowLines;
  4822   nsLineList* lineList = &mLines;
  4823   nsLineList::iterator prevSibLine = lineList->end();
  4824   int32_t prevSiblingIndex = -1;
  4825   if (aPrevSibling) {
  4826     // XXX_perf This is technically O(N^2) in some cases, but by using
  4827     // RFind instead of Find, we make it O(N) in the most common case,
  4828     // which is appending content.
  4830     // Find the line that contains the previous sibling
  4831     if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
  4832                                         prevSibLine, mFrames.LastChild(),
  4833                                         &prevSiblingIndex)) {
  4834       // Not in mLines - try overflow lines.
  4835       overflowLines = GetOverflowLines();
  4836       lineList = overflowLines ? &overflowLines->mLines : nullptr;
  4837       if (overflowLines) {
  4838         prevSibLine = overflowLines->mLines.end();
  4839         prevSiblingIndex = -1;
  4840         if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
  4841                                             prevSibLine,
  4842                                             overflowLines->mFrames.LastChild(),
  4843                                             &prevSiblingIndex)) {
  4844           lineList = nullptr;
  4847       if (!lineList) {
  4848         // Note: defensive code! RFindLineContaining must not return
  4849         // false in this case, so if it does...
  4850         NS_NOTREACHED("prev sibling not in line list");
  4851         lineList = &mLines;
  4852         aPrevSibling = nullptr;
  4853         prevSibLine = lineList->end();
  4858   // Find the frame following aPrevSibling so that we can join up the
  4859   // two lists of frames.
  4860   if (aPrevSibling) {
  4861     // Split line containing aPrevSibling in two if the insertion
  4862     // point is somewhere in the middle of the line.
  4863     int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
  4864     if (rem) {
  4865       // Split the line in two where the frame(s) are being inserted.
  4866       nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
  4867       lineList->after_insert(prevSibLine, line);
  4868       // Mark prevSibLine dirty and as needing textrun invalidation, since
  4869       // we may be breaking up text in the line. Its previous line may also
  4870       // need to be invalidated because it may be able to pull some text up.
  4871       MarkLineDirty(prevSibLine, lineList);
  4872       // The new line will also need its textruns recomputed because of the
  4873       // frame changes.
  4874       line->MarkDirty();
  4875       line->SetInvalidateTextRuns(true);
  4878   else if (! lineList->empty()) {
  4879     lineList->front()->MarkDirty();
  4880     lineList->front()->SetInvalidateTextRuns(true);
  4882   nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames;
  4883   const nsFrameList::Slice& newFrames =
  4884     frames.InsertFrames(nullptr, aPrevSibling, aFrameList);
  4886   // Walk through the new frames being added and update the line data
  4887   // structures to fit.
  4888   for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
  4889     nsIFrame* newFrame = e.get();
  4890     NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
  4891                  "Unexpected aPrevSibling");
  4892     NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
  4893                  (!newFrame->IsAbsolutelyPositioned() &&
  4894                   !newFrame->IsFloating()),
  4895                  "Placeholders should not float or be positioned");
  4897     bool isBlock = newFrame->IsBlockOutside();
  4899     // If the frame is a block frame, or if there is no previous line or if the
  4900     // previous line is a block line we need to make a new line.  We also make
  4901     // a new line, as an optimization, in the two cases we know we'll need it:
  4902     // if the previous line ended with a <br>, or if it has significant whitespace
  4903     // and ended in a newline.
  4904     if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
  4905         (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
  4906       // Create a new line for the frame and add its line to the line
  4907       // list.
  4908       nsLineBox* line = NewLineBox(newFrame, isBlock);
  4909       if (prevSibLine != lineList->end()) {
  4910         // Append new line after prevSibLine
  4911         lineList->after_insert(prevSibLine, line);
  4912         ++prevSibLine;
  4914       else {
  4915         // New line is going before the other lines
  4916         lineList->push_front(line);
  4917         prevSibLine = lineList->begin();
  4920     else {
  4921       prevSibLine->NoteFrameAdded(newFrame);
  4922       // We're adding inline content to prevSibLine, so we need to mark it
  4923       // dirty, ensure its textruns are recomputed, and possibly do the same
  4924       // to its previous line since that line may be able to pull content up.
  4925       MarkLineDirty(prevSibLine, lineList);
  4928     aPrevSibling = newFrame;
  4931 #ifdef DEBUG
  4932   MOZ_ASSERT(aFrameList.IsEmpty());
  4933   VerifyLines(true);
  4934 #endif
  4937 void
  4938 nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat)
  4940   // Find which line contains the float, so we can update
  4941   // the float cache.
  4942   line_iterator line = begin_lines(), line_end = end_lines();
  4943   for ( ; line != line_end; ++line) {
  4944     if (line->IsInline() && line->RemoveFloat(aFloat)) {
  4945       break;
  4950 void
  4951 nsBlockFrame::RemoveFloat(nsIFrame* aFloat)
  4953 #ifdef DEBUG
  4954   // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows
  4955   // frame list properties.
  4956   if (!mFloats.ContainsFrame(aFloat)) {
  4957     MOZ_ASSERT((GetOverflowOutOfFlows() &&
  4958                 GetOverflowOutOfFlows()->ContainsFrame(aFloat)) ||
  4959                (GetPushedFloats() &&
  4960                 GetPushedFloats()->ContainsFrame(aFloat)),
  4961                "aFloat is not our child or on an unexpected frame list");
  4963 #endif
  4965   if (mFloats.StartRemoveFrame(aFloat)) {
  4966     return;
  4969   nsFrameList* list = GetPushedFloats();
  4970   if (list && list->ContinueRemoveFrame(aFloat)) {
  4971 #if 0
  4972     // XXXmats not yet - need to investigate nsBlockReflowState::mPushedFloats
  4973     // first so we don't leave it pointing to a deleted list.
  4974     if (list->IsEmpty()) {
  4975       delete RemovePushedFloats();
  4977 #endif
  4978     return;
  4982     nsAutoOOFFrameList oofs(this);
  4983     if (oofs.mList.ContinueRemoveFrame(aFloat)) {
  4984       return;
  4989 static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
  4991   nsBlockFrame* blockWithFloatMgr = aBlock;
  4992   while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) {
  4993     nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent());
  4994     if (!bf) {
  4995       break;
  4997     blockWithFloatMgr = bf;
  5000   // Mark every line at and below the line where the float was
  5001   // dirty, and mark their lines dirty too. We could probably do
  5002   // something more efficient --- e.g., just dirty the lines that intersect
  5003   // the float vertically.
  5004   MarkAllDescendantLinesDirty(blockWithFloatMgr);
  5007 /**
  5008  * Returns true if aFrame is a block that has one or more float children.
  5009  */
  5010 static bool BlockHasAnyFloats(nsIFrame* aFrame)
  5012   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
  5013   if (!block)
  5014     return false;
  5015   if (block->GetFirstChild(nsIFrame::kFloatList))
  5016     return true;
  5018   nsLineList::iterator line = block->begin_lines();
  5019   nsLineList::iterator endLine = block->end_lines();
  5020   while (line != endLine) {
  5021     if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
  5022       return true;
  5023     ++line;
  5025   return false;
  5028 nsresult
  5029 nsBlockFrame::RemoveFrame(ChildListID aListID,
  5030                           nsIFrame* aOldFrame)
  5032   nsresult rv = NS_OK;
  5034 #ifdef NOISY_REFLOW_REASON
  5035   ListTag(stdout);
  5036   printf(": remove ");
  5037   nsFrame::ListTag(stdout, aOldFrame);
  5038   printf("\n");
  5039 #endif
  5041   if (aListID == kPrincipalList) {
  5042     bool hasFloats = BlockHasAnyFloats(aOldFrame);
  5043     rv = DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
  5044     if (hasFloats) {
  5045       MarkSameFloatManagerLinesDirty(this);
  5048   else if (kAbsoluteList == aListID) {
  5049     nsContainerFrame::RemoveFrame(aListID, aOldFrame);
  5050     return NS_OK;
  5052   else if (kFloatList == aListID) {
  5053     // Make sure to mark affected lines dirty for the float frame
  5054     // we are removing; this way is a bit messy, but so is the rest of the code.
  5055     // See bug 390762.
  5056     NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
  5057                  "RemoveFrame should not be called on pushed floats.");
  5058     for (nsIFrame* f = aOldFrame;
  5059          f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
  5060          f = f->GetNextContinuation()) {
  5061       MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
  5063     DoRemoveOutOfFlowFrame(aOldFrame);
  5065   else if (kNoReflowPrincipalList == aListID) {
  5066     // Skip the call to |FrameNeedsReflow| below by returning now.
  5067     return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
  5069   else {
  5070     NS_ERROR("unexpected child list");
  5071     rv = NS_ERROR_INVALID_ARG;
  5074   if (NS_SUCCEEDED(rv)) {
  5075     PresContext()->PresShell()->
  5076       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  5077                        NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
  5079   return rv;
  5082 void
  5083 nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
  5085   // The containing block is always the parent of aFrame.
  5086   nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
  5088   // Remove aFrame from the appropriate list.
  5089   if (aFrame->IsAbsolutelyPositioned()) {
  5090     // This also deletes the next-in-flows
  5091     block->GetAbsoluteContainingBlock()->RemoveFrame(block,
  5092                                                      kAbsoluteList,
  5093                                                      aFrame);
  5095   else {
  5096     // First remove aFrame's next-in-flows.
  5097     nsIFrame* nif = aFrame->GetNextInFlow();
  5098     if (nif) {
  5099       static_cast<nsContainerFrame*>(nif->GetParent())
  5100         ->DeleteNextInFlowChild(nif, false);
  5102     // Now remove aFrame from its child list and Destroy it.
  5103     block->RemoveFloatFromFloatCache(aFrame);
  5104     block->RemoveFloat(aFrame);
  5105     aFrame->Destroy();
  5109 /**
  5110  * This helps us iterate over the list of all normal + overflow lines
  5111  */
  5112 void
  5113 nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
  5114                           nsLineList::iterator* aStartIterator,
  5115                           nsLineList::iterator* aEndIterator,
  5116                           bool* aInOverflowLines,
  5117                           FrameLines** aOverflowLines)
  5119   if (*aIterator == *aEndIterator) {
  5120     if (!*aInOverflowLines) {
  5121       // Try the overflow lines
  5122       *aInOverflowLines = true;
  5123       FrameLines* lines = GetOverflowLines();
  5124       if (lines) {
  5125         *aStartIterator = lines->mLines.begin();
  5126         *aIterator = *aStartIterator;
  5127         *aEndIterator = lines->mLines.end();
  5128         *aOverflowLines = lines;
  5134 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
  5135     line_iterator aLine)
  5136   : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines)
  5138   // This will assert if aLine isn't in mLines of aFrame:
  5139   DebugOnly<bool> check = aLine == mFrame->begin_lines();
  5142 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
  5143     line_iterator aLine, bool aInOverflow)
  5144   : mFrame(aFrame), mLine(aLine),
  5145     mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines
  5146                           : &aFrame->mLines)
  5150 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
  5151     bool* aFoundValidLine)
  5152   : mFrame(aFrame), mLineList(&aFrame->mLines)
  5154   mLine = aFrame->begin_lines();
  5155   *aFoundValidLine = FindValidLine();
  5158 static nsIFrame*
  5159 FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
  5161   NS_ASSERTION(aFrame, "must have frame");
  5162   nsIFrame* child;
  5163   while (true) {
  5164     nsIFrame* block = aFrame;
  5165     do {
  5166       child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
  5167       if (child)
  5168         break;
  5169       block = block->GetNextContinuation();
  5170     } while (block);
  5171     if (!child)
  5172       return nullptr;
  5173     if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
  5174       break;
  5175     aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child);
  5178   return child;
  5181 nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
  5182     nsIFrame* aFindFrame, bool* aFoundValidLine)
  5183   : mFrame(aFrame), mLineList(&aFrame->mLines)
  5185   *aFoundValidLine = false;
  5187   nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
  5188   if (!child)
  5189     return;
  5191   // Try to use the cursor if it exists, otherwise fall back to the first line
  5192   nsLineBox* cursor = aFrame->GetLineCursor();
  5193   if (!cursor) {
  5194     line_iterator iter = aFrame->begin_lines();
  5195     if (iter != aFrame->end_lines()) {
  5196       cursor = iter;
  5200   if (cursor) {
  5201     // Perform a simultaneous forward and reverse search starting from the
  5202     // line cursor.
  5203     nsBlockFrame::line_iterator line = aFrame->line(cursor);
  5204     nsBlockFrame::reverse_line_iterator rline = aFrame->rline(cursor);
  5205     nsBlockFrame::line_iterator line_end = aFrame->end_lines();
  5206     nsBlockFrame::reverse_line_iterator rline_end = aFrame->rend_lines();
  5207     // rline is positioned on the line containing 'cursor', so it's not
  5208     // rline_end. So we can safely increment it (i.e. move it to one line
  5209     // earlier) to start searching there.
  5210     ++rline;
  5211     while (line != line_end || rline != rline_end) {
  5212       if (line != line_end) {
  5213         if (line->Contains(child)) {
  5214           *aFoundValidLine = true;
  5215           mLine = line;
  5216           return;
  5218         ++line;
  5220       if (rline != rline_end) {
  5221         if (rline->Contains(child)) {
  5222           *aFoundValidLine = true;
  5223           mLine = rline;
  5224           return;
  5226         ++rline;
  5229     // Didn't find the line
  5232   // If we reach here, it means that we have not been able to find the
  5233   // desired frame in our in-flow lines.  So we should start looking at
  5234   // our overflow lines. In order to do that, we set mLine to the end
  5235   // iterator so that FindValidLine starts to look at overflow lines,
  5236   // if any.
  5238   mLine = aFrame->end_lines();
  5240   if (!FindValidLine())
  5241     return;
  5243   do {
  5244     if (mLine->Contains(child)) {
  5245       *aFoundValidLine = true;
  5246       return;
  5248   } while (Next());
  5251 nsBlockFrame::line_iterator
  5252 nsBlockInFlowLineIterator::End()
  5254   return mLineList->end();
  5257 bool
  5258 nsBlockInFlowLineIterator::IsLastLineInList()
  5260   line_iterator end = End();
  5261   return mLine != end && mLine.next() == end;
  5264 bool
  5265 nsBlockInFlowLineIterator::Next()
  5267   ++mLine;
  5268   return FindValidLine();
  5271 bool
  5272 nsBlockInFlowLineIterator::Prev()
  5274   line_iterator begin = mLineList->begin();
  5275   if (mLine != begin) {
  5276     --mLine;
  5277     return true;
  5279   bool currentlyInOverflowLines = GetInOverflow();
  5280   while (true) {
  5281     if (currentlyInOverflowLines) {
  5282       mLineList = &mFrame->mLines;
  5283       mLine = mLineList->end();
  5284       if (mLine != mLineList->begin()) {
  5285         --mLine;
  5286         return true;
  5288     } else {
  5289       mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
  5290       if (!mFrame)
  5291         return false;
  5292       nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
  5293       if (overflowLines) {
  5294         mLineList = &overflowLines->mLines;
  5295         mLine = mLineList->end();
  5296         NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?");
  5297         --mLine;
  5298         return true;
  5301     currentlyInOverflowLines = !currentlyInOverflowLines;
  5305 bool
  5306 nsBlockInFlowLineIterator::FindValidLine()
  5308   line_iterator end = mLineList->end();
  5309   if (mLine != end)
  5310     return true;
  5311   bool currentlyInOverflowLines = GetInOverflow();
  5312   while (true) {
  5313     if (currentlyInOverflowLines) {
  5314       mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
  5315       if (!mFrame)
  5316         return false;
  5317       mLineList = &mFrame->mLines;
  5318       mLine = mLineList->begin();
  5319       if (mLine != mLineList->end())
  5320         return true;
  5321     } else {
  5322       nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
  5323       if (overflowLines) {
  5324         mLineList = &overflowLines->mLines;
  5325         mLine = mLineList->begin();
  5326         NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?");
  5327         return true;
  5330     currentlyInOverflowLines = !currentlyInOverflowLines;
  5334 static nsresult RemoveBlockChild(nsIFrame* aFrame,
  5335                                  bool      aRemoveOnlyFluidContinuations)
  5337   if (!aFrame)
  5338     return NS_OK;
  5340   nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent());
  5341   NS_ASSERTION(nextBlock,
  5342                "Our child's continuation's parent is not a block?");
  5343   return nextBlock->DoRemoveFrame(aFrame,
  5344       (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS));
  5347 // This function removes aDeletedFrame and all its continuations.  It
  5348 // is optimized for deleting a whole series of frames. The easy
  5349 // implementation would invoke itself recursively on
  5350 // aDeletedFrame->GetNextContinuation, then locate the line containing
  5351 // aDeletedFrame and remove aDeletedFrame from that line. But here we
  5352 // start by locating aDeletedFrame and then scanning from that point
  5353 // on looking for continuations.
  5354 nsresult
  5355 nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags)
  5357   // Clear our line cursor, since our lines may change.
  5358   ClearLineCursor();
  5360   if (aDeletedFrame->GetStateBits() &
  5361       (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
  5362     if (!aDeletedFrame->GetPrevInFlow()) {
  5363       NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
  5364                    "Expected out-of-flow frame");
  5365       DoRemoveOutOfFlowFrame(aDeletedFrame);
  5367     else {
  5368       nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame,
  5369                                               (aFlags & FRAMES_ARE_EMPTY) != 0);
  5371     return NS_OK;
  5374   // Find the line that contains deletedFrame
  5375   nsLineList::iterator line_start = mLines.begin(),
  5376                        line_end = mLines.end();
  5377   nsLineList::iterator line = line_start;
  5378   FrameLines* overflowLines = nullptr;
  5379   bool searchingOverflowList = false;
  5380   // Make sure we look in the overflow lines even if the normal line
  5381   // list is empty
  5382   TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
  5383               &overflowLines);
  5384   while (line != line_end) {
  5385     if (line->Contains(aDeletedFrame)) {
  5386       break;
  5388     ++line;
  5389     TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
  5390                 &overflowLines);
  5393   if (line == line_end) {
  5394     NS_ERROR("can't find deleted frame in lines");
  5395     return NS_ERROR_FAILURE;
  5398   if (!(aFlags & FRAMES_ARE_EMPTY)) {
  5399     if (line != line_start) {
  5400       line.prev()->MarkDirty();
  5401       line.prev()->SetInvalidateTextRuns(true);
  5403     else if (searchingOverflowList && !mLines.empty()) {
  5404       mLines.back()->MarkDirty();
  5405       mLines.back()->SetInvalidateTextRuns(true);
  5409   while (line != line_end && aDeletedFrame) {
  5410     NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
  5411     NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
  5413     if (!(aFlags & FRAMES_ARE_EMPTY)) {
  5414       line->MarkDirty();
  5415       line->SetInvalidateTextRuns(true);
  5418     // If the frame being deleted is the last one on the line then
  5419     // optimize away the line->Contains(next-in-flow) call below.
  5420     bool isLastFrameOnLine = 1 == line->GetChildCount();
  5421     if (!isLastFrameOnLine) {
  5422       line_iterator next = line.next();
  5423       nsIFrame* lastFrame = next != line_end ?
  5424         next->mFirstChild->GetPrevSibling() :
  5425         (searchingOverflowList ? overflowLines->mFrames.LastChild() : 
  5426                                  mFrames.LastChild());
  5427       NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
  5428                    "unexpected line frames");
  5429       isLastFrameOnLine = lastFrame == aDeletedFrame;
  5432     // Remove aDeletedFrame from the line
  5433     if (line->mFirstChild == aDeletedFrame) {
  5434       // We should be setting this to null if aDeletedFrame
  5435       // is the only frame on the line. HOWEVER in that case
  5436       // we will be removing the line anyway, see below.
  5437       line->mFirstChild = aDeletedFrame->GetNextSibling();
  5440     // Hmm, this won't do anything if we're removing a frame in the first
  5441     // overflow line... Hopefully doesn't matter
  5442     --line;
  5443     if (line != line_end && !line->IsBlock()) {
  5444       // Since we just removed a frame that follows some inline
  5445       // frames, we need to reflow the previous line.
  5446       line->MarkDirty();
  5448     ++line;
  5450     // Take aDeletedFrame out of the sibling list. Note that
  5451     // prevSibling will only be nullptr when we are deleting the very
  5452     // first frame in the main or overflow list.
  5453     if (searchingOverflowList) {
  5454       overflowLines->mFrames.RemoveFrame(aDeletedFrame);
  5455     } else {
  5456       mFrames.RemoveFrame(aDeletedFrame);
  5459     // Update the child count of the line to be accurate
  5460     line->NoteFrameRemoved(aDeletedFrame);
  5462     // Destroy frame; capture its next continuation first in case we need
  5463     // to destroy that too.
  5464     nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ?
  5465         aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow();
  5466 #ifdef NOISY_REMOVE_FRAME
  5467     printf("DoRemoveFrame: %s line=%p frame=",
  5468            searchingOverflowList?"overflow":"normal", line.get());
  5469     nsFrame::ListTag(stdout, aDeletedFrame);
  5470     printf(" prevSibling=%p deletedNextContinuation=%p\n",
  5471            aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
  5472 #endif
  5474     // If next-in-flow is an overflow container, must remove it first.
  5475     if (deletedNextContinuation &&
  5476         deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
  5477       static_cast<nsContainerFrame*>(deletedNextContinuation->GetParent())
  5478         ->DeleteNextInFlowChild(deletedNextContinuation, false);
  5479       deletedNextContinuation = nullptr;
  5482     aDeletedFrame->Destroy();
  5483     aDeletedFrame = deletedNextContinuation;
  5485     bool haveAdvancedToNextLine = false;
  5486     // If line is empty, remove it now.
  5487     if (0 == line->GetChildCount()) {
  5488 #ifdef NOISY_REMOVE_FRAME
  5489         printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
  5490                searchingOverflowList?"overflow":"normal", line.get());
  5491 #endif
  5492       nsLineBox *cur = line;
  5493       if (!searchingOverflowList) {
  5494         line = mLines.erase(line);
  5495         // Invalidate the space taken up by the line.
  5496         // XXX We need to do this if we're removing a frame as a result of
  5497         // a call to RemoveFrame(), but we may not need to do this in all
  5498         // cases...
  5499 #ifdef NOISY_BLOCK_INVALIDATE
  5500         nsRect visOverflow(cur->GetVisualOverflowArea());
  5501         printf("%p invalidate 10 (%d, %d, %d, %d)\n",
  5502                this, visOverflow.x, visOverflow.y,
  5503                visOverflow.width, visOverflow.height);
  5504 #endif
  5505       } else {
  5506         line = overflowLines->mLines.erase(line);
  5507         if (overflowLines->mLines.empty()) {
  5508           DestroyOverflowLines();
  5509           overflowLines = nullptr;
  5510           // We just invalidated our iterators.  Since we were in
  5511           // the overflow lines list, which is now empty, set them
  5512           // so we're at the end of the regular line list.
  5513           line_start = mLines.begin();
  5514           line_end = mLines.end();
  5515           line = line_end;
  5518       FreeLineBox(cur);
  5520       // If we're removing a line, ReflowDirtyLines isn't going to
  5521       // know that it needs to slide lines unless something is marked
  5522       // dirty.  So mark the previous margin of the next line dirty if
  5523       // there is one.
  5524       if (line != line_end) {
  5525         line->MarkPreviousMarginDirty();
  5527       haveAdvancedToNextLine = true;
  5528     } else {
  5529       // Make the line that just lost a frame dirty, and advance to
  5530       // the next line.
  5531       if (!deletedNextContinuation || isLastFrameOnLine ||
  5532           !line->Contains(deletedNextContinuation)) {
  5533         line->MarkDirty();
  5534         ++line;
  5535         haveAdvancedToNextLine = true;
  5539     if (deletedNextContinuation) {
  5540       // See if we should keep looking in the current flow's line list.
  5541       if (deletedNextContinuation->GetParent() != this) {
  5542         // The deceased frames continuation is not a child of the
  5543         // current block. So break out of the loop so that we advance
  5544         // to the next parent.
  5545         //
  5546         // If we have a continuation in a different block then all bets are
  5547         // off regarding whether we are deleting frames without actual content,
  5548         // so don't propagate FRAMES_ARE_EMPTY any further.
  5549         aFlags &= ~FRAMES_ARE_EMPTY;
  5550         break;
  5553       // If we advanced to the next line then check if we should switch to the
  5554       // overflow line list.
  5555       if (haveAdvancedToNextLine) {
  5556         if (line != line_end && !searchingOverflowList &&
  5557             !line->Contains(deletedNextContinuation)) {
  5558           // We have advanced to the next *normal* line but the next-in-flow
  5559           // is not there - force a switch to the overflow line list.
  5560           line = line_end;
  5563         TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
  5564                     &overflowLines);
  5565 #ifdef NOISY_REMOVE_FRAME
  5566         printf("DoRemoveFrame: now on %s line=%p\n",
  5567                searchingOverflowList?"overflow":"normal", line.get());
  5568 #endif
  5573   if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
  5574     line.next()->MarkDirty();
  5575     line.next()->SetInvalidateTextRuns(true);
  5578 #ifdef DEBUG
  5579   VerifyLines(true);
  5580   VerifyOverflowSituation();
  5581 #endif
  5583   // Advance to next flow block if the frame has more continuations
  5584   return RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS));
  5587 static bool
  5588 FindBlockLineFor(nsIFrame*             aChild,
  5589                  nsLineList::iterator  aBegin,
  5590                  nsLineList::iterator  aEnd,
  5591                  nsLineList::iterator* aResult)
  5593   MOZ_ASSERT(aChild->IsBlockOutside());
  5594   for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
  5595     MOZ_ASSERT(line->GetChildCount() > 0);
  5596     if (line->IsBlock() && line->mFirstChild == aChild) {
  5597       MOZ_ASSERT(line->GetChildCount() == 1);
  5598       *aResult = line;
  5599       return true;
  5602   return false;
  5605 static bool
  5606 FindInlineLineFor(nsIFrame*             aChild,
  5607                   const nsFrameList&    aFrameList,
  5608                   nsLineList::iterator  aBegin,
  5609                   nsLineList::iterator  aEnd,
  5610                   nsLineList::iterator* aResult)
  5612   MOZ_ASSERT(!aChild->IsBlockOutside());
  5613   for (nsLineList::iterator line = aBegin; line != aEnd; ++line) {
  5614     MOZ_ASSERT(line->GetChildCount() > 0);
  5615     if (!line->IsBlock()) {
  5616       // Optimize by comparing the line's last child first.
  5617       nsLineList::iterator next = line.next();
  5618       if (aChild == (next == aEnd ? aFrameList.LastChild()
  5619                                   : next->mFirstChild->GetPrevSibling()) ||
  5620           line->Contains(aChild)) {
  5621         *aResult = line;
  5622         return true;
  5626   return false;
  5629 static bool
  5630 FindLineFor(nsIFrame*             aChild,
  5631             const nsFrameList&    aFrameList,
  5632             nsLineList::iterator  aBegin,
  5633             nsLineList::iterator  aEnd,
  5634             nsLineList::iterator* aResult)
  5636   return aChild->IsBlockOutside() ?
  5637     FindBlockLineFor(aChild, aBegin, aEnd, aResult) :
  5638     FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult);
  5641 nsresult
  5642 nsBlockFrame::StealFrame(nsIFrame* aChild,
  5643                          bool      aForceNormal)
  5645   MOZ_ASSERT(aChild->GetParent() == this);
  5647   if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
  5648       aChild->IsFloating()) {
  5649     RemoveFloat(aChild);
  5650     return NS_OK;
  5653   if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
  5654       && !aForceNormal) {
  5655     return nsContainerFrame::StealFrame(aChild);
  5658   MOZ_ASSERT(!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
  5660   nsLineList::iterator line;
  5661   if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) {
  5662     RemoveFrameFromLine(aChild, line, mFrames, mLines);
  5663   } else {
  5664     FrameLines* overflowLines = GetOverflowLines();
  5665     DebugOnly<bool> found;
  5666     found = FindLineFor(aChild, overflowLines->mFrames,
  5667                         overflowLines->mLines.begin(),
  5668                         overflowLines->mLines.end(), &line);
  5669     MOZ_ASSERT(found);
  5670     RemoveFrameFromLine(aChild, line, overflowLines->mFrames,
  5671                         overflowLines->mLines);
  5672     if (overflowLines->mLines.empty()) {
  5673       DestroyOverflowLines();
  5677   return NS_OK;
  5680 void
  5681 nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine,
  5682                                   nsFrameList& aFrameList, nsLineList& aLineList)
  5684   aFrameList.RemoveFrame(aChild);
  5685   if (aChild == aLine->mFirstChild) {
  5686     aLine->mFirstChild = aChild->GetNextSibling();
  5688   aLine->NoteFrameRemoved(aChild);
  5689   if (aLine->GetChildCount() > 0) {
  5690     aLine->MarkDirty();
  5691   } else {
  5692     // The line became empty - destroy it.
  5693     nsLineBox* lineBox = aLine;
  5694     aLine = aLineList.erase(aLine);
  5695     if (aLine != aLineList.end()) {
  5696       aLine->MarkPreviousMarginDirty();
  5698     FreeLineBox(lineBox);
  5702 void
  5703 nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
  5704                                     bool      aDeletingEmptyFrames)
  5706   NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
  5708   if (aNextInFlow->GetStateBits() &
  5709       (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
  5710     nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames);
  5712   else {
  5713 #ifdef DEBUG
  5714     if (aDeletingEmptyFrames) {
  5715       nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
  5717 #endif
  5718     DoRemoveFrame(aNextInFlow,
  5719         aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
  5723 const nsStyleText*
  5724 nsBlockFrame::StyleTextForLineLayout()
  5726   // Return the pointer to an unmodified style text
  5727   return StyleText();
  5730 ////////////////////////////////////////////////////////////////////////
  5731 // Float support
  5733 nsRect
  5734 nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState,
  5735                                         const nsRect& aFloatAvailableSpace,
  5736                                         nsIFrame* aFloatFrame)
  5738   // Compute the available width. By default, assume the width of the
  5739   // containing block.
  5740   nscoord availWidth;
  5741   const nsStyleDisplay* floatDisplay = aFloatFrame->StyleDisplay();
  5743   if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
  5744       eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
  5745     availWidth = aState.mContentArea.width;
  5747   else {
  5748     // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
  5749     // give tables only the available space
  5750     // if they can shrink we may not be constrained to place
  5751     // them in the next line
  5752     availWidth = aFloatAvailableSpace.width;
  5755   nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height
  5756                         ? NS_UNCONSTRAINEDSIZE
  5757                         : std::max(0, aState.mContentArea.YMost() - aState.mY);
  5759 #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS
  5760   if (availHeight != NS_UNCONSTRAINEDSIZE &&
  5761       nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) {
  5762     // Tell the float it has unrestricted height, so it won't break.
  5763     // If the float doesn't actually fit in the column it will fail to be
  5764     // placed, and either move to the top of the next column or just
  5765     // overflow.
  5766     availHeight = NS_UNCONSTRAINEDSIZE;
  5768 #endif
  5770   return nsRect(aState.mContentArea.x,
  5771                 aState.mContentArea.y,
  5772                 availWidth, availHeight);
  5775 nscoord
  5776 nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState,
  5777                                 const nsRect&       aFloatAvailableSpace,
  5778                                 nsIFrame*           aFloat)
  5780   NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
  5781                   "aFloat must be an out-of-flow frame");
  5782   // Reflow the float.
  5783   nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
  5784                                                 aFloat);
  5786   nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat, 
  5787                             availSpace.Size());
  5788   return floatRS.ComputedWidth() + floatRS.ComputedPhysicalBorderPadding().LeftRight() +
  5789     floatRS.ComputedPhysicalMargin().LeftRight();
  5792 nsresult
  5793 nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
  5794                           const nsRect&       aAdjustedAvailableSpace,
  5795                           nsIFrame*           aFloat,
  5796                           nsMargin&           aFloatMargin,
  5797                           nsMargin&           aFloatOffsets,
  5798                           bool                aFloatPushedDown,
  5799                           nsReflowStatus&     aReflowStatus)
  5801   NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
  5802                   "aFloat must be an out-of-flow frame");
  5803   // Reflow the float.
  5804   aReflowStatus = NS_FRAME_COMPLETE;
  5806 #ifdef NOISY_FLOAT
  5807   printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
  5808           aFloat, this, 
  5809           aFloatAvailableSpace.x, aFloatAvailableSpace.y, 
  5810           aFloatAvailableSpace.width, aFloatAvailableSpace.height
  5811   );
  5812 #endif
  5814   nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
  5815                             nsSize(aAdjustedAvailableSpace.width,
  5816                                    aAdjustedAvailableSpace.height));
  5818   // Normally the mIsTopOfPage state is copied from the parent reflow
  5819   // state.  However, when reflowing a float, if we've placed other
  5820   // floats that force this float *down* or *narrower*, we should unset
  5821   // the mIsTopOfPage state.
  5822   // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
  5823   // variable below, which has the exact same effect.  Perhaps it should
  5824   // be merged into that, except that the test for narrowing here is not
  5825   // about adjacency with the top, so it seems misleading.
  5826   if (floatRS.mFlags.mIsTopOfPage &&
  5827       (aFloatPushedDown ||
  5828        aAdjustedAvailableSpace.width != aState.mContentArea.width)) {
  5829     floatRS.mFlags.mIsTopOfPage = false;
  5832   // Setup a block reflow context to reflow the float.
  5833   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
  5835   // Reflow the float
  5836   bool isAdjacentWithTop = aState.IsAdjacentWithTop();
  5838   nsIFrame* clearanceFrame = nullptr;
  5839   nsresult rv;
  5840   do {
  5841     nsCollapsingMargin margin;
  5842     bool mayNeedRetry = false;
  5843     floatRS.mDiscoveredClearance = nullptr;
  5844     // Only first in flow gets a top margin.
  5845     if (!aFloat->GetPrevInFlow()) {
  5846       nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
  5847                                                       clearanceFrame, &mayNeedRetry);
  5849       if (mayNeedRetry && !clearanceFrame) {
  5850         floatRS.mDiscoveredClearance = &clearanceFrame;
  5851         // We don't need to push the float manager state because the the block has its own
  5852         // float manager that will be destroyed and recreated
  5856     rv = brc.ReflowBlock(aAdjustedAvailableSpace, true, margin,
  5857                          0, isAdjacentWithTop,
  5858                          nullptr, floatRS,
  5859                          aReflowStatus, aState);
  5860   } while (NS_SUCCEEDED(rv) && clearanceFrame);
  5862   if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus) &&
  5863       ShouldAvoidBreakInside(floatRS)) {
  5864     aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
  5865   } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
  5866              (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.height)) {
  5867     // An incomplete reflow status means we should split the float 
  5868     // if the height is constrained (bug 145305). 
  5869     aReflowStatus = NS_FRAME_COMPLETE;
  5872   if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
  5873     aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  5876   if (aFloat->GetType() == nsGkAtoms::letterFrame) {
  5877     // We never split floating first letters; an incomplete state for
  5878     // such frames simply means that there is more content to be
  5879     // reflowed on the line.
  5880     if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) 
  5881       aReflowStatus = NS_FRAME_COMPLETE;
  5884   if (NS_FAILED(rv)) {
  5885     return rv;
  5888   // Capture the margin and offsets information for the caller
  5889   aFloatMargin = floatRS.ComputedPhysicalMargin(); // float margins don't collapse
  5890   aFloatOffsets = floatRS.ComputedPhysicalOffsets();
  5892   const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
  5894   // Set the rect, make sure the view is properly sized and positioned,
  5895   // and tell the frame we're done reflowing it
  5896   // XXXldb This seems like the wrong place to be doing this -- shouldn't
  5897   // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
  5898   // we've positioned the float, and shouldn't we be doing the equivalent
  5899   // of |PlaceFrameView| here?
  5900   aFloat->SetSize(nsSize(metrics.Width(), metrics.Height()));
  5901   if (aFloat->HasView()) {
  5902     nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat,
  5903                                                aFloat->GetView(),
  5904                                                metrics.VisualOverflow(),
  5905                                                NS_FRAME_NO_MOVE_VIEW);
  5907   // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)  
  5908   aFloat->DidReflow(aState.mPresContext, &floatRS,
  5909                     nsDidReflowStatus::FINISHED);
  5911 #ifdef NOISY_FLOAT
  5912   printf("end ReflowFloat %p, sized to %d,%d\n",
  5913          aFloat, metrics.Width(), metrics.Height());
  5914 #endif
  5916   return NS_OK;
  5919 uint8_t
  5920 nsBlockFrame::FindTrailingClear()
  5922   // find the break type of the last line
  5923   for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
  5924     nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
  5925     line_iterator endLine = block->end_lines();
  5926     if (endLine != block->begin_lines()) {
  5927       --endLine;
  5928       return endLine->GetBreakTypeAfter();
  5931   return NS_STYLE_CLEAR_NONE;
  5934 void
  5935 nsBlockFrame::ReflowPushedFloats(nsBlockReflowState& aState,
  5936                                  nsOverflowAreas&    aOverflowAreas,
  5937                                  nsReflowStatus&     aStatus)
  5939   // Pushed floats live at the start of our float list; see comment
  5940   // above nsBlockFrame::DrainPushedFloats.
  5941   for (nsIFrame* f = mFloats.FirstChild(), *next;
  5942        f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
  5943        f = next) {
  5944     // save next sibling now, since reflowing could push the entire
  5945     // float, changing its siblings
  5946     next = f->GetNextSibling();
  5948     // When we push a first-continuation float in a non-initial reflow,
  5949     // it's possible that we end up with two continuations with the same
  5950     // parent.  This happens if, on the previous reflow of the block or
  5951     // a previous reflow of the line containing the block, the float was
  5952     // split between continuations A and B of the parent, but on the
  5953     // current reflow, none of the float can fit in A.
  5954     //
  5955     // When this happens, we might even have the two continuations
  5956     // out-of-order due to the management of the pushed floats.  In
  5957     // particular, if the float's placeholder was in a pushed line that
  5958     // we reflowed before it was pushed, and we split the float during
  5959     // that reflow, we might have the continuation of the float before
  5960     // the float itself.  (In the general case, however, it's correct
  5961     // for floats in the pushed floats list to come before floats
  5962     // anchored in pushed lines; however, in this case it's wrong.  We
  5963     // should probably find a way to fix it somehow, since it leads to
  5964     // incorrect layout in some cases.)
  5965     //
  5966     // When we have these out-of-order continuations, we might hit the
  5967     // next-continuation before the previous-continuation.  When that
  5968     // happens, just push it.  When we reflow the next continuation,
  5969     // we'll either pull all of its content back and destroy it (by
  5970     // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
  5971     // pull it out of its current position and push it again (and
  5972     // potentially repeat this cycle for the next continuation, although
  5973     // hopefully then they'll be in the right order).
  5974     //
  5975     // We should also need this code for the in-order case if the first
  5976     // continuation of a float gets moved across more than one
  5977     // continuation of the containing block.  In this case we'd manage
  5978     // to push the second continuation without this check, but not the
  5979     // third and later.
  5980     nsIFrame *prevContinuation = f->GetPrevContinuation();
  5981     if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
  5982       mFloats.RemoveFrame(f);
  5983       aState.AppendPushedFloat(f);
  5984       continue;
  5987     // Always call FlowAndPlaceFloat; we might need to place this float
  5988     // if didn't belong to this block the last time it was reflowed.
  5989     aState.FlowAndPlaceFloat(f);
  5991     ConsiderChildOverflow(aOverflowAreas, f);
  5994   // If there are continued floats, then we may need to continue BR clearance
  5995   if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_BOTH)) {
  5996     aState.mFloatBreakType = static_cast<nsBlockFrame*>(GetPrevInFlow())
  5997                                ->FindTrailingClear();
  6001 void
  6002 nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager)
  6004   // Recover our own floats
  6005   nsIFrame* stop = nullptr; // Stop before we reach pushed floats that
  6006                            // belong to our next-in-flow
  6007   for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
  6008     nsRect region = nsFloatManager::GetRegionFor(f);
  6009     aFloatManager.AddFloat(f, region);
  6010     if (!stop && f->GetNextInFlow())
  6011       stop = f->GetNextInFlow();
  6014   // Recurse into our overflow container children
  6015   for (nsIFrame* oc = GetFirstChild(kOverflowContainersList);
  6016        oc; oc = oc->GetNextSibling()) {
  6017     RecoverFloatsFor(oc, aFloatManager);
  6020   // Recurse into our normal children
  6021   for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) {
  6022     if (line->IsBlock()) {
  6023       RecoverFloatsFor(line->mFirstChild, aFloatManager);
  6028 void
  6029 nsBlockFrame::RecoverFloatsFor(nsIFrame*       aFrame,
  6030                                nsFloatManager& aFloatManager)
  6032   NS_PRECONDITION(aFrame, "null frame");
  6033   // Only blocks have floats
  6034   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
  6035   // Don't recover any state inside a block that has its own space manager
  6036   // (we don't currently have any blocks like this, though, thanks to our
  6037   // use of extra frames for 'overflow')
  6038   if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
  6039     // If the element is relatively positioned, then adjust x and y
  6040     // accordingly so that we consider relatively positioned frames
  6041     // at their original position.
  6042     nsPoint pos = block->GetNormalPosition();
  6043     aFloatManager.Translate(pos.x, pos.y);
  6044     block->RecoverFloats(aFloatManager);
  6045     aFloatManager.Translate(-pos.x, -pos.y);
  6049 //////////////////////////////////////////////////////////////////////
  6050 // Painting, event handling
  6052 #ifdef DEBUG
  6053 static void ComputeVisualOverflowArea(nsLineList& aLines,
  6054                                       nscoord aWidth, nscoord aHeight,
  6055                                       nsRect& aResult)
  6057   nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
  6058   for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
  6059        line != line_end;
  6060        ++line) {
  6061     // Compute min and max x/y values for the reflowed frame's
  6062     // combined areas
  6063     nsRect visOverflow(line->GetVisualOverflowArea());
  6064     nscoord x = visOverflow.x;
  6065     nscoord y = visOverflow.y;
  6066     nscoord xmost = x + visOverflow.width;
  6067     nscoord ymost = y + visOverflow.height;
  6068     if (x < xa) {
  6069       xa = x;
  6071     if (xmost > xb) {
  6072       xb = xmost;
  6074     if (y < ya) {
  6075       ya = y;
  6077     if (ymost > yb) {
  6078       yb = ymost;
  6082   aResult.x = xa;
  6083   aResult.y = ya;
  6084   aResult.width = xb - xa;
  6085   aResult.height = yb - ya;
  6087 #endif
  6089 bool
  6090 nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
  6092   if (mContent->IsHTML() && (mContent->Tag() == nsGkAtoms::html ||
  6093                              mContent->Tag() == nsGkAtoms::body))
  6094     return true;
  6096   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
  6097   bool visible;
  6098   nsresult rv = aSelection->ContainsNode(node, true, &visible);
  6099   return NS_SUCCEEDED(rv) && visible;
  6102 #ifdef DEBUG
  6103 static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) {
  6104   if (nsBlockFrame::gNoisyDamageRepair) {
  6105     nsFrame::IndentBy(stdout, aDepth+1);
  6106     nsRect lineArea = aLine->GetVisualOverflowArea();
  6107     printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
  6108            aDrawn ? "draw" : "skip",
  6109            static_cast<void*>(aLine),
  6110            aLine->IStart(), aLine->BStart(),
  6111            aLine->ISize(), aLine->BSize(),
  6112            lineArea.x, lineArea.y,
  6113            lineArea.width, lineArea.height);
  6116 #endif
  6118 static void
  6119 DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
  6120             const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
  6121             int32_t aDepth, int32_t& aDrawnLines, const nsDisplayListSet& aLists,
  6122             nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
  6123   // If the line's combined area (which includes child frames that
  6124   // stick outside of the line's bounding box or our bounding box)
  6125   // intersects the dirty rect then paint the line.
  6126   bool intersect = aLineArea.Intersects(aDirtyRect);
  6127 #ifdef DEBUG
  6128   if (nsBlockFrame::gLamePaintMetrics) {
  6129     aDrawnLines++;
  6131   DebugOutputDrawLine(aDepth, aLine.get(), intersect);
  6132 #endif
  6133   // The line might contain a placeholder for a visible out-of-flow, in which
  6134   // case we need to descend into it. If there is such a placeholder, we will
  6135   // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
  6136   // In particular, we really want to check ShouldDescendIntoFrame()
  6137   // on all the frames on the line, but that might be expensive.  So
  6138   // we approximate it by checking it on aFrame; if it's true for any
  6139   // frame in the line, it's also true for aFrame.
  6140   bool lineInline = aLine->IsInline();
  6141   bool lineMayHaveTextOverflow = aTextOverflow && lineInline;
  6142   if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
  6143       !lineMayHaveTextOverflow)
  6144     return;
  6146   // Collect our line's display items in a temporary nsDisplayListCollection,
  6147   // so that we can apply any "text-overflow" clipping to the entire collection
  6148   // without affecting previous lines.
  6149   nsDisplayListCollection collection;
  6151   // Block-level child backgrounds go on the blockBorderBackgrounds list ...
  6152   // Inline-level child backgrounds go on the regular child content list.
  6153   nsDisplayListSet childLists(collection,
  6154     lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
  6156   uint32_t flags = lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0;
  6158   nsIFrame* kid = aLine->mFirstChild;
  6159   int32_t n = aLine->GetChildCount();
  6160   while (--n >= 0) {
  6161     aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect,
  6162                                      childLists, flags);
  6163     kid = kid->GetNextSibling();
  6166   if (lineMayHaveTextOverflow) {
  6167     aTextOverflow->ProcessLine(collection, aLine.get());
  6170   collection.MoveTo(aLists);
  6173 void
  6174 nsBlockFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  6175                                const nsRect&           aDirtyRect,
  6176                                const nsDisplayListSet& aLists)
  6178   int32_t drawnLines; // Will only be used if set (gLamePaintMetrics).
  6179   int32_t depth = 0;
  6180 #ifdef DEBUG
  6181   if (gNoisyDamageRepair) {
  6182       depth = GetDepth();
  6183       nsRect ca;
  6184       ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca);
  6185       nsFrame::IndentBy(stdout, depth);
  6186       ListTag(stdout);
  6187       printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
  6188              mRect.x, mRect.y, mRect.width, mRect.height,
  6189              aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
  6190              ca.x, ca.y, ca.width, ca.height);
  6192   PRTime start = 0; // Initialize these variables to silence the compiler.
  6193   if (gLamePaintMetrics) {
  6194     start = PR_Now();
  6195     drawnLines = 0;
  6197 #endif
  6199   DisplayBorderBackgroundOutline(aBuilder, aLists);
  6201   if (GetPrevInFlow()) {
  6202     DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
  6203     for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
  6204       if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
  6205          BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
  6209   aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
  6211   // Prepare for text-overflow processing.
  6212   nsAutoPtr<TextOverflow> textOverflow(
  6213     TextOverflow::WillProcessLines(aBuilder, this));
  6215   // We'll collect our lines' display items here, & then append this to aLists.
  6216   nsDisplayListCollection linesDisplayListCollection;
  6218   // Don't use the line cursor if we might have a descendant placeholder ...
  6219   // it might skip lines that contain placeholders but don't themselves
  6220   // intersect with the dirty area.
  6221   // In particular, we really want to check ShouldDescendIntoFrame()
  6222   // on all our child frames, but that might be expensive.  So we
  6223   // approximate it by checking it on |this|; if it's true for any
  6224   // frame in our child list, it's also true for |this|.
  6225   nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ?
  6226     nullptr : GetFirstLineContaining(aDirtyRect.y);
  6227   line_iterator line_end = end_lines();
  6229   if (cursor) {
  6230     for (line_iterator line = mLines.begin(cursor);
  6231          line != line_end;
  6232          ++line) {
  6233       nsRect lineArea = line->GetVisualOverflowArea();
  6234       if (!lineArea.IsEmpty()) {
  6235         // Because we have a cursor, the combinedArea.ys are non-decreasing.
  6236         // Once we've passed aDirtyRect.YMost(), we can never see it again.
  6237         if (lineArea.y >= aDirtyRect.YMost()) {
  6238           break;
  6240         DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
  6241                     linesDisplayListCollection, this, textOverflow);
  6244   } else {
  6245     bool nonDecreasingYs = true;
  6246     int32_t lineCount = 0;
  6247     nscoord lastY = INT32_MIN;
  6248     nscoord lastYMost = INT32_MIN;
  6249     for (line_iterator line = begin_lines();
  6250          line != line_end;
  6251          ++line) {
  6252       nsRect lineArea = line->GetVisualOverflowArea();
  6253       DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
  6254                   linesDisplayListCollection, this, textOverflow);
  6255       if (!lineArea.IsEmpty()) {
  6256         if (lineArea.y < lastY
  6257             || lineArea.YMost() < lastYMost) {
  6258           nonDecreasingYs = false;
  6260         lastY = lineArea.y;
  6261         lastYMost = lineArea.YMost();
  6263       lineCount++;
  6266     if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
  6267       SetupLineCursor();
  6271   // Pick up the resulting text-overflow markers.  We append them to
  6272   // PositionedDescendants just before we append the lines' display items,
  6273   // so that our text-overflow markers will appear on top of this block's
  6274   // normal content but below any of its its' positioned children.
  6275   if (textOverflow) {
  6276     aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers());
  6278   linesDisplayListCollection.MoveTo(aLists);
  6280   if (HasOutsideBullet()) {
  6281     // Display outside bullets manually
  6282     nsIFrame* bullet = GetOutsideBullet();
  6283     BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists);
  6286 #ifdef DEBUG
  6287   if (gLamePaintMetrics) {
  6288     PRTime end = PR_Now();
  6290     int32_t numLines = mLines.size();
  6291     if (!numLines) numLines = 1;
  6292     PRTime lines, deltaPerLine, delta;
  6293     lines = int64_t(numLines);
  6294     delta = end - start;
  6295     deltaPerLine = delta / lines;
  6297     ListTag(stdout);
  6298     char buf[400];
  6299     PR_snprintf(buf, sizeof(buf),
  6300                 ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
  6301                 delta, deltaPerLine,
  6302                 numLines, drawnLines, numLines - drawnLines);
  6303     printf("%s\n", buf);
  6305 #endif
  6308 #ifdef ACCESSIBILITY
  6309 a11y::AccType
  6310 nsBlockFrame::AccessibleType()
  6312   // block frame may be for <hr>
  6313   if (mContent->Tag() == nsGkAtoms::hr) {
  6314     return a11y::eHTMLHRType;
  6317   if (!HasBullet() || !PresContext()) {
  6318     if (!mContent->GetParent()) {
  6319       // Don't create accessible objects for the root content node, they are redundant with
  6320       // the nsDocAccessible object created with the document node
  6321       return a11y::eNoType;
  6324     nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
  6325       do_QueryInterface(mContent->GetDocument());
  6326     if (htmlDoc) {
  6327       nsCOMPtr<nsIDOMHTMLElement> body;
  6328       htmlDoc->GetBody(getter_AddRefs(body));
  6329       if (SameCOMIdentity(body, mContent)) {
  6330         // Don't create accessible objects for the body, they are redundant with
  6331         // the nsDocAccessible object created with the document node
  6332         return a11y::eNoType;
  6336     // Not a bullet, treat as normal HTML container
  6337     return a11y::eHyperTextType;
  6340   // Create special list bullet accessible
  6341   return a11y::eHTMLLiType;
  6343 #endif
  6345 void nsBlockFrame::ClearLineCursor()
  6347   if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
  6348     return;
  6351   Properties().Delete(LineCursorProperty());
  6352   RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
  6355 void nsBlockFrame::SetupLineCursor()
  6357   if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
  6358       || mLines.empty()) {
  6359     return;
  6362   Properties().Set(LineCursorProperty(), mLines.front());
  6363   AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
  6366 nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
  6368   if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
  6369     return nullptr;
  6372   FrameProperties props = Properties();
  6374   nsLineBox* property = static_cast<nsLineBox*>
  6375     (props.Get(LineCursorProperty()));
  6376   line_iterator cursor = mLines.begin(property);
  6377   nsRect cursorArea = cursor->GetVisualOverflowArea();
  6379   while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
  6380          && cursor != mLines.front()) {
  6381     cursor = cursor.prev();
  6382     cursorArea = cursor->GetVisualOverflowArea();
  6384   while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
  6385          && cursor != mLines.back()) {
  6386     cursor = cursor.next();
  6387     cursorArea = cursor->GetVisualOverflowArea();
  6390   if (cursor.get() != property) {
  6391     props.Set(LineCursorProperty(), cursor.get());
  6394   return cursor.get();
  6397 /* virtual */ void
  6398 nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
  6400   // See if the child is absolutely positioned
  6401   if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
  6402       aChild->IsAbsolutelyPositioned()) {
  6403     // do nothing
  6404   } else if (aChild == GetOutsideBullet()) {
  6405     // The bullet lives in the first line, unless the first line has
  6406     // height 0 and there is a second line, in which case it lives
  6407     // in the second line.
  6408     line_iterator bulletLine = begin_lines();
  6409     if (bulletLine != end_lines() && bulletLine->BSize() == 0 &&
  6410         bulletLine != mLines.back()) {
  6411       bulletLine = bulletLine.next();
  6414     if (bulletLine != end_lines()) {
  6415       MarkLineDirty(bulletLine, &mLines);
  6417     // otherwise we have an empty line list, and ReflowDirtyLines
  6418     // will handle reflowing the bullet.
  6419   } else {
  6420     // Note that we should go through our children to mark lines dirty
  6421     // before the next reflow.  Doing it now could make things O(N^2)
  6422     // since finding the right line is O(N).
  6423     // We don't need to worry about marking lines on the overflow list
  6424     // as dirty; we're guaranteed to reflow them if we take them off the
  6425     // overflow list.
  6426     // However, we might have gotten a float, in which case we need to
  6427     // reflow the line containing its placeholder.  So find the
  6428     // ancestor-or-self of the placeholder that's a child of the block,
  6429     // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
  6430     // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
  6431     // We need to take some care to handle the case where a float is in
  6432     // a different continuation than its placeholder, including marking
  6433     // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
  6434     if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
  6435       AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
  6436     } else {
  6437       NS_ASSERTION(aChild->IsFloating(), "should be a float");
  6438       nsIFrame *thisFC = FirstContinuation();
  6439       nsIFrame *placeholderPath =
  6440         PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild);
  6441       // SVG code sometimes sends FrameNeedsReflow notifications during
  6442       // frame destruction, leading to null placeholders, but we're safe
  6443       // ignoring those.
  6444       if (placeholderPath) {
  6445         for (;;) {
  6446           nsIFrame *parent = placeholderPath->GetParent();
  6447           if (parent->GetContent() == mContent &&
  6448               parent->FirstContinuation() == thisFC) {
  6449             parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
  6450             break;
  6452           placeholderPath = parent;
  6454         placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  6459   nsBlockFrameSuper::ChildIsDirty(aChild);
  6462 void
  6463 nsBlockFrame::Init(nsIContent*      aContent,
  6464                    nsIFrame*        aParent,
  6465                    nsIFrame*        aPrevInFlow)
  6467   if (aPrevInFlow) {
  6468     // Copy over the inherited block frame bits from the prev-in-flow.
  6469     SetFlags(aPrevInFlow->GetStateBits() &
  6470              (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
  6473   nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow);
  6475   if (!aPrevInFlow ||
  6476       aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
  6477     AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
  6479   if ((GetStateBits() &
  6480        (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) ==
  6481       (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) {
  6482     AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
  6486 nsresult
  6487 nsBlockFrame::SetInitialChildList(ChildListID     aListID,
  6488                                   nsFrameList&    aChildList)
  6490   NS_ASSERTION(aListID != kPrincipalList ||
  6491                (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET |
  6492                                   NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0,
  6493                "how can we have a bullet already?");
  6495   if (kAbsoluteList == aListID) {
  6496     nsContainerFrame::SetInitialChildList(aListID, aChildList);
  6498   else if (kFloatList == aListID) {
  6499     mFloats.SetFrames(aChildList);
  6501   else {
  6502     nsPresContext* presContext = PresContext();
  6504 #ifdef DEBUG
  6505     // The only times a block that is an anonymous box is allowed to have a
  6506     // first-letter frame are when it's the block inside a non-anonymous cell,
  6507     // the block inside a fieldset, a scrolled content block, or a column
  6508     // content block.  Note that this means that blocks which are the anonymous
  6509     // block in {ib} splits do NOT get first-letter frames.  Note that
  6510     // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the
  6511     // block.
  6512     nsIAtom *pseudo = StyleContext()->GetPseudo();
  6513     bool haveFirstLetterStyle =
  6514       (!pseudo ||
  6515        (pseudo == nsCSSAnonBoxes::cellContent &&
  6516         mParent->StyleContext()->GetPseudo() == nullptr) ||
  6517        pseudo == nsCSSAnonBoxes::fieldsetContent ||
  6518        pseudo == nsCSSAnonBoxes::scrolledContent ||
  6519        pseudo == nsCSSAnonBoxes::columnContent ||
  6520        pseudo == nsCSSAnonBoxes::mozSVGText) &&
  6521       !IsFrameOfType(eMathML) &&
  6522       nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nullptr;
  6523     NS_ASSERTION(haveFirstLetterStyle ==
  6524                  ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
  6525                  "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
  6526 #endif
  6528     AddFrames(aChildList, nullptr);
  6530     // Create a list bullet if this is a list-item. Note that this is
  6531     // done here so that RenumberLists will work (it needs the bullets
  6532     // to store the bullet numbers).  Also note that due to various
  6533     // wrapper frames (scrollframes, columns) we want to use the
  6534     // outermost (primary, ideally, but it's not set yet when we get
  6535     // here) frame of our content for the display check.  On the other
  6536     // hand, we look at ourselves for the GetPrevInFlow() check, since
  6537     // for a columnset we don't want a bullet per column.  Note that
  6538     // the outermost frame for the content is the primary frame in
  6539     // most cases; the ones when it's not (like tables) can't be
  6540     // NS_STYLE_DISPLAY_LIST_ITEM).
  6541     nsIFrame* possibleListItem = this;
  6542     while (1) {
  6543       nsIFrame* parent = possibleListItem->GetParent();
  6544       if (parent->GetContent() != GetContent()) {
  6545         break;
  6547       possibleListItem = parent;
  6549     if (NS_STYLE_DISPLAY_LIST_ITEM ==
  6550           possibleListItem->StyleDisplay()->mDisplay &&
  6551         !GetPrevInFlow()) {
  6552       // Resolve style for the bullet frame
  6553       const nsStyleList* styleList = StyleList();
  6554       nsCSSPseudoElements::Type pseudoType;
  6555       switch (styleList->mListStyleType) {
  6556         case NS_STYLE_LIST_STYLE_DISC:
  6557         case NS_STYLE_LIST_STYLE_CIRCLE:
  6558         case NS_STYLE_LIST_STYLE_SQUARE:
  6559           pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet;
  6560           break;
  6561         default:
  6562           pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber;
  6563           break;
  6566       nsIPresShell *shell = presContext->PresShell();
  6568       nsStyleContext* parentStyle =
  6569         CorrectStyleParentFrame(this,
  6570           nsCSSPseudoElements::GetPseudoAtom(pseudoType))->StyleContext();
  6571       nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
  6572         ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
  6573                                   parentStyle, nullptr);
  6575       // Create bullet frame
  6576       nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
  6577       bullet->Init(mContent, this, nullptr);
  6579       // If the list bullet frame should be positioned inside then add
  6580       // it to the flow now.
  6581       if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
  6582             styleList->mListStylePosition) {
  6583         nsFrameList bulletList(bullet, bullet);
  6584         AddFrames(bulletList, nullptr);
  6585         Properties().Set(InsideBulletProperty(), bullet);
  6586         AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
  6587       } else {
  6588         nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
  6589         Properties().Set(OutsideBulletProperty(), bulletList);
  6590         AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
  6595   return NS_OK;
  6598 bool
  6599 nsBlockFrame::BulletIsEmpty() const
  6601   NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
  6602                  NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(),
  6603                "should only care when we have an outside bullet");
  6604   const nsStyleList* list = StyleList();
  6605   return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE &&
  6606          !list->GetListStyleImage();
  6609 void
  6610 nsBlockFrame::GetBulletText(nsAString& aText) const
  6612   aText.Truncate();
  6614   const nsStyleList* myList = StyleList();
  6615   if (myList->GetListStyleImage() ||
  6616       myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) {
  6617     aText.Assign(kDiscCharacter);
  6619   else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) {
  6620     aText.Assign(kCircleCharacter);
  6622   else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
  6623     aText.Assign(kSquareCharacter);
  6625   else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
  6626     nsBulletFrame* bullet = GetBullet();
  6627     if (bullet) {
  6628       nsAutoString text;
  6629       bullet->GetListItemText(*myList, text);
  6630       aText = text;
  6635 // static
  6636 bool
  6637 nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
  6639   nsIContent* content = aFrame->GetContent();
  6640   if (!content || !content->IsHTML())
  6641     return false;
  6643   nsIAtom *localName = content->NodeInfo()->NameAtom();
  6644   return localName == nsGkAtoms::ol ||
  6645          localName == nsGkAtoms::ul ||
  6646          localName == nsGkAtoms::dir ||
  6647          localName == nsGkAtoms::menu;
  6650 bool
  6651 nsBlockFrame::RenumberLists(nsPresContext* aPresContext)
  6653   if (!FrameStartsCounterScope(this)) {
  6654     // If this frame doesn't start a counter scope then we don't need
  6655     // to renumber child list items.
  6656     return false;
  6659   MOZ_ASSERT(mContent->IsHTML(),
  6660              "FrameStartsCounterScope should only return true for HTML elements");
  6662   // Setup initial list ordinal value
  6663   // XXX Map html's start property to counter-reset style
  6664   int32_t ordinal = 1;
  6665   int32_t increment;
  6666   if (mContent->Tag() == nsGkAtoms::ol &&
  6667       mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) {
  6668     increment = -1;
  6669   } else {
  6670     increment = 1;
  6673   nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
  6674   // Must be non-null, since FrameStartsCounterScope only returns true
  6675   // for HTML elements.
  6676   MOZ_ASSERT(hc, "How is mContent not HTML?");
  6677   const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
  6678   if (attr && attr->Type() == nsAttrValue::eInteger) {
  6679     ordinal = attr->GetIntegerValue();
  6680   } else if (increment < 0) {
  6681     // <ol reversed> case, or some other case with a negative increment: count
  6682     // up the child list
  6683     ordinal = 0;
  6684     for (nsIContent* kid = mContent->GetFirstChild(); kid;
  6685          kid = kid->GetNextSibling()) {
  6686       if (kid->IsHTML(nsGkAtoms::li)) {
  6687         // FIXME: This isn't right in terms of what CSS says to do for
  6688         // overflow of counters (but it only matters when this node has
  6689         // more than numeric_limits<int32_t>::max() children).
  6690         ordinal -= increment;
  6695   // Get to first-in-flow
  6696   nsBlockFrame* block = static_cast<nsBlockFrame*>(FirstInFlow());
  6697   return RenumberListsInBlock(aPresContext, block, &ordinal, 0, increment);
  6700 bool
  6701 nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext,
  6702                                    nsBlockFrame* aBlockFrame,
  6703                                    int32_t* aOrdinal,
  6704                                    int32_t aDepth,
  6705                                    int32_t aIncrement)
  6707   // Examine each line in the block
  6708   bool foundValidLine;
  6709   nsBlockInFlowLineIterator bifLineIter(aBlockFrame, &foundValidLine);
  6711   if (!foundValidLine)
  6712     return false;
  6714   bool renumberedABullet = false;
  6716   do {
  6717     nsLineList::iterator line = bifLineIter.GetLine();
  6718     nsIFrame* kid = line->mFirstChild;
  6719     int32_t n = line->GetChildCount();
  6720     while (--n >= 0) {
  6721       bool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal,
  6722                                                    aDepth, aIncrement);
  6723       if (kidRenumberedABullet) {
  6724         line->MarkDirty();
  6725         renumberedABullet = true;
  6727       kid = kid->GetNextSibling();
  6729   } while (bifLineIter.Next());
  6731   // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
  6732   // the bullet and the caller of RenumberLists.  But the caller itself
  6733   // has to be responsible for setting the bit itself, since that caller
  6734   // might be making a FrameNeedsReflow call, which requires that the
  6735   // bit not be set yet.
  6736   if (renumberedABullet && aDepth != 0) {
  6737     aBlockFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  6740   return renumberedABullet;
  6743 bool
  6744 nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
  6745                                nsIFrame* aKid,
  6746                                int32_t* aOrdinal,
  6747                                int32_t aDepth,
  6748                                int32_t aIncrement)
  6750   NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
  6752   // add in a sanity check for absurdly deep frame trees.  See bug 42138
  6753   if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
  6754     return false;
  6756   // if the frame is a placeholder, then get the out of flow frame
  6757   nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid);
  6758   const nsStyleDisplay* display = kid->StyleDisplay();
  6760   // drill down through any wrappers to the real frame
  6761   kid = kid->GetContentInsertionFrame();
  6763   // possible there is no content insertion frame
  6764   if (!kid)
  6765     return false;
  6767   bool kidRenumberedABullet = false;
  6769   // If the frame is a list-item and the frame implements our
  6770   // block frame API then get its bullet and set the list item
  6771   // ordinal.
  6772   if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
  6773     // Make certain that the frame is a block frame in case
  6774     // something foreign has crept in.
  6775     nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
  6776     if (listItem) {
  6777       nsBulletFrame* bullet = listItem->GetBullet();
  6778       if (bullet) {
  6779         bool changed;
  6780         *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
  6781         if (changed) {
  6782           kidRenumberedABullet = true;
  6784           // The ordinal changed - mark the bullet frame, and any
  6785           // intermediate frames between it and the block (are there
  6786           // ever any?), dirty.
  6787           // The calling code will make the necessary FrameNeedsReflow
  6788           // call for the list ancestor.
  6789           bullet->AddStateBits(NS_FRAME_IS_DIRTY);
  6790           nsIFrame *f = bullet;
  6791           do {
  6792             nsIFrame *parent = f->GetParent();
  6793             parent->ChildIsDirty(f);
  6794             f = parent;
  6795           } while (f != listItem);
  6799       // XXX temporary? if the list-item has child list-items they
  6800       // should be numbered too; especially since the list-item is
  6801       // itself (ASSUMED!) not to be a counter-resetter.
  6802       bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal,
  6803                                         aDepth + 1, aIncrement);
  6804       if (meToo) {
  6805         kidRenumberedABullet = true;
  6809   else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
  6810     if (FrameStartsCounterScope(kid)) {
  6811       // Don't bother recursing into a block frame that is a new
  6812       // counter scope. Any list-items in there will be handled by
  6813       // it.
  6815     else {
  6816       // If the display=block element is a block frame then go ahead
  6817       // and recurse into it, as it might have child list-items.
  6818       nsBlockFrame* kidBlock = nsLayoutUtils::GetAsBlock(kid);
  6819       if (kidBlock) {
  6820         kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock,
  6821                                                     aOrdinal, aDepth + 1,
  6822                                                     aIncrement);
  6826   return kidRenumberedABullet;
  6829 void
  6830 nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
  6831                            nsBlockReflowState& aState,
  6832                            nsHTMLReflowMetrics& aMetrics,
  6833                            nscoord aLineTop)
  6835   const nsHTMLReflowState &rs = aState.mReflowState;
  6837   // Reflow the bullet now
  6838   nsSize availSize;
  6839   // Make up a width since it doesn't really matter (XXX).
  6840   availSize.width = aState.mContentArea.width;
  6841   availSize.height = NS_UNCONSTRAINEDSIZE;
  6843   // Get the reason right.
  6844   // XXXwaterson Should this look just like the logic in
  6845   // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
  6846   nsHTMLReflowState reflowState(aState.mPresContext, rs,
  6847                                 aBulletFrame, availSize);
  6848   nsReflowStatus  status;
  6849   aBulletFrame->WillReflow(aState.mPresContext);
  6850   aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
  6852   // Get the float available space using our saved state from before we
  6853   // started reflowing the block, so that we ignore any floats inside
  6854   // the block.
  6855   // FIXME: aLineTop isn't actually set correctly by some callers, since
  6856   // they reposition the line.
  6857   nsRect floatAvailSpace =
  6858     aState.GetFloatAvailableSpaceWithState(aLineTop,
  6859                                            &aState.mFloatManagerStateBefore)
  6860           .mRect;
  6861   // FIXME (bug 25888): need to check the entire region that the first
  6862   // line overlaps, not just the top pixel.
  6864   // Place the bullet now.  We want to place the bullet relative to the
  6865   // border-box of the associated block (using the right/left margin of
  6866   // the bullet frame as separation).  However, if a line box would be
  6867   // displaced by floats that are *outside* the associated block, we
  6868   // want to displace it by the same amount.  That is, we act as though
  6869   // the edge of the floats is the content-edge of the block, and place
  6870   // the bullet at a position offset from there by the block's padding,
  6871   // the block's border, and the bullet frame's margin.
  6873   // IStart from floatAvailSpace gives us the content/float start edge
  6874   // in the current writing mode. Then we subtract out the start
  6875   // border/padding and the bullet's width and margin to offset the position.
  6876   WritingMode wm = rs.GetWritingMode();
  6877   nscoord containerWidth = floatAvailSpace.XMost();
  6878   LogicalRect logicalFAS(wm, floatAvailSpace, containerWidth);
  6879   // Get the bullet's margin, converted to our writing mode so that we can
  6880   // combine it with other logical values here.
  6881   WritingMode bulletWM = reflowState.GetWritingMode();
  6882   LogicalMargin bulletMargin =
  6883     reflowState.ComputedLogicalMargin().ConvertTo(wm, bulletWM);
  6884   nscoord iStart = logicalFAS.IStart(wm) -
  6885                    rs.ComputedLogicalBorderPadding().IStart(wm) -
  6886                    bulletMargin.IEnd(wm) -
  6887                    aMetrics.ISize();
  6889   // Approximate the bullets position; vertical alignment will provide
  6890   // the final vertical location. We pass our writing-mode here, because
  6891   // it may be different from the bullet frame's mode.
  6892   nscoord bStart = logicalFAS.BStart(wm);
  6893   aBulletFrame->SetRect(wm, LogicalRect(wm, LogicalPoint(wm, iStart, bStart),
  6894                                         LogicalSize(wm, aMetrics.ISize(),
  6895                                                     aMetrics.BSize())),
  6896                         containerWidth);
  6897   aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState,
  6898                           nsDidReflowStatus::FINISHED);
  6901 // This is used to scan frames for any float placeholders, add their
  6902 // floats to the list represented by aList, and remove the
  6903 // floats from whatever list they might be in. We don't search descendants
  6904 // that are float containing blocks.  Floats that or not children of 'this'
  6905 // are ignored (they are not added to aList).
  6906 void
  6907 nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList,
  6908                               bool aCollectSiblings)
  6910   while (aFrame) {
  6911     // Don't descend into float containing blocks.
  6912     if (!aFrame->IsFloatContainingBlock()) {
  6913       nsIFrame *outOfFlowFrame =
  6914         aFrame->GetType() == nsGkAtoms::placeholderFrame ?
  6915           nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nullptr;
  6916       if (outOfFlowFrame && outOfFlowFrame->GetParent() == this) {
  6917         RemoveFloat(outOfFlowFrame);
  6918         aList.AppendFrame(nullptr, outOfFlowFrame);
  6919         // FIXME: By not pulling floats whose parent is one of our
  6920         // later siblings, are we risking the pushed floats getting
  6921         // out-of-order?
  6922         // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that.
  6925       DoCollectFloats(aFrame->GetFirstPrincipalChild(), aList, true);
  6926       DoCollectFloats(aFrame->GetFirstChild(kOverflowList), aList, true);
  6928     if (!aCollectSiblings)
  6929       break;
  6930     aFrame = aFrame->GetNextSibling();
  6934 void
  6935 nsBlockFrame::CheckFloats(nsBlockReflowState& aState)
  6937 #ifdef DEBUG
  6938   // If any line is still dirty, that must mean we're going to reflow this
  6939   // block again soon (e.g. because we bailed out after noticing that
  6940   // clearance was imposed), so don't worry if the floats are out of sync.
  6941   bool anyLineDirty = false;
  6943   // Check that the float list is what we would have built
  6944   nsAutoTArray<nsIFrame*, 8> lineFloats;
  6945   for (line_iterator line = begin_lines(), line_end = end_lines();
  6946        line != line_end; ++line) {
  6947     if (line->HasFloats()) {
  6948       nsFloatCache* fc = line->GetFirstFloat();
  6949       while (fc) {
  6950         lineFloats.AppendElement(fc->mFloat);
  6951         fc = fc->Next();
  6954     if (line->IsDirty()) {
  6955       anyLineDirty = true;
  6959   nsAutoTArray<nsIFrame*, 8> storedFloats;
  6960   bool equal = true;
  6961   uint32_t i = 0;
  6962   for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
  6963     if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
  6964       continue;
  6965     storedFloats.AppendElement(f);
  6966     if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
  6967       equal = false;
  6969     ++i;
  6972   if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) {
  6973     NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
  6974 #if defined(DEBUG_roc)
  6975     nsFrame::RootFrameList(PresContext(), stdout, 0);
  6976     for (i = 0; i < lineFloats.Length(); ++i) {
  6977       printf("Line float: %p\n", lineFloats.ElementAt(i));
  6979     for (i = 0; i < storedFloats.Length(); ++i) {
  6980       printf("Stored float: %p\n", storedFloats.ElementAt(i));
  6982 #endif
  6984 #endif
  6986   const nsFrameList* oofs = GetOverflowOutOfFlows();
  6987   if (oofs && oofs->NotEmpty()) {
  6988     // Floats that were pushed should be removed from our float
  6989     // manager.  Otherwise the float manager's YMost or XMost might
  6990     // be larger than necessary, causing this block to get an
  6991     // incorrect desired height (or width).  Some of these floats
  6992     // may not actually have been added to the float manager because
  6993     // they weren't reflowed before being pushed; that's OK,
  6994     // RemoveRegions will ignore them. It is safe to do this here
  6995     // because we know from here on the float manager will only be
  6996     // used for its XMost and YMost, not to place new floats and
  6997     // lines.
  6998     aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild());
  7002 void
  7003 nsBlockFrame::IsMarginRoot(bool* aTopMarginRoot, bool* aBottomMarginRoot)
  7005   if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT)) {
  7006     nsIFrame* parent = GetParent();
  7007     if (!parent || parent->IsFloatContainingBlock()) {
  7008       *aTopMarginRoot = false;
  7009       *aBottomMarginRoot = false;
  7010       return;
  7012     if (parent->GetType() == nsGkAtoms::columnSetFrame) {
  7013       *aTopMarginRoot = GetPrevInFlow() == nullptr;
  7014       *aBottomMarginRoot = GetNextInFlow() == nullptr;
  7015       return;
  7019   *aTopMarginRoot = true;
  7020   *aBottomMarginRoot = true;
  7023 /* static */
  7024 bool
  7025 nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock)
  7027   NS_PRECONDITION(aBlock, "Must have a frame");
  7028   NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
  7030   nsIFrame* parent = aBlock->GetParent();
  7031   return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) ||
  7032     (parent && !parent->IsFloatContainingBlock());
  7035 /* static */
  7036 bool
  7037 nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame)
  7039   return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) &&
  7040          !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
  7041          !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR);
  7044 // Note that this width can vary based on the vertical position.
  7045 // However, the cases where it varies are the cases where the width fits
  7046 // in the available space given, which means that variation shouldn't
  7047 // matter.
  7048 /* static */
  7049 nsBlockFrame::ReplacedElementWidthToClear
  7050 nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState& aState,
  7051                                      const nsRect& aFloatAvailableSpace,
  7052                                      nsIFrame* aFrame)
  7054   nscoord leftOffset, rightOffset;
  7055   nsCSSOffsetState offsetState(aFrame, aState.mReflowState.rendContext,
  7056                                aState.mContentArea.width);
  7058   ReplacedElementWidthToClear result;
  7059   aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace,
  7060                                               leftOffset, rightOffset);
  7061   nscoord availWidth = aState.mContentArea.width - leftOffset - rightOffset;
  7063   // We actually don't want the min width here; see bug 427782; we only
  7064   // want to displace if the width won't compute to a value small enough
  7065   // to fit.
  7066   // All we really need here is the result of ComputeSize, and we
  7067   // could *almost* get that from an nsCSSOffsetState, except for the
  7068   // last argument.
  7069   nsSize availSpace(availWidth, NS_UNCONSTRAINEDSIZE);
  7070   nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
  7071                                 aFrame, availSpace);
  7072   result.borderBoxWidth = reflowState.ComputedWidth() +
  7073                           reflowState.ComputedPhysicalBorderPadding().LeftRight();
  7074   // Use the margins from offsetState rather than reflowState so that
  7075   // they aren't reduced by ignoring margins in overconstrained cases.
  7076   result.marginLeft  = offsetState.ComputedPhysicalMargin().left;
  7077   result.marginRight = offsetState.ComputedPhysicalMargin().right;
  7078   return result;
  7081 /* static */
  7082 nsBlockFrame*
  7083 nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
  7085   nsBlockFrame* block = nullptr;
  7086   while(aCandidate) {
  7087     block = nsLayoutUtils::GetAsBlock(aCandidate);
  7088     if (block) { 
  7089       // yay, candidate is a block!
  7090       return block;
  7092     // Not a block. Check its parent next.
  7093     aCandidate = aCandidate->GetParent();
  7095   NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
  7096   return nullptr;
  7099 void
  7100 nsBlockFrame::ComputeFinalHeight(const nsHTMLReflowState& aReflowState,
  7101                                       nsReflowStatus*          aStatus,
  7102                                       nscoord                  aContentHeight,
  7103                                       const nsMargin&          aBorderPadding,
  7104                                       nsHTMLReflowMetrics&     aMetrics,
  7105                                       nscoord                  aConsumed)
  7108   // Figure out how much of the computed height should be
  7109   // applied to this frame.
  7110   nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState,
  7111                                                               aConsumed);
  7112   NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
  7113                   && computedHeightLeftOver ),
  7114                "overflow container must not have computedHeightLeftOver");
  7116   aMetrics.Height() =
  7117     NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.top,
  7118                                               computedHeightLeftOver),
  7119                          aBorderPadding.bottom);
  7121   if (NS_FRAME_IS_NOT_COMPLETE(*aStatus)
  7122       && aMetrics.Height() < aReflowState.AvailableHeight()) {
  7123     // We ran out of height on this page but we're incomplete
  7124     // Set status to complete except for overflow
  7125     NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus);
  7128   if (NS_FRAME_IS_COMPLETE(*aStatus)) {
  7129     if (computedHeightLeftOver > 0 &&
  7130         NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
  7131         aMetrics.Height() > aReflowState.AvailableHeight()) {
  7132       if (ShouldAvoidBreakInside(aReflowState)) {
  7133         *aStatus = NS_INLINE_LINE_BREAK_BEFORE();
  7134         return;
  7136       // We don't fit and we consumed some of the computed height,
  7137       // so we should consume all the available height and then
  7138       // break.  If our bottom border/padding straddles the break
  7139       // point, then this will increase our height and push the
  7140       // border/padding to the next page/column.
  7141       aMetrics.Height() = std::max(aReflowState.AvailableHeight(),
  7142                                  aContentHeight);
  7143       NS_FRAME_SET_INCOMPLETE(*aStatus);
  7144       if (!GetNextInFlow())
  7145         *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
  7150 nsresult
  7151 nsBlockFrame::ResolveBidi()
  7153   NS_ASSERTION(!GetPrevInFlow(),
  7154                "ResolveBidi called on non-first continuation");
  7156   nsPresContext* presContext = PresContext();
  7157   if (!presContext->BidiEnabled()) {
  7158     return NS_OK;
  7161   return nsBidiPresUtils::Resolve(this);
  7164 #ifdef DEBUG
  7165 void
  7166 nsBlockFrame::VerifyLines(bool aFinalCheckOK)
  7168   if (!gVerifyLines) {
  7169     return;
  7171   if (mLines.empty()) {
  7172     return;
  7175   nsLineBox* cursor = GetLineCursor();
  7177   // Add up the counts on each line. Also validate that IsFirstLine is
  7178   // set properly.
  7179   int32_t count = 0;
  7180   line_iterator line, line_end;
  7181   for (line = begin_lines(), line_end = end_lines();
  7182        line != line_end;
  7183        ++line) {
  7184     if (line == cursor) {
  7185       cursor = nullptr;
  7187     if (aFinalCheckOK) {
  7188       NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
  7189       if (line->IsBlock()) {
  7190         NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
  7193     count += line->GetChildCount();
  7196   // Then count the frames
  7197   int32_t frameCount = 0;
  7198   nsIFrame* frame = mLines.front()->mFirstChild;
  7199   while (frame) {
  7200     frameCount++;
  7201     frame = frame->GetNextSibling();
  7203   NS_ASSERTION(count == frameCount, "bad line list");
  7205   // Next: test that each line has right number of frames on it
  7206   for (line = begin_lines(), line_end = end_lines();
  7207        line != line_end;
  7208         ) {
  7209     count = line->GetChildCount();
  7210     frame = line->mFirstChild;
  7211     while (--count >= 0) {
  7212       frame = frame->GetNextSibling();
  7214     ++line;
  7215     if ((line != line_end) && (0 != line->GetChildCount())) {
  7216       NS_ASSERTION(frame == line->mFirstChild, "bad line list");
  7220   if (cursor) {
  7221     FrameLines* overflowLines = GetOverflowLines();
  7222     if (overflowLines) {
  7223       line_iterator line = overflowLines->mLines.begin();
  7224       line_iterator line_end = overflowLines->mLines.end();
  7225       for (; line != line_end; ++line) {
  7226         if (line == cursor) {
  7227           cursor = nullptr;
  7228           break;
  7233   NS_ASSERTION(!cursor, "stale LineCursorProperty");
  7236 void
  7237 nsBlockFrame::VerifyOverflowSituation()
  7239   nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow());
  7240   while (flow) {
  7241     FrameLines* overflowLines = flow->GetOverflowLines();
  7242     if (overflowLines) {
  7243       NS_ASSERTION(!overflowLines->mLines.empty(),
  7244                    "should not be empty if present");
  7245       NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
  7246                    "bad overflow lines");
  7247       NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
  7248                    overflowLines->mFrames.FirstChild(),
  7249                    "bad overflow frames / lines");
  7251     nsLineBox* cursor = flow->GetLineCursor();
  7252     if (cursor) {
  7253       line_iterator line = flow->begin_lines();
  7254       line_iterator line_end = flow->end_lines();
  7255       for (; line != line_end && line != cursor; ++line)
  7257       if (line == line_end && overflowLines) {
  7258         line = overflowLines->mLines.begin();
  7259         line_end = overflowLines->mLines.end();
  7260         for (; line != line_end && line != cursor; ++line)
  7263       MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
  7265     flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
  7269 int32_t
  7270 nsBlockFrame::GetDepth() const
  7272   int32_t depth = 0;
  7273   nsIFrame* parent = mParent;
  7274   while (parent) {
  7275     parent = parent->GetParent();
  7276     depth++;
  7278   return depth;
  7280 #endif

mercurial