LCOV - code coverage report
Current view: directory - layout/generic - nsBlockReflowContext.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 140 0 0.0 %
Date: 2012-06-02 Functions: 5 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : // vim:cindent:ts=2:et:sw=2:
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla Communicator client code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications Corporation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   David Baron <dbaron@dbaron.org>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : /* class that a parent frame uses to reflow a block frame */
      41                 : 
      42                 : #include "nsBlockReflowContext.h"
      43                 : #include "nsLineLayout.h"
      44                 : #include "nsFloatManager.h"
      45                 : #include "nsPresContext.h"
      46                 : #include "nsFrameManager.h"
      47                 : #include "nsIContent.h"
      48                 : #include "nsStyleContext.h"
      49                 : #include "nsContainerFrame.h"
      50                 : #include "nsBlockFrame.h"
      51                 : #include "nsLineBox.h"
      52                 : #include "nsIDOMHTMLTableCellElement.h"
      53                 : #include "nsIDOMHTMLBodyElement.h"
      54                 : #include "nsGkAtoms.h"
      55                 : #include "nsCOMPtr.h"
      56                 : #include "nsLayoutUtils.h"
      57                 : 
      58                 : #ifdef NS_DEBUG
      59                 : #undef  NOISY_MAX_ELEMENT_SIZE
      60                 : #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
      61                 : #undef  NOISY_VERTICAL_MARGINS
      62                 : #else
      63                 : #undef  NOISY_MAX_ELEMENT_SIZE
      64                 : #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
      65                 : #undef  NOISY_VERTICAL_MARGINS
      66                 : #endif
      67                 : 
      68               0 : nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
      69                 :                                            const nsHTMLReflowState& aParentRS)
      70                 :   : mPresContext(aPresContext),
      71                 :     mOuterReflowState(aParentRS),
      72               0 :     mMetrics()
      73                 : {
      74               0 : }
      75                 : 
      76               0 : static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
      77                 : {
      78               0 :   nsIAtom* type = aFrame->GetType();
      79               0 :   if (type == nsGkAtoms::columnSetFrame)
      80               0 :     return DescendIntoBlockLevelFrame(aFrame->GetFirstPrincipalChild());
      81               0 :   return aFrame;
      82                 : }
      83                 : 
      84                 : bool
      85               0 : nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
      86                 :   nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame,
      87                 :   bool* aMayNeedRetry, bool* aBlockIsEmpty)
      88                 : {
      89                 :   // Include frame's top margin
      90               0 :   aMargin->Include(aRS.mComputedMargin.top);
      91                 : 
      92                 :   // The inclusion of the bottom margin when empty is done by the caller
      93                 :   // since it doesn't need to be done by the top-level (non-recursive)
      94                 :   // caller.
      95                 : 
      96                 : #ifdef NOISY_VERTICAL_MARGINS
      97                 :   nsFrame::ListTag(stdout, aRS.frame);
      98                 :   printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get());
      99                 : #endif
     100                 : 
     101               0 :   bool dirtiedLine = false;
     102               0 :   bool setBlockIsEmpty = false;
     103                 : 
     104                 :   // Calculate the frame's generational top-margin from its child
     105                 :   // blocks. Note that if the frame has a non-zero top-border or
     106                 :   // top-padding then this step is skipped because it will be a margin
     107                 :   // root.  It is also skipped if the frame is a margin root for other
     108                 :   // reasons.
     109               0 :   nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame);
     110               0 :   nsPresContext* prescontext = frame->PresContext();
     111               0 :   if (0 == aRS.mComputedBorderPadding.top &&
     112               0 :       nsLayoutUtils::GetAsBlock(frame) &&
     113               0 :       !nsBlockFrame::BlockIsMarginRoot(frame)) {
     114                 :     // iterate not just through the lines of 'block' but also its
     115                 :     // overflow lines and the normal and overflow lines of its next in
     116                 :     // flows. Note that this will traverse some frames more than once:
     117                 :     // for example, if A contains B and A->nextinflow contains
     118                 :     // B->nextinflow, we'll traverse B->nextinflow twice. But this is
     119                 :     // OK because our traversal is idempotent.
     120               0 :     for (nsBlockFrame* block = static_cast<nsBlockFrame*>(frame);
     121               0 :          block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) {
     122               0 :       for (PRIntn overflowLines = false; overflowLines <= true; ++overflowLines) {
     123               0 :         nsBlockFrame::line_iterator line;
     124               0 :         nsBlockFrame::line_iterator line_end;
     125               0 :         bool anyLines = true;
     126               0 :         if (overflowLines) {
     127               0 :           nsBlockFrame::FrameLines* frames = block->GetOverflowLines();
     128               0 :           nsLineList* lines = frames ? &frames->mLines : nsnull;
     129               0 :           if (!lines) {
     130               0 :             anyLines = false;
     131                 :           } else {
     132               0 :             line = lines->begin();
     133               0 :             line_end = lines->end();
     134                 :           }
     135                 :         } else {
     136               0 :           line = block->begin_lines();
     137               0 :           line_end = block->end_lines();
     138                 :         }
     139               0 :         for (; anyLines && line != line_end; ++line) {
     140               0 :           if (!aClearanceFrame && line->HasClearance()) {
     141                 :             // If we don't have a clearance frame, then we're computing
     142                 :             // the collapsed margin in the first pass, assuming that all
     143                 :             // lines have no clearance. So clear their clearance flags.
     144               0 :             line->ClearHasClearance();
     145               0 :             line->MarkDirty();
     146               0 :             dirtiedLine = true;
     147                 :           }
     148                 :           
     149                 :           bool isEmpty;
     150               0 :           if (line->IsInline()) {
     151               0 :             isEmpty = line->IsEmpty();
     152                 :           } else {
     153               0 :             nsIFrame* kid = line->mFirstChild;
     154               0 :             if (kid == aClearanceFrame) {
     155               0 :               line->SetHasClearance();
     156               0 :               line->MarkDirty();
     157               0 :               dirtiedLine = true;
     158               0 :               goto done;
     159                 :             }
     160                 :             // Here is where we recur. Now that we have determined that a
     161                 :             // generational collapse is required we need to compute the
     162                 :             // child blocks margin and so in so that we can look into
     163                 :             // it. For its margins to be computed we need to have a reflow
     164                 :             // state for it.
     165                 :             
     166                 :             // We may have to construct an extra reflow state here if
     167                 :             // we drilled down through a block wrapper. At the moment
     168                 :             // we can only drill down one level so we only have to support
     169                 :             // one extra reflow state.
     170               0 :             const nsHTMLReflowState* outerReflowState = &aRS;
     171               0 :             if (frame != aRS.frame) {
     172               0 :               NS_ASSERTION(frame->GetParent() == aRS.frame,
     173                 :                            "Can only drill through one level of block wrapper");
     174               0 :               nsSize availSpace(aRS.ComputedWidth(), aRS.ComputedHeight());
     175                 :               outerReflowState = new nsHTMLReflowState(prescontext,
     176               0 :                                                        aRS, frame, availSpace);
     177                 :             }
     178                 :             {
     179                 :               nsSize availSpace(outerReflowState->ComputedWidth(),
     180               0 :                                 outerReflowState->ComputedHeight());
     181                 :               nsHTMLReflowState innerReflowState(prescontext,
     182                 :                                                  *outerReflowState, kid,
     183               0 :                                                  availSpace);
     184                 :               // Record that we're being optimistic by assuming the kid
     185                 :               // has no clearance
     186               0 :               if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
     187               0 :                 *aMayNeedRetry = true;
     188                 :               }
     189               0 :               if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) {
     190               0 :                 line->MarkDirty();
     191               0 :                 dirtiedLine = true;
     192                 :               }
     193               0 :               if (isEmpty)
     194               0 :                 aMargin->Include(innerReflowState.mComputedMargin.bottom);
     195                 :             }
     196               0 :             if (outerReflowState != &aRS) {
     197               0 :               delete const_cast<nsHTMLReflowState*>(outerReflowState);
     198                 :             }
     199                 :           }
     200               0 :           if (!isEmpty) {
     201               0 :             if (!setBlockIsEmpty && aBlockIsEmpty) {
     202               0 :               setBlockIsEmpty = true;
     203               0 :               *aBlockIsEmpty = false;
     204                 :             }
     205               0 :             goto done;
     206                 :           }
     207                 :         }
     208               0 :         if (!setBlockIsEmpty && aBlockIsEmpty) {
     209                 :           // The first time we reach here is when this is the first block
     210                 :           // and we have processed all its normal lines.
     211               0 :           setBlockIsEmpty = true;
     212                 :           // All lines are empty, or we wouldn't be here!
     213               0 :           *aBlockIsEmpty = aRS.frame->IsSelfEmpty();
     214                 :         }
     215                 :       }
     216                 :     }
     217                 :   done:
     218                 :     ;
     219                 :   }
     220                 : 
     221               0 :   if (!setBlockIsEmpty && aBlockIsEmpty) {
     222               0 :     *aBlockIsEmpty = aRS.frame->IsEmpty();
     223                 :   }
     224                 :   
     225                 : #ifdef NOISY_VERTICAL_MARGINS
     226                 :   nsFrame::ListTag(stdout, aRS.frame);
     227                 :   printf(": => %d\n", aMargin->get());
     228                 : #endif
     229                 : 
     230               0 :   return dirtiedLine;
     231                 : }
     232                 : 
     233                 : nsresult
     234               0 : nsBlockReflowContext::ReflowBlock(const nsRect&       aSpace,
     235                 :                                   bool                aApplyTopMargin,
     236                 :                                   nsCollapsingMargin& aPrevMargin,
     237                 :                                   nscoord             aClearance,
     238                 :                                   bool                aIsAdjacentWithTop,
     239                 :                                   nsLineBox*          aLine,
     240                 :                                   nsHTMLReflowState&  aFrameRS,
     241                 :                                   nsReflowStatus&     aFrameReflowStatus,
     242                 :                                   nsBlockReflowState& aState)
     243                 : {
     244               0 :   nsresult rv = NS_OK;
     245               0 :   mFrame = aFrameRS.frame;
     246               0 :   mSpace = aSpace;
     247                 : 
     248               0 :   if (!aIsAdjacentWithTop) {
     249               0 :     aFrameRS.mFlags.mIsTopOfPage = false;  // make sure this is cleared
     250                 :   }
     251                 : 
     252               0 :   if (aApplyTopMargin) {
     253               0 :     mTopMargin = aPrevMargin;
     254                 : 
     255                 : #ifdef NOISY_VERTICAL_MARGINS
     256                 :     nsFrame::ListTag(stdout, mOuterReflowState.frame);
     257                 :     printf(": reflowing ");
     258                 :     nsFrame::ListTag(stdout, mFrame);
     259                 :     printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
     260                 : #endif
     261                 : 
     262                 :     // Adjust the available height if its constrained so that the
     263                 :     // child frame doesn't think it can reflow into its margin area.
     264               0 :     if (NS_UNCONSTRAINEDSIZE != aFrameRS.availableHeight) {
     265               0 :       aFrameRS.availableHeight -= mTopMargin.get() + aClearance;
     266                 :     }
     267                 :   }
     268                 : 
     269               0 :   nscoord tx = 0, ty = 0;
     270                 :   // The values of x and y do not matter for floats, so don't bother calculating
     271                 :   // them. Floats are guaranteed to have their own float manager, so tx and ty
     272                 :   // don't matter.  mX and mY don't matter becacuse they are only used in
     273                 :   // PlaceBlock, which is not used for floats.
     274               0 :   if (aLine) {
     275                 :     // Compute x/y coordinate where reflow will begin. Use the rules
     276                 :     // from 10.3.3 to determine what to apply. At this point in the
     277                 :     // reflow auto left/right margins will have a zero value.
     278                 : 
     279               0 :     mX = tx = mSpace.x + aFrameRS.mComputedMargin.left;
     280               0 :     mY = ty = mSpace.y + mTopMargin.get() + aClearance;
     281                 : 
     282               0 :     if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
     283               0 :       aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + ty - aLine->mBounds.y;
     284                 :   }
     285                 : 
     286                 :   // Let frame know that we are reflowing it
     287               0 :   mFrame->WillReflow(mPresContext);
     288                 : 
     289                 : #ifdef DEBUG
     290               0 :   mMetrics.width = nscoord(0xdeadbeef);
     291               0 :   mMetrics.height = nscoord(0xdeadbeef);
     292                 : #endif
     293                 : 
     294               0 :   mOuterReflowState.mFloatManager->Translate(tx, ty);
     295               0 :   rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
     296               0 :   mOuterReflowState.mFloatManager->Translate(-tx, -ty);
     297                 : 
     298                 : #ifdef DEBUG
     299               0 :   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
     300               0 :     if (CRAZY_WIDTH(mMetrics.width) || CRAZY_HEIGHT(mMetrics.height)) {
     301               0 :       printf("nsBlockReflowContext: ");
     302               0 :       nsFrame::ListTag(stdout, mFrame);
     303               0 :       printf(" metrics=%d,%d!\n", mMetrics.width, mMetrics.height);
     304                 :     }
     305               0 :     if ((mMetrics.width == nscoord(0xdeadbeef)) ||
     306                 :         (mMetrics.height == nscoord(0xdeadbeef))) {
     307               0 :       printf("nsBlockReflowContext: ");
     308               0 :       nsFrame::ListTag(stdout, mFrame);
     309               0 :       printf(" didn't set w/h %d,%d!\n", mMetrics.width, mMetrics.height);
     310                 :     }
     311                 :   }
     312                 : #endif
     313                 : 
     314               0 :   if (!mFrame->HasOverflowAreas()) {
     315               0 :     mMetrics.SetOverflowAreasToDesiredBounds();
     316                 :   }
     317                 : 
     318               0 :   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) ||
     319               0 :       (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
     320                 :     // If frame is complete and has a next-in-flow, we need to delete
     321                 :     // them now. Do not do this when a break-before is signaled because
     322                 :     // the frame is going to get reflowed again (and may end up wanting
     323                 :     // a next-in-flow where it ends up), unless it is an out of flow frame.
     324               0 :     if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) {
     325               0 :       nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
     326               0 :       if (nsnull != kidNextInFlow) {
     327                 :         // Remove all of the childs next-in-flows. Make sure that we ask
     328                 :         // the right parent to do the removal (it's possible that the
     329                 :         // parent is not this because we are executing pullup code).
     330                 :         // Floats will eventually be removed via nsBlockFrame::RemoveFloat
     331                 :         // which detaches the placeholder from the float.
     332                 : /* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */
     333               0 :         aState.mOverflowTracker->Finish(mFrame);
     334               0 :         static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
     335               0 :           ->DeleteNextInFlowChild(mPresContext, kidNextInFlow, true);
     336                 :       }
     337                 :     }
     338                 :   }
     339                 : 
     340               0 :   return rv;
     341                 : }
     342                 : 
     343                 : /**
     344                 :  * Attempt to place the block frame within the available space.  If
     345                 :  * it fits, apply horizontal positioning (CSS 10.3.3), collapse
     346                 :  * margins (CSS2 8.3.1). Also apply relative positioning.
     347                 :  */
     348                 : bool
     349               0 : nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
     350                 :                                  bool                     aForceFit,
     351                 :                                  nsLineBox*               aLine,
     352                 :                                  nsCollapsingMargin&      aBottomMarginResult,
     353                 :                                  nsRect&                  aInFlowBounds,
     354                 :                                  nsOverflowAreas&         aOverflowAreas,
     355                 :                                  nsReflowStatus           aReflowStatus)
     356                 : {
     357                 :   // Compute collapsed bottom margin value.
     358               0 :   if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
     359               0 :     aBottomMarginResult = mMetrics.mCarriedOutBottomMargin;
     360               0 :     aBottomMarginResult.Include(aReflowState.mComputedMargin.bottom);
     361                 :   } else {
     362                 :     // The used bottom-margin is set to zero above a break.
     363               0 :     aBottomMarginResult.Zero();
     364                 :   }
     365                 : 
     366               0 :   nscoord x = mX;
     367               0 :   nscoord y = mY;
     368               0 :   nscoord backupContainingBlockAdvance = 0;
     369                 : 
     370                 :   // Check whether the block's bottom margin collapses with its top
     371                 :   // margin. See CSS 2.1 section 8.3.1; those rules seem to match
     372                 :   // nsBlockFrame::IsEmpty(). Any such block must have zero height so
     373                 :   // check that first. Note that a block can have clearance and still
     374                 :   // have adjoining top/bottom margins, because the clearance goes
     375                 :   // above the top margin.
     376                 :   // Mark the frame as non-dirty; it has been reflowed (or we wouldn't
     377                 :   // be here), and we don't want to assert in CachedIsEmpty()
     378               0 :   mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY);
     379               0 :   bool empty = 0 == mMetrics.height && aLine->CachedIsEmpty();
     380               0 :   if (empty) {
     381                 :     // Collapse the bottom margin with the top margin that was already
     382                 :     // applied.
     383               0 :     aBottomMarginResult.Include(mTopMargin);
     384                 : 
     385                 : #ifdef NOISY_VERTICAL_MARGINS
     386                 :     printf("  ");
     387                 :     nsFrame::ListTag(stdout, mOuterReflowState.frame);
     388                 :     printf(": ");
     389                 :     nsFrame::ListTag(stdout, mFrame);
     390                 :     printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n",
     391                 :            y, mSpace.y);
     392                 : #endif
     393                 :     // Section 8.3.1 of CSS 2.1 says that blocks with adjoining
     394                 :     // top/bottom margins whose top margin collapses with their
     395                 :     // parent's top margin should have their top border-edge at the
     396                 :     // top border-edge of their parent. We actually don't have to do
     397                 :     // anything special to make this happen. In that situation,
     398                 :     // nsBlockFrame::ShouldApplyTopMargin will have returned false,
     399                 :     // and mTopMargin and aClearance will have been zero in
     400                 :     // ReflowBlock.
     401                 : 
     402                 :     // If we did apply our top margin, but now we're collapsing it
     403                 :     // into the bottom margin, we need to back up the containing
     404                 :     // block's y-advance by our top margin so that it doesn't get
     405                 :     // counted twice. Note that here we're allowing the line's bounds
     406                 :     // to become different from the block's position; we do this
     407                 :     // because the containing block will place the next line at the
     408                 :     // line's YMost, and it must place the next line at a different
     409                 :     // point from where this empty block will be.
     410               0 :     backupContainingBlockAdvance = mTopMargin.get();
     411                 :   }
     412                 : 
     413                 :   // See if the frame fit. If it's the first frame or empty then it
     414                 :   // always fits. If the height is unconstrained then it always fits,
     415                 :   // even if there's some sort of integer overflow that makes y +
     416                 :   // mMetrics.height appear to go beyond the available height.
     417               0 :   if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) {
     418               0 :     nscoord yMost = y - backupContainingBlockAdvance + mMetrics.height;
     419               0 :     if (yMost > mSpace.YMost()) {
     420                 :       // didn't fit, we must acquit.
     421               0 :       mFrame->DidReflow(mPresContext, &aReflowState, NS_FRAME_REFLOW_FINISHED);
     422               0 :       return false;
     423                 :     }
     424                 :   }
     425                 : 
     426                 :   aInFlowBounds = nsRect(x, y - backupContainingBlockAdvance,
     427               0 :                          mMetrics.width, mMetrics.height);
     428                 :   
     429                 :   // Apply CSS relative positioning
     430               0 :   const nsStyleDisplay* styleDisp = mFrame->GetStyleDisplay();
     431               0 :   if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) {
     432               0 :     x += aReflowState.mComputedOffsets.left;
     433               0 :     y += aReflowState.mComputedOffsets.top;
     434                 :   }
     435                 :   
     436                 :   // Now place the frame and complete the reflow process
     437               0 :   nsContainerFrame::FinishReflowChild(mFrame, mPresContext, &aReflowState, mMetrics, x, y, 0);
     438                 : 
     439               0 :   aOverflowAreas = mMetrics.mOverflowAreas + nsPoint(x, y);
     440                 : 
     441               0 :   return true;
     442                 : }

Generated by: LCOV version 1.7