Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLReflowState.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 Communicator client 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 "nsStyleConsts.h"
00039 #include "nsCSSAnonBoxes.h"
00040 #include "nsFrame.h"
00041 #include "nsIContent.h"
00042 #include "nsHTMLAtoms.h"
00043 #include "nsPresContext.h"
00044 #include "nsIPresShell.h"
00045 #include "nsLayoutAtoms.h"
00046 #include "nsIDeviceContext.h"
00047 #include "nsIRenderingContext.h"
00048 #include "nsIFontMetrics.h"
00049 #include "nsBlockFrame.h"
00050 #include "nsLineBox.h"
00051 #include "nsImageFrame.h"
00052 #include "nsIServiceManager.h"
00053 #include "nsIPercentHeightObserver.h"
00054 #include "nsContentUtils.h"
00055 #include "nsLayoutUtils.h"
00056 #ifdef IBMBIDI
00057 #include "nsBidiUtils.h"
00058 #endif
00059 
00060 #ifdef NS_DEBUG
00061 #undef NOISY_VERTICAL_ALIGN
00062 #else
00063 #undef NOISY_VERTICAL_ALIGN
00064 #endif
00065 
00066 // Prefs-driven control for |text-decoration: blink|
00067 static PRPackedBool sPrefIsLoaded = PR_FALSE;
00068 static PRPackedBool sBlinkIsAllowed = PR_TRUE;
00069 
00070 enum eNormalLineHeightControl {
00071   eUninitialized = -1,
00072   eNoExternalLeading = 0,   // does not include external leading 
00073   eIncludeExternalLeading,  // use whatever value font vendor provides
00074   eCompensateLeading        // compensate leading if leading provided by font vendor is not enough
00075 };
00076 
00077 #ifdef FONT_LEADING_APIS_V2
00078 static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
00079 #endif
00080 
00081 #ifdef DEBUG
00082 const char*
00083 nsHTMLReflowState::ReasonToString(nsReflowReason aReason)
00084 {
00085   static const char* reasons[] = {
00086     "initial", "incremental", "resize", "style-change", "dirty"
00087   };
00088 
00089   return reasons[aReason];
00090 }
00091 #endif
00092 
00093 // Initialize a <b>root</b> reflow state with a rendering context to
00094 // use for measuring things.
00095 nsHTMLReflowState::nsHTMLReflowState(nsPresContext*      aPresContext,
00096                                      nsIFrame*            aFrame,
00097                                      nsReflowReason       aReason,
00098                                      nsIRenderingContext* aRenderingContext,
00099                                      const nsSize&        aAvailableSpace)
00100   : mReflowDepth(0)
00101 {
00102   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");
00103   parentReflowState = nsnull;
00104   frame = aFrame;
00105   reason = aReason;
00106   path = nsnull;
00107   availableWidth = aAvailableSpace.width;
00108   availableHeight = aAvailableSpace.height;
00109   rendContext = aRenderingContext;
00110   mSpaceManager = nsnull;
00111   mLineLayout = nsnull;
00112   mFlags.mSpecialHeightReflow = PR_FALSE;
00113   mFlags.mIsTopOfPage = PR_FALSE;
00114   mFlags.mNextInFlowUntouched = PR_FALSE;
00115   mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
00116   mFlags.mHasClearance = PR_FALSE;
00117   mDiscoveredClearance = nsnull;
00118   mPercentHeightObserver = nsnull;
00119   mPercentHeightReflowInitiator = nsnull;
00120   Init(aPresContext);
00121 #ifdef IBMBIDI
00122   mFlags.mVisualBidiFormControl = IsBidiFormControl(aPresContext);
00123   mRightEdge = NS_UNCONSTRAINEDSIZE;
00124 #endif
00125 }
00126 
00127 // Initialize a <b>root</b> reflow state for an <b>incremental</b>
00128 // reflow.
00129 nsHTMLReflowState::nsHTMLReflowState(nsPresContext*      aPresContext,
00130                                      nsIFrame*            aFrame,
00131                                      nsReflowPath*        aReflowPath,
00132                                      nsIRenderingContext* aRenderingContext,
00133                                      const nsSize&        aAvailableSpace)
00134   : mReflowDepth(0)
00135 {
00136   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");  
00137 
00138   reason = eReflowReason_Incremental;
00139   path = aReflowPath;
00140   parentReflowState = nsnull;
00141   frame = aFrame;
00142   availableWidth = aAvailableSpace.width;
00143   availableHeight = aAvailableSpace.height;
00144   rendContext = aRenderingContext;
00145   mSpaceManager = nsnull;
00146   mLineLayout = nsnull;
00147   mFlags.mSpecialHeightReflow = PR_FALSE;
00148   mFlags.mIsTopOfPage = PR_FALSE;
00149   mFlags.mNextInFlowUntouched = PR_FALSE;
00150   mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
00151   mFlags.mHasClearance = PR_FALSE;
00152   mDiscoveredClearance = nsnull;
00153   mPercentHeightObserver = nsnull;
00154   mPercentHeightReflowInitiator = nsnull;
00155   Init(aPresContext);
00156 #ifdef IBMBIDI
00157   mFlags.mVisualBidiFormControl = IsBidiFormControl(aPresContext);
00158   mRightEdge = NS_UNCONSTRAINEDSIZE;
00159 #endif // IBMBIDI
00160 }
00161 
00162 static PRBool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
00163 {
00164   nsIFrame* frameNext = aFrame->GetNextInFlow();
00165   nsIFrame* parentNext = aParent->GetNextInFlow();
00166   return frameNext && parentNext && frameNext->GetParent() == parentNext;
00167 }
00168 
00169 // Initialize a reflow state for a child frames reflow. Some state
00170 // is copied from the parent reflow state; the remaining state is
00171 // computed.
00172 nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
00173                                      const nsHTMLReflowState& aParentReflowState,
00174                                      nsIFrame*                aFrame,
00175                                      const nsSize&            aAvailableSpace,
00176                                      nsReflowReason           aReason,
00177                                      PRBool                   aInit)
00178   : mReflowDepth(aParentReflowState.mReflowDepth + 1),
00179     mFlags(aParentReflowState.mFlags)
00180 {
00181   parentReflowState = &aParentReflowState;
00182   frame = aFrame;
00183   reason = aReason;
00184   if (reason == eReflowReason_Incremental) {
00185     // If the child frame isn't along the reflow path, then convert
00186     // the incremental reflow to a dirty reflow.
00187     path = aParentReflowState.path->GetSubtreeFor(aFrame);
00188     if (! path)
00189       reason = eReflowReason_Dirty;
00190   }
00191   else
00192     path = nsnull;
00193 
00194   availableWidth = aAvailableSpace.width;
00195   availableHeight = aAvailableSpace.height;
00196 
00197   rendContext = aParentReflowState.rendContext;
00198   mSpaceManager = aParentReflowState.mSpaceManager;
00199   mLineLayout = aParentReflowState.mLineLayout;
00200   mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
00201   mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
00202     CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
00203   mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
00204   mFlags.mHasClearance = PR_FALSE;
00205   mDiscoveredClearance = nsnull;
00206   mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
00207                             aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
00208                            ? aParentReflowState.mPercentHeightObserver : nsnull;
00209   mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;
00210 
00211   if (aInit) {
00212     Init(aPresContext);
00213   }
00214 
00215 #ifdef IBMBIDI
00216   mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
00217                                   PR_TRUE : IsBidiFormControl(aPresContext);
00218   mRightEdge = aParentReflowState.mRightEdge;
00219 #endif // IBMBIDI
00220 }
00221 
00222 // Same as the previous except that the reason is taken from the
00223 // parent's reflow state.
00224 nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
00225                                      const nsHTMLReflowState& aParentReflowState,
00226                                      nsIFrame*                aFrame,
00227                                      const nsSize&            aAvailableSpace)
00228   : mReflowDepth(aParentReflowState.mReflowDepth + 1),
00229     mFlags(aParentReflowState.mFlags)
00230 {
00231   parentReflowState = &aParentReflowState;
00232   frame = aFrame;
00233   reason = aParentReflowState.reason;
00234   if (reason == eReflowReason_Incremental) {
00235     // If the child frame isn't along the reflow path, then convert
00236     // the incremental reflow to a dirty reflow.
00237     path = aParentReflowState.path->GetSubtreeFor(aFrame);
00238     if (! path)
00239       reason = eReflowReason_Dirty;
00240   }
00241   else
00242     path = nsnull;
00243 
00244   availableWidth = aAvailableSpace.width;
00245   availableHeight = aAvailableSpace.height;
00246 
00247   rendContext = aParentReflowState.rendContext;
00248   mSpaceManager = aParentReflowState.mSpaceManager;
00249   mLineLayout = aParentReflowState.mLineLayout;
00250   mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
00251   mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
00252     CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
00253   mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
00254   mFlags.mHasClearance = PR_FALSE;
00255   mDiscoveredClearance = nsnull;
00256   mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
00257                             aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
00258                            ? aParentReflowState.mPercentHeightObserver : nsnull;
00259   mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;
00260 
00261   Init(aPresContext);
00262 
00263 #ifdef IBMBIDI
00264   mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
00265                                    PR_TRUE : IsBidiFormControl(aPresContext);
00266   mRightEdge = aParentReflowState.mRightEdge;
00267 #endif // IBMBIDI
00268 }
00269 
00270 // Version that species the containing block width and height
00271 nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
00272                                      const nsHTMLReflowState& aParentReflowState,
00273                                      nsIFrame*                aFrame,
00274                                      const nsSize&            aAvailableSpace,
00275                                      nscoord                  aContainingBlockWidth,
00276                                      nscoord                  aContainingBlockHeight,
00277                                      nsReflowReason           aReason)
00278   : mReflowDepth(aParentReflowState.mReflowDepth + 1),
00279     mFlags(aParentReflowState.mFlags)
00280 {
00281   parentReflowState = &aParentReflowState;
00282   frame = aFrame;
00283   reason = aReason;
00284   if (reason == eReflowReason_Incremental) {
00285     // If the child frame isn't along the reflow path, then convert
00286     // the incremental reflow to a dirty reflow.
00287     path = aParentReflowState.path->GetSubtreeFor(aFrame);
00288     if (! path)
00289       reason = eReflowReason_Dirty;
00290   }
00291   else
00292     path = nsnull;
00293 
00294   availableWidth = aAvailableSpace.width;
00295   availableHeight = aAvailableSpace.height;
00296 
00297   rendContext = aParentReflowState.rendContext;
00298   mSpaceManager = aParentReflowState.mSpaceManager;
00299   mLineLayout = aParentReflowState.mLineLayout;
00300   mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
00301   mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
00302     CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
00303   mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
00304   mFlags.mHasClearance = PR_FALSE;
00305   mDiscoveredClearance = nsnull;
00306   mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
00307                             aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
00308                            ? aParentReflowState.mPercentHeightObserver : nsnull;
00309   mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;
00310 
00311   Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
00312 
00313 #ifdef IBMBIDI
00314   mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
00315                                    PR_TRUE : IsBidiFormControl(aPresContext);
00316   mRightEdge = aParentReflowState.mRightEdge;
00317 #endif // IBMBIDI
00318 }
00319 
00320 void
00321 nsHTMLReflowState::Init(nsPresContext* aPresContext,
00322                         nscoord         aContainingBlockWidth,
00323                         nscoord         aContainingBlockHeight,
00324                         nsMargin*       aBorder,
00325                         nsMargin*       aPadding)
00326 {
00327   mCompactMarginWidth = 0;
00328 #ifdef DEBUG
00329   mDebugHook = nsnull;
00330 #endif
00331 
00332   mStylePosition = frame->GetStylePosition();
00333   mStyleDisplay = frame->GetStyleDisplay();
00334   mStyleVisibility = frame->GetStyleVisibility();
00335   mStyleBorder = frame->GetStyleBorder();
00336   mStyleMargin = frame->GetStyleMargin();
00337   mStylePadding = frame->GetStylePadding();
00338   mStyleText = frame->GetStyleText();
00339 
00340   InitFrameType();
00341   InitCBReflowState();
00342   InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding);
00343 }
00344 
00345 void nsHTMLReflowState::InitCBReflowState()
00346 {
00347   if (!parentReflowState) {
00348     mCBReflowState = nsnull;
00349     return;
00350   }
00351 
00352   if (parentReflowState->frame->IsContainingBlock() ||
00353       // Absolutely positioned frames should always be kids of the frames that
00354       // determine their containing block
00355       (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {
00356     // a block inside a table cell needs to use the table cell
00357     if (parentReflowState->parentReflowState &&
00358         IS_TABLE_CELL(parentReflowState->parentReflowState->frame->GetType())) {
00359       mCBReflowState = parentReflowState->parentReflowState;
00360     } else {
00361       mCBReflowState = parentReflowState;
00362     }
00363       
00364     return;
00365   }
00366   
00367   mCBReflowState = parentReflowState->mCBReflowState;
00368 }
00369 
00370 const nsHTMLReflowState*
00371 nsHTMLReflowState::GetPageBoxReflowState(const nsHTMLReflowState* aParentRS)
00372 {
00373   // XXX write me as soon as we can ask a frame if it's a page frame...
00374   return nsnull;
00375 }
00376 
00377 /* static */
00378 nscoord
00379 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
00380 {
00381   const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
00382   if (!rs)
00383     return 0;
00384   return rs->mComputedWidth;
00385 }
00386 
00387 nscoord
00388 nsHTMLReflowState::AdjustIntrinsicMinContentWidthForStyle(nscoord aWidth) const
00389 {
00390   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
00391   if (eStyleUnit_Percent == widthUnit) {
00392     aWidth = 0;
00393   } else if (eStyleUnit_Coord == widthUnit) {
00394     // Sometimes we can get an unconstrained size here because we're
00395     // computing the maximum-width. Although it doesn't seem right
00396     // for max-width computation to change our computed width.
00397     if (NS_UNCONSTRAINEDSIZE != mComputedWidth) {
00398       aWidth = mComputedWidth;
00399     }
00400   }
00401 
00402   nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
00403   if (eStyleUnit_Percent == maxWidthUnit) {
00404     aWidth = 0;
00405   } else if (eStyleUnit_Coord == maxWidthUnit) {
00406     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMaxWidth,
00407                  "Should be a computed max-width here");
00408     aWidth = PR_MIN(aWidth, mComputedMaxWidth);
00409   }
00410 
00411   nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
00412   if (eStyleUnit_Coord == minWidthUnit) { 
00413     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMinWidth,
00414                  "Should be a computed max-width here");
00415     aWidth = PR_MAX(aWidth, mComputedMinWidth);
00416   }
00417   
00418   return aWidth;
00419 }
00420 
00421 nscoord
00422 nsHTMLReflowState::AdjustIntrinsicContentWidthForStyle(nscoord aWidth) const
00423 {
00424   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
00425   if (eStyleUnit_Coord == widthUnit) {
00426     // Sometimes we can get an unconstrained size here because we're
00427     // computing the maximum-width. Although it doesn't seem right
00428     // for max-width computation to change our computed width.
00429     if (NS_UNCONSTRAINEDSIZE != mComputedWidth) {
00430       aWidth = mComputedWidth;
00431     }
00432   }
00433 
00434   nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
00435   if (eStyleUnit_Coord == maxWidthUnit) {
00436     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMaxWidth,
00437                  "Should be a computed max-width here");
00438     aWidth = PR_MIN(aWidth, mComputedMaxWidth);
00439   }
00440 
00441   nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
00442   if (eStyleUnit_Coord == minWidthUnit) { 
00443     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMinWidth,
00444                  "Should be a computed max-width here");
00445     aWidth = PR_MAX(aWidth, mComputedMinWidth);
00446   }
00447   
00448   return aWidth;
00449 }
00450 
00451 /* static */
00452 nsIFrame*
00453 nsHTMLReflowState::GetContainingBlockFor(const nsIFrame* aFrame)
00454 {
00455   NS_PRECONDITION(aFrame, "Must have frame to work with");
00456   nsIFrame* container = aFrame->GetParent();
00457   if (aFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
00458     // Absolutely positioned frames are just kids of their containing
00459     // blocks (which may happen to be inlines).
00460     return container;
00461   }
00462   while (container && !container->IsContainingBlock()) {
00463     container = container->GetParent();
00464   }
00465   return container;
00466 }
00467 
00468 void
00469 nsHTMLReflowState::InitFrameType()
00470 {
00471   const nsStyleDisplay *disp = mStyleDisplay;
00472   nsCSSFrameType frameType;
00473 
00474   // Section 9.7 of the CSS2 spec indicates that absolute position
00475   // takes precedence over float which takes precedence over display.
00476   // Make sure the frame was actually moved out of the flow, and don't
00477   // just assume what the style says
00478   // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
00479   if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
00480     if (disp->IsAbsolutelyPositioned()) {
00481       frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
00482     }
00483     else {
00484       NS_ASSERTION(NS_STYLE_FLOAT_NONE != disp->mFloats,
00485                    "unknown out of flow frame type");
00486       frameType = NS_CSS_FRAME_TYPE_FLOATING;
00487     }
00488   }
00489   else {
00490     switch (disp->mDisplay) {
00491     case NS_STYLE_DISPLAY_BLOCK:
00492     case NS_STYLE_DISPLAY_LIST_ITEM:
00493     case NS_STYLE_DISPLAY_TABLE:
00494     case NS_STYLE_DISPLAY_TABLE_CAPTION:
00495       frameType = NS_CSS_FRAME_TYPE_BLOCK;
00496       break;
00497 
00498     case NS_STYLE_DISPLAY_INLINE:
00499     case NS_STYLE_DISPLAY_MARKER:
00500     case NS_STYLE_DISPLAY_INLINE_TABLE:
00501     case NS_STYLE_DISPLAY_INLINE_BOX:
00502     case NS_STYLE_DISPLAY_INLINE_GRID:
00503     case NS_STYLE_DISPLAY_INLINE_STACK:
00504       frameType = NS_CSS_FRAME_TYPE_INLINE;
00505       break;
00506 
00507     case NS_STYLE_DISPLAY_RUN_IN:
00508     case NS_STYLE_DISPLAY_COMPACT:
00509       // XXX need to look ahead at the frame's sibling
00510       frameType = NS_CSS_FRAME_TYPE_BLOCK;
00511       break;
00512 
00513     case NS_STYLE_DISPLAY_TABLE_CELL:
00514     case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
00515     case NS_STYLE_DISPLAY_TABLE_COLUMN:
00516     case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
00517     case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
00518     case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
00519     case NS_STYLE_DISPLAY_TABLE_ROW:
00520       frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
00521       break;
00522 
00523     case NS_STYLE_DISPLAY_NONE:
00524     default:
00525       frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
00526       break;
00527     }
00528   }
00529 
00530   // See if the frame is replaced
00531   if (frame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT) {
00532     frameType = NS_FRAME_REPLACED(frameType);
00533   }
00534 
00535   mFrameType = frameType;
00536 }
00537 
00538 void
00539 nsHTMLReflowState::ComputeRelativeOffsets(const nsHTMLReflowState* cbrs,
00540                                           nscoord aContainingBlockWidth,
00541                                           nscoord aContainingBlockHeight)
00542 {
00543   nsStyleCoord  coord;
00544 
00545   // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
00546   // and 'right' moves the boxes to the left. The computed values are always:
00547   // left=-right
00548   PRBool  leftIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit();
00549   PRBool  rightIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit();
00550 
00551   // Check for percentage based values and an unconstrained containing
00552   // block width. Treat them like 'auto'
00553   if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
00554     if (eStyleUnit_Percent == mStylePosition->mOffset.GetLeftUnit()) {
00555       leftIsAuto = PR_TRUE;
00556     }
00557     if (eStyleUnit_Percent == mStylePosition->mOffset.GetRightUnit()) {
00558       rightIsAuto = PR_TRUE;
00559     }
00560   }
00561 
00562   // If neither 'left' not 'right' are auto, then we're over-constrained and
00563   // we ignore one of them
00564   if (!leftIsAuto && !rightIsAuto) {
00565     const nsStyleVisibility* vis = frame->GetStyleVisibility();
00566     
00567     if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
00568       rightIsAuto = PR_TRUE;
00569     } else {
00570       leftIsAuto = PR_TRUE;
00571     }
00572   }
00573 
00574   if (leftIsAuto) {
00575     if (rightIsAuto) {
00576       // If both are 'auto' (their initial values), the computed values are 0
00577       mComputedOffsets.left = mComputedOffsets.right = 0;
00578     } else {
00579       // 'Right' isn't 'auto' so compute its value
00580       ComputeHorizontalValue(aContainingBlockWidth,
00581                              mStylePosition->mOffset.GetRightUnit(),
00582                              mStylePosition->mOffset.GetRight(coord),
00583                              mComputedOffsets.right);
00584       
00585       // Computed value for 'left' is minus the value of 'right'
00586       mComputedOffsets.left = -mComputedOffsets.right;
00587     }
00588 
00589   } else {
00590     NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
00591     
00592     // 'Left' isn't 'auto' so compute its value
00593     ComputeHorizontalValue(aContainingBlockWidth,
00594                            mStylePosition->mOffset.GetLeftUnit(),
00595                            mStylePosition->mOffset.GetLeft(coord),
00596                            mComputedOffsets.left);
00597 
00598     // Computed value for 'right' is minus the value of 'left'
00599     mComputedOffsets.right = -mComputedOffsets.left;
00600   }
00601 
00602   // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
00603   // move relatively positioned elements up and down. They also must be each 
00604   // other's negative
00605   PRBool  topIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit();
00606   PRBool  bottomIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit();
00607 
00608   // Check for percentage based values and a containing block height that
00609   // depends on the content height. Treat them like 'auto'
00610   if (NS_AUTOHEIGHT == aContainingBlockHeight) {
00611     if (eStyleUnit_Percent == mStylePosition->mOffset.GetTopUnit()) {
00612       topIsAuto = PR_TRUE;
00613     }
00614     if (eStyleUnit_Percent == mStylePosition->mOffset.GetBottomUnit()) {
00615       bottomIsAuto = PR_TRUE;
00616     }
00617   }
00618 
00619   // If neither is 'auto', 'bottom' is ignored
00620   if (!topIsAuto && !bottomIsAuto) {
00621     bottomIsAuto = PR_TRUE;
00622   }
00623 
00624   if (topIsAuto) {
00625     if (bottomIsAuto) {
00626       // If both are 'auto' (their initial values), the computed values are 0
00627       mComputedOffsets.top = mComputedOffsets.bottom = 0;
00628     } else {
00629       // 'Bottom' isn't 'auto' so compute its value
00630       ComputeVerticalValue(aContainingBlockHeight,
00631                            mStylePosition->mOffset.GetBottomUnit(),
00632                            mStylePosition->mOffset.GetBottom(coord),
00633                            mComputedOffsets.bottom);
00634       
00635       // Computed value for 'top' is minus the value of 'bottom'
00636       mComputedOffsets.top = -mComputedOffsets.bottom;
00637     }
00638 
00639   } else {
00640     NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
00641     
00642     // 'Top' isn't 'auto' so compute its value
00643     ComputeVerticalValue(aContainingBlockHeight,
00644                          mStylePosition->mOffset.GetTopUnit(),
00645                          mStylePosition->mOffset.GetTop(coord),
00646                          mComputedOffsets.top);
00647 
00648     // Computed value for 'bottom' is minus the value of 'top'
00649     mComputedOffsets.bottom = -mComputedOffsets.top;
00650   }
00651 }
00652 
00653 // Returns the nearest containing block frame for the specified frame.
00654 // Also returns the left, top, right, and bottom edges of the specified
00655 // frame's content area. These are in the coordinate space of the block
00656 // frame itself
00657 static nsIFrame*
00658 GetNearestContainingBlock(nsIFrame* aFrame, nsMargin& aContentArea)
00659 {
00660   for (aFrame = aFrame->GetParent(); aFrame && !aFrame->IsContainingBlock();
00661        aFrame = aFrame->GetParent())
00662     /* do nothing */;
00663 
00664   if (aFrame) {
00665     nsSize  size = aFrame->GetSize();
00666 
00667     aContentArea.left = 0;
00668     aContentArea.top = 0;
00669     aContentArea.right = size.width;
00670     aContentArea.bottom = size.height;
00671   
00672     // Subtract off for border and padding. If it can't be computed because
00673     // it's percentage based (for example) then just ignore it
00674     nsStyleBorderPadding  bPad;
00675     nsMargin              borderPadding;
00676     nsStyleContext* styleContext = aFrame->GetStyleContext();
00677     styleContext->GetBorderPaddingFor(bPad);
00678     if (bPad.GetBorderPadding(borderPadding)) {
00679       aContentArea.left += borderPadding.left;
00680       aContentArea.top += borderPadding.top;
00681       aContentArea.right -= borderPadding.right;
00682       aContentArea.bottom -= borderPadding.bottom;
00683     }
00684   }
00685 
00686   return aFrame;
00687 }
00688 
00689 // When determining the hypothetical box that would have been if the element
00690 // had been in the flow we may not be able to exactly determine both the left
00691 // and right edges. For example, if the element is a non-replaced inline-level
00692 // element we would have to reflow it in order to determine it desired width.
00693 // In that case depending on the progression direction either the left or
00694 // right edge would be marked as not being exact
00695 struct nsHypotheticalBox {
00696   nscoord       mLeft, mRight;
00697   nscoord       mTop;
00698   PRPackedBool  mLeftIsExact, mRightIsExact;
00699 
00700   nsHypotheticalBox() {
00701     mLeftIsExact = mRightIsExact = PR_FALSE;
00702   }
00703 };
00704       
00705 static PRBool
00706 GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize)
00707 {
00708   // See if it is an image frame
00709   PRBool    result = PR_FALSE;
00710 
00711   // Currently the only type of replaced frame that we can get the intrinsic
00712   // size for is an image frame
00713   // XXX We should add back the GetReflowMetrics() function and one of the
00714   // things should be the intrinsic size...
00715   if (aFrame->GetType() == nsLayoutAtoms::imageFrame) {
00716     nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
00717 
00718     imageFrame->GetIntrinsicImageSize(aIntrinsicSize);
00719     result = (aIntrinsicSize != nsSize(0, 0));
00720   }
00721   return result;
00722 }
00723 
00724 nscoord
00725 nsHTMLReflowState::CalculateHorizBorderPaddingMargin(nscoord aContainingBlockWidth)
00726 {
00727   const nsMargin& border = mStyleBorder->GetBorder();
00728   nsMargin padding, margin;
00729 
00730   // See if the style system can provide us the padding directly
00731   if (!mStylePadding->GetPadding(padding)) {
00732     nsStyleCoord left, right;
00733 
00734     // We have to compute the left and right values
00735     ComputeHorizontalValue(aContainingBlockWidth,
00736                            mStylePadding->mPadding.GetLeftUnit(),
00737                            mStylePadding->mPadding.GetLeft(left),
00738                            padding.left);
00739     ComputeHorizontalValue(aContainingBlockWidth,
00740                            mStylePadding->mPadding.GetRightUnit(),
00741                            mStylePadding->mPadding.GetRight(right),
00742                            padding.right);
00743   }
00744 
00745   // See if the style system can provide us the margin directly
00746   if (!mStyleMargin->GetMargin(margin)) {
00747     nsStyleCoord left, right;
00748 
00749     // We have to compute the left and right values
00750     if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
00751       margin.left = 0;  // just ignore
00752     } else {
00753       ComputeHorizontalValue(aContainingBlockWidth,
00754                              mStyleMargin->mMargin.GetLeftUnit(),
00755                              mStyleMargin->mMargin.GetLeft(left),
00756                              margin.left);
00757     }
00758     if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
00759       margin.right = 0;  // just ignore
00760     } else {
00761       ComputeHorizontalValue(aContainingBlockWidth,
00762                              mStyleMargin->mMargin.GetRightUnit(),
00763                              mStyleMargin->mMargin.GetRight(right),
00764                              margin.right);
00765     }
00766   }
00767 
00768   return padding.left + padding.right + border.left + border.right +
00769          margin.left + margin.right;
00770 }
00771 
00772 static nsIFrame*
00773 FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame)
00774 {
00775   nsIFrame* result = aDescendantFrame;
00776 
00777   while (result) {
00778     nsIFrame* parent = result->GetParent();
00779     if (parent == aParent) {
00780       break;
00781     }
00782 
00783     // The frame is not an immediate child of aParent so walk up another level
00784     result = parent;
00785   }
00786 
00787   return result;
00788 }
00789 
00794 static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
00795   nsIFrame* aDescendant, PRBool* aFound) {
00796   if (aFrame == aDescendant) {
00797     *aFound = PR_TRUE;
00798     return PR_TRUE;
00799   }
00800   if (!aFrame->IsSelfEmpty()) {
00801     *aFound = PR_FALSE;
00802     return PR_FALSE;
00803   }
00804   for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
00805     PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
00806     if (*aFound || !allEmpty) {
00807       return allEmpty;
00808     }
00809   }
00810   *aFound = PR_FALSE;
00811   return PR_TRUE;
00812 }
00813 
00814 // Calculate the hypothetical box that the element would have if it were in
00815 // the flow. The values returned are relative to the padding edge of the
00816 // absolute containing block
00817 void
00818 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext*    aPresContext,
00819                                             nsIFrame*          aPlaceholderFrame,
00820                                             nsIFrame*          aContainingBlock,
00821                                             nsMargin&          aBlockContentArea,
00822                                             const nsHTMLReflowState* cbrs,
00823                                             nsHypotheticalBox& aHypotheticalBox)
00824 {
00825   NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
00826                "mOriginalDisplay has not been properly initialized");
00827   
00828   // If it's a replaced element and it has a 'auto' value for 'width', see if we
00829   // can get the intrinsic size. This will allow us to exactly determine both the
00830   // left and right edges
00831   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
00832   nsSize      intrinsicSize;
00833   PRBool      knowIntrinsicSize = PR_FALSE;
00834   if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
00835     // See if we can get the intrinsic size of the element
00836     knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize);
00837   }
00838 
00839   // See if we can calculate what the box width would have been if the
00840   // element had been in the flow
00841   nscoord boxWidth;
00842   PRBool  knowBoxWidth = PR_FALSE;
00843   if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) &&
00844       !NS_FRAME_IS_REPLACED(mFrameType)) {
00845     // For non-replaced inline-level elements the 'width' property doesn't apply,
00846     // so we don't know what the width would have been without reflowing it
00847 
00848   } else {
00849     // It's either a replaced inline-level element or a block-level element
00850     nscoord horizBorderPaddingMargin;
00851 
00852     // Determine the total amount of horizontal border/padding/margin that
00853     // the element would have had if it had been in the flow. Note that we
00854     // ignore any 'auto' and 'inherit' values
00855     horizBorderPaddingMargin = CalculateHorizBorderPaddingMargin(aBlockContentArea.right -
00856                                                                  aBlockContentArea.left);
00857 
00858     if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
00859       // It's a replaced element with an 'auto' width so the box width is
00860       // its intrinsic size plus any border/padding/margin
00861       if (knowIntrinsicSize) {
00862         boxWidth = intrinsicSize.width + horizBorderPaddingMargin;
00863         knowBoxWidth = PR_TRUE;
00864       }
00865 
00866     } else if (eStyleUnit_Auto == widthUnit) {
00867       // The box width is the containing block width
00868       boxWidth = aBlockContentArea.right - aBlockContentArea.left;
00869       knowBoxWidth = PR_TRUE;
00870     
00871     } else {
00872       // We need to compute it. It's important we do this, because if it's
00873       // percentage based this computed value may be different from the comnputed
00874       // value calculated using the absolute containing block width
00875       ComputeHorizontalValue(aBlockContentArea.right - aBlockContentArea.left,
00876                              widthUnit, mStylePosition->mWidth, boxWidth);
00877       boxWidth += horizBorderPaddingMargin;
00878       knowBoxWidth = PR_TRUE;
00879     }
00880   }
00881   
00882   // Get the 'direction' of the block
00883   const nsStyleVisibility* blockVis = aContainingBlock->GetStyleVisibility();
00884 
00885   // Get the placeholder x-offset and y-offset in the coordinate
00886   // space of the block frame that contains it
00887   // XXXbz the placeholder is not fully reflown yet if our containing block is
00888   // relatively positioned...
00889   nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock);
00890 
00891   // First, determine the hypothetical box's mTop
00892   nsBlockFrame* blockFrame;
00893   if (NS_SUCCEEDED(aContainingBlock->QueryInterface(kBlockFrameCID,
00894                                   NS_REINTERPRET_CAST(void**, &blockFrame)))) {
00895     // We need the immediate child of the block frame, and that may not be
00896     // the placeholder frame
00897     nsIFrame *blockChild = FindImmediateChildOf(blockFrame, aPlaceholderFrame);
00898     nsBlockFrame::line_iterator lineBox = blockFrame->FindLineFor(blockChild);
00899 
00900     // How we determine the hypothetical box depends on whether the element
00901     // would have been inline-level or block-level
00902     if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
00903       // Use the top of the inline box which the placeholder lives in as the
00904       // hypothetical box's top.
00905       aHypotheticalBox.mTop = lineBox->mBounds.y;
00906     } else {
00907       // The element would have been block-level which means it would be below
00908       // the line containing the placeholder frame, unless all the frames
00909       // before it are empty.  In that case, it would have been just before
00910       // this line.      
00911       // XXXbz the line box is not fully reflown yet if our containing block is
00912       // relatively positioned...
00913       if (lineBox != blockFrame->end_lines()) {
00914         nsIFrame * firstFrame = lineBox->mFirstChild;
00915         PRBool found = PR_FALSE;
00916         PRBool allEmpty = PR_TRUE;
00917         while (firstFrame) { // See bug 223064
00918           allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
00919             aPlaceholderFrame, &found);
00920           if (found || !allEmpty)
00921             break;
00922           firstFrame = firstFrame->GetNextSibling();
00923         }
00924         NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
00925 
00926         if (allEmpty) {
00927           // The top of the hypothetical box is the top of the line containing
00928           // the placeholder, since there is nothing in the line before our
00929           // placeholder except empty frames.
00930           aHypotheticalBox.mTop = lineBox->mBounds.y;
00931         } else {
00932           // The top of the hypothetical box is just below the line containing
00933           // the placeholder.
00934           aHypotheticalBox.mTop = lineBox->mBounds.YMost();
00935         }
00936       } else {
00937         // Just use the placeholder's y-offset
00938         aHypotheticalBox.mTop = placeholderOffset.y;
00939       }
00940     }
00941   } else {
00942     // The containing block is not a block, so it's probably something
00943     // like a XUL box, etc.
00944     // Just use the placeholder's y-offset
00945     aHypotheticalBox.mTop = placeholderOffset.y;
00946   }
00947 
00948   // Second, determine the hypothetical box's mLeft & mRight
00949   // To determine the left and right offsets we need to look at the block's 'direction'
00950   if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
00951     // How we determine the hypothetical box depends on whether the element
00952     // would have been inline-level or block-level
00953     if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
00954       // The placeholder represents the left edge of the hypothetical box
00955       aHypotheticalBox.mLeft = placeholderOffset.x;
00956     } else {
00957       aHypotheticalBox.mLeft = aBlockContentArea.left;
00958     }
00959     aHypotheticalBox.mLeftIsExact = PR_TRUE;
00960 
00961     if (knowBoxWidth) {
00962       aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
00963       aHypotheticalBox.mRightIsExact = PR_TRUE;
00964     } else {
00965       // We can't compute the right edge because we don't know the desired
00966       // width. So instead use the right content edge of the block parent,
00967       // but remember it's not exact
00968       aHypotheticalBox.mRight = aBlockContentArea.right;
00969       aHypotheticalBox.mRightIsExact = PR_FALSE;
00970     }
00971 
00972   } else {
00973     // The placeholder represents the right edge of the hypothetical box
00974     if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
00975       aHypotheticalBox.mRight = placeholderOffset.x;
00976     } else {
00977       aHypotheticalBox.mRight = aBlockContentArea.right;
00978     }
00979     aHypotheticalBox.mRightIsExact = PR_TRUE;
00980     
00981     if (knowBoxWidth) {
00982       aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
00983       aHypotheticalBox.mLeftIsExact = PR_TRUE;
00984     } else {
00985       // We can't compute the left edge because we don't know the desired
00986       // width. So instead use the left content edge of the block parent,
00987       // but remember it's not exact
00988       aHypotheticalBox.mLeft = aBlockContentArea.left;
00989       aHypotheticalBox.mLeftIsExact = PR_FALSE;
00990     }
00991 
00992   }
00993 
00994   // The current coordinate space is that of the nearest block to the placeholder.
00995   // Convert to the coordinate space of the absolute containing block
00996   // One weird thing here is that for fixed-positioned elements we want to do
00997   // the conversion incorrectly; specifically we want to ignore any scrolling
00998   // that may have happened;
00999   nsPoint cbOffset;
01000   if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED) {
01001     // In this case, cbrs->frame will always be an ancestor of
01002     // aContainingBlock, so can just walk our way up the frame tree.
01003     cbOffset.MoveTo(0, 0);
01004     do {
01005       cbOffset += aContainingBlock->GetPosition();
01006       aContainingBlock = aContainingBlock->GetParent();
01007       NS_ASSERTION(aContainingBlock,
01008                    "Should hit cbrs->frame before we run off the frame tree!");
01009     } while (aContainingBlock != cbrs->frame);
01010   } else {
01011     cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame);
01012   }
01013   aHypotheticalBox.mLeft += cbOffset.x;
01014   aHypotheticalBox.mTop += cbOffset.y;
01015   aHypotheticalBox.mRight += cbOffset.x;
01016   
01017   // The specified offsets are relative to the absolute containing block's
01018   // padding edge and our current values are relative to the border edge, so
01019   // translate.
01020   nsMargin border = cbrs->mComputedBorderPadding - cbrs->mComputedPadding;
01021   aHypotheticalBox.mLeft -= border.left;
01022   aHypotheticalBox.mRight -= border.right;
01023   aHypotheticalBox.mTop -= border.top;
01024 }
01025 
01026 void
01027 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
01028                                            const nsHTMLReflowState* cbrs,
01029                                            nscoord containingBlockWidth,
01030                                            nscoord containingBlockHeight)
01031 {
01032   NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
01033                   "containing block height must be constrained");
01034 
01035   // Get the placeholder frame
01036   nsIFrame*     placeholderFrame;
01037 
01038   aPresContext->PresShell()->GetPlaceholderFrameFor(frame, &placeholderFrame);
01039   NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");
01040 
01041   // Find the nearest containing block frame to the placeholder frame,
01042   // and return its content area left, top, right, and bottom edges
01043   nsMargin  blockContentArea;
01044   nsIFrame* blockFrame = GetNearestContainingBlock(placeholderFrame,
01045                                                    blockContentArea);
01046   
01047   // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
01048   // 'auto', then compute the hypothetical box of where the element would
01049   // have been if it had been in the flow
01050   nsHypotheticalBox hypotheticalBox;
01051   if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
01052        (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
01053       ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
01054        (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {
01055 
01056     CalculateHypotheticalBox(aPresContext, placeholderFrame, blockFrame,
01057                              blockContentArea, cbrs, hypotheticalBox);
01058   }
01059 
01060   // Initialize the 'left' and 'right' computed offsets
01061   // XXX Handle new 'static-position' value...
01062   PRBool        leftIsAuto = PR_FALSE, rightIsAuto = PR_FALSE;
01063   nsStyleCoord  coord;
01064   if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
01065     mComputedOffsets.left = 0;
01066     leftIsAuto = PR_TRUE;
01067   } else {
01068     ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetLeftUnit(),
01069                            mStylePosition->mOffset.GetLeft(coord),
01070                            mComputedOffsets.left);
01071   }
01072   if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
01073     mComputedOffsets.right = 0;
01074     rightIsAuto = PR_TRUE;
01075   } else {
01076     ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetRightUnit(),
01077                            mStylePosition->mOffset.GetRight(coord),
01078                            mComputedOffsets.right);
01079   }
01080 
01081   PRUint8 direction = mStyleVisibility->mDirection;
01082 
01083   // Initialize the 'width' computed value
01084   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
01085   PRBool      widthIsAuto = (eStyleUnit_Auto == widthUnit);
01086   if (!widthIsAuto) {
01087     // Use the specified value for the computed width
01088     ComputeHorizontalValue(containingBlockWidth, widthUnit,
01089                            mStylePosition->mWidth, mComputedWidth);
01090 
01091     AdjustComputedWidth(PR_TRUE);
01092   }
01093 
01094   // See if none of 'left', 'width', and 'right', is 'auto'
01095   if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
01096     // See whether we're over-constrained
01097     PRInt32 availBoxSpace = containingBlockWidth - mComputedOffsets.left - mComputedOffsets.right;
01098     PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.left -
01099                                 mComputedBorderPadding.right;
01100 
01101     if (availContentSpace < mComputedWidth) {
01102       // We're over-constrained so use 'direction' to dictate which value to
01103       // ignore
01104       if (NS_STYLE_DIRECTION_LTR == direction) {
01105         // Ignore the specified value for 'right'
01106         mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
01107           mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right;
01108       } else {
01109         // Ignore the specified value for 'left'
01110         mComputedOffsets.left = containingBlockWidth - mComputedBorderPadding.left -
01111           mComputedWidth - mComputedBorderPadding.right - mComputedOffsets.right;
01112       }
01113 
01114     } else {
01115       // Calculate any 'auto' margin values
01116       PRBool  marginLeftIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit());
01117       PRBool  marginRightIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit());
01118       PRInt32 availMarginSpace = availContentSpace - mComputedWidth;
01119 
01120       if (marginLeftIsAuto) {
01121         if (marginRightIsAuto) {
01122           // Both 'margin-left' and 'margin-right' are 'auto', so they get
01123           // equal values
01124           mComputedMargin.left = availMarginSpace / 2;
01125           mComputedMargin.right = availMarginSpace - mComputedMargin.left;
01126         } else {
01127           // Just 'margin-left' is 'auto'
01128           mComputedMargin.left = availMarginSpace - mComputedMargin.right;
01129         }
01130       } else {
01131         // Just 'margin-right' is 'auto'
01132         mComputedMargin.right = availMarginSpace - mComputedMargin.left;
01133       }
01134     }
01135 
01136   } else {
01137     // See if all three of 'left', 'width', and 'right', are 'auto'
01138     if (leftIsAuto && widthIsAuto && rightIsAuto) {
01139       // Use the 'direction' to dictate whether 'left' or 'right' is
01140       // treated like 'static-position'
01141       if (NS_STYLE_DIRECTION_LTR == direction) {
01142         if (hypotheticalBox.mLeftIsExact) {
01143           mComputedOffsets.left = hypotheticalBox.mLeft;
01144           leftIsAuto = PR_FALSE;
01145         } else {
01146           // Well, we don't know 'left' so we have to use 'right' and
01147           // then solve for 'left'
01148           mComputedOffsets.right = hypotheticalBox.mRight;
01149           rightIsAuto = PR_FALSE;
01150         }
01151       } else {
01152         if (hypotheticalBox.mRightIsExact) {
01153           mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
01154           rightIsAuto = PR_FALSE;
01155         } else {
01156           // Well, we don't know 'right' so we have to use 'left' and
01157           // then solve for 'right'
01158           mComputedOffsets.left = hypotheticalBox.mLeft;
01159           leftIsAuto = PR_FALSE;
01160         }
01161       }
01162     }
01163 
01164     // At this point we know that at least one of 'left', 'width', and 'right'
01165     // is 'auto', but not all three. Examine the various combinations
01166     if (widthIsAuto) {
01167       if (leftIsAuto || rightIsAuto) {
01168         if (NS_FRAME_IS_REPLACED(mFrameType)) {
01169           // For a replaced element we use the intrinsic size
01170           mComputedWidth = NS_INTRINSICSIZE;
01171         } else {
01172           // The width is shrink-to-fit
01173           mComputedWidth = NS_SHRINKWRAPWIDTH;
01174         }
01175 
01176         if (leftIsAuto) {
01177           mComputedOffsets.left = NS_AUTOOFFSET;   // solve for 'left'
01178         } else {
01179           mComputedOffsets.right = NS_AUTOOFFSET;  // solve for 'right'
01180         }
01181 
01182       } else {
01183         // Only 'width' is 'auto' so just solve for 'width'
01184         PRInt32 autoWidth = containingBlockWidth - mComputedOffsets.left -
01185           mComputedMargin.left - mComputedBorderPadding.left -
01186           mComputedBorderPadding.right -
01187           mComputedMargin.right - mComputedOffsets.right;
01188 
01189         if (autoWidth < 0) {
01190           autoWidth = 0;
01191         }
01192         mComputedWidth = autoWidth;
01193 
01194         AdjustComputedWidth(PR_FALSE);
01195 
01196         if (autoWidth != mComputedWidth) {
01197           // Re-calculate any 'auto' margin values since the computed width
01198           // was adjusted by a 'min-width' or 'max-width'.
01199           PRInt32 availMarginSpace = autoWidth - mComputedWidth;
01200 
01201           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
01202             if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
01203               // Both margins are 'auto' so their computed values are equal.
01204               mComputedMargin.left = availMarginSpace / 2;
01205               mComputedMargin.right = availMarginSpace - mComputedMargin.left;
01206             } else {
01207               mComputedMargin.left = availMarginSpace - mComputedMargin.right;
01208             }
01209           } else if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
01210             mComputedMargin.right = availMarginSpace - mComputedMargin.left;
01211           } else {
01212             // We're over-constrained - ignore the value for 'left' or 'right'
01213             // and solve for that value.
01214             if (NS_STYLE_DIRECTION_LTR == direction) {
01215               // ignore 'right'
01216               mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
01217                 mComputedMargin.left - mComputedBorderPadding.left -
01218                 mComputedWidth - mComputedBorderPadding.right -
01219                 mComputedMargin.right;
01220             } else {
01221               // ignore 'left'
01222               mComputedOffsets.left = containingBlockWidth - 
01223                 mComputedMargin.left - mComputedBorderPadding.left -
01224                 mComputedWidth - mComputedBorderPadding.right -
01225                 mComputedMargin.right - mComputedOffsets.right;
01226             }
01227           }
01228         }
01229       }
01230 
01231     } else {
01232       // Either 'left' or 'right' or both is 'auto'
01233       if (leftIsAuto && rightIsAuto) {
01234         // Use the 'direction' to dictate whether 'left' or 'right' is treated like
01235         // 'static-position'
01236         if (NS_STYLE_DIRECTION_LTR == direction) {
01237           if (hypotheticalBox.mLeftIsExact) {
01238             mComputedOffsets.left = hypotheticalBox.mLeft;
01239             leftIsAuto = PR_FALSE;
01240           } else {
01241             // Well, we don't know 'left' so we have to use 'right' and
01242             // then solve for 'left'
01243             mComputedOffsets.right = hypotheticalBox.mRight;
01244             rightIsAuto = PR_FALSE;
01245           }
01246         } else {
01247           if (hypotheticalBox.mRightIsExact) {
01248             mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
01249             rightIsAuto = PR_FALSE;
01250           } else {
01251             // Well, we don't know 'right' so we have to use 'left' and
01252             // then solve for 'right'
01253             mComputedOffsets.left = hypotheticalBox.mLeft;
01254             leftIsAuto = PR_FALSE;
01255           }
01256         }
01257       }
01258 
01259       if (leftIsAuto) {
01260         // Solve for 'left'
01261         mComputedOffsets.left = containingBlockWidth - mComputedMargin.left -
01262           mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right - 
01263           mComputedMargin.right - mComputedOffsets.right;
01264 
01265       } else if (rightIsAuto) {
01266         // Solve for 'right'
01267         mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
01268           mComputedMargin.left - mComputedBorderPadding.left - mComputedWidth -
01269           mComputedBorderPadding.right - mComputedMargin.right;
01270       }
01271     }
01272   }
01273 
01274   // Initialize the 'top' and 'bottom' computed offsets
01275   nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
01276   PRBool      topIsAuto = PR_FALSE, bottomIsAuto = PR_FALSE;
01277   if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
01278     mComputedOffsets.top = 0;
01279     topIsAuto = PR_TRUE;
01280   } else {
01281     nsStyleCoord c;
01282     ComputeVerticalValue(containingBlockHeight,
01283                          mStylePosition->mOffset.GetTopUnit(),
01284                          mStylePosition->mOffset.GetTop(c),
01285                          mComputedOffsets.top);
01286   }
01287   if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
01288     mComputedOffsets.bottom = 0;        
01289     bottomIsAuto = PR_TRUE;
01290   } else {
01291     nsStyleCoord c;
01292     ComputeVerticalValue(containingBlockHeight,
01293                          mStylePosition->mOffset.GetBottomUnit(),
01294                          mStylePosition->mOffset.GetBottom(c),
01295                          mComputedOffsets.bottom);
01296   }
01297 
01298   // Initialize the 'height' computed value
01299   PRBool  heightIsAuto = (eStyleUnit_Auto == heightUnit);
01300   if (!heightIsAuto) {
01301     // Use the specified value for the computed height
01302     ComputeVerticalValue(containingBlockHeight, heightUnit,
01303                          mStylePosition->mHeight, mComputedHeight);
01304 
01305     AdjustComputedHeight(PR_TRUE);
01306   }
01307 
01308   // See if none of 'top', 'height', and 'bottom', is 'auto'
01309   if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
01310     // See whether we're over-constrained
01311     PRInt32 availBoxSpace = containingBlockHeight - mComputedOffsets.top - mComputedOffsets.bottom;
01312     PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.top -
01313                                 mComputedBorderPadding.bottom;
01314 
01315     if (availContentSpace < mComputedHeight) {
01316       // We're over-constrained so ignore the specified value for 'bottom'
01317       mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
01318         mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom;
01319 
01320     } else {
01321       // Calculate any 'auto' margin values
01322       PRBool  marginTopIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit());
01323       PRBool  marginBottomIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit());
01324       PRInt32 availMarginSpace = availContentSpace - mComputedHeight;
01325 
01326       if (marginTopIsAuto) {
01327         if (marginBottomIsAuto) {
01328           // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
01329           // equal values
01330           mComputedMargin.top = availMarginSpace / 2;
01331           mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
01332         } else {
01333           // Just 'margin-top' is 'auto'
01334           mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
01335         }
01336       } else {
01337         // Just 'margin-bottom' is 'auto'
01338         mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
01339       }
01340     }
01341 
01342   } else {
01343     // See if all three of 'top', 'height', and 'bottom', are 'auto'
01344     if (topIsAuto && heightIsAuto && bottomIsAuto) {
01345       // Treat 'top' like 'static-position'
01346       mComputedOffsets.top = hypotheticalBox.mTop;
01347       topIsAuto = PR_FALSE;
01348     }
01349 
01350     // At this point we know that at least one of 'top', 'height', and 'bottom'
01351     // is 'auto', but not all three. Examine the various combinations
01352     if (heightIsAuto) {
01353       if (topIsAuto || bottomIsAuto) {
01354         if (NS_FRAME_IS_REPLACED(mFrameType)) {
01355           // For a replaced element we use the intrinsic size
01356           mComputedHeight = NS_INTRINSICSIZE;
01357         } else {
01358           // The height is based on the content
01359           mComputedHeight = NS_AUTOHEIGHT;
01360         }
01361 
01362         if (topIsAuto) {
01363           mComputedOffsets.top = NS_AUTOOFFSET;     // solve for 'top'
01364         } else {
01365           mComputedOffsets.bottom = NS_AUTOOFFSET;  // solve for 'bottom'
01366         }
01367 
01368       } else {
01369         // Only 'height' is 'auto' so just solve for 'height'
01370         PRInt32 autoHeight = containingBlockHeight - mComputedOffsets.top -
01371           mComputedMargin.top - mComputedBorderPadding.top -
01372           mComputedBorderPadding.bottom -
01373           mComputedMargin.bottom - mComputedOffsets.bottom;
01374 
01375         if (autoHeight < 0) {
01376           autoHeight = 0;
01377         }
01378         mComputedHeight = autoHeight;
01379         
01380         AdjustComputedHeight(PR_FALSE);
01381 
01382         if (autoHeight != mComputedHeight) {
01383           // Re-calculate any 'auto' margin values since the computed height
01384           // was adjusted by a 'min-height' or 'max-height'.
01385           PRInt32 availMarginSpace = autoHeight - mComputedHeight;
01386     
01387           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit()) {
01388             if (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit()) {
01389               // Both margins are 'auto' so their computed values are equal
01390               mComputedMargin.top = availMarginSpace / 2;
01391               mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
01392             } else {
01393               mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
01394             }
01395           } else if (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit()) {
01396             mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
01397           } else {
01398             // We're over-constrained - ignore 'bottom'.
01399             mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
01400               mComputedMargin.top - mComputedBorderPadding.top -
01401               mComputedHeight - mComputedBorderPadding.bottom -
01402               mComputedMargin.bottom;
01403           }
01404         }
01405       }
01406 
01407     } else {
01408       // Either 'top' or 'bottom' or both is 'auto'
01409       if (topIsAuto && bottomIsAuto) {
01410         // Treat 'top' like 'static-position'
01411         mComputedOffsets.top = hypotheticalBox.mTop;
01412         topIsAuto = PR_FALSE;
01413       }
01414 
01415       if (topIsAuto) {
01416         // Solve for 'top'
01417         mComputedOffsets.top = containingBlockHeight - mComputedMargin.top -
01418           mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom - 
01419           mComputedMargin.bottom - mComputedOffsets.bottom;
01420 
01421       } else if (bottomIsAuto) {
01422         // Solve for 'bottom'
01423         mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
01424           mComputedMargin.top - mComputedBorderPadding.top - mComputedHeight -
01425           mComputedBorderPadding.bottom - mComputedMargin.bottom;
01426       }
01427     }
01428   }
01429 }
01430 
01431 nscoord 
01432 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
01433 {
01434   nscoord result = 0;
01435   if (!aReflowState) return result;
01436 
01437   // zero auto margins
01438   nsMargin margin = aReflowState->mComputedMargin;
01439   if (NS_AUTOMARGIN == margin.top) 
01440     margin.top = 0;
01441   if (NS_AUTOMARGIN == margin.bottom) 
01442     margin.bottom = 0;
01443 
01444   result += margin.top + margin.bottom;
01445   result += aReflowState->mComputedBorderPadding.top + 
01446             aReflowState->mComputedBorderPadding.bottom;
01447 
01448   return result;
01449 }
01450 
01451 /* Get the height based on the viewport of the containing block specified 
01452  * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
01453  * This will walk up the chain of containing blocks looking for a computed height
01454  * until it finds the canvas frame, or it encounters a frame that is not a block,
01455  * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
01456  *
01457  *  When we encounter scrolledContent area frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
01458  */
01459 nscoord
01460 CalcQuirkContainingBlockHeight(const nsHTMLReflowState& aReflowState)
01461 {
01462   nsHTMLReflowState* firstAncestorRS = nsnull; // a candidate for html frame
01463   nsHTMLReflowState* secondAncestorRS = nsnull; // a candidate for body frame
01464   
01465   // initialize the default to NS_AUTOHEIGHT as this is the containings block
01466   // computed height when this function is called. It is possible that we 
01467   // don't alter this height especially if we are restricted to one level
01468   nscoord result = NS_AUTOHEIGHT; 
01469                              
01470   const nsHTMLReflowState* rs = &aReflowState;
01471   for (; rs && rs->frame; rs = (nsHTMLReflowState *)(rs->parentReflowState)) { 
01472     nsIAtom* frameType = rs->frame->GetType();
01473     // if the ancestor is auto height then skip it and continue up if it 
01474     // is the first block/area frame and possibly the body/html
01475     if (nsLayoutAtoms::blockFrame == frameType ||
01476         nsLayoutAtoms::areaFrame == frameType ||
01477         nsLayoutAtoms::scrollFrame == frameType) {
01478       
01479       if (nsLayoutAtoms::areaFrame == frameType) {
01480         // Skip over scrolled-content area frames
01481         if (rs->frame->GetStyleContext()->GetPseudoType() ==
01482             nsCSSAnonBoxes::scrolledContent) {
01483           continue;
01484         }
01485       }
01486 
01487       secondAncestorRS = firstAncestorRS;
01488       firstAncestorRS = (nsHTMLReflowState*)rs;
01489 
01490       // If the current frame we're looking at is positioned, we don't want to
01491       // go any further (see bug 221784).  The behavior we want here is: 1) If
01492       // not auto-height, use this as the percentage base.  2) If auto-height,
01493       // keep looking, unless the frame is positioned.
01494       if (NS_AUTOHEIGHT == rs->mComputedHeight) {
01495         if (rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
01496           break;
01497         } else {
01498           continue;
01499         }
01500       }
01501     }
01502     else if (nsLayoutAtoms::canvasFrame == frameType) {
01503       // Use scroll frames' computed height if we have one, this will
01504       // allow us to get viewport height for native scrollbars.
01505       nsHTMLReflowState* scrollState = (nsHTMLReflowState *)rs->parentReflowState;
01506       if (nsLayoutAtoms::scrollFrame == scrollState->frame->GetType()) {
01507         rs = scrollState;
01508       }
01509     }
01510     else if (nsLayoutAtoms::pageContentFrame == frameType) {
01511       nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
01512       // only use the page content frame for a height basis if it is the first in flow
01513       if (prevInFlow) 
01514         break;
01515     }
01516     else {
01517       break;
01518     }
01519 
01520     // if the ancestor is the page content frame then the percent base is 
01521     // the avail height, otherwise it is the computed height
01522     result = (nsLayoutAtoms::pageContentFrame == frameType)
01523              ? rs->availableHeight : rs->mComputedHeight;
01524     // if unconstrained - don't sutract borders - would result in huge height
01525     if (NS_AUTOHEIGHT == result) return result;
01526 
01527     // if we got to the canvas or page content frame, then subtract out 
01528     // margin/border/padding for the BODY and HTML elements
01529     if ((nsLayoutAtoms::canvasFrame == frameType) || 
01530         (nsLayoutAtoms::pageContentFrame == frameType)) {
01531 
01532       result -= GetVerticalMarginBorderPadding(firstAncestorRS); 
01533       result -= GetVerticalMarginBorderPadding(secondAncestorRS); 
01534 
01535 #ifdef DEBUG
01536       // make sure the first ancestor is the HTML and the second is the BODY
01537       if (firstAncestorRS) {
01538         nsIContent* frameContent = firstAncestorRS->frame->GetContent();
01539         if (frameContent) {
01540           nsIAtom *contentTag = frameContent->Tag();
01541           NS_ASSERTION(contentTag == nsHTMLAtoms::html, "First ancestor is not HTML");
01542         }
01543       }
01544       if (secondAncestorRS) {
01545         nsIContent* frameContent = secondAncestorRS->frame->GetContent();
01546         if (frameContent) {
01547           nsIAtom *contentTag = frameContent->Tag();
01548           NS_ASSERTION(contentTag == nsHTMLAtoms::body, "Second ancestor is not BODY");
01549         }
01550       }
01551 #endif
01552       
01553     }
01554     // if we got to the html frame, then subtract out 
01555     // margin/border/padding for the BODY element
01556     else if (nsLayoutAtoms::areaFrame == frameType) {
01557       // make sure it is the body
01558       if (nsLayoutAtoms::canvasFrame == rs->parentReflowState->frame->GetType()) {
01559         result -= GetVerticalMarginBorderPadding(secondAncestorRS);
01560       }
01561     }
01562     break;
01563   }
01564 
01565   // Make sure not to return a negative height here!
01566   return PR_MAX(result, 0);
01567 }
01568 // Called by InitConstraints() to compute the containing block rectangle for
01569 // the element. Handles the special logic for absolutely positioned elements
01570 void
01571 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext*          aPresContext,
01572                                                    const nsHTMLReflowState* aContainingBlockRS,
01573                                                    nscoord&                 aContainingBlockWidth,
01574                                                    nscoord&                 aContainingBlockHeight)
01575 {
01576   // Unless the element is absolutely positioned, the containing block is
01577   // formed by the content edge of the nearest block-level ancestor
01578   aContainingBlockWidth = aContainingBlockRS->mComputedWidth;
01579   aContainingBlockHeight = aContainingBlockRS->mComputedHeight;
01580   
01581   if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
01582     // See if the ancestor is block-level or inline-level
01583     if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
01584       // Base our size on the actual size of the frame.  In cases when this is
01585       // completely bogus (eg initial reflow), this code shouldn't even be
01586       // called, since the code in nsPositionedInlineFrame::Reflow will pass in
01587       // the containing block dimensions to our constructor.
01588       // XXXbz we should be taking the in-flows into account too, but
01589       // that's very hard.
01590       nsMargin computedBorder = aContainingBlockRS->mComputedBorderPadding -
01591         aContainingBlockRS->mComputedPadding;
01592       aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width -
01593         computedBorder.LeftRight();;
01594       NS_ASSERTION(aContainingBlockWidth >= 0,
01595                    "Negative containing block width!");
01596       aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height -
01597         computedBorder.TopBottom();
01598       NS_ASSERTION(aContainingBlockHeight >= 0,
01599                    "Negative containing block height!");
01600     } else {
01601       // If the ancestor is block-level, the containing block is formed by the
01602       // padding edge of the ancestor
01603       aContainingBlockWidth += aContainingBlockRS->mComputedPadding.LeftRight();
01604 
01605       // If the containing block is the initial containing block and it has a
01606       // height that depends on its content, then use the viewport height instead.
01607       // This gives us a reasonable value against which to compute percentage
01608       // based heights and to do bottom relative positioning
01609       if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
01610           nsLayoutUtils::IsInitialContainingBlock(aContainingBlockRS->frame)) {
01611 
01612         // Use the viewport height as the containing block height
01613         const nsHTMLReflowState* rs = aContainingBlockRS->parentReflowState;
01614         while (rs) {
01615           aContainingBlockHeight = rs->mComputedHeight;
01616           rs = rs->parentReflowState;
01617         }
01618 
01619       } else {
01620         aContainingBlockHeight +=
01621           aContainingBlockRS->mComputedPadding.TopBottom();
01622       }
01623     }
01624   } else {
01625     // If this is an unconstrained reflow, then reset the containing block
01626     // width to NS_UNCONSTRAINEDSIZE. This way percentage based values have
01627     // no effect
01628     if (NS_UNCONSTRAINEDSIZE == availableWidth) {
01629       aContainingBlockWidth = NS_UNCONSTRAINEDSIZE;
01630     }
01631     // an element in quirks mode gets a containing block based on looking for a
01632     // parent with a non-auto height if the element has a percent height
01633     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
01634       if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
01635           mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
01636         aContainingBlockHeight = CalcQuirkContainingBlockHeight(*aContainingBlockRS);
01637       }
01638     }
01639   }
01640 }
01641 
01642 // Prefs callback to pick up changes
01643 PR_STATIC_CALLBACK(int)
01644 PrefsChanged(const char *aPrefName, void *instance)
01645 {
01646   sBlinkIsAllowed =
01647     nsContentUtils::GetBoolPref("browser.blink_allowed", sBlinkIsAllowed);
01648 
01649   return 0; /* PREF_OK */
01650 }
01651 
01652 // Check to see if |text-decoration: blink| is allowed.  The first time
01653 // called, register the callback and then force-load the pref.  After that,
01654 // just use the cached value.
01655 static PRBool BlinkIsAllowed(void)
01656 {
01657   if (!sPrefIsLoaded) {
01658     // Set up a listener and check the initial value
01659     nsContentUtils::RegisterPrefCallback("browser.blink_allowed", PrefsChanged,
01660                                          nsnull);
01661     PrefsChanged(nsnull, nsnull);
01662     sPrefIsLoaded = PR_TRUE;
01663   }
01664   return sBlinkIsAllowed;
01665 }
01666 
01667 #ifdef FONT_LEADING_APIS_V2
01668 static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
01669 {
01670   if (sNormalLineHeightControl == eUninitialized) {
01671     // browser.display.normal_lineheight_calc_control is not user
01672     // changable, so no need to register callback for it.
01673     sNormalLineHeightControl =
01674       NS_STATIC_CAST(eNormalLineHeightControl,
01675                      nsContentUtils::GetIntPref("browser.display.normal_lineheight_calc_control", eNoExternalLeading));
01676   }
01677   return sNormalLineHeightControl;
01678 }
01679 #endif
01680 
01681 // XXX refactor this code to have methods for each set of properties
01682 // we are computing: width,height,line-height; margin; offsets
01683 
01684 void
01685 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
01686                                    nscoord         aContainingBlockWidth,
01687                                    nscoord         aContainingBlockHeight,
01688                                    nsMargin*       aBorder,
01689                                    nsMargin*       aPadding)
01690 {
01691   // If this is the root frame, then set the computed width and
01692   // height equal to the available space
01693   if (nsnull == parentReflowState) {
01694     mComputedWidth = availableWidth;
01695     mComputedHeight = availableHeight;
01696     mComputedMargin.SizeTo(0, 0, 0, 0);
01697     mComputedPadding.SizeTo(0, 0, 0, 0);
01698     mComputedBorderPadding.SizeTo(0, 0, 0, 0);
01699     mComputedOffsets.SizeTo(0, 0, 0, 0);
01700     mComputedMinWidth = mComputedMinHeight = 0;
01701     mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
01702   } else {
01703     // Get the containing block reflow state
01704     const nsHTMLReflowState* cbrs = mCBReflowState;
01705     NS_ASSERTION(nsnull != cbrs, "no containing block");
01706 
01707     // If we weren't given a containing block width and height, then
01708     // compute one
01709     if (aContainingBlockWidth == -1) {
01710       ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, 
01711                                       aContainingBlockHeight);
01712     }
01713 
01714 #if 0
01715     nsFrame::ListTag(stdout, frame); printf(": cb=");
01716     nsFrame::ListTag(stdout, cbrs->frame); printf(" size=%d,%d\n", aContainingBlockWidth, aContainingBlockHeight);
01717 #endif
01718 
01719     // See if the containing block height is based on the size of its
01720     // content
01721     nsIAtom* fType;
01722     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
01723       // See if the containing block is (1) a scrolled frame, i.e. its
01724       // parent is a scroll frame. The presence of the intervening
01725       // frame (that the scroll frame scrolls) needs to be hidden from
01726       // the containingBlockHeight calcuation, or (2) a cell frame which needs
01727       // to use the mComputedHeight of the cell instead of what the cell block passed in.
01728       if (cbrs->parentReflowState) {
01729         nsIFrame* f = cbrs->parentReflowState->frame;
01730         fType = f->GetType();
01731         if (nsLayoutAtoms::scrollFrame == fType) {
01732           // Use the scroll frame's computed height instead
01733           aContainingBlockHeight = cbrs->parentReflowState->mComputedHeight;
01734         }
01735         else {
01736           fType = cbrs->frame->GetType();
01737           if (IS_TABLE_CELL(fType)) {
01738             // use the cell's computed height 
01739             aContainingBlockHeight = cbrs->mComputedHeight;
01740           }
01741         }
01742       }
01743     }
01744 
01745     // Compute margins from the specified margin style information. These
01746     // become the default computed values, and may be adjusted below
01747     // XXX fix to provide 0,0 for the top&bottom margins for
01748     // inline-non-replaced elements
01749     ComputeMargin(aContainingBlockWidth, cbrs);
01750     if (aPadding) { // padding is an input arg
01751       mComputedPadding.top    = aPadding->top;
01752       mComputedPadding.right  = aPadding->right;
01753       mComputedPadding.bottom = aPadding->bottom;
01754       mComputedPadding.left   = aPadding->left;
01755     }
01756     else {
01757       ComputePadding(aContainingBlockWidth, cbrs);
01758     }
01759     if (aBorder) {  // border is an input arg
01760       mComputedBorderPadding = *aBorder;
01761     }
01762     else {
01763       mComputedBorderPadding = mStyleBorder->GetBorder();
01764     }
01765     mComputedBorderPadding += mComputedPadding;
01766 
01767     nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
01768     nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
01769 
01770     // Check for a percentage based width and an unconstrained containing
01771     // block width
01772     if (eStyleUnit_Percent == widthUnit) {
01773       if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
01774         widthUnit = eStyleUnit_Auto;
01775       }
01776     }
01777     // Check for a percentage based height and a containing block height
01778     // that depends on the content height
01779     if (eStyleUnit_Percent == heightUnit) {
01780       if (NS_AUTOHEIGHT == aContainingBlockHeight) {
01781         // this if clause enables %-height on replaced inline frames,
01782         // such as images.  See bug 54119.  The else clause "heightUnit = eStyleUnit_Auto;"
01783         // used to be called exclusively.
01784         if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
01785           // Get the containing block reflow state
01786           NS_ASSERTION(nsnull != cbrs, "no containing block");
01787           // in quirks mode, get the cb height using the special quirk method
01788           if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
01789             if (!IS_TABLE_CELL(fType)) {
01790               aContainingBlockHeight = CalcQuirkContainingBlockHeight(*cbrs);
01791               if (aContainingBlockHeight == NS_AUTOHEIGHT) {
01792                 heightUnit = eStyleUnit_Auto;
01793               }
01794             }
01795             else {
01796               heightUnit = eStyleUnit_Auto;
01797             }
01798           }
01799           // in standard mode, use the cb height.  if it's "auto", as will be the case
01800           // by default in BODY, use auto height as per CSS2 spec.
01801           else 
01802           {
01803             if (NS_AUTOHEIGHT != cbrs->mComputedHeight)
01804               aContainingBlockHeight = cbrs->mComputedHeight;
01805             else
01806               heightUnit = eStyleUnit_Auto;
01807           }
01808         }
01809         else {
01810           // default to interpreting the height like 'auto'
01811           heightUnit = eStyleUnit_Auto;
01812         }
01813       }
01814     }
01815 
01816     // Compute our offsets if the element is relatively positioned.  We need
01817     // the correct containing block width and height here, which is why we need
01818     // to do it after all the quirks-n-such above.
01819     if (NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
01820       ComputeRelativeOffsets(cbrs, aContainingBlockWidth, aContainingBlockHeight);
01821     } else {
01822       // Initialize offsets to 0
01823       mComputedOffsets.SizeTo(0, 0, 0, 0);
01824     }
01825 
01826     // Calculate the computed values for min and max properties
01827     ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
01828 
01829     // Calculate the computed width and height. This varies by frame type
01830     if ((NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) ||
01831         (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_FLOATING) == mFrameType)) {
01832       // Inline replaced element and floating replaced element are basically
01833       // treated the same. First calculate the computed width
01834       if (eStyleUnit_Auto == widthUnit) {
01835         // A specified value of 'auto' uses the element's intrinsic width
01836         mComputedWidth = NS_INTRINSICSIZE;
01837       } else {
01838         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
01839                                mStylePosition->mWidth, mComputedWidth);
01840       }
01841 
01842       AdjustComputedWidth(PR_TRUE);
01843 
01844       // Now calculate the computed height
01845       if (eStyleUnit_Auto == heightUnit) {
01846         // A specified value of 'auto' uses the element's intrinsic height
01847         mComputedHeight = NS_INTRINSICSIZE;
01848       } else {
01849         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
01850                              mStylePosition->mHeight,
01851                              mComputedHeight);
01852       }
01853 
01854       AdjustComputedHeight(PR_TRUE);
01855 
01856     } else if (NS_CSS_FRAME_TYPE_FLOATING == mFrameType) {
01857       // Floating non-replaced element. First calculate the computed width
01858       if (eStyleUnit_Auto == widthUnit) {
01859         if ((NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) &&
01860             (eStyleUnit_Percent == mStylePosition->mWidth.GetUnit())) {
01861           // The element has a percentage width, but since the containing
01862           // block width is unconstrained we set 'widthUnit' to 'auto'
01863           // above. However, we want the element to be unconstrained, too
01864           mComputedWidth = NS_UNCONSTRAINEDSIZE;
01865 
01866         } else if (NS_STYLE_DISPLAY_TABLE == mStyleDisplay->mDisplay) {
01867           // It's an outer table because an inner table is not positioned
01868           // shrink wrap its width since the outer table is anonymous
01869           mComputedWidth = NS_SHRINKWRAPWIDTH;
01870 
01871         } else {
01872           NS_ASSERTION(eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(),
01873                        "How did we get here?");
01874           // The CSS2 spec says the computed width should be 0; however, that's
01875           // not what Nav and IE do and even the spec doesn't really want that
01876           // to happen.
01877           //
01878           // Instead, have the element shrink wrap its width
01879           mComputedWidth = NS_SHRINKWRAPWIDTH;
01880 
01881           // Limit the width to the available width.  This factors in
01882           // other floats that impact this float.
01883           // XXX It's possible that this should be quirks-only.  Probable, in fact.
01884           nscoord widthFromCB = availableWidth;
01885           if (NS_UNCONSTRAINEDSIZE != widthFromCB) {
01886             widthFromCB -= mComputedBorderPadding.left + mComputedBorderPadding.right +
01887                            mComputedMargin.left + mComputedMargin.right;
01888           }
01889           if (mComputedMaxWidth > widthFromCB) {
01890             mComputedMaxWidth = widthFromCB;
01891           }
01892         }
01893 
01894       } else {
01895         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
01896                                mStylePosition->mWidth, mComputedWidth);
01897       }
01898 
01899       // Take into account minimum and maximum sizes
01900       AdjustComputedWidth(PR_TRUE);
01901 
01902       // Now calculate the computed height
01903       if (eStyleUnit_Auto == heightUnit) {
01904         mComputedHeight = NS_AUTOHEIGHT;  // let it choose its height
01905       } else {
01906         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
01907                              mStylePosition->mHeight,
01908                              mComputedHeight);
01909       }
01910 
01911       AdjustComputedHeight(PR_TRUE);
01912     
01913     } else if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
01914       // Internal table elements. The rules vary depending on the type.
01915       // Calculate the computed width
01916       PRBool rowOrRowGroup = PR_FALSE;
01917       if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
01918           (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
01919         // 'width' property doesn't apply to table rows and row groups
01920         widthUnit = eStyleUnit_Auto;
01921         rowOrRowGroup = PR_TRUE;
01922       }
01923 
01924       if (eStyleUnit_Auto == widthUnit) {
01925         mComputedWidth = availableWidth;
01926 
01927         if ((mComputedWidth != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
01928           // Internal table elements don't have margins. Only tables and
01929           // cells have border and padding
01930           mComputedWidth -= mComputedBorderPadding.left +
01931             mComputedBorderPadding.right;
01932         }
01933       
01934       } else {
01935         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
01936                                mStylePosition->mWidth, mComputedWidth);
01937       }
01938 
01939       // Calculate the computed height
01940       if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
01941           (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
01942         // 'height' property doesn't apply to table columns and column groups
01943         heightUnit = eStyleUnit_Auto;
01944       }
01945       if (eStyleUnit_Auto == heightUnit) {
01946         mComputedHeight = NS_AUTOHEIGHT;
01947       } else {
01948         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
01949                              mStylePosition->mHeight,
01950                              mComputedHeight);
01951       }
01952 
01953       // Doesn't apply to table elements
01954       mComputedMinWidth = mComputedMinHeight = 0;
01955       mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
01956 
01957     } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
01958       // XXX not sure if this belongs here or somewhere else - cwk
01959       InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
01960                               aContainingBlockHeight);
01961     } else if (NS_CSS_FRAME_TYPE_INLINE == mFrameType) {
01962       // Inline non-replaced elements do not have computed widths or heights
01963       // XXX add this check to HaveFixedContentHeight/Width too
01964       mComputedWidth = NS_UNCONSTRAINEDSIZE;
01965       mComputedHeight = NS_UNCONSTRAINEDSIZE;
01966       mComputedMargin.top = 0;
01967       mComputedMargin.bottom = 0;
01968       mComputedMinWidth = mComputedMinHeight = 0;
01969       mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
01970     } else {
01971       ComputeBlockBoxData(aPresContext, cbrs, widthUnit, heightUnit,
01972                           aContainingBlockWidth,
01973                           aContainingBlockHeight);
01974     }
01975   }
01976   // Check for blinking text and permission to display it
01977   mFlags.mBlinks = (parentReflowState && parentReflowState->mFlags.mBlinks);
01978   if (!mFlags.mBlinks && BlinkIsAllowed()) {
01979     const nsStyleTextReset* st = frame->GetStyleTextReset();
01980     mFlags.mBlinks = 
01981       ((st->mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) != 0);
01982   }
01983 }
01984 
01985 // Compute the box data for block and block-replaced elements in the
01986 // normal flow.
01987 void
01988 nsHTMLReflowState::ComputeBlockBoxData(nsPresContext* aPresContext,
01989                                        const nsHTMLReflowState* cbrs,
01990                                        nsStyleUnit aWidthUnit,
01991                                        nsStyleUnit aHeightUnit,
01992                                        nscoord aContainingBlockWidth,
01993                                        nscoord aContainingBlockHeight)
01994 {
01995   // Compute the content width
01996   if (eStyleUnit_Auto == aWidthUnit) {
01997     if (NS_FRAME_IS_REPLACED(mFrameType)) {
01998       // Block-level replaced element in the flow. A specified value of 
01999       // 'auto' uses the element's intrinsic width (CSS2 10.3.4)
02000       mComputedWidth = NS_INTRINSICSIZE;
02001     } else {
02002       // Block-level non-replaced element in the flow. 'auto' values
02003       // for margin-left and margin-right become 0, and the sum of the
02004       // areas must equal the width of the content-area of the parent
02005       // element.
02006       if (NS_UNCONSTRAINEDSIZE == availableWidth) {
02007         // During pass1 table reflow, auto side margin values are
02008         // uncomputable (== 0).
02009         mComputedWidth = NS_UNCONSTRAINEDSIZE;
02010       } else if (NS_SHRINKWRAPWIDTH == aContainingBlockWidth) {
02011         // The containing block should shrink wrap its width, so have
02012         // the child block do the same
02013         mComputedWidth = NS_UNCONSTRAINEDSIZE;
02014 
02015         // Let its content area be as wide as the containing block's max width
02016         // minus any margin and border/padding
02017         nscoord maxWidth = cbrs->mComputedMaxWidth;
02018         if (NS_UNCONSTRAINEDSIZE != maxWidth) {
02019           maxWidth -= mComputedMargin.left + mComputedBorderPadding.left + 
02020                       mComputedMargin.right + mComputedBorderPadding.right;
02021         }
02022         if (maxWidth < mComputedMaxWidth) {
02023           mComputedMaxWidth = maxWidth;
02024         }
02025 
02026       } else {
02027         // tables act like replaced elements regarding mComputedWidth 
02028         nsIAtom* fType = frame->GetType();
02029         if (nsLayoutAtoms::tableOuterFrame == fType) {
02030           mComputedWidth = 0; // XXX temp fix for trees
02031         } else if ((nsLayoutAtoms::tableFrame == fType) ||
02032                    (nsLayoutAtoms::tableCaptionFrame == fType)) {
02033           mComputedWidth = NS_SHRINKWRAPWIDTH;
02034           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
02035             mComputedMargin.left = NS_AUTOMARGIN;
02036           }
02037           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
02038             mComputedMargin.right = NS_AUTOMARGIN;
02039           }
02040         } else {
02041           mComputedWidth = availableWidth - mComputedMargin.left -
02042             mComputedMargin.right - mComputedBorderPadding.left -
02043             mComputedBorderPadding.right;
02044           mComputedWidth = PR_MAX(mComputedWidth, 0);
02045         }
02046 
02047         AdjustComputedWidth(PR_FALSE);
02048         CalculateBlockSideMargins(cbrs->mComputedWidth, mComputedWidth);
02049       }
02050     }
02051   } else {
02052     ComputeHorizontalValue(aContainingBlockWidth, aWidthUnit,
02053                            mStylePosition->mWidth, mComputedWidth);
02054 
02055     AdjustComputedWidth(PR_TRUE); 
02056 
02057     // Now that we have the computed-width, compute the side margins
02058     CalculateBlockSideMargins(cbrs->mComputedWidth, mComputedWidth);
02059   }
02060 
02061   // Compute the content height
02062   if (eStyleUnit_Auto == aHeightUnit) {
02063     if (NS_FRAME_IS_REPLACED(mFrameType)) {
02064       // For replaced elements use the intrinsic size for "auto"
02065       mComputedHeight = NS_INTRINSICSIZE;
02066     } else {
02067       // For non-replaced elements auto means unconstrained
02068       mComputedHeight = NS_UNCONSTRAINEDSIZE;
02069     }
02070   } else {
02071     ComputeVerticalValue(aContainingBlockHeight, aHeightUnit,
02072                          mStylePosition->mHeight, mComputedHeight);
02073   }
02074   AdjustComputedHeight(PR_TRUE);
02075 }
02076 
02077 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
02078 //
02079 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
02080 //   'padding-right' + 'border-right-width' + 'margin-right'
02081 //   = width of containing block 
02082 //
02083 // Note: the width unit is not auto when this is called
02084 void
02085 nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
02086                                              nscoord aComputedWidth)
02087 {
02088   // Because of the ugly way we do intrinsic sizing within Reflow, this method
02089   // doesn't necessarily produce the right results.  The results will be
02090   // adjusted in nsBlockReflowContext::AlignBlockHorizontally after reflow.
02091   // The code for tables is particularly sensitive to regressions; the
02092   // numerous |isTable| checks are technically incorrect, but necessary
02093   // for basic testcases.
02094 
02095   // We can only provide values for auto side margins in a constrained
02096   // reflow. For unconstrained reflow there is no effective width to
02097   // compute against...
02098   if (NS_UNCONSTRAINEDSIZE == aComputedWidth ||
02099       NS_UNCONSTRAINEDSIZE == aAvailWidth)
02100     return;
02101 
02102   nscoord sum = mComputedMargin.left + mComputedBorderPadding.left +
02103     aComputedWidth + mComputedBorderPadding.right + mComputedMargin.right;
02104   if (sum == aAvailWidth)
02105     // The sum is already correct
02106     return;
02107 
02108   // Determine the left and right margin values. The width value
02109   // remains constant while we do this.
02110 
02111   PRBool isTable = mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
02112                    mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION;
02113 
02114   // Calculate how much space is available for margins
02115   nscoord availMarginSpace = aAvailWidth - sum;
02116 
02117   // XXXldb Should this be quirks-mode only?  And why captions?
02118   if (isTable)
02119     // XXXldb Why does this break things so badly if this is changed to
02120     // availMarginSpace += mComputedBorderPadding.left +
02121     //                     mComputedBorderPadding.right;
02122     availMarginSpace = aAvailWidth - aComputedWidth;
02123 
02124   // If the available margin space is negative, then don't follow the
02125   // usual overconstraint rules.
02126   if (availMarginSpace < 0) {
02127     if (!isTable) {
02128       if (mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR) {
02129         mComputedMargin.right += availMarginSpace;
02130       } else {
02131         mComputedMargin.left += availMarginSpace;
02132       }
02133     } else {
02134       mComputedMargin.left = 0;
02135       mComputedMargin.right = 0;
02136       if (mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) {
02137         mComputedMargin.left = availMarginSpace;
02138       }
02139     }
02140     return;
02141   }
02142 
02143   // The css2 spec clearly defines how block elements should behave
02144   // in section 10.3.3.
02145   PRBool isAutoLeftMargin =
02146     eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
02147   PRBool isAutoRightMargin =
02148     eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
02149   if (!isAutoLeftMargin && !isAutoRightMargin && !isTable) {
02150     // Neither margin is 'auto' so we're over constrained. Use the
02151     // 'direction' property of the parent to tell which margin to
02152     // ignore
02153     // First check if there is an HTML alignment that we should honor
02154     const nsHTMLReflowState* prs = parentReflowState;
02155     if (prs &&
02156         (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
02157          prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
02158          prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
02159       isAutoLeftMargin =
02160         prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
02161       isAutoRightMargin =
02162         prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
02163     }
02164     // Otherwise apply the CSS rules, and ignore one margin by forcing
02165     // it to 'auto', depending on 'direction'.
02166     else if (NS_STYLE_DIRECTION_LTR == mStyleVisibility->mDirection) {
02167       isAutoRightMargin = PR_TRUE;
02168     }
02169     else {
02170       isAutoLeftMargin = PR_TRUE;
02171     }
02172   }
02173 
02174   // Logic which is common to blocks and tables
02175   if (isAutoLeftMargin) {
02176     if (isAutoRightMargin) {
02177       // Both margins are 'auto' so their computed values are equal
02178       mComputedMargin.left = availMarginSpace / 2;
02179       mComputedMargin.right = availMarginSpace - mComputedMargin.left;
02180     } else {
02181       mComputedMargin.left = availMarginSpace;
02182     }
02183   } else if (isAutoRightMargin) {
02184     mComputedMargin.right = availMarginSpace;
02185   }
02186 }
02187 
02188 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f    // in term of emHeight 
02189 // For "normal" we use the font's normal line height (em height + leading).
02190 // If both internal leading and  external leading specified by font itself
02191 // are zeros, we should compensate this by creating extra (external) leading 
02192 // in eCompensateLeading mode. This is necessary because without this 
02193 // compensation, normal line height might looks too tight. 
02194 
02195 // For risk management, we use preference to control the behavior, and 
02196 // eNoExternalLeading is the old behavior.
02197 static nscoord
02198 GetNormalLineHeight(nsIFontMetrics* aFontMetrics)
02199 {
02200   NS_PRECONDITION(nsnull != aFontMetrics, "no font metrics");
02201 
02202   nscoord normalLineHeight;
02203 
02204 #ifdef FONT_LEADING_APIS_V2
02205   nscoord externalLeading, internalLeading, emHeight;
02206   aFontMetrics->GetExternalLeading(externalLeading);
02207   aFontMetrics->GetInternalLeading(internalLeading);
02208   aFontMetrics->GetEmHeight(emHeight);
02209   switch (GetNormalLineHeightCalcControl()) {
02210   case eIncludeExternalLeading:
02211     normalLineHeight = emHeight+ internalLeading + externalLeading;
02212     break;
02213   case eCompensateLeading:
02214     if (!internalLeading && !externalLeading)
02215       normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
02216     else
02217       normalLineHeight = emHeight+ internalLeading + externalLeading;
02218     break;
02219   default:
02220     //case eNoExternalLeading:
02221     normalLineHeight = emHeight + internalLeading;
02222   }
02223 #else
02224   aFontMetrics->GetNormalLineHeight(normalLineHeight);
02225 #endif // FONT_LEADING_APIS_V2
02226   return normalLineHeight;
02227 }
02228 
02229 static nscoord
02230 ComputeLineHeight(nsPresContext* aPresContext,
02231                   nsIRenderingContext* aRenderingContext,
02232                   nsStyleContext* aStyleContext)
02233 {
02234   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");
02235 
02236   nscoord lineHeight;
02237 
02238   const nsStyleFont* font = aStyleContext->GetStyleFont();
02239   const nsStyleCoord& lhCoord = aStyleContext->GetStyleText()->mLineHeight;
02240   
02241   nsStyleUnit unit = lhCoord.GetUnit();
02242 
02243   if (unit == eStyleUnit_Coord) {
02244     // For length values just use the pre-computed value
02245     lineHeight = lhCoord.GetCoordValue();
02246   } else if (unit == eStyleUnit_Factor) {
02247     // For factor units the computed value of the line-height property 
02248     // is found by multiplying the factor by the font's computed size
02249     // (adjusted for min-size prefs and text zoom).
02250     float factor = lhCoord.GetFactorValue();
02251     lineHeight = NSToCoordRound(factor * font->mFont.size);
02252   } else {
02253     NS_ASSERTION(eStyleUnit_Normal == unit, "bad unit");
02254     nsCOMPtr<nsIDeviceContext> deviceContext;
02255     aRenderingContext->GetDeviceContext(*getter_AddRefs(deviceContext));
02256     const nsStyleVisibility* vis = aStyleContext->GetStyleVisibility();
02257     nsCOMPtr<nsIFontMetrics> fm;
02258     deviceContext->GetMetricsFor(font->mFont, vis->mLangGroup,
02259                                  *getter_AddRefs(fm));
02260     lineHeight = GetNormalLineHeight(fm);
02261   }
02262   return lineHeight;
02263 }
02264 
02265 nscoord
02266 nsHTMLReflowState::CalcLineHeight(nsPresContext* aPresContext,
02267                                   nsIRenderingContext* aRenderingContext,
02268                                   nsIFrame* aFrame)
02269 {
02270   NS_ASSERTION(aFrame && aFrame->GetStyleContext(),
02271                "Bogus data passed in to CalcLineHeight");
02272 
02273   nscoord lineHeight = ComputeLineHeight(aPresContext, aRenderingContext,
02274                                          aFrame->GetStyleContext());
02275 
02276   NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
02277 
02278   return lineHeight;
02279 }
02280 
02281 void
02282 nsHTMLReflowState::ComputeHorizontalValue(nscoord aContainingBlockWidth,
02283                                           nsStyleUnit aUnit,
02284                                           const nsStyleCoord& aCoord,
02285                                           nscoord& aResult)
02286 {
02287   aResult = 0;
02288   if (eStyleUnit_Percent == aUnit) {
02289     if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
02290       aResult = 0;
02291     } else {
02292       float pct = aCoord.GetPercentValue();
02293       aResult = NSToCoordFloor(aContainingBlockWidth * pct);
02294     }
02295   
02296   } else if (eStyleUnit_Coord == aUnit) {
02297     aResult = aCoord.GetCoordValue();
02298   }
02299   else if (eStyleUnit_Chars == aUnit) {
02300     if ((nsnull == rendContext) || (nsnull == frame)) {
02301       // We can't compute it without a rendering context or frame, so
02302       // pretend its zero...
02303     }
02304     else {
02305       nsStyleContext* styleContext = frame->GetStyleContext();
02306       SetFontFromStyle(rendContext, styleContext);
02307       nscoord fontWidth;
02308       rendContext->GetWidth('M', fontWidth);
02309       aResult = aCoord.GetIntValue() * fontWidth;
02310     }
02311   }
02312 }
02313 
02314 void
02315 nsHTMLReflowState::ComputeVerticalValue(nscoord aContainingBlockHeight,
02316                                         nsStyleUnit aUnit,
02317                                         const nsStyleCoord& aCoord,
02318                                         nscoord& aResult)
02319 {
02320   aResult = 0;
02321   if (eStyleUnit_Percent == aUnit) {
02322     // Verify no one is trying to calculate a percentage based height against
02323     // a height that's shrink wrapping to its content. In that case they should
02324     // treat the specified value like 'auto'
02325     NS_ASSERTION(NS_AUTOHEIGHT != aContainingBlockHeight, "unexpected containing block height");
02326     if (NS_AUTOHEIGHT!=aContainingBlockHeight)
02327     {
02328       float pct = aCoord.GetPercentValue();
02329       aResult = NSToCoordFloor(aContainingBlockHeight * pct);
02330     }
02331     else {  // safest thing to do for an undefined height is to make it 0
02332       aResult = 0;
02333     }
02334 
02335   } else if (eStyleUnit_Coord == aUnit) {
02336     aResult = aCoord.GetCoordValue();
02337   }
02338 }
02339 
02340 void
02341 nsHTMLReflowState::ComputeMargin(nscoord aContainingBlockWidth,
02342                                  const nsHTMLReflowState* aContainingBlockRS)
02343 {
02344   // If style style can provide us the margin directly, then use it.
02345   if (!mStyleMargin->GetMargin(mComputedMargin)) {
02346     // We have to compute the value
02347     if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
02348       mComputedMargin.left = 0;
02349       mComputedMargin.right = 0;
02350 
02351       if (eStyleUnit_Coord == mStyleMargin->mMargin.GetLeftUnit()) {
02352         nsStyleCoord left;
02353         
02354         mStyleMargin->mMargin.GetLeft(left),
02355         mComputedMargin.left = left.GetCoordValue();
02356       }
02357       if (eStyleUnit_Coord == mStyleMargin->mMargin.GetRightUnit()) {
02358         nsStyleCoord right;
02359         
02360         mStyleMargin->mMargin.GetRight(right),
02361         mComputedMargin.right = right.GetCoordValue();
02362       }
02363 
02364     } else {
02365       nsStyleCoord left, right;
02366 
02367       ComputeHorizontalValue(aContainingBlockWidth,
02368                              mStyleMargin->mMargin.GetLeftUnit(),
02369                              mStyleMargin->mMargin.GetLeft(left),
02370                              mComputedMargin.left);
02371       ComputeHorizontalValue(aContainingBlockWidth,
02372                              mStyleMargin->mMargin.GetRightUnit(),
02373                              mStyleMargin->mMargin.GetRight(right),
02374                              mComputedMargin.right);
02375     }
02376 
02377     const nsHTMLReflowState* rs2 = GetPageBoxReflowState(parentReflowState);
02378     nsStyleCoord top, bottom;
02379     if (nsnull != rs2) {
02380       // According to the CSS2 spec, margin percentages are
02381       // calculated with respect to the *height* of the containing
02382       // block when in a paginated context.
02383       ComputeVerticalValue(rs2->mComputedHeight,
02384                            mStyleMargin->mMargin.GetTopUnit(),
02385                            mStyleMargin->mMargin.GetTop(top),
02386                            mComputedMargin.top);
02387       ComputeVerticalValue(rs2->mComputedHeight,
02388                            mStyleMargin->mMargin.GetBottomUnit(),
02389                            mStyleMargin->mMargin.GetBottom(bottom),
02390                            mComputedMargin.bottom);
02391     }
02392     else {
02393       // According to the CSS2 spec, margin percentages are
02394       // calculated with respect to the *width* of the containing
02395       // block, even for margin-top and margin-bottom.
02396       ComputeHorizontalValue(aContainingBlockWidth,
02397                              mStyleMargin->mMargin.GetTopUnit(),
02398                              mStyleMargin->mMargin.GetTop(top),
02399                              mComputedMargin.top);
02400       ComputeHorizontalValue(aContainingBlockWidth,
02401                              mStyleMargin->mMargin.GetBottomUnit(),
02402                              mStyleMargin->mMargin.GetBottom(bottom),
02403                              mComputedMargin.bottom);
02404     }
02405   }
02406 }
02407 
02408 void
02409 nsHTMLReflowState::ComputePadding(nscoord aContainingBlockWidth,
02410                                   const nsHTMLReflowState* aContainingBlockRS)
02411 
02412 {
02413   // If style can provide us the padding directly, then use it.
02414   if (!mStylePadding->GetPadding(mComputedPadding)) {
02415     // We have to compute the value
02416     nsStyleCoord left, right, top, bottom;
02417 
02418     ComputeHorizontalValue(aContainingBlockWidth,
02419                            mStylePadding->mPadding.GetLeftUnit(),
02420                            mStylePadding->mPadding.GetLeft(left),
02421                            mComputedPadding.left);
02422     ComputeHorizontalValue(aContainingBlockWidth,
02423                            mStylePadding->mPadding.GetRightUnit(),
02424                            mStylePadding->mPadding.GetRight(right),
02425                            mComputedPadding.right);
02426 
02427     // According to the CSS2 spec, percentages are calculated with respect to
02428     // containing block width for padding-top and padding-bottom
02429     ComputeHorizontalValue(aContainingBlockWidth,
02430                            mStylePadding->mPadding.GetTopUnit(),
02431                            mStylePadding->mPadding.GetTop(top),
02432                            mComputedPadding.top);
02433     ComputeHorizontalValue(aContainingBlockWidth,
02434                            mStylePadding->mPadding.GetBottomUnit(),
02435                            mStylePadding->mPadding.GetBottom(bottom),
02436                            mComputedPadding.bottom);
02437   }
02438   // a table row/col group, row/col doesn't have padding
02439   if (frame) {
02440     nsIAtom* frameType = frame->GetType();
02441     if ((nsLayoutAtoms::tableRowGroupFrame == frameType) ||
02442         (nsLayoutAtoms::tableColGroupFrame == frameType) ||
02443         (nsLayoutAtoms::tableRowFrame      == frameType) ||
02444         (nsLayoutAtoms::tableColFrame      == frameType)) {
02445       mComputedPadding.top    = 0;
02446       mComputedPadding.right  = 0;
02447       mComputedPadding.bottom = 0;
02448       mComputedPadding.left   = 0;
02449     }
02450   }
02451 }
02452 
02453 void
02454 nsHTMLReflowState::ApplyMinMaxConstraints(nscoord* aFrameWidth,
02455                                           nscoord* aFrameHeight) const
02456 {
02457   if (aFrameWidth) {
02458     if (NS_UNCONSTRAINEDSIZE != mComputedMaxWidth) {
02459       *aFrameWidth = PR_MIN(*aFrameWidth, mComputedMaxWidth);
02460     }
02461     *aFrameWidth = PR_MAX(*aFrameWidth, mComputedMinWidth);
02462   }
02463 
02464   if (aFrameHeight) {
02465     if (NS_UNCONSTRAINEDSIZE != mComputedMaxHeight) {
02466       *aFrameHeight = PR_MIN(*aFrameHeight, mComputedMaxHeight);
02467     }
02468     *aFrameHeight = PR_MAX(*aFrameHeight, mComputedMinHeight);
02469   }
02470 }
02471 
02472 void
02473 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
02474                                        nscoord aContainingBlockHeight,
02475                                        const nsHTMLReflowState* aContainingBlockRS)
02476 {
02477   nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
02478   ComputeHorizontalValue(aContainingBlockWidth, minWidthUnit,
02479                          mStylePosition->mMinWidth, mComputedMinWidth);
02480   nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
02481   if (eStyleUnit_Null == maxWidthUnit) {
02482     // Specified value of 'none'
02483     mComputedMaxWidth = NS_UNCONSTRAINEDSIZE;  // no limit
02484   } else {
02485     ComputeHorizontalValue(aContainingBlockWidth, maxWidthUnit,
02486                            mStylePosition->mMaxWidth, mComputedMaxWidth);
02487   }
02488 
02489   // If the computed value of 'min-width' is greater than the value of
02490   // 'max-width', 'max-width' is set to the value of 'min-width'
02491   if (mComputedMinWidth > mComputedMaxWidth) {
02492     mComputedMaxWidth = mComputedMinWidth;
02493   }
02494 
02495   nsStyleUnit minHeightUnit = mStylePosition->mMinHeight.GetUnit();
02496   // Check for percentage based values and a containing block height that
02497   // depends on the content height. Treat them like 'auto'
02498   if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
02499       (eStyleUnit_Percent == minHeightUnit)) {
02500     mComputedMinHeight = 0;
02501   } else {
02502     ComputeVerticalValue(aContainingBlockHeight, minHeightUnit,
02503                          mStylePosition->mMinHeight, mComputedMinHeight);
02504   }
02505   nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
02506   if (eStyleUnit_Null == maxHeightUnit) {
02507     // Specified value of 'none'
02508     mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;  // no limit
02509   } else {
02510     // Check for percentage based values and a containing block height that
02511     // depends on the content height. Treat them like 'auto'
02512     if ((NS_AUTOHEIGHT == aContainingBlockHeight) && 
02513         (eStyleUnit_Percent == maxHeightUnit)) {
02514       mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
02515     } else {
02516       ComputeVerticalValue(aContainingBlockHeight, maxHeightUnit,
02517                            mStylePosition->mMaxHeight, mComputedMaxHeight);
02518     }
02519   }
02520 
02521   // If the computed value of 'min-height' is greater than the value of
02522   // 'max-height', 'max-height' is set to the value of 'min-height'
02523   if (mComputedMinHeight > mComputedMaxHeight) {
02524     mComputedMaxHeight = mComputedMinHeight;
02525   }
02526 }
02527 
02528 
02529 void nsHTMLReflowState::AdjustComputedHeight(PRBool aAdjustForBoxSizing)
02530 {
02531   // only do the math if the height  is not a symbolic value
02532   if (mComputedHeight == NS_UNCONSTRAINEDSIZE) {
02533     return;
02534   }
02535   
02536   NS_ASSERTION(mComputedHeight >= 0, "Negative Height Input - very bad");
02537 
02538   // Factor in any minimum and maximum size information
02539   if (mComputedHeight > mComputedMaxHeight) {
02540     mComputedHeight = mComputedMaxHeight;
02541   } else if (mComputedHeight < mComputedMinHeight) {
02542     mComputedHeight = mComputedMinHeight;
02543   }
02544 
02545   if (aAdjustForBoxSizing) {
02546     // remove extra padding/border if box-sizing property is set
02547     switch (mStylePosition->mBoxSizing) {
02548     case NS_STYLE_BOX_SIZING_PADDING : {
02549       mComputedHeight -= mComputedPadding.top + mComputedPadding.bottom;
02550       break;
02551     }
02552     case NS_STYLE_BOX_SIZING_BORDER : {
02553       mComputedHeight -= mComputedBorderPadding.top + mComputedBorderPadding.bottom;
02554     }
02555     default : break;
02556     }
02557     
02558     // If it did go bozo because of too much border or padding, set to 0
02559     if(mComputedHeight < 0) mComputedHeight = 0;
02560   }  
02561 }
02562 
02563 void nsHTMLReflowState::AdjustComputedWidth(PRBool aAdjustForBoxSizing)
02564 {
02565   // only do the math if the width is not a symbolic value
02566   if (mComputedWidth == NS_UNCONSTRAINEDSIZE) {
02567     return;
02568   }
02569   
02570   NS_ASSERTION(mComputedWidth >= 0, "Negative Width Input - very bad");
02571 
02572   // Factor in any minimum and maximum size information
02573   if (mComputedWidth > mComputedMaxWidth) {
02574     mComputedWidth = mComputedMaxWidth;
02575   } else if (mComputedWidth < mComputedMinWidth) {
02576     mComputedWidth = mComputedMinWidth;
02577   }
02578   
02579   if (aAdjustForBoxSizing) {
02580     // remove extra padding/border if box-sizing property is set
02581     switch (mStylePosition->mBoxSizing) {
02582     case NS_STYLE_BOX_SIZING_PADDING : {
02583       mComputedWidth -= mComputedPadding.left + mComputedPadding.right;
02584       break;
02585     }
02586     case NS_STYLE_BOX_SIZING_BORDER : {
02587       mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right;
02588     }
02589     default : break;
02590     }
02591 
02592     // If it did go bozo because of too much border or padding, set to 0
02593     if(mComputedWidth < 0) mComputedWidth = 0;
02594   }
02595 }
02596 
02597 #ifdef IBMBIDI
02598 PRBool
02599 nsHTMLReflowState::IsBidiFormControl(nsPresContext* aPresContext)
02600 {
02601   // This check is only necessary on visual bidi pages, because most
02602   // visual pages use logical order for form controls so that they will
02603   // display correctly on native widgets in OSs with Bidi support.
02604   // So bail out if the page is not Bidi, or not visual, or if the pref is
02605   // set to use visual order on forms in visual pages
02606   if (!aPresContext->BidiEnabled()) {
02607     return PR_FALSE;
02608   }
02609 
02610   if (!aPresContext->IsVisualMode()) {
02611     return PR_FALSE;
02612   }
02613 
02614   PRUint32 options = aPresContext->GetBidi();
02615   if (IBMBIDI_CONTROLSTEXTMODE_LOGICAL != GET_BIDI_OPTION_CONTROLSTEXTMODE(options)) {
02616     return PR_FALSE;
02617   }
02618 
02619   nsIContent* content = frame->GetContent();
02620   if (!content) {
02621     return PR_FALSE;
02622   }
02623 
02624   // If this is a root reflow, we have to walk up the content tree to
02625   // find out if the reflow root is a descendant of a form control.
02626   // Otherwise, just test this content node
02627   if (mReflowDepth == 0) {
02628     for ( ; content; content = content->GetParent()) {
02629       if (content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) {
02630         return PR_TRUE;
02631       }
02632     }
02633   } else {
02634     return (content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL));
02635   }
02636   
02637   return PR_FALSE;
02638 }
02639 #endif