layout/generic/nsBlockReflowContext.cpp

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

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

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

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 // 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 /* class that a parent frame uses to reflow a block frame */
     9 #include "nsBlockReflowContext.h"
    10 #include "nsBlockReflowState.h"
    11 #include "nsFloatManager.h"
    12 #include "nsContainerFrame.h"
    13 #include "nsBlockFrame.h"
    14 #include "nsLineBox.h"
    15 #include "nsLayoutUtils.h"
    17 #ifdef DEBUG
    18 #undef  NOISY_MAX_ELEMENT_SIZE
    19 #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
    20 #undef  NOISY_VERTICAL_MARGINS
    21 #else
    22 #undef  NOISY_MAX_ELEMENT_SIZE
    23 #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
    24 #undef  NOISY_VERTICAL_MARGINS
    25 #endif
    27 nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
    28                                            const nsHTMLReflowState& aParentRS)
    29   : mPresContext(aPresContext),
    30     mOuterReflowState(aParentRS),
    31     mMetrics(aParentRS.GetWritingMode())
    32 {
    33 }
    35 static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
    36 {
    37   nsIAtom* type = aFrame->GetType();
    38   if (type == nsGkAtoms::columnSetFrame)
    39     return DescendIntoBlockLevelFrame(aFrame->GetFirstPrincipalChild());
    40   return aFrame;
    41 }
    43 bool
    44 nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
    45   nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
    46   bool* aMayNeedRetry, bool* aBlockIsEmpty)
    47 {
    48   // Include frame's top margin
    49   aMargin->Include(aRS.ComputedPhysicalMargin().top);
    51   // The inclusion of the bottom margin when empty is done by the caller
    52   // since it doesn't need to be done by the top-level (non-recursive)
    53   // caller.
    55 #ifdef NOISY_VERTICAL_MARGINS
    56   nsFrame::ListTag(stdout, aRS.frame);
    57   printf(": %d => %d\n", aRS.ComputedPhysicalMargin().top, aMargin->get());
    58 #endif
    60   bool dirtiedLine = false;
    61   bool setBlockIsEmpty = false;
    63   // Calculate the frame's generational top-margin from its child
    64   // blocks. Note that if the frame has a non-zero top-border or
    65   // top-padding then this step is skipped because it will be a margin
    66   // root.  It is also skipped if the frame is a margin root for other
    67   // reasons.
    68   nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame);
    69   nsPresContext* prescontext = frame->PresContext();
    70   nsBlockFrame* block = nullptr;
    71   if (0 == aRS.ComputedPhysicalBorderPadding().top) {
    72     block = nsLayoutUtils::GetAsBlock(frame);
    73     if (block) {
    74       bool topMarginRoot, unused;
    75       block->IsMarginRoot(&topMarginRoot, &unused);
    76       if (topMarginRoot) {
    77         block = nullptr;
    78       }
    79     }
    80   }
    82   // iterate not just through the lines of 'block' but also its
    83   // overflow lines and the normal and overflow lines of its next in
    84   // flows. Note that this will traverse some frames more than once:
    85   // for example, if A contains B and A->nextinflow contains
    86   // B->nextinflow, we'll traverse B->nextinflow twice. But this is
    87   // OK because our traversal is idempotent.
    88   for ( ;block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) {
    89     for (int overflowLines = 0; overflowLines <= 1; ++overflowLines) {
    90       nsBlockFrame::line_iterator line;
    91       nsBlockFrame::line_iterator line_end;
    92       bool anyLines = true;
    93       if (overflowLines) {
    94         nsBlockFrame::FrameLines* frames = block->GetOverflowLines();
    95         nsLineList* lines = frames ? &frames->mLines : nullptr;
    96         if (!lines) {
    97           anyLines = false;
    98         } else {
    99           line = lines->begin();
   100           line_end = lines->end();
   101         }
   102       } else {
   103         line = block->begin_lines();
   104         line_end = block->end_lines();
   105       }
   106       for (; anyLines && line != line_end; ++line) {
   107         if (!aClearanceFrame && line->HasClearance()) {
   108           // If we don't have a clearance frame, then we're computing
   109           // the collapsed margin in the first pass, assuming that all
   110           // lines have no clearance. So clear their clearance flags.
   111           line->ClearHasClearance();
   112           line->MarkDirty();
   113           dirtiedLine = true;
   114         }
   116         bool isEmpty;
   117         if (line->IsInline()) {
   118           isEmpty = line->IsEmpty();
   119         } else {
   120           nsIFrame* kid = line->mFirstChild;
   121           if (kid == aClearanceFrame) {
   122             line->SetHasClearance();
   123             line->MarkDirty();
   124             dirtiedLine = true;
   125             goto done;
   126           }
   127           // Here is where we recur. Now that we have determined that a
   128           // generational collapse is required we need to compute the
   129           // child blocks margin and so in so that we can look into
   130           // it. For its margins to be computed we need to have a reflow
   131           // state for it.
   133           // We may have to construct an extra reflow state here if
   134           // we drilled down through a block wrapper. At the moment
   135           // we can only drill down one level so we only have to support
   136           // one extra reflow state.
   137           const nsHTMLReflowState* outerReflowState = &aRS;
   138           if (frame != aRS.frame) {
   139             NS_ASSERTION(frame->GetParent() == aRS.frame,
   140                          "Can only drill through one level of block wrapper");
   141             nsSize availSpace(aRS.ComputedWidth(), aRS.ComputedHeight());
   142             outerReflowState = new nsHTMLReflowState(prescontext,
   143                                                      aRS, frame, availSpace);
   144           }
   145           {
   146             nsSize availSpace(outerReflowState->ComputedWidth(),
   147                               outerReflowState->ComputedHeight());
   148             nsHTMLReflowState innerReflowState(prescontext,
   149                                                *outerReflowState, kid,
   150                                                availSpace);
   151             // Record that we're being optimistic by assuming the kid
   152             // has no clearance
   153             if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
   154               *aMayNeedRetry = true;
   155             }
   156             if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) {
   157               line->MarkDirty();
   158               dirtiedLine = true;
   159             }
   160             if (isEmpty)
   161               aMargin->Include(innerReflowState.ComputedPhysicalMargin().bottom);
   162           }
   163           if (outerReflowState != &aRS) {
   164             delete const_cast<nsHTMLReflowState*>(outerReflowState);
   165           }
   166         }
   167         if (!isEmpty) {
   168           if (!setBlockIsEmpty && aBlockIsEmpty) {
   169             setBlockIsEmpty = true;
   170             *aBlockIsEmpty = false;
   171           }
   172           goto done;
   173         }
   174       }
   175       if (!setBlockIsEmpty && aBlockIsEmpty) {
   176         // The first time we reach here is when this is the first block
   177         // and we have processed all its normal lines.
   178         setBlockIsEmpty = true;
   179         // All lines are empty, or we wouldn't be here!
   180         *aBlockIsEmpty = aRS.frame->IsSelfEmpty();
   181       }
   182     }
   183   }
   184   done:
   186   if (!setBlockIsEmpty && aBlockIsEmpty) {
   187     *aBlockIsEmpty = aRS.frame->IsEmpty();
   188   }
   190 #ifdef NOISY_VERTICAL_MARGINS
   191   nsFrame::ListTag(stdout, aRS.frame);
   192   printf(": => %d\n", aMargin->get());
   193 #endif
   195   return dirtiedLine;
   196 }
   198 nsresult
   199 nsBlockReflowContext::ReflowBlock(const nsRect&       aSpace,
   200                                   bool                aApplyTopMargin,
   201                                   nsCollapsingMargin& aPrevMargin,
   202                                   nscoord             aClearance,
   203                                   bool                aIsAdjacentWithTop,
   204                                   nsLineBox*          aLine,
   205                                   nsHTMLReflowState&  aFrameRS,
   206                                   nsReflowStatus&     aFrameReflowStatus,
   207                                   nsBlockReflowState& aState)
   208 {
   209   nsresult rv = NS_OK;
   210   mFrame = aFrameRS.frame;
   211   mSpace = aSpace;
   213   if (!aIsAdjacentWithTop) {
   214     aFrameRS.mFlags.mIsTopOfPage = false;  // make sure this is cleared
   215   }
   217   if (aApplyTopMargin) {
   218     mTopMargin = aPrevMargin;
   220 #ifdef NOISY_VERTICAL_MARGINS
   221     nsFrame::ListTag(stdout, mOuterReflowState.frame);
   222     printf(": reflowing ");
   223     nsFrame::ListTag(stdout, mFrame);
   224     printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
   225 #endif
   227     // Adjust the available height if its constrained so that the
   228     // child frame doesn't think it can reflow into its margin area.
   229     if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableHeight()) {
   230       aFrameRS.AvailableHeight() -= mTopMargin.get() + aClearance;
   231     }
   232   }
   234   nscoord tx = 0, ty = 0;
   235   // The values of x and y do not matter for floats, so don't bother calculating
   236   // them. Floats are guaranteed to have their own float manager, so tx and ty
   237   // don't matter.  mX and mY don't matter becacuse they are only used in
   238   // PlaceBlock, which is not used for floats.
   239   if (aLine) {
   240     // Compute x/y coordinate where reflow will begin. Use the rules
   241     // from 10.3.3 to determine what to apply. At this point in the
   242     // reflow auto left/right margins will have a zero value.
   244     mX = tx = mSpace.x + aFrameRS.ComputedPhysicalMargin().left;
   245     mY = ty = mSpace.y + mTopMargin.get() + aClearance;
   247     if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
   248       aFrameRS.mBlockDelta =
   249         mOuterReflowState.mBlockDelta + ty - aLine->BStart();
   250   }
   252   // Let frame know that we are reflowing it
   253   mFrame->WillReflow(mPresContext);
   255 #ifdef DEBUG
   256   mMetrics.Width() = nscoord(0xdeadbeef);
   257   mMetrics.Height() = nscoord(0xdeadbeef);
   258 #endif
   260   mOuterReflowState.mFloatManager->Translate(tx, ty);
   261   rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
   262   mOuterReflowState.mFloatManager->Translate(-tx, -ty);
   264 #ifdef DEBUG
   265   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
   266     if (CRAZY_SIZE(mMetrics.Width()) || CRAZY_SIZE(mMetrics.Height())) {
   267       printf("nsBlockReflowContext: ");
   268       nsFrame::ListTag(stdout, mFrame);
   269       printf(" metrics=%d,%d!\n", mMetrics.Width(), mMetrics.Height());
   270     }
   271     if ((mMetrics.Width() == nscoord(0xdeadbeef)) ||
   272         (mMetrics.Height() == nscoord(0xdeadbeef))) {
   273       printf("nsBlockReflowContext: ");
   274       nsFrame::ListTag(stdout, mFrame);
   275       printf(" didn't set w/h %d,%d!\n", mMetrics.Width(), mMetrics.Height());
   276     }
   277   }
   278 #endif
   280   if (!mFrame->HasOverflowAreas()) {
   281     mMetrics.SetOverflowAreasToDesiredBounds();
   282   }
   284   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) ||
   285       (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
   286     // If frame is complete and has a next-in-flow, we need to delete
   287     // them now. Do not do this when a break-before is signaled because
   288     // the frame is going to get reflowed again (and may end up wanting
   289     // a next-in-flow where it ends up), unless it is an out of flow frame.
   290     if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) {
   291       nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
   292       if (nullptr != kidNextInFlow) {
   293         // Remove all of the childs next-in-flows. Make sure that we ask
   294         // the right parent to do the removal (it's possible that the
   295         // parent is not this because we are executing pullup code).
   296         // Floats will eventually be removed via nsBlockFrame::RemoveFloat
   297         // which detaches the placeholder from the float.
   298         nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame);
   299         static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
   300           ->DeleteNextInFlowChild(kidNextInFlow, true);
   301       }
   302     }
   303   }
   305   return rv;
   306 }
   308 /**
   309  * Attempt to place the block frame within the available space.  If
   310  * it fits, apply horizontal positioning (CSS 10.3.3), collapse
   311  * margins (CSS2 8.3.1). Also apply relative positioning.
   312  */
   313 bool
   314 nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState&  aReflowState,
   315                                  bool                      aForceFit,
   316                                  nsLineBox*                aLine,
   317                                  nsCollapsingMargin&       aBottomMarginResult,
   318                                  nsOverflowAreas&          aOverflowAreas,
   319                                  nsReflowStatus            aReflowStatus,
   320                                  nscoord                   aContainerWidth)
   321 {
   322   // Compute collapsed bottom margin value.
   323   if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
   324     aBottomMarginResult = mMetrics.mCarriedOutBottomMargin;
   325     aBottomMarginResult.Include(aReflowState.ComputedPhysicalMargin().bottom);
   326   } else {
   327     // The used bottom-margin is set to zero above a break.
   328     aBottomMarginResult.Zero();
   329   }
   331   nsPoint position(mX, mY);
   332   nscoord backupContainingBlockAdvance = 0;
   334   // Check whether the block's bottom margin collapses with its top
   335   // margin. See CSS 2.1 section 8.3.1; those rules seem to match
   336   // nsBlockFrame::IsEmpty(). Any such block must have zero height so
   337   // check that first. Note that a block can have clearance and still
   338   // have adjoining top/bottom margins, because the clearance goes
   339   // above the top margin.
   340   // Mark the frame as non-dirty; it has been reflowed (or we wouldn't
   341   // be here), and we don't want to assert in CachedIsEmpty()
   342   mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY);
   343   bool empty = 0 == mMetrics.Height() && aLine->CachedIsEmpty();
   344   if (empty) {
   345     // Collapse the bottom margin with the top margin that was already
   346     // applied.
   347     aBottomMarginResult.Include(mTopMargin);
   349 #ifdef NOISY_VERTICAL_MARGINS
   350     printf("  ");
   351     nsFrame::ListTag(stdout, mOuterReflowState.frame);
   352     printf(": ");
   353     nsFrame::ListTag(stdout, mFrame);
   354     printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n",
   355            position.y, mSpace.y);
   356 #endif
   357     // Section 8.3.1 of CSS 2.1 says that blocks with adjoining
   358     // top/bottom margins whose top margin collapses with their
   359     // parent's top margin should have their top border-edge at the
   360     // top border-edge of their parent. We actually don't have to do
   361     // anything special to make this happen. In that situation,
   362     // nsBlockFrame::ShouldApplyTopMargin will have returned false,
   363     // and mTopMargin and aClearance will have been zero in
   364     // ReflowBlock.
   366     // If we did apply our top margin, but now we're collapsing it
   367     // into the bottom margin, we need to back up the containing
   368     // block's y-advance by our top margin so that it doesn't get
   369     // counted twice. Note that here we're allowing the line's bounds
   370     // to become different from the block's position; we do this
   371     // because the containing block will place the next line at the
   372     // line's YMost, and it must place the next line at a different
   373     // point from where this empty block will be.
   374     backupContainingBlockAdvance = mTopMargin.get();
   375   }
   377   // See if the frame fit. If it's the first frame or empty then it
   378   // always fits. If the height is unconstrained then it always fits,
   379   // even if there's some sort of integer overflow that makes y +
   380   // mMetrics.Height() appear to go beyond the available height.
   381   if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) {
   382     nscoord yMost = position.y - backupContainingBlockAdvance + mMetrics.Height();
   383     if (yMost > mSpace.YMost()) {
   384       // didn't fit, we must acquit.
   385       mFrame->DidReflow(mPresContext, &aReflowState, nsDidReflowStatus::FINISHED);
   386       return false;
   387     }
   388   }
   390   aLine->SetBounds(aReflowState.GetWritingMode(),
   391                    nsRect(position.x,
   392                           position.y - backupContainingBlockAdvance,
   393                           mMetrics.Width(),
   394                           mMetrics.Height()),
   395                    aContainerWidth);
   397   aReflowState.ApplyRelativePositioning(&position);
   399   // Now place the frame and complete the reflow process
   400   nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics,
   401                                       &aReflowState, position.x, position.y, 0);
   403   aOverflowAreas = mMetrics.mOverflowAreas + position;
   405   return true;
   406 }

mercurial