Back to index

lightning-sunbird  0.9+nobinonly
nsBlockReflowContext.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 // vim:cindent:ts=2:et:sw=2:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   David Baron <dbaron@dbaron.org>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsBlockReflowContext.h"
00040 #include "nsLineLayout.h"
00041 #include "nsSpaceManager.h"
00042 #include "nsIFontMetrics.h"
00043 #include "nsPresContext.h"
00044 #include "nsFrameManager.h"
00045 #include "nsIContent.h"
00046 #include "nsStyleContext.h"
00047 #include "nsHTMLReflowCommand.h"
00048 #include "nsHTMLContainerFrame.h"
00049 #include "nsBlockFrame.h"
00050 #include "nsLineBox.h"
00051 #include "nsIDOMHTMLTableCellElement.h"
00052 #include "nsIDOMHTMLBodyElement.h"
00053 #include "nsLayoutAtoms.h"
00054 #include "nsCOMPtr.h"
00055 #include "nsLayoutUtils.h"
00056 
00057 #ifdef NS_DEBUG
00058 #undef  NOISY_MAX_ELEMENT_SIZE
00059 #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
00060 #undef  NOISY_VERTICAL_MARGINS
00061 #else
00062 #undef  NOISY_MAX_ELEMENT_SIZE
00063 #undef   REALLY_NOISY_MAX_ELEMENT_SIZE
00064 #undef  NOISY_VERTICAL_MARGINS
00065 #endif
00066 
00067 nsBlockReflowContext::nsBlockReflowContext(nsPresContext* aPresContext,
00068                                            const nsHTMLReflowState& aParentRS,
00069                                            PRBool aComputeMaxElementWidth,
00070                                            PRBool aComputeMaximumWidth)
00071   : mPresContext(aPresContext),
00072     mOuterReflowState(aParentRS),
00073     mMetrics(aComputeMaxElementWidth),
00074     mComputeMaximumWidth(aComputeMaximumWidth)
00075 {
00076   mStyleBorder = nsnull;
00077   mStyleMargin = nsnull;
00078   mStylePadding = nsnull;
00079   if (mComputeMaximumWidth)
00080     mMetrics.mFlags |= NS_REFLOW_CALC_MAX_WIDTH;
00081 }
00082 
00083 static nsIFrame* DescendIntoBlockLevelFrame(nsIFrame* aFrame)
00084 {
00085   nsIAtom* type = aFrame->GetType();
00086   if (type == nsLayoutAtoms::columnSetFrame)
00087     return DescendIntoBlockLevelFrame(aFrame->GetFirstChild(nsnull));
00088   return aFrame;
00089 }
00090 
00091 PRBool
00092 nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS,
00093   nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, PRBool* aMayNeedRetry)
00094 {
00095   // Include frame's top margin
00096   aMargin->Include(aRS.mComputedMargin.top);
00097 
00098   // The inclusion of the bottom margin when empty is done by the caller
00099   // since it doesn't need to be done by the top-level (non-recursive)
00100   // caller.
00101 
00102 #ifdef NOISY_VERTICAL_MARGINS
00103   nsFrame::ListTag(stdout, aRS.frame);
00104   printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get());
00105 #endif
00106 
00107   PRBool dirtiedLine = PR_FALSE;
00108 
00109   // Calculate the frame's generational top-margin from its child
00110   // blocks. Note that if the frame has a non-zero top-border or
00111   // top-padding then this step is skipped because it will be a margin
00112   // root.  It is also skipped if the frame is a margin root for other
00113   // reasons.
00114   void* bf;
00115   nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame);
00116   nsPresContext* prescontext = frame->GetPresContext();
00117   if (0 == aRS.mComputedBorderPadding.top &&
00118       !(frame->GetStateBits() & NS_BLOCK_MARGIN_ROOT) &&
00119       NS_SUCCEEDED(frame->QueryInterface(kBlockFrameCID, &bf))) {
00120     // iterate not just through the lines of 'block' but also its
00121     // overflow lines and the normal and overflow lines of its next in
00122     // flows. Note that this will traverse some frames more than once:
00123     // for example, if A contains B and A->nextinflow contains
00124     // B->nextinflow, we'll traverse B->nextinflow twice. But this is
00125     // OK because our traversal is idempotent.
00126     for (nsBlockFrame* block = NS_STATIC_CAST(nsBlockFrame*, frame);
00127          block; block = NS_STATIC_CAST(nsBlockFrame*, block->GetNextInFlow())) {
00128       for (PRBool overflowLines = PR_FALSE; overflowLines <= PR_TRUE; ++overflowLines) {
00129         nsBlockFrame::line_iterator line;
00130         nsBlockFrame::line_iterator line_end;
00131         PRBool anyLines = PR_TRUE;
00132         if (overflowLines) {
00133           nsLineList* lines = block->GetOverflowLines();
00134           if (!lines) {
00135             anyLines = PR_FALSE;
00136           } else {
00137             line = lines->begin();
00138             line_end = lines->end();
00139           }
00140         } else {
00141           line = block->begin_lines();
00142           line_end = block->end_lines();
00143         }
00144         for (; anyLines && line != line_end; ++line) {
00145           if (!aClearanceFrame && line->HasClearance()) {
00146             // If we don't have a clearance frame, then we're computing
00147             // the collapsed margin in the first pass, assuming that all
00148             // lines have no clearance. So clear their clearance flags.
00149             line->ClearHasClearance();
00150             line->MarkDirty();
00151             dirtiedLine = PR_TRUE;
00152           }
00153           
00154           PRBool isEmpty = line->IsEmpty();
00155           if (line->IsBlock()) {
00156             nsIFrame* kid = line->mFirstChild;
00157             if (kid == aClearanceFrame) {
00158               line->SetHasClearance();
00159               line->MarkDirty();
00160               dirtiedLine = PR_TRUE;
00161               goto done;
00162             }
00163             // Here is where we recur. Now that we have determined that a
00164             // generational collapse is required we need to compute the
00165             // child blocks margin and so in so that we can look into
00166             // it. For its margins to be computed we need to have a reflow
00167             // state for it. Since the reflow reason is irrelevant, we'll
00168             // arbitrarily make it a `resize' to avoid the path-plucking
00169             // behavior if we're in an incremental reflow.
00170             
00171             // We may have to construct an extra reflow state here if
00172             // we drilled down through a block wrapper. At the moment
00173             // we can only drill down one level so we only have to support
00174             // one extra reflow state.
00175             const nsHTMLReflowState* outerReflowState = &aRS;
00176             if (frame != aRS.frame) {
00177               NS_ASSERTION(frame->GetParent() == aRS.frame,
00178                            "Can only drill through one level of block wrapper");
00179               nsSize availSpace(aRS.mComputedWidth, aRS.mComputedHeight);
00180               outerReflowState = new nsHTMLReflowState(prescontext,
00181                                                      aRS, frame,
00182                                                      availSpace, eReflowReason_Resize);
00183               if (!outerReflowState)
00184                 goto done;
00185             }
00186             {
00187               nsSize availSpace(outerReflowState->mComputedWidth,
00188                                 outerReflowState->mComputedHeight);
00189               nsHTMLReflowState innerReflowState(prescontext,
00190                                                  *outerReflowState, kid,
00191                                                  availSpace, eReflowReason_Resize);
00192               // Record that we're being optimistic by assuming the kid
00193               // has no clearance
00194               if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
00195                 *aMayNeedRetry = PR_TRUE;
00196               }
00197               if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry)) {
00198                 line->MarkDirty();
00199                 dirtiedLine = PR_TRUE;
00200               }
00201               if (isEmpty)
00202                 aMargin->Include(innerReflowState.mComputedMargin.bottom);
00203             }
00204             if (outerReflowState != &aRS) {
00205               delete NS_CONST_CAST(nsHTMLReflowState*, outerReflowState);
00206             }
00207           }
00208           if (!isEmpty)
00209             goto done;
00210         }
00211       }
00212     }
00213   done:
00214     ;
00215   }
00216   
00217 #ifdef NOISY_VERTICAL_MARGINS
00218   nsFrame::ListTag(stdout, aRS.frame);
00219   printf(": => %d\n", aMargin->get());
00220 #endif
00221 
00222   return dirtiedLine;
00223 }
00224 
00225 struct nsBlockHorizontalAlign {
00226   nscoord mXOffset;  // left edge
00227   nscoord mLeftMargin;
00228   nscoord mRightMargin;
00229 };
00230 
00231 // Given the width of the block frame and a suggested x-offset calculate
00232 // the actual x-offset taking into account horizontal alignment. Also returns
00233 // the actual left and right margin
00234 void
00235 nsBlockReflowContext::AlignBlockHorizontally(nscoord                 aWidth,
00236                                              nsBlockHorizontalAlign &aAlign)
00237 {
00238   // Initialize OUT parameters
00239   aAlign.mLeftMargin = mMargin.left;
00240   aAlign.mRightMargin = mMargin.right;
00241 
00242   // Get style unit associated with the left and right margins
00243   nsStyleUnit leftUnit = mStyleMargin->mMargin.GetLeftUnit();
00244   nsStyleUnit rightUnit = mStyleMargin->mMargin.GetRightUnit();
00245 
00246   // Apply post-reflow horizontal alignment. When a block element
00247   // doesn't use it all of the available width then we need to
00248   // align it using the text-align property.
00249   if (NS_UNCONSTRAINEDSIZE != mSpace.width &&
00250       NS_UNCONSTRAINEDSIZE != mOuterReflowState.mComputedWidth) {
00251     // It is possible that the object reflowed was given a
00252     // constrained width and ended up picking a different width
00253     // (e.g. a table width a set width that ended up larger
00254     // because its contents required it). When this happens we
00255     // need to recompute auto margins because the reflow state's
00256     // computations are no longer valid.
00257     if (aWidth != mComputedWidth) {
00258       if (eStyleUnit_Auto == leftUnit) {
00259         aAlign.mXOffset = mSpace.x;
00260         aAlign.mLeftMargin = 0;
00261       }
00262       if (eStyleUnit_Auto == rightUnit) {
00263         aAlign.mRightMargin = 0;
00264       }
00265     }
00266 
00267     // Compute how much remaining space there is, and in special
00268     // cases apply it (normally we should get zero here because of
00269     // the logic in nsHTMLReflowState).
00270     nscoord remainingSpace = mSpace.XMost() - (aAlign.mXOffset + aWidth +
00271                              aAlign.mRightMargin);
00272     if (remainingSpace > 0) {
00273       // The block/table frame didn't use all of the available
00274       // space. Synthesize margins for its horizontal placement.
00275       if (eStyleUnit_Auto == leftUnit) {
00276         if (eStyleUnit_Auto == rightUnit) {
00277           // When both margins are auto, we center the block
00278           aAlign.mXOffset += remainingSpace / 2;
00279         }
00280         else {
00281           // When the left margin is auto we right align the block
00282           aAlign.mXOffset += remainingSpace;
00283         }
00284       }
00285       else if (eStyleUnit_Auto != rightUnit) {
00286         // The block/table doesn't have auto margins.
00287 
00288         // For normal (non-table) blocks we don't get here because
00289         // nsHTMLReflowState::CalculateBlockSideMargins handles this.
00290         // (I think there may be an exception to that, though...)
00291 
00292         // We use a special value of the text-align property for
00293         // HTML alignment (the CENTER element and DIV ALIGN=...)
00294         // since it acts on blocks and tables rather than just
00295         // being a text-align.
00296         // So, check the text-align value from the parent to see if
00297         // it has one of these special values.
00298         const nsStyleText* styleText = mOuterReflowState.mStyleText;
00299         if (styleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) {
00300           aAlign.mXOffset += remainingSpace;
00301         } else if (styleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
00302           aAlign.mXOffset += remainingSpace / 2;
00303         } else if (styleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT) {
00304           // If we don't have a special text-align value indicating
00305           // HTML alignment, then use the CSS rules.
00306 
00307           // When neither margin is auto then the block is said to
00308           // be over constrained, Depending on the direction, choose
00309           // which margin to treat as auto.
00310           PRUint8 direction = mOuterReflowState.mStyleVisibility->mDirection;
00311           if (NS_STYLE_DIRECTION_RTL == direction) {
00312             // The left margin becomes auto
00313             aAlign.mXOffset += remainingSpace;
00314           }
00315           //else {
00316             // The right margin becomes auto which is a no-op
00317           //}
00318         }
00319       }
00320     }
00321   }
00322 }
00323 
00324 static void
00325 ComputeShrinkwrapMargins(const nsStyleMargin* aStyleMargin, nscoord aWidth,
00326                          nsMargin& aMargin, nscoord& aXToUpdate)
00327 {
00328   nscoord boxWidth = aWidth;
00329   float leftPct = 0.0, rightPct = 0.0;
00330   const nsStyleSides& margin = aStyleMargin->mMargin;
00331   
00332   if (eStyleUnit_Percent == margin.GetLeftUnit()) {
00333     nsStyleCoord coord;
00334     leftPct = margin.GetLeft(coord).GetPercentValue();
00335   } else {
00336     boxWidth += aMargin.left;
00337   }
00338   
00339   if (eStyleUnit_Percent == margin.GetRightUnit()) {
00340     nsStyleCoord coord;
00341     rightPct = margin.GetRight(coord).GetPercentValue();
00342   } else {
00343     boxWidth += aMargin.right;
00344   }
00345   
00346   // The total shrink wrap width "sww" (i.e., the width that the
00347   // containing block needs to be to shrink-wrap this block) is
00348   // calculated by the expression:
00349   //   sww = bw + (mp * sww)
00350   // where "bw" is the box width (frame width plus margins that aren't
00351   // percentage based) and "mp" are the total margin percentages (i.e.,
00352   // the left percentage value plus the right percentage value).
00353   // Solving for "sww" gives:
00354   //  sww = bw / (1 - mp)
00355   // Note that this is only well defined for "mp" less than 100% and 
00356   // greater than -100% (XXXldb but we only accept 0 to 100%).
00357 
00358   float marginPct = leftPct + rightPct;
00359   if (marginPct >= 1.0) {
00360     // Ignore the right percentage and just use the left percentage
00361     // XXX Pay attention to direction property...
00362     marginPct = leftPct;
00363     rightPct = 0.0;
00364   }
00365   
00366   if ((marginPct > 0.0) && (marginPct < 1.0)) {
00367     double shrinkWrapWidth = float(boxWidth) / (1.0 - marginPct);
00368     
00369     if (eStyleUnit_Percent == margin.GetLeftUnit()) {
00370       aMargin.left = NSToCoordFloor((float)(shrinkWrapWidth * leftPct));
00371       aXToUpdate += aMargin.left;
00372     }
00373     if (eStyleUnit_Percent == margin.GetRightUnit()) {
00374       aMargin.right = NSToCoordFloor((float)(shrinkWrapWidth * rightPct));
00375     }
00376   }
00377 }
00378 
00379 static void
00380 nsPointDtor(void *aFrame, nsIAtom *aPropertyName,
00381             void *aPropertyValue, void *aDtorData)
00382 {
00383   nsPoint *point = NS_STATIC_CAST(nsPoint*, aPropertyValue);
00384   delete point;
00385 }
00386 
00387 nsresult
00388 nsBlockReflowContext::ReflowBlock(const nsRect&       aSpace,
00389                                   PRBool              aApplyTopMargin,
00390                                   nsCollapsingMargin& aPrevMargin,
00391                                   nscoord             aClearance,
00392                                   PRBool              aIsAdjacentWithTop,
00393                                   nsMargin&           aComputedOffsets,
00394                                   nsHTMLReflowState&  aFrameRS,
00395                                   nsReflowStatus&     aFrameReflowStatus)
00396 {
00397   nsresult rv = NS_OK;
00398   mFrame = aFrameRS.frame;
00399   mSpace = aSpace;
00400 
00401   // Get reflow reason set correctly. It's possible that a child was
00402   // created and then it was decided that it could not be reflowed
00403   // (for example, a block frame that isn't at the start of a
00404   // line). In this case the reason will be wrong so we need to check
00405   // the frame state.
00406   aFrameRS.reason = eReflowReason_Resize;
00407   if (NS_FRAME_FIRST_REFLOW & mFrame->GetStateBits()) {
00408     aFrameRS.reason = eReflowReason_Initial;
00409   }
00410   else if (mOuterReflowState.reason == eReflowReason_Incremental) {
00411     // If the frame we're about to reflow is on the reflow path, then
00412     // propagate the reflow as `incremental' so it unwinds correctly
00413     // to the target frames below us.
00414     PRBool frameIsOnReflowPath = mOuterReflowState.path->HasChild(mFrame);
00415     if (frameIsOnReflowPath)
00416       aFrameRS.reason = eReflowReason_Incremental;
00417 
00418     // But...if the incremental reflow command is a StyleChanged
00419     // reflow and its target is the current block, change the reason
00420     // to `style change', so that it propagates through the entire
00421     // subtree.
00422     nsHTMLReflowCommand* rc = mOuterReflowState.path->mReflowCommand;
00423     if (rc) {
00424       nsReflowType type;
00425       rc->GetType(type);
00426       if (type == eReflowType_StyleChanged)
00427         aFrameRS.reason = eReflowReason_StyleChange;
00428       else if (type == eReflowType_ReflowDirty &&
00429                (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
00430                !frameIsOnReflowPath) {
00431         aFrameRS.reason = eReflowReason_Dirty;
00432       }
00433     }
00434   }
00435   else if (mOuterReflowState.reason == eReflowReason_StyleChange) {
00436     aFrameRS.reason = eReflowReason_StyleChange;
00437   }
00438   else if (mOuterReflowState.reason == eReflowReason_Dirty) {
00439     if (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY)
00440       aFrameRS.reason = eReflowReason_Dirty;
00441   }
00442 
00443   const nsStyleDisplay* display = mFrame->GetStyleDisplay();
00444 
00445   aComputedOffsets = aFrameRS.mComputedOffsets;
00446   if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
00447     nsPropertyTable *propTable = mPresContext->PropertyTable();
00448 
00449     nsPoint *offsets = NS_STATIC_CAST(nsPoint*,
00450         propTable->GetProperty(mFrame, nsLayoutAtoms::computedOffsetProperty));
00451 
00452     if (offsets)
00453       offsets->MoveTo(aComputedOffsets.left, aComputedOffsets.top);
00454     else {
00455       offsets = new nsPoint(aComputedOffsets.left, aComputedOffsets.top);
00456       if (offsets)
00457         propTable->SetProperty(mFrame, nsLayoutAtoms::computedOffsetProperty,
00458                                offsets, nsPointDtor, nsnull);
00459     }
00460   }
00461 
00462   aFrameRS.mLineLayout = nsnull;
00463   if (!aIsAdjacentWithTop) {
00464     aFrameRS.mFlags.mIsTopOfPage = PR_FALSE;  // make sure this is cleared
00465   }
00466   mComputedWidth = aFrameRS.mComputedWidth;
00467 
00468   if (aApplyTopMargin) {
00469     mTopMargin = aPrevMargin;
00470 
00471 #ifdef NOISY_VERTICAL_MARGINS
00472     nsFrame::ListTag(stdout, mOuterReflowState.frame);
00473     printf(": reflowing ");
00474     nsFrame::ListTag(stdout, mFrame);
00475     printf(" margin => %d, clearance => %d\n", mTopMargin.get(), aClearance);
00476 #endif
00477 
00478     // Adjust the available height if its constrained so that the
00479     // child frame doesn't think it can reflow into its margin area.
00480     if (NS_UNCONSTRAINEDSIZE != aFrameRS.availableHeight) {
00481       aFrameRS.availableHeight -= mTopMargin.get() + aClearance;
00482     }
00483   }
00484 
00485   // Compute x/y coordinate where reflow will begin. Use the rules
00486   // from 10.3.3 to determine what to apply. At this point in the
00487   // reflow auto left/right margins will have a zero value.
00488   mMargin = aFrameRS.mComputedMargin;
00489   mStyleBorder = aFrameRS.mStyleBorder;
00490   mStyleMargin = aFrameRS.mStyleMargin;
00491   mStylePadding = aFrameRS.mStylePadding;
00492   nscoord x;
00493   nscoord y = mSpace.y + mTopMargin.get() + aClearance;
00494 
00495   // If it's a right floated element, then calculate the x-offset
00496   // differently
00497   if (NS_STYLE_FLOAT_RIGHT == aFrameRS.mStyleDisplay->mFloats) {
00498     nscoord frameWidth;
00499      
00500     if (NS_UNCONSTRAINEDSIZE == aFrameRS.mComputedWidth) {
00501       // Use the current frame width
00502       frameWidth = mFrame->GetSize().width;
00503     } else {
00504       frameWidth = aFrameRS.mComputedWidth +
00505                    aFrameRS.mComputedBorderPadding.left +
00506                    aFrameRS.mComputedBorderPadding.right;
00507     }
00508 
00509     // if this is an unconstrained width reflow, then just place the float at the left margin
00510     if (NS_UNCONSTRAINEDSIZE == mSpace.width)
00511       x = mSpace.x;
00512     else
00513       x = mSpace.XMost() - mMargin.right - frameWidth;
00514 
00515   } else {
00516     x = mSpace.x + mMargin.left;
00517   }
00518   mX = x;
00519   mY = y;
00520 
00521   // If it's an auto-width table, then it doesn't behave like other blocks
00522   // XXX why not for a floating table too?
00523   if (aFrameRS.mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE &&
00524       !aFrameRS.mStyleDisplay->IsFloating()) {
00525     // If this isn't the table's initial reflow, then use its existing
00526     // width to determine where it will be placed horizontally
00527     if (aFrameRS.reason != eReflowReason_Initial) {
00528       nsBlockHorizontalAlign  align;
00529 
00530       align.mXOffset = x;
00531       AlignBlockHorizontally(mFrame->GetSize().width, align);
00532       // Don't reset "mX". because PlaceBlock() will recompute the
00533       // x-offset and expects "mX" to be at the left margin edge
00534       x = align.mXOffset;
00535     }
00536   }
00537 
00538    // Compute the translation to be used for adjusting the spacemanagager
00539    // coordinate system for the frame.  The spacemanager coordinates are
00540    // <b>inside</b> the callers border+padding, but the x/y coordinates
00541    // are not (recall that frame coordinates are relative to the parents
00542    // origin and that the parents border/padding is <b>inside</b> the
00543    // parent frame. Therefore we have to subtract out the parents
00544    // border+padding before translating.
00545    nscoord tx = x - mOuterReflowState.mComputedBorderPadding.left;
00546    nscoord ty = y - mOuterReflowState.mComputedBorderPadding.top;
00547  
00548   // If the element is relatively positioned, then adjust x and y accordingly
00549   if (NS_STYLE_POSITION_RELATIVE == aFrameRS.mStyleDisplay->mPosition) {
00550     x += aFrameRS.mComputedOffsets.left;
00551     y += aFrameRS.mComputedOffsets.top;
00552   }
00553 
00554   // Let frame know that we are reflowing it
00555   mFrame->WillReflow(mPresContext);
00556 
00557   // Position it and its view (if it has one)
00558   // Note: Use "x" and "y" and not "mX" and "mY" because they more accurately
00559   // represents where we think the block will be placed
00560   mFrame->SetPosition(nsPoint(x, y));
00561   nsContainerFrame::PositionFrameView(mFrame);
00562 
00563 #ifdef DEBUG
00564   mMetrics.width = nscoord(0xdeadbeef);
00565   mMetrics.height = nscoord(0xdeadbeef);
00566   mMetrics.ascent = nscoord(0xdeadbeef);
00567   mMetrics.descent = nscoord(0xdeadbeef);
00568   if (mMetrics.mComputeMEW) {
00569     mMetrics.mMaxElementWidth = nscoord(0xdeadbeef);
00570   }
00571 #endif
00572 
00573   mOuterReflowState.mSpaceManager->Translate(tx, ty);
00574 
00575   // See if this is the child's initial reflow and we are supposed to
00576   // compute our maximum width
00577   if (mComputeMaximumWidth && (eReflowReason_Initial == aFrameRS.reason)) {
00578     mOuterReflowState.mSpaceManager->PushState();
00579 
00580     nscoord oldAvailableWidth = aFrameRS.availableWidth;
00581     nscoord oldComputedWidth = aFrameRS.mComputedWidth;
00582 
00583     aFrameRS.availableWidth = NS_UNCONSTRAINEDSIZE;
00584     // XXX Is this really correct? This means we don't compute the
00585     // correct maximum width if the element's width is determined by
00586     // its 'width' style
00587     aFrameRS.mComputedWidth = NS_UNCONSTRAINEDSIZE;
00588     rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
00589 
00590     // Update the reflow metrics with the maximum width
00591     mMetrics.mMaximumWidth = mMetrics.width;
00592 #ifdef NOISY_REFLOW
00593     printf("*** nsBlockReflowContext::ReflowBlock block %p returning max width %d\n", 
00594            mFrame, mMetrics.mMaximumWidth);
00595 #endif
00596     // The second reflow is just as a resize reflow with the constrained
00597     // width
00598     aFrameRS.availableWidth = oldAvailableWidth;
00599     aFrameRS.mComputedWidth = oldComputedWidth;
00600     aFrameRS.reason         = eReflowReason_Resize;
00601 
00602     mOuterReflowState.mSpaceManager->PopState();
00603   }
00604 
00605   rv = mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
00606   mOuterReflowState.mSpaceManager->Translate(-tx, -ty);
00607 
00608 #ifdef DEBUG
00609   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
00610     if (CRAZY_WIDTH(mMetrics.width) || CRAZY_HEIGHT(mMetrics.height)) {
00611       printf("nsBlockReflowContext: ");
00612       nsFrame::ListTag(stdout, mFrame);
00613       printf(" metrics=%d,%d!\n", mMetrics.width, mMetrics.height);
00614     }
00615     if (mMetrics.mComputeMEW &&
00616         (nscoord(0xdeadbeef) == mMetrics.mMaxElementWidth)) {
00617       printf("nsBlockReflowContext: ");
00618       nsFrame::ListTag(stdout, mFrame);
00619       printf(" didn't set max-element-size!\n");
00620     }
00621 #ifdef REALLY_NOISY_MAX_ELEMENT_SIZE
00622     // Note: there are common reflow situations where this *correctly*
00623     // occurs; so only enable this debug noise when you really need to
00624     // analyze in detail.
00625     if (mMetrics.mComputeMEW &&
00626         (mMetrics.mMaxElementWidth > mMetrics.width)) {
00627       printf("nsBlockReflowContext: ");
00628       nsFrame::ListTag(stdout, mFrame);
00629       printf(": WARNING: maxElementWidth=%d > metrics=%d\n",
00630              mMetrics.mMaxElementWidth, mMetrics.width);
00631     }
00632 #endif
00633     if ((mMetrics.width == nscoord(0xdeadbeef)) ||
00634         (mMetrics.height == nscoord(0xdeadbeef)) ||
00635         (mMetrics.ascent == nscoord(0xdeadbeef)) ||
00636         (mMetrics.descent == nscoord(0xdeadbeef))) {
00637       printf("nsBlockReflowContext: ");
00638       nsFrame::ListTag(stdout, mFrame);
00639       printf(" didn't set whad %d,%d,%d,%d!\n",
00640              mMetrics.width, mMetrics.height,
00641              mMetrics.ascent, mMetrics.descent);
00642     }
00643   }
00644 #endif
00645 #ifdef DEBUG
00646   if (nsBlockFrame::gNoisyMaxElementWidth) {
00647     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
00648     if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
00649       if (mMetrics.mComputeMEW) {
00650         printf("  ");
00651         nsFrame::ListTag(stdout, mFrame);
00652         printf(": maxElementSize=%d wh=%d,%d\n",
00653                mMetrics.mMaxElementWidth,
00654                mMetrics.width, mMetrics.height);
00655       }
00656     }
00657   }
00658 #endif
00659 
00660   if (!(NS_FRAME_OUTSIDE_CHILDREN & mFrame->GetStateBits())) {
00661     // Provide overflow area for child that doesn't have any
00662     mMetrics.mOverflowArea.x = 0;
00663     mMetrics.mOverflowArea.y = 0;
00664     mMetrics.mOverflowArea.width = mMetrics.width;
00665     mMetrics.mOverflowArea.height = mMetrics.height;
00666   }
00667 
00668   // Now that frame has been reflowed at least one time make sure that
00669   // the NS_FRAME_FIRST_REFLOW bit is cleared so that never give it an
00670   // initial reflow reason again.
00671   if (eReflowReason_Initial == aFrameRS.reason) {
00672     mFrame->RemoveStateBits(NS_FRAME_FIRST_REFLOW);
00673   }
00674 
00675   if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) ||
00676       (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
00677     // If frame is complete and has a next-in-flow, we need to delete
00678     // them now. Do not do this when a break-before is signaled because
00679     // the frame is going to get reflowed again (and may end up wanting
00680     // a next-in-flow where it ends up), unless it is an out of flow frame.
00681     if (NS_FRAME_IS_COMPLETE(aFrameReflowStatus)) {
00682       nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
00683       if (nsnull != kidNextInFlow) {
00684         // Remove all of the childs next-in-flows. Make sure that we ask
00685         // the right parent to do the removal (it's possible that the
00686         // parent is not this because we are executing pullup code).
00687         // Floats will eventually be removed via nsBlockFrame::RemoveFloat
00688         // which detaches the placeholder from the float.
00689 /* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */
00690         NS_STATIC_CAST(nsHTMLContainerFrame*, kidNextInFlow->GetParent())
00691           ->DeleteNextInFlowChild(mPresContext, kidNextInFlow);
00692       }
00693     }
00694   }
00695 
00696   // If the block is shrink wrapping its width, then see if we have percentage
00697   // based margins. If so, we can calculate them now that we know the shrink
00698   // wrap width
00699   if (NS_SHRINKWRAPWIDTH == aFrameRS.mComputedWidth) {
00700     ComputeShrinkwrapMargins(aFrameRS.mStyleMargin, mMetrics.width, mMargin, mX);
00701   }
00702 
00703   return rv;
00704 }
00705 
00711 PRBool
00712 nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState,
00713                                  PRBool                   aForceFit,
00714                                  nsLineBox*               aLine,
00715                                  const nsMargin&          aComputedOffsets,
00716                                  nsCollapsingMargin&      aBottomMarginResult,
00717                                  nsRect&                  aInFlowBounds,
00718                                  nsRect&                  aCombinedRect,
00719                                  nsReflowStatus           aReflowStatus)
00720 {
00721   // Compute collapsed bottom margin value.
00722   if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
00723     aBottomMarginResult = mMetrics.mCarriedOutBottomMargin;
00724     aBottomMarginResult.Include(mMargin.bottom);
00725   } else {
00726     // The used bottom-margin is set to zero above a break.
00727     aBottomMarginResult.Zero();
00728   }
00729 
00730   nscoord x = mX;
00731   nscoord y = mY;
00732   nscoord backupContainingBlockAdvance = 0;
00733 
00734   // Check whether the block's bottom margin collapses with its top
00735   // margin. See CSS 2.1 section 8.3.1; those rules seem to match
00736   // nsBlockFrame::IsEmpty(). Any such block must have zero height so
00737   // check that first. Note that a block can have clearance and still
00738   // have adjoining top/bottom margins, because the clearance goes
00739   // above the top margin.
00740   PRBool empty = 0 == mMetrics.height && aLine->CachedIsEmpty();
00741   if (empty) {
00742     // Collapse the bottom margin with the top margin that was already
00743     // applied.
00744     aBottomMarginResult.Include(mTopMargin);
00745 
00746 #ifdef NOISY_VERTICAL_MARGINS
00747     printf("  ");
00748     nsFrame::ListTag(stdout, mOuterReflowState.frame);
00749     printf(": ");
00750     nsFrame::ListTag(stdout, mFrame);
00751     printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n",
00752            y, mSpace.y);
00753 #endif
00754     // Section 8.3.1 of CSS 2.1 says that blocks with adjoining
00755     // top/bottom margins whose top margin collapses with their
00756     // parent's top margin should have their top border-edge at the
00757     // top border-edge of their parent. We actually don't have to do
00758     // anything special to make this happen. In that situation,
00759     // nsBlockFrame::ShouldApplyTopMargin will have returned PR_FALSE,
00760     // and mTopMargin and aClearance will have been zero in
00761     // ReflowBlock.
00762 
00763     // If we did apply our top margin, but now we're collapsing it
00764     // into the bottom margin, we need to back up the containing
00765     // block's y-advance by our top margin so that it doesn't get
00766     // counted twice. Note that here we're allowing the line's bounds
00767     // to become different from the block's position; we do this
00768     // because the containing block will place the next line at the
00769     // line's YMost, and it must place the next line at a different
00770     // point from where this empty block will be.
00771     backupContainingBlockAdvance = mTopMargin.get();
00772   }
00773 
00774   // See if the frame fit. If it's the first frame or empty then it
00775   // always fits. If the height is unconstrained then it always fits,
00776   // even if there's some sort of integer overflow that makes y +
00777   // mMetrics.height appear to go beyond the available height.
00778   if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) {
00779     nscoord yMost = y - backupContainingBlockAdvance + mMetrics.height;
00780     if (yMost > mSpace.YMost()) {
00781       // didn't fit, we must acquit.
00782       mFrame->DidReflow(mPresContext, &aReflowState, NS_FRAME_REFLOW_FINISHED);
00783       return PR_FALSE;
00784     }
00785   }
00786 
00787   if (!empty)
00788   {
00789     // Adjust the max-element-size in the metrics to take into
00790     // account the margins around the block element.
00791     // Do not allow auto margins to impact the max-element size
00792     // since they are springy and don't really count!
00793     if (mMetrics.mComputeMEW) {
00794       nsMargin maxElemMargin;
00795       const nsStyleSides &styleMargin = mStyleMargin->mMargin;
00796       nsStyleCoord coord;
00797       if (styleMargin.GetLeftUnit() == eStyleUnit_Coord)
00798         maxElemMargin.left = styleMargin.GetLeft(coord).GetCoordValue();
00799       else
00800         maxElemMargin.left = 0;
00801       if (styleMargin.GetRightUnit() == eStyleUnit_Coord)
00802         maxElemMargin.right = styleMargin.GetRight(coord).GetCoordValue();
00803       else
00804         maxElemMargin.right = 0;
00805       
00806       nscoord dummyXOffset;
00807       // Base the margins on the max-element size
00808       ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaxElementWidth,
00809                                maxElemMargin, dummyXOffset);
00810       
00811       mMetrics.mMaxElementWidth += maxElemMargin.left + maxElemMargin.right;
00812     }
00813     
00814     // do the same for the maximum width
00815     if (mComputeMaximumWidth) {
00816       nsMargin maxWidthMargin;
00817       const nsStyleSides &styleMargin = mStyleMargin->mMargin;
00818       nsStyleCoord coord;
00819       if (styleMargin.GetLeftUnit() == eStyleUnit_Coord)
00820         maxWidthMargin.left = styleMargin.GetLeft(coord).GetCoordValue();
00821       else
00822         maxWidthMargin.left = 0;
00823       if (styleMargin.GetRightUnit() == eStyleUnit_Coord)
00824         maxWidthMargin.right = styleMargin.GetRight(coord).GetCoordValue();
00825       else
00826         maxWidthMargin.right = 0;
00827       
00828       nscoord dummyXOffset;
00829       // Base the margins on the maximum width
00830       ComputeShrinkwrapMargins(mStyleMargin, mMetrics.mMaximumWidth,
00831                                maxWidthMargin, dummyXOffset);
00832       
00833       mMetrics.mMaximumWidth += maxWidthMargin.left + maxWidthMargin.right;
00834     }
00835   }
00836 
00837   // Calculate the actual x-offset and left and right margin
00838   nsBlockHorizontalAlign  align;
00839   align.mXOffset = x;
00840   AlignBlockHorizontally(mMetrics.width, align);
00841   x = align.mXOffset;
00842   mMargin.left = align.mLeftMargin;
00843   mMargin.right = align.mRightMargin;
00844   
00845   aInFlowBounds = nsRect(x, y - backupContainingBlockAdvance,
00846                          mMetrics.width, mMetrics.height);
00847   
00848   // Apply CSS relative positioning
00849   const nsStyleDisplay* styleDisp = mFrame->GetStyleDisplay();
00850   if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) {
00851     x += aComputedOffsets.left;
00852     y += aComputedOffsets.top;
00853   }
00854   
00855   // Now place the frame and complete the reflow process
00856   nsContainerFrame::FinishReflowChild(mFrame, mPresContext, &aReflowState, mMetrics, x, y, 0);
00857   
00858   aCombinedRect = mMetrics.mOverflowArea + nsPoint(x, y);
00859 
00860   return PR_TRUE;
00861 }