Back to index

lightning-sunbird  0.9+nobinonly
nsAbsoluteContainingBlock.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsCOMPtr.h"
00038 #include "nsAbsoluteContainingBlock.h"
00039 #include "nsContainerFrame.h"
00040 #include "nsReflowPath.h"
00041 #include "nsIViewManager.h"
00042 #include "nsLayoutAtoms.h"
00043 #include "nsIPresShell.h"
00044 #include "nsHTMLParts.h"
00045 #include "nsPresContext.h"
00046 
00047 #ifdef DEBUG
00048 #include "nsBlockFrame.h"
00049 #endif
00050 
00051 
00052 nsresult
00053 nsAbsoluteContainingBlock::FirstChild(const nsIFrame* aDelegatingFrame,
00054                                       nsIAtom*        aListName,
00055                                       nsIFrame**      aFirstChild) const
00056 {
00057   NS_PRECONDITION(GetChildListName() == aListName, "unexpected child list name");
00058   *aFirstChild = mAbsoluteFrames.FirstChild();
00059   return NS_OK;
00060 }
00061 
00062 nsresult
00063 nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame*       aDelegatingFrame,
00064                                                nsPresContext* aPresContext,
00065                                                nsIAtom*        aListName,
00066                                                nsIFrame*       aChildList)
00067 {
00068   NS_PRECONDITION(GetChildListName() == aListName, "unexpected child list name");
00069 #ifdef NS_DEBUG
00070   nsFrame::VerifyDirtyBitSet(aChildList);
00071 #endif
00072   mAbsoluteFrames.SetFrames(aChildList);
00073   return NS_OK;
00074 }
00075 
00076 nsresult
00077 nsAbsoluteContainingBlock::AppendFrames(nsIFrame*      aDelegatingFrame,
00078                                         nsIAtom*       aListName,
00079                                         nsIFrame*      aFrameList)
00080 {
00081   // Append the frames to our list of absolutely positioned frames
00082 #ifdef NS_DEBUG
00083   nsFrame::VerifyDirtyBitSet(aFrameList);
00084 #endif
00085   mAbsoluteFrames.AppendFrames(nsnull, aFrameList);
00086 
00087   // Generate a reflow command to reflow the dirty frames
00088   return aDelegatingFrame->GetPresContext()->PresShell()->
00089           AppendReflowCommand(aDelegatingFrame, eReflowType_ReflowDirty,
00090                               GetChildListName());
00091 }
00092 
00093 nsresult
00094 nsAbsoluteContainingBlock::InsertFrames(nsIFrame*      aDelegatingFrame,
00095                                         nsIAtom*       aListName,
00096                                         nsIFrame*      aPrevFrame,
00097                                         nsIFrame*      aFrameList)
00098 {
00099   // Insert the new frames
00100 #ifdef NS_DEBUG
00101   nsFrame::VerifyDirtyBitSet(aFrameList);
00102 #endif
00103   mAbsoluteFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
00104 
00105   // Generate a reflow command to reflow the dirty frames
00106   return aDelegatingFrame->GetPresContext()->PresShell()->
00107           AppendReflowCommand(aDelegatingFrame, eReflowType_ReflowDirty,
00108                               GetChildListName());
00109 }
00110 
00111 nsresult
00112 nsAbsoluteContainingBlock::RemoveFrame(nsIFrame*       aDelegatingFrame,
00113                                        nsIAtom*        aListName,
00114                                        nsIFrame*       aOldFrame)
00115 {
00116   PRBool result = mAbsoluteFrames.DestroyFrame(aDelegatingFrame->
00117                                                GetPresContext(), aOldFrame);
00118   NS_ASSERTION(result, "didn't find frame to delete");
00119   // Because positioned frames aren't part of a flow, there's no additional
00120   // work to do, e.g. reflowing sibling frames. And because positioned frames
00121   // have a view, we don't need to repaint
00122   return result ? NS_OK : NS_ERROR_FAILURE;
00123 }
00124 
00125 nsresult
00126 nsAbsoluteContainingBlock::ReplaceFrame(nsIFrame*      aDelegatingFrame,
00127                                         nsIAtom*       aListName,
00128                                         nsIFrame*      aOldFrame,
00129                                         nsIFrame*      aNewFrame)
00130 {
00131   PRBool result = mAbsoluteFrames.ReplaceFrame(aDelegatingFrame,
00132                                                aOldFrame, aNewFrame, PR_TRUE);
00133   NS_ASSERTION(result, "Problems replacing a frame");
00134   return result ? NS_OK : NS_ERROR_FAILURE;
00135 }
00136 
00137 static void
00138 AddFrameToChildBounds(nsIFrame* aKidFrame, nsRect* aChildBounds)
00139 {
00140   NS_PRECONDITION(aKidFrame, "Must have kid frame");
00141   
00142   if (!aChildBounds) {
00143     return;
00144   }
00145 
00146   // Add in the child's bounds
00147   nsRect kidBounds = aKidFrame->GetRect();
00148   nsRect* kidOverflow = aKidFrame->GetOverflowAreaProperty();
00149   if (kidOverflow) {
00150     // Put it in the parent's coordinate system
00151     kidBounds = *kidOverflow + kidBounds.TopLeft();
00152   }
00153   aChildBounds->UnionRect(*aChildBounds, kidBounds);
00154 }
00155 
00156 nsresult
00157 nsAbsoluteContainingBlock::Reflow(nsIFrame*                aDelegatingFrame,
00158                                   nsPresContext*          aPresContext,
00159                                   const nsHTMLReflowState& aReflowState,
00160                                   nscoord                  aContainingBlockWidth,
00161                                   nscoord                  aContainingBlockHeight,
00162                                   nsRect*                  aChildBounds,
00163                                   PRBool                   aForceReflow,
00164                                   PRBool                   aCBWidthChanged,
00165                                   PRBool                   aCBHeightChanged)
00166 {
00167   // Initialize OUT parameter
00168   if (aChildBounds)
00169     aChildBounds->SetRect(0, 0, 0, 0);
00170 
00171   // Make a copy of the reflow state.  If the reason is
00172   // eReflowReason_Incremental (which should mean either that the target
00173   // is the frame for which this is the absolute container or that the
00174   // container changed size due to incremental reflow of its children),
00175   // then change it to eReflowReason_Resize.
00176   // XXXldb If the target is this frame, shouldn't we be setting it
00177   // appropriately (which might mean to StyleChanged)?
00178   nsHTMLReflowState reflowState(aReflowState);
00179   if (eReflowReason_Incremental == reflowState.reason) {
00180     reflowState.reason = eReflowReason_Resize;
00181   }
00182 
00183   nsIFrame* kidFrame;
00184   for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
00185     if (!aForceReflow &&
00186         !FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged)) {
00187       // Skip this frame, but add it in to the child bounds as needed
00188       AddFrameToChildBounds(kidFrame, aChildBounds);
00189       continue;
00190     }
00191     nsReflowReason  reason = reflowState.reason;
00192 
00193     nsFrameState kidState = kidFrame->GetStateBits();
00194     if (NS_FRAME_FIRST_REFLOW & kidState) {
00195       // The frame has never had a reflow, so change the reason to eReflowReason_Initial
00196       reason = eReflowReason_Initial;
00197 
00198     } else if (NS_FRAME_IS_DIRTY & kidState) {
00199       // The frame is dirty so give it the correct reflow reason
00200       reason = eReflowReason_Dirty;
00201     }
00202 
00203     // Reflow the frame
00204     nsReflowStatus  kidStatus;
00205     ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, reflowState, aContainingBlockWidth,
00206                         aContainingBlockHeight, kidFrame, reason, kidStatus);
00207 
00208     AddFrameToChildBounds(kidFrame, aChildBounds);
00209   }
00210   return NS_OK;
00211 }
00212 
00213 void
00214 nsAbsoluteContainingBlock::CalculateChildBounds(nsPresContext* aPresContext,
00215                                                 nsRect&         aChildBounds)
00216 {
00217   // Initialize the OUT parameters
00218   aChildBounds.SetRect(0, 0, 0, 0);
00219 
00220   for (nsIFrame* f = mAbsoluteFrames.FirstChild(); f; f = f->GetNextSibling()) {
00221     AddFrameToChildBounds(f, &aChildBounds);
00222   }
00223 }
00224 
00225 PRBool
00226 nsAbsoluteContainingBlock::ReflowingAbsolutesOnly(nsIFrame* aDelegatingFrame,
00227                                                   const nsHTMLReflowState& aReflowState)
00228 {
00229   // See if the reflow command is targeted at us.
00230   nsReflowPath *path = aReflowState.path;
00231   nsHTMLReflowCommand *command = path->mReflowCommand;
00232 
00233   if (command) {
00234     // It's targeted at us. See if it's for the positioned child frames
00235     if (GetChildListName() != command->GetChildListName()) {
00236       // A reflow command is targeted directly at this block.
00237       // The block will have to do a proper reflow.
00238       return PR_FALSE;
00239     }
00240   }
00241 
00242   nsReflowPath::iterator iter = path->FirstChild();
00243   nsReflowPath::iterator end = path->EndChildren();
00244 
00245   if (iter != end && mAbsoluteFrames.NotEmpty()) {
00246     for ( ; iter != end; ++iter) {
00247       // See if it's one of our absolutely positioned child frames
00248       if (!mAbsoluteFrames.ContainsFrame(*iter)) {
00249         // At least one of the frames along the reflow path wasn't
00250         // absolutely positioned, so we'll need to deal with it in
00251         // normal block reflow.
00252         return PR_FALSE;
00253       }
00254     }
00255   }
00256 
00257   return PR_TRUE;
00258 }
00259 
00260 static PRBool IsFixedPaddingSize(nsStyleUnit aUnit) {
00261   return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null;
00262 }
00263 static PRBool IsFixedMarginSize(nsStyleUnit aUnit) {
00264   return aUnit == eStyleUnit_Coord || aUnit == eStyleUnit_Null;
00265 }
00266 static PRBool IsFixedMaxSize(nsStyleUnit aUnit) {
00267   return aUnit == eStyleUnit_Null || aUnit == eStyleUnit_Coord;
00268 }
00269 
00270 PRBool
00271 nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f,
00272                                                    PRBool aCBWidthChanged,
00273                                                    PRBool aCBHeightChanged)
00274 {
00275   const nsStylePosition* pos = f->GetStylePosition();
00276   // See if f's position might have changed because it depends on a
00277   // placeholder's position
00278   // This can happen in the following cases:
00279   // 1) Vertical positioning.  "top" must be auto and "bottom" must be auto
00280   //    (otherwise the vertical position is completely determined by
00281   //    whichever of them is not auto and the height).
00282   // 2) Horizontal positioning.  "left" must be auto and "right" must be auto
00283   //    (otherwise the horizontal position is completely determined by
00284   //    whichever of them is not auto and the width).
00285   // See nsHTMLReflowState::InitAbsoluteConstraints -- these are the
00286   // only cases when we call CalculateHypotheticalBox().
00287   if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto &&
00288        pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) ||
00289       (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
00290        pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) {
00291     return PR_TRUE;
00292   }
00293   if (!aCBWidthChanged && !aCBHeightChanged) {
00294     // skip getting style data
00295     return PR_FALSE;
00296   }
00297   const nsStylePadding* padding = f->GetStylePadding();
00298   const nsStyleMargin* margin = f->GetStyleMargin();
00299   if (aCBWidthChanged) {
00300     // See if f's width might have changed.
00301     // If border-left, border-right, padding-left, padding-right,
00302     // width, min-width, and max-width are all lengths, 'none', or enumerated,
00303     // then our frame width does not depend on the parent width.
00304     // Note that borders never depend on the parent width
00305     if (pos->mWidth.GetUnit() != eStyleUnit_Coord ||
00306         pos->mMinWidth.GetUnit() != eStyleUnit_Coord ||
00307         !IsFixedMaxSize(pos->mMaxWidth.GetUnit()) ||
00308         !IsFixedPaddingSize(padding->mPadding.GetLeftUnit()) ||
00309         !IsFixedPaddingSize(padding->mPadding.GetRightUnit())) {
00310       return PR_TRUE;
00311     }
00312 
00313     // See if f's position might have changed. If we're RTL then the
00314     // rules are slightly different. We'll assume percentage or auto
00315     // margins will always induce a dependency on the size
00316     if (!IsFixedMarginSize(margin->mMargin.GetLeftUnit()) ||
00317         !IsFixedMarginSize(margin->mMargin.GetRightUnit())) {
00318       return PR_TRUE;
00319     }
00320     if (f->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
00321       // Note that even if 'left' is a length, our position can
00322       // still depend on the containing block width, because if
00323       // 'right' is also a length we will discard 'left' and be
00324       // positioned relative to the containing block right edge.
00325       // 'left' length and 'right' auto is the only combination
00326       // we can be sure of.
00327       if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord ||
00328           pos->mOffset.GetRightUnit() != eStyleUnit_Auto) {
00329         return PR_TRUE;
00330       }
00331     } else {
00332       if (pos->mOffset.GetLeftUnit() != eStyleUnit_Coord) {
00333         return PR_TRUE;
00334       }
00335     }
00336   }
00337   if (aCBHeightChanged) {
00338     // See if f's height might have changed.
00339     // If border-top, border-bottom, padding-top, padding-bottom,
00340     // min-height, and max-height are all lengths or 'none',
00341     // and height is a length or height and bottom are auto and top is not auto,
00342     // then our frame height does not depend on the parent height.
00343     // Note that borders never depend on the parent height
00344     if (!(pos->mHeight.GetUnit() == eStyleUnit_Coord ||
00345           (pos->mHeight.GetUnit() == eStyleUnit_Auto &&
00346            pos->mOffset.GetBottomUnit() == eStyleUnit_Auto &&
00347            pos->mOffset.GetTopUnit() != eStyleUnit_Auto)) ||
00348         pos->mMinHeight.GetUnit() != eStyleUnit_Coord ||
00349         !IsFixedMaxSize(pos->mMaxHeight.GetUnit()) ||
00350         !IsFixedPaddingSize(padding->mPadding.GetTopUnit()) ||
00351         !IsFixedPaddingSize(padding->mPadding.GetBottomUnit())) { 
00352       return PR_TRUE;
00353     }
00354       
00355     // See if f's position might have changed.
00356     if (!IsFixedMarginSize(margin->mMargin.GetTopUnit()) ||
00357         !IsFixedMarginSize(margin->mMargin.GetBottomUnit())) {
00358       return PR_TRUE;
00359     }
00360     if (pos->mOffset.GetTopUnit() != eStyleUnit_Coord) {
00361       return PR_TRUE;
00362     }
00363   }
00364   return PR_FALSE;
00365 }
00366 
00367 void
00368 nsAbsoluteContainingBlock::IncrementalReflow(nsIFrame*                aDelegatingFrame,
00369                                              nsPresContext*           aPresContext,
00370                                              const nsHTMLReflowState& aReflowState,
00371                                              nscoord                  aContainingBlockWidth,
00372                                              nscoord                  aContainingBlockHeight)
00373 {
00374   // See if the reflow command is targeted at us.
00375   nsReflowPath *path = aReflowState.path;
00376   nsHTMLReflowCommand *command = path->mReflowCommand;
00377 
00378   if (command) {
00379     // It's targeted at us. See if it's for the positioned child frames
00380     if (GetChildListName() == command->GetChildListName()) {
00381       // The only type of reflow command we expect is that we have dirty
00382       // child frames to reflow
00383       NS_ASSERTION(command->Type() == eReflowType_ReflowDirty,
00384                    "unexpected reflow type");
00385 
00386       // Walk the positioned frames and reflow the dirty frames
00387       for (nsIFrame* f = mAbsoluteFrames.FirstChild(); f; f = f->GetNextSibling()) {
00388         nsFrameState  frameState = f->GetStateBits();
00389 
00390         if (frameState & NS_FRAME_IS_DIRTY) {
00391           nsReflowStatus  status;
00392           nsReflowReason  reason;
00393 
00394           reason = (frameState & NS_FRAME_FIRST_REFLOW)
00395             ? eReflowReason_Initial
00396             : eReflowReason_Dirty;
00397 
00398           ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
00399                               aContainingBlockWidth, aContainingBlockHeight, f,
00400                               reason, status);
00401         }
00402       }
00403     }
00404   }
00405 
00406   nsReflowPath::iterator iter = path->FirstChild();
00407   nsReflowPath::iterator end = path->EndChildren();
00408 
00409   if (iter != end && mAbsoluteFrames.NotEmpty()) {
00410     for ( ; iter != end; ++iter) {
00411       // See if it's one of our absolutely positioned child frames
00412       if (mAbsoluteFrames.ContainsFrame(*iter)) {
00413         // Remove the next frame from the reflow path
00414         nsReflowStatus kidStatus;
00415         ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
00416                             aContainingBlockWidth, aContainingBlockHeight, *iter,
00417                             aReflowState.reason, kidStatus);
00418 
00419         // We don't need to invalidate anything because the frame
00420         // should invalidate any area within its frame that needs
00421         // repainting, and because it has a view if it changes size
00422         // the view manager will damage the dirty area
00423 
00424         aReflowState.path->Remove(iter);
00425       }
00426     }
00427   }
00428 }
00429 
00430 void
00431 nsAbsoluteContainingBlock::DestroyFrames(nsIFrame*       aDelegatingFrame,
00432                                          nsPresContext* aPresContext)
00433 {
00434   mAbsoluteFrames.DestroyFrames(aPresContext);
00435 }
00436 
00437 // XXX Optimize the case where it's a resize reflow and the absolutely
00438 // positioned child has the exact same size and position and skip the
00439 // reflow...
00440 
00441 // When bug 154892 is checked in, make sure that when 
00442 // GetChildListName() == nsLayoutAtoms::fixedList, the height is unconstrained.
00443 // since we don't allow replicated frames to split.
00444 
00445 nsresult
00446 nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame*                aDelegatingFrame,
00447                                                nsPresContext*          aPresContext,
00448                                                const nsHTMLReflowState& aReflowState,
00449                                                nscoord                  aContainingBlockWidth,
00450                                                nscoord                  aContainingBlockHeight,
00451                                                nsIFrame*                aKidFrame,
00452                                                nsReflowReason           aReason,
00453                                                nsReflowStatus&          aStatus)
00454 {
00455 #ifdef DEBUG
00456   if (nsBlockFrame::gNoisyReflow) {
00457     nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent);
00458     printf("abs pos ");
00459     if (nsnull != aKidFrame) {
00460       nsIFrameDebug*  frameDebug;
00461       if (NS_SUCCEEDED(CallQueryInterface(aKidFrame, &frameDebug))) {
00462         nsAutoString name;
00463         frameDebug->GetFrameName(name);
00464         printf("%s ", NS_LossyConvertUCS2toASCII(name).get());
00465       }
00466     }
00467     printf("r=%d",aReflowState.reason);
00468 
00469     if (aReflowState.reason == eReflowReason_Incremental) {
00470       nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
00471 
00472       if (command) {
00473         // We're the target.
00474         printf("(%d)", command->Type());      
00475       }
00476     }
00477     char width[16];
00478     char height[16];
00479     PrettyUC(aReflowState.availableWidth, width);
00480     PrettyUC(aReflowState.availableHeight, height);
00481     printf(" a=%s,%s ", width, height);
00482     PrettyUC(aReflowState.mComputedWidth, width);
00483     PrettyUC(aReflowState.mComputedHeight, height);
00484     printf("c=%s,%s \n", width, height);
00485   }
00486   AutoNoisyIndenter indent(nsBlockFrame::gNoisy);
00487 #endif // DEBUG
00488 
00489   nsresult  rv;
00490   // Get the border values
00491   const nsMargin& border = aReflowState.mStyleBorder->GetBorder();
00492 
00493   nscoord availWidth = aReflowState.mComputedWidth;
00494   enum { NOT_SHRINK_TO_FIT, SHRINK_TO_FIT_AVAILWIDTH, SHRINK_TO_FIT_MEW };
00495   PRUint32 situation = NOT_SHRINK_TO_FIT;
00496   while (1) {
00497     nsHTMLReflowMetrics kidDesiredSize(nsnull);
00498     if (situation == NOT_SHRINK_TO_FIT &&
00499         !(aKidFrame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT)) {
00500       // CSS2.1 10.3.7 width:auto and at least one of left/right is auto...
00501       const nsStylePosition* stylePosition = aKidFrame->GetStylePosition();
00502       if (eStyleUnit_Auto == stylePosition->mWidth.GetUnit() &&
00503           (eStyleUnit_Auto == stylePosition->mOffset.GetLeftUnit() ||
00504            eStyleUnit_Auto == stylePosition->mOffset.GetRightUnit())) {
00505         situation = SHRINK_TO_FIT_AVAILWIDTH;
00506         if (aContainingBlockWidth != -1) {
00507           availWidth = aContainingBlockWidth;
00508         } else {
00509           availWidth = aReflowState.mComputedWidth;
00510         }
00511         kidDesiredSize.mComputeMEW = PR_TRUE;
00512       }
00513     }
00514 
00515     nsSize            availSize(availWidth, NS_UNCONSTRAINEDSIZE);
00516     nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame,
00517                                      availSize, aContainingBlockWidth,
00518                                      aContainingBlockHeight,
00519                                      aReason);
00520 
00521     if (situation == SHRINK_TO_FIT_MEW) {
00522       situation = NOT_SHRINK_TO_FIT; // This is the last reflow
00523       kidReflowState.mComputedWidth = PR_MIN(availWidth, kidReflowState.mComputedMaxWidth);
00524       if (kidReflowState.mComputedWidth < kidReflowState.mComputedMinWidth) {
00525         kidReflowState.mComputedWidth = kidReflowState.mComputedMinWidth;
00526       }
00527     } else if (situation == SHRINK_TO_FIT_AVAILWIDTH) {
00528       NS_ASSERTION(availWidth != NS_UNCONSTRAINEDSIZE,
00529                    "shrink-to-fit: expected a constrained available width");
00530       PRInt32 maxWidth = availWidth -
00531         (kidReflowState.mComputedMargin.left + kidReflowState.mComputedBorderPadding.left +
00532          kidReflowState.mComputedBorderPadding.right + kidReflowState.mComputedMargin.right);
00533       if (NS_AUTOOFFSET != kidReflowState.mComputedOffsets.right) {
00534         maxWidth -= kidReflowState.mComputedOffsets.right;
00535       }
00536       if (NS_AUTOOFFSET != kidReflowState.mComputedOffsets.left) {
00537         maxWidth -= kidReflowState.mComputedOffsets.left;
00538       }
00539       // The following also takes care of maxWidth<0
00540       if (kidReflowState.mComputedMaxWidth > maxWidth) {
00541         kidReflowState.mComputedMaxWidth = PR_MAX(maxWidth, kidReflowState.mComputedMinWidth);
00542       }
00543     }
00544 
00545     // Send the WillReflow() notification and position the frame
00546     aKidFrame->WillReflow(aPresContext);
00547 
00548     // XXXldb We can simplify this if we come up with a better way to
00549     // position views.
00550     nscoord x;
00551     if (NS_AUTOOFFSET == kidReflowState.mComputedOffsets.left) {
00552       // Just use the current x-offset
00553       x = aKidFrame->GetPosition().x;
00554     } else {
00555       x = border.left + kidReflowState.mComputedOffsets.left + kidReflowState.mComputedMargin.left;
00556     }
00557     aKidFrame->SetPosition(nsPoint(x, border.top +
00558                                       kidReflowState.mComputedOffsets.top +
00559                                       kidReflowState.mComputedMargin.top));
00560 
00561     // Position its view, but don't bother it doing it now if we haven't
00562     // yet determined the left offset
00563     if (NS_AUTOOFFSET != kidReflowState.mComputedOffsets.left) {
00564       nsContainerFrame::PositionFrameView(aKidFrame);
00565     }
00566 
00567     // Do the reflow
00568     rv = aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus);
00569 
00570     if (situation == SHRINK_TO_FIT_AVAILWIDTH) {
00571       // ...continued CSS2.1 10.3.7 width:auto and at least one of left/right is auto
00572       availWidth -= kidReflowState.mComputedMargin.left + kidReflowState.mComputedMargin.right;
00573 
00574       if (NS_AUTOOFFSET == kidReflowState.mComputedOffsets.right) {
00575         NS_ASSERTION(NS_AUTOOFFSET != kidReflowState.mComputedOffsets.left,
00576                      "Can't solve for both left and right");
00577         availWidth -= kidReflowState.mComputedOffsets.left;
00578       } else {
00579         NS_ASSERTION(NS_AUTOOFFSET == kidReflowState.mComputedOffsets.left,
00580                      "Expected to solve for left");
00581         availWidth -= kidReflowState.mComputedOffsets.right;
00582       }
00583       if (availWidth < 0) {
00584         availWidth = 0;
00585       }
00586 
00587       // Shrink-to-fit: min(max(preferred minimum width, available width), preferred width).
00588       // XXX this is not completely correct - see bug 201897 comment 56/58 and bug 268499.
00589       if (kidDesiredSize.mMaxElementWidth > availWidth) {
00590         aKidFrame->DidReflow(aPresContext, &kidReflowState, NS_FRAME_REFLOW_FINISHED);
00591         availWidth = PR_MAX(0, kidDesiredSize.mMaxElementWidth -
00592                                kidReflowState.mComputedBorderPadding.left -
00593                                kidReflowState.mComputedBorderPadding.right);
00594         situation = SHRINK_TO_FIT_MEW;
00595         aReason = eReflowReason_Resize;
00596         continue; // Do a second reflow constrained to MEW.
00597       }
00598     }
00599 
00600     // If we're solving for 'left' or 'top', then compute it now that we know the
00601     // width/height
00602     if ((NS_AUTOOFFSET == kidReflowState.mComputedOffsets.left) ||
00603         (NS_AUTOOFFSET == kidReflowState.mComputedOffsets.top)) {
00604       if (-1 == aContainingBlockWidth) {
00605         // Get the containing block width/height
00606         kidReflowState.ComputeContainingBlockRectangle(aPresContext,
00607                                                        &aReflowState,
00608                                                        aContainingBlockWidth,
00609                                                        aContainingBlockHeight);
00610       }
00611 
00612       if (NS_AUTOOFFSET == kidReflowState.mComputedOffsets.left) {
00613         NS_ASSERTION(NS_AUTOOFFSET != kidReflowState.mComputedOffsets.right,
00614                      "Can't solve for both left and right");
00615         kidReflowState.mComputedOffsets.left = aContainingBlockWidth -
00616                                                kidReflowState.mComputedOffsets.right -
00617                                                kidReflowState.mComputedMargin.right -
00618                                                kidDesiredSize.width -
00619                                                kidReflowState.mComputedMargin.left;
00620       }
00621       if (NS_AUTOOFFSET == kidReflowState.mComputedOffsets.top) {
00622         kidReflowState.mComputedOffsets.top = aContainingBlockHeight -
00623                                               kidReflowState.mComputedOffsets.bottom -
00624                                               kidReflowState.mComputedMargin.bottom -
00625                                               kidDesiredSize.height -
00626                                               kidReflowState.mComputedMargin.top;
00627       }
00628     }
00629 
00630     // Position the child relative to our padding edge
00631     nsRect  rect(border.left + kidReflowState.mComputedOffsets.left + kidReflowState.mComputedMargin.left,
00632                  border.top + kidReflowState.mComputedOffsets.top + kidReflowState.mComputedMargin.top,
00633                  kidDesiredSize.width, kidDesiredSize.height);
00634     aKidFrame->SetRect(rect);
00635 
00636     // Size and position the view and set its opacity, visibility, content
00637     // transparency, and clip
00638     nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame,
00639                                                aKidFrame->GetView(),
00640                                                &kidDesiredSize.mOverflowArea);
00641     aKidFrame->DidReflow(aPresContext, &kidReflowState, NS_FRAME_REFLOW_FINISHED);
00642 
00643     // If the frame has visible overflow, then store it as a property on the
00644     // frame. This allows us to be able to recover it without having to reflow
00645     // the frame
00646     if (aKidFrame->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {
00647       // Get the property (creating a rect struct if necessary)
00648       nsRect* overflowArea = aKidFrame->GetOverflowAreaProperty(PR_TRUE);
00649 
00650       NS_ASSERTION(overflowArea, "should have created rect");
00651       if (overflowArea) {
00652         *overflowArea = kidDesiredSize.mOverflowArea;
00653       }
00654     }
00655 #ifdef DEBUG
00656     if (nsBlockFrame::gNoisyReflow) {
00657       nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1);
00658       printf("abs pos ");
00659       if (nsnull != aKidFrame) {
00660         nsIFrameDebug*  frameDebug;
00661         if (NS_SUCCEEDED(CallQueryInterface(aKidFrame, &frameDebug))) {
00662           nsAutoString name;
00663           frameDebug->GetFrameName(name);
00664           printf("%s ", NS_LossyConvertUCS2toASCII(name).get());
00665         }
00666       }
00667       printf("%p rect=%d,%d,%d,%d", aKidFrame, rect.x, rect.y, rect.width, rect.height);
00668       printf("\n");
00669     }
00670 #endif
00671 
00672     break;
00673   }
00674   return rv;
00675 }
00676 
00677 #ifdef DEBUG
00678  void nsAbsoluteContainingBlock::PrettyUC(nscoord aSize,
00679                         char*   aBuf)
00680 {
00681   if (NS_UNCONSTRAINEDSIZE == aSize) {
00682     strcpy(aBuf, "UC");
00683   }
00684   else {
00685     if((PRInt32)0xdeadbeef == aSize)
00686     {
00687       strcpy(aBuf, "deadbeef");
00688     }
00689     else {
00690       sprintf(aBuf, "%d", aSize);
00691     }
00692   }
00693 }
00694 #endif