forked from mirrors/gecko-dev
		
	 157c017816
			
		
	
	
		157c017816
		
	
	
	
	
		
			
			Make the line list doubly linked and access it through a list class and iterators. Stop recomputing margins on all of the children of each block in the reflow chain (which causes O(N^2) state recovery during incremental reflow). Instead, add a second dirty bit to the lines and walk backwards through the line list to recompute vertical margins only when either dirty bit is set and the previous line was not reflowed. Add nsIFrame::IsEmpty to identify frames through which margins collapse. Fix O(N^2) propagation of float damage by maintaining a set of intervals damaged by floats (bug 61962) and be sure to damage the correct areas (bug 48138). Introduce nsCollapsingMargin to do correct collapsing of combinations of positive and negative margins (bug 50142). Clean up some odds and ends and fix another smaller O(N^2) problem in nsBlockFrame::AddFrames. r=attinasi, rbs sr=waterson
		
			
				
	
	
		
			2639 lines
		
	
	
	
		
			101 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2639 lines
		
	
	
	
		
			101 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* ***** BEGIN LICENSE BLOCK *****
 | |
|  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 | |
|  *
 | |
|  * The contents of this file are subject to the Netscape Public License
 | |
|  * Version 1.1 (the "License"); you may not use this file except in
 | |
|  * compliance with the License. You may obtain a copy of the License at
 | |
|  * http://www.mozilla.org/NPL/
 | |
|  *
 | |
|  * Software distributed under the License is distributed on an "AS IS" basis,
 | |
|  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 | |
|  * for the specific language governing rights and limitations under the
 | |
|  * License.
 | |
|  *
 | |
|  * The Original Code is Mozilla Communicator client code.
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is 
 | |
|  * Netscape Communications Corporation.
 | |
|  * Portions created by the Initial Developer are Copyright (C) 1998
 | |
|  * the Initial Developer. All Rights Reserved.
 | |
|  *
 | |
|  * Contributor(s):
 | |
|  *
 | |
|  * Alternatively, the contents of this file may be used under the terms of
 | |
|  * either the GNU General Public License Version 2 or later (the "GPL"), or 
 | |
|  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 | |
|  * in which case the provisions of the GPL or the LGPL are applicable instead
 | |
|  * of those above. If you wish to allow use of your version of this file only
 | |
|  * under the terms of either the GPL or the LGPL, and not to allow others to
 | |
|  * use your version of this file under the terms of the NPL, indicate your
 | |
|  * decision by deleting the provisions above and replace them with the notice
 | |
|  * and other provisions required by the GPL or the LGPL. If you do not delete
 | |
|  * the provisions above, a recipient may use your version of this file under
 | |
|  * the terms of any one of the NPL, the GPL or the LGPL.
 | |
|  *
 | |
|  * ***** END LICENSE BLOCK ***** */
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsFrame.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsHTMLAtoms.h"
 | |
| #include "nsIPresContext.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsLayoutAtoms.h"
 | |
| #include "nsIDeviceContext.h"
 | |
| #include "nsIRenderingContext.h"
 | |
| #include "nsIFontMetrics.h"
 | |
| #include "nsBlockFrame.h"
 | |
| #include "nsLineBox.h"
 | |
| #include "nsImageFrame.h"
 | |
| #include "nsIPref.h"
 | |
| #include "nsIServiceManager.h"
 | |
| 
 | |
| #ifdef NS_DEBUG
 | |
| #undef NOISY_VERTICAL_ALIGN
 | |
| #else
 | |
| #undef NOISY_VERTICAL_ALIGN
 | |
| #endif
 | |
| 
 | |
| // hack for bug 50695
 | |
| #include "nsIFormManager.h"
 | |
| 
 | |
| // Prefs-driven control for |text-decoration: blink|
 | |
| static PRPackedBool sBlinkPrefIsLoaded = PR_FALSE;
 | |
| static PRPackedBool sBlinkIsAllowed = PR_TRUE;
 | |
| 
 | |
| #ifdef DEBUG
 | |
| const char*
 | |
| nsHTMLReflowState::ReasonToString(nsReflowReason aReason)
 | |
| {
 | |
|   static const char* reasons[] = {
 | |
|     "initial", "incremental", "resize", "style-change", "dirty"
 | |
|   };
 | |
| 
 | |
|   return reasons[aReason];
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Initialize a <b>root</b> reflow state with a rendering context to
 | |
| // use for measuring things.
 | |
| nsHTMLReflowState::nsHTMLReflowState(nsIPresContext*      aPresContext,
 | |
|                                      nsIFrame*            aFrame,
 | |
|                                      nsReflowReason       aReason,
 | |
|                                      nsIRenderingContext* aRenderingContext,
 | |
|                                      const nsSize&        aAvailableSpace)
 | |
|   : mReflowDepth(0)
 | |
| {
 | |
|   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");
 | |
| 
 | |
|   parentReflowState = nsnull;
 | |
|   frame = aFrame;
 | |
|   reason = aReason;
 | |
|   reflowCommand = nsnull;
 | |
|   availableWidth = aAvailableSpace.width;
 | |
|   availableHeight = aAvailableSpace.height;
 | |
|   rendContext = aRenderingContext;
 | |
|   mSpaceManager = nsnull;
 | |
|   mLineLayout = nsnull;
 | |
|   isTopOfPage = PR_FALSE;
 | |
|   Init(aPresContext);
 | |
| #ifdef IBMBIDI
 | |
|   mRightEdge = NS_UNCONSTRAINEDSIZE;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| // Initialize a <b>root</b> reflow state for an <b>incremental</b>
 | |
| // reflow.
 | |
| nsHTMLReflowState::nsHTMLReflowState(nsIPresContext*      aPresContext,
 | |
|                                      nsIFrame*            aFrame,
 | |
|                                      nsIReflowCommand&    aReflowCommand,
 | |
|                                      nsIRenderingContext* aRenderingContext,
 | |
|                                      const nsSize&        aAvailableSpace)
 | |
|   : mReflowDepth(0)
 | |
| {
 | |
|   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");  
 | |
| 
 | |
|   reason = eReflowReason_Incremental;
 | |
|   parentReflowState = nsnull;
 | |
|   frame = aFrame;
 | |
|   reflowCommand = &aReflowCommand;
 | |
|   availableWidth = aAvailableSpace.width;
 | |
|   availableHeight = aAvailableSpace.height;
 | |
|   rendContext = aRenderingContext;
 | |
|   mSpaceManager = nsnull;
 | |
|   mLineLayout = nsnull;
 | |
|   isTopOfPage = PR_FALSE;
 | |
|   Init(aPresContext);
 | |
| #ifdef IBMBIDI
 | |
|   mRightEdge = NS_UNCONSTRAINEDSIZE;
 | |
| #endif // IBMBIDI
 | |
| }
 | |
| 
 | |
| // Initialize a reflow state for a child frames reflow. Some state
 | |
| // is copied from the parent reflow state; the remaining state is
 | |
| // computed.
 | |
| nsHTMLReflowState::nsHTMLReflowState(nsIPresContext*          aPresContext,
 | |
|                                      const nsHTMLReflowState& aParentReflowState,
 | |
|                                      nsIFrame*                aFrame,
 | |
|                                      const nsSize&            aAvailableSpace,
 | |
|                                      nsReflowReason           aReason)
 | |
|   : mReflowDepth(aParentReflowState.mReflowDepth + 1)
 | |
| {
 | |
|   parentReflowState = &aParentReflowState;
 | |
|   frame = aFrame;
 | |
|   reason = aReason;
 | |
|   reflowCommand = (reason == eReflowReason_Incremental)
 | |
|     ? aParentReflowState.reflowCommand
 | |
|     : nsnull;
 | |
|   availableWidth = aAvailableSpace.width;
 | |
|   availableHeight = aAvailableSpace.height;
 | |
| 
 | |
|   rendContext = aParentReflowState.rendContext;
 | |
|   mSpaceManager = aParentReflowState.mSpaceManager;
 | |
|   mLineLayout = aParentReflowState.mLineLayout;
 | |
|   isTopOfPage = aParentReflowState.isTopOfPage;
 | |
| 
 | |
|   Init(aPresContext);
 | |
| 
 | |
| #ifdef IBMBIDI
 | |
|   mRightEdge = aParentReflowState.mRightEdge;
 | |
| #endif // IBMBIDI
 | |
| }
 | |
| 
 | |
| // Same as the previous except that the reason is taken from the
 | |
| // parent's reflow state.
 | |
| nsHTMLReflowState::nsHTMLReflowState(nsIPresContext*          aPresContext,
 | |
|                                      const nsHTMLReflowState& aParentReflowState,
 | |
|                                      nsIFrame*                aFrame,
 | |
|                                      const nsSize&            aAvailableSpace)
 | |
|   : mReflowDepth(aParentReflowState.mReflowDepth + 1)
 | |
| {
 | |
|   parentReflowState = &aParentReflowState;
 | |
|   frame = aFrame;
 | |
|   reason = aParentReflowState.reason;
 | |
|   reflowCommand = aParentReflowState.reflowCommand;
 | |
|   availableWidth = aAvailableSpace.width;
 | |
|   availableHeight = aAvailableSpace.height;
 | |
| 
 | |
|   rendContext = aParentReflowState.rendContext;
 | |
|   mSpaceManager = aParentReflowState.mSpaceManager;
 | |
|   mLineLayout = aParentReflowState.mLineLayout;
 | |
|   isTopOfPage = aParentReflowState.isTopOfPage;
 | |
| 
 | |
|   Init(aPresContext);
 | |
| 
 | |
| #ifdef IBMBIDI
 | |
|   mRightEdge = aParentReflowState.mRightEdge;
 | |
| #endif // IBMBIDI
 | |
| }
 | |
| 
 | |
| // Version that species the containing block width and height
 | |
| nsHTMLReflowState::nsHTMLReflowState(nsIPresContext*          aPresContext,
 | |
|                                      const nsHTMLReflowState& aParentReflowState,
 | |
|                                      nsIFrame*                aFrame,
 | |
|                                      const nsSize&            aAvailableSpace,
 | |
|                                      nscoord                  aContainingBlockWidth,
 | |
|                                      nscoord                  aContainingBlockHeight)
 | |
|   : mReflowDepth(aParentReflowState.mReflowDepth + 1)
 | |
| {
 | |
|   parentReflowState = &aParentReflowState;
 | |
|   frame = aFrame;
 | |
|   reason = aParentReflowState.reason;
 | |
|   reflowCommand = aParentReflowState.reflowCommand;
 | |
|   availableWidth = aAvailableSpace.width;
 | |
|   availableHeight = aAvailableSpace.height;
 | |
| 
 | |
|   rendContext = aParentReflowState.rendContext;
 | |
|   mSpaceManager = aParentReflowState.mSpaceManager;
 | |
|   mLineLayout = aParentReflowState.mLineLayout;
 | |
|   isTopOfPage = aParentReflowState.isTopOfPage;
 | |
| 
 | |
|   Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
 | |
| 
 | |
| #ifdef IBMBIDI
 | |
|   mRightEdge = aParentReflowState.mRightEdge;
 | |
| #endif // IBMBIDI
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::Init(nsIPresContext* aPresContext,
 | |
|                         nscoord         aContainingBlockWidth,
 | |
|                         nscoord         aContainingBlockHeight)
 | |
| {
 | |
|   mCompactMarginWidth = 0;
 | |
|   mAlignCharOffset = 0;
 | |
|   mUseAlignCharOffset = 0;
 | |
| #ifdef DEBUG
 | |
|   mDebugHook = nsnull;
 | |
| #endif
 | |
| 
 | |
|   frame->GetStyleData(eStyleStruct_Position,
 | |
|                       (const nsStyleStruct*&)mStylePosition);
 | |
|   frame->GetStyleData(eStyleStruct_Display,
 | |
|                       (const nsStyleStruct*&)mStyleDisplay);
 | |
|   frame->GetStyleData(eStyleStruct_Visibility,
 | |
|                       (const nsStyleStruct*&)mStyleVisibility);
 | |
|   frame->GetStyleData(eStyleStruct_Border,
 | |
|                       (const nsStyleStruct*&)mStyleBorder);
 | |
|   frame->GetStyleData(eStyleStruct_Margin,
 | |
|                       (const nsStyleStruct*&)mStyleMargin);
 | |
|   frame->GetStyleData(eStyleStruct_Padding,
 | |
|                       (const nsStyleStruct*&)mStylePadding);
 | |
|   frame->GetStyleData(eStyleStruct_Text,
 | |
|                       (const nsStyleStruct*&)mStyleText);
 | |
|   mFrameType = DetermineFrameType(frame, mStyleDisplay);
 | |
|   InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
 | |
| }
 | |
| 
 | |
| const nsHTMLReflowState*
 | |
| nsHTMLReflowState::GetContainingBlockReflowState(const nsHTMLReflowState* aParentRS)
 | |
| {
 | |
|   while (nsnull != aParentRS) {
 | |
|     if (nsnull != aParentRS->frame) {
 | |
|       PRBool isContainingBlock;
 | |
|       // XXX This needs to go and we need to start using the info in the
 | |
|       // reflow state...
 | |
|       nsresult rv = aParentRS->frame->IsPercentageBase(isContainingBlock);
 | |
|       if (NS_SUCCEEDED(rv) && isContainingBlock) {
 | |
|         // a block inside a table cell needs to use the table cell
 | |
|         if (aParentRS->parentReflowState) {
 | |
|           nsCOMPtr<nsIAtom> fType;
 | |
|           aParentRS->parentReflowState->frame->GetFrameType(getter_AddRefs(fType));
 | |
|           if (nsLayoutAtoms::tableCellFrame == fType.get()) {
 | |
|             aParentRS = aParentRS->parentReflowState;
 | |
|           }
 | |
|         }
 | |
|         return aParentRS;
 | |
|       }
 | |
|     }
 | |
|     aParentRS = aParentRS->parentReflowState;
 | |
|   }
 | |
|   return nsnull;
 | |
| }
 | |
| 
 | |
| const nsHTMLReflowState*
 | |
| nsHTMLReflowState::GetPageBoxReflowState(const nsHTMLReflowState* aParentRS)
 | |
| {
 | |
|   // XXX write me as soon as we can ask a frame if it's a page frame...
 | |
|   return nsnull;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aParentRS)
 | |
| {
 | |
|   nscoord width = 0;
 | |
|   const nsHTMLReflowState* rs =
 | |
|     GetContainingBlockReflowState(aParentRS);
 | |
|   if (nsnull != rs) {
 | |
|     return aParentRS->mComputedWidth;
 | |
|   }
 | |
|   return width;
 | |
| }
 | |
| 
 | |
| nsCSSFrameType
 | |
| nsHTMLReflowState::DetermineFrameType(nsIFrame* aFrame)
 | |
| {
 | |
|   const nsStyleDisplay* styleDisplay;
 | |
|   aFrame->GetStyleData(eStyleStruct_Display,
 | |
|                        (const nsStyleStruct*&)styleDisplay);
 | |
|   return DetermineFrameType(aFrame, styleDisplay);
 | |
| }
 | |
| 
 | |
| nsCSSFrameType
 | |
| nsHTMLReflowState::DetermineFrameType(nsIFrame* aFrame,
 | |
|                                       const nsStyleDisplay* aDisplay)
 | |
| {
 | |
|   nsCSSFrameType frameType;
 | |
| 
 | |
|   // Get the frame state
 | |
|   nsFrameState  frameState;
 | |
|   aFrame->GetFrameState(&frameState);
 | |
|   
 | |
|   // Section 9.7 of the CSS2 spec indicates that absolute position
 | |
|   // takes precedence over float which takes precedence over display.
 | |
|   // Make sure the frame was actually moved out of the flow, and don't
 | |
|   // just assume what the style says
 | |
|   if (frameState & NS_FRAME_OUT_OF_FLOW) {
 | |
|     if (aDisplay->IsAbsolutelyPositioned()) {
 | |
|       frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
 | |
|     }
 | |
|     else if (NS_STYLE_FLOAT_NONE != aDisplay->mFloats) {
 | |
|       frameType = NS_CSS_FRAME_TYPE_FLOATING;
 | |
|     }
 | |
|     // XXXldb UMR in this case (else, else) we don't initialize frameType
 | |
|   }
 | |
|   else {
 | |
|     switch (aDisplay->mDisplay) {
 | |
|     case NS_STYLE_DISPLAY_BLOCK:
 | |
|     case NS_STYLE_DISPLAY_LIST_ITEM:
 | |
|     case NS_STYLE_DISPLAY_TABLE:
 | |
|     case NS_STYLE_DISPLAY_TABLE_CAPTION:
 | |
|       frameType = NS_CSS_FRAME_TYPE_BLOCK;
 | |
|       break;
 | |
| 
 | |
|     case NS_STYLE_DISPLAY_INLINE:
 | |
|     case NS_STYLE_DISPLAY_MARKER:
 | |
|     case NS_STYLE_DISPLAY_INLINE_TABLE:
 | |
|     case NS_STYLE_DISPLAY_INLINE_BOX:
 | |
|     case NS_STYLE_DISPLAY_INLINE_GRID:
 | |
|     case NS_STYLE_DISPLAY_INLINE_STACK:
 | |
|       frameType = NS_CSS_FRAME_TYPE_INLINE;
 | |
|       break;
 | |
| 
 | |
|     case NS_STYLE_DISPLAY_RUN_IN:
 | |
|     case NS_STYLE_DISPLAY_COMPACT:
 | |
|       // XXX need to look ahead at the frame's sibling
 | |
|       frameType = NS_CSS_FRAME_TYPE_BLOCK;
 | |
|       break;
 | |
| 
 | |
|     case NS_STYLE_DISPLAY_TABLE_CELL:
 | |
|     case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
 | |
|     case NS_STYLE_DISPLAY_TABLE_COLUMN:
 | |
|     case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
 | |
|     case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
 | |
|     case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
 | |
|     case NS_STYLE_DISPLAY_TABLE_ROW:
 | |
|       frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
 | |
|       break;
 | |
| 
 | |
|     case NS_STYLE_DISPLAY_NONE:
 | |
|     default:
 | |
|       frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if the frame is replaced
 | |
|   if (frameState & NS_FRAME_REPLACED_ELEMENT) {
 | |
|     frameType = NS_FRAME_REPLACED(frameType);
 | |
|   }
 | |
| 
 | |
|   return frameType;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputeRelativeOffsets(const nsHTMLReflowState* cbrs,
 | |
|                                           nscoord aContainingBlockWidth,
 | |
|                                           nscoord aContainingBlockHeight)
 | |
| {
 | |
|   nsStyleCoord  coord;
 | |
| 
 | |
|   // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
 | |
|   // and 'right' moves the boxes to the left. The computed values are always:
 | |
|   // left=-right
 | |
|   PRBool  leftIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit();
 | |
|   PRBool  rightIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit();
 | |
| 
 | |
|   // Check for percentage based values and an unconstrained containing
 | |
|   // block width. Treat them like 'auto'
 | |
|   if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
 | |
|     if (eStyleUnit_Percent == mStylePosition->mOffset.GetLeftUnit()) {
 | |
|       leftIsAuto = PR_TRUE;
 | |
|     }
 | |
|     if (eStyleUnit_Percent == mStylePosition->mOffset.GetRightUnit()) {
 | |
|       rightIsAuto = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If neither 'left' not 'right' are auto, then we're over-constrained and
 | |
|   // we ignore one of them
 | |
|   if (!leftIsAuto && !rightIsAuto) {
 | |
|     const nsStyleVisibility* vis;
 | |
|     frame->GetStyleData(eStyleStruct_Visibility, (const nsStyleStruct*&)vis);
 | |
|     
 | |
|     if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
 | |
|       rightIsAuto = PR_TRUE;
 | |
|     } else {
 | |
|       leftIsAuto = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (leftIsAuto) {
 | |
|     if (rightIsAuto) {
 | |
|       // If both are 'auto' (their initial values), the computed values are 0
 | |
|       mComputedOffsets.left = mComputedOffsets.right = 0;
 | |
|     } else {
 | |
|       // 'Right' isn't 'auto' so compute its value
 | |
|       if (eStyleUnit_Inherit == mStylePosition->mOffset.GetRightUnit()) {
 | |
|         mComputedOffsets.right = cbrs->mComputedOffsets.right;
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth, mStylePosition->mOffset.GetRightUnit(),
 | |
|                                mStylePosition->mOffset.GetRight(coord),
 | |
|                                mComputedOffsets.right);
 | |
|       }
 | |
|       
 | |
|       // Computed value for 'left' is minus the value of 'right'
 | |
|       mComputedOffsets.left = -mComputedOffsets.right;
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
 | |
|     
 | |
|     // 'Left' isn't 'auto' so compute its value
 | |
|     if (eStyleUnit_Inherit == mStylePosition->mOffset.GetLeftUnit()) {
 | |
|       mComputedOffsets.left = cbrs->mComputedOffsets.left;
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth, mStylePosition->mOffset.GetLeftUnit(),
 | |
|                              mStylePosition->mOffset.GetLeft(coord),
 | |
|                              mComputedOffsets.left);
 | |
|     }
 | |
| 
 | |
|     // Computed value for 'right' is minus the value of 'left'
 | |
|     mComputedOffsets.right = -mComputedOffsets.left;
 | |
|   }
 | |
| 
 | |
|   // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
 | |
|   // move relatively positioned elements up and down. They also must be each 
 | |
|   // other's negative
 | |
|   PRBool  topIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit();
 | |
|   PRBool  bottomIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit();
 | |
| 
 | |
|   // Check for percentage based values and a containing block height that
 | |
|   // depends on the content height. Treat them like 'auto'
 | |
|   if (NS_AUTOHEIGHT == aContainingBlockHeight) {
 | |
|     if (eStyleUnit_Percent == mStylePosition->mOffset.GetTopUnit()) {
 | |
|       topIsAuto = PR_TRUE;
 | |
|     }
 | |
|     if (eStyleUnit_Percent == mStylePosition->mOffset.GetBottomUnit()) {
 | |
|       bottomIsAuto = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If neither is 'auto', 'bottom' is ignored
 | |
|   if (!topIsAuto && !bottomIsAuto) {
 | |
|     bottomIsAuto = PR_TRUE;
 | |
|   }
 | |
| 
 | |
|   if (topIsAuto) {
 | |
|     if (bottomIsAuto) {
 | |
|       // If both are 'auto' (their initial values), the computed values are 0
 | |
|       mComputedOffsets.top = mComputedOffsets.bottom = 0;
 | |
|     } else {
 | |
|       // 'Bottom' isn't 'auto' so compute its value
 | |
|       if (eStyleUnit_Inherit == mStylePosition->mOffset.GetBottomUnit()) {
 | |
|         mComputedOffsets.bottom = cbrs->mComputedOffsets.bottom;
 | |
|       } else {
 | |
|         ComputeVerticalValue(aContainingBlockHeight, mStylePosition->mOffset.GetBottomUnit(),
 | |
|                                mStylePosition->mOffset.GetBottom(coord),
 | |
|                                mComputedOffsets.bottom);
 | |
|       }
 | |
|       
 | |
|       // Computed value for 'top' is minus the value of 'bottom'
 | |
|       mComputedOffsets.top = -mComputedOffsets.bottom;
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
 | |
|     
 | |
|     // 'Top' isn't 'auto' so compute its value
 | |
|     if (eStyleUnit_Inherit == mStylePosition->mOffset.GetTopUnit()) {
 | |
|       mComputedOffsets.top = cbrs->mComputedOffsets.top;
 | |
|     } else {
 | |
|       ComputeVerticalValue(aContainingBlockHeight, mStylePosition->mOffset.GetTopUnit(),
 | |
|                              mStylePosition->mOffset.GetTop(coord),
 | |
|                              mComputedOffsets.top);
 | |
|     }
 | |
| 
 | |
|     // Computed value for 'bottom' is minus the value of 'top'
 | |
|     mComputedOffsets.bottom = -mComputedOffsets.top;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Returns the nearest containing block frame for the specified frame.
 | |
| // Also returns the left, top, right, and bottom edges of the specified
 | |
| // frame's content area. These are in the coordinate space of the block
 | |
| // frame itself
 | |
| static nsIFrame*
 | |
| GetNearestContainingBlock(nsIFrame* aFrame, nsMargin& aContentArea)
 | |
| {
 | |
|   aFrame->GetParent(&aFrame);
 | |
|   while (aFrame) {
 | |
|     nsIAtom*  frameType;
 | |
|     PRBool    isBlock;
 | |
| 
 | |
|     aFrame->GetFrameType(&frameType);
 | |
|     isBlock = (frameType == nsLayoutAtoms::blockFrame) ||
 | |
|               (frameType == nsLayoutAtoms::areaFrame);
 | |
|     NS_IF_RELEASE(frameType);
 | |
| 
 | |
|     if (isBlock) {
 | |
|       break;
 | |
|     }
 | |
|     aFrame->GetParent(&aFrame);
 | |
|   }
 | |
| 
 | |
|   if (aFrame) {
 | |
|     nsSize  size;
 | |
|   
 | |
|     aFrame->GetSize(size);
 | |
|     aContentArea.left = 0;
 | |
|     aContentArea.top = 0;
 | |
|     aContentArea.right = size.width;
 | |
|     aContentArea.bottom = size.height;
 | |
|   
 | |
|     // Subtract off for border and padding. If it can't be computed because
 | |
|     // it's percentage based (for example) then just ignore it
 | |
|     nsStyleBorderPadding  bPad;
 | |
|     nsMargin              borderPadding;
 | |
|     nsCOMPtr<nsIStyleContext> styleContext;
 | |
|     aFrame->GetStyleContext(getter_AddRefs(styleContext));
 | |
|     styleContext->GetBorderPaddingFor(bPad);
 | |
|     if (bPad.GetBorderPadding(borderPadding)) {
 | |
|       aContentArea.left += borderPadding.left;
 | |
|       aContentArea.top += borderPadding.top;
 | |
|       aContentArea.right -= borderPadding.right;
 | |
|       aContentArea.bottom -= borderPadding.bottom;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return aFrame;
 | |
| }
 | |
| 
 | |
| // When determining the hypothetical box that would have been if the element
 | |
| // had been in the flow we may not be able to exactly determine both the left
 | |
| // and right edges. For example, if the element is a non-replaced inline-level
 | |
| // element we would have to reflow it in order to determine it desired width.
 | |
| // In that case depending on the progression direction either the left or
 | |
| // right edge would be marked as not being exact
 | |
| struct nsHypotheticalBox {
 | |
|   nscoord       mLeft, mRight;
 | |
|   nscoord       mTop;
 | |
|   PRPackedBool  mLeftIsExact, mRightIsExact;
 | |
| 
 | |
|   nsHypotheticalBox() {
 | |
|     mLeftIsExact = mRightIsExact = PR_FALSE;
 | |
|   }
 | |
| };
 | |
|       
 | |
| static PRBool
 | |
| GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize)
 | |
| {
 | |
|   // See if it is an image frame
 | |
|   nsIAtom*  frameType;
 | |
|   PRBool    result = PR_FALSE;
 | |
| 
 | |
|   // Currently the only type of replaced frame that we can get the intrinsic
 | |
|   // size for is an image frame
 | |
|   // XXX We should add back the GetReflowMetrics() function and one of the
 | |
|   // things should be the intrinsic size...
 | |
|   aFrame->GetFrameType(&frameType);
 | |
|   if (frameType == nsLayoutAtoms::imageFrame) {
 | |
|     nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
 | |
| 
 | |
|     imageFrame->GetIntrinsicImageSize(aIntrinsicSize);
 | |
|     result = (aIntrinsicSize != nsSize(0, 0));
 | |
|   }
 | |
|   
 | |
|   NS_IF_RELEASE(frameType);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsHTMLReflowState::CalculateHorizBorderPaddingMargin(nscoord aContainingBlockWidth)
 | |
| {
 | |
|   nsMargin  border, padding, margin;
 | |
| 
 | |
|   // Get the border
 | |
|   if (!mStyleBorder->GetBorder(border)) {
 | |
|     // CSS2 has no percentage borders
 | |
|     border.SizeTo(0, 0, 0, 0);
 | |
|   }
 | |
| 
 | |
|   // See if the style system can provide us the padding directly
 | |
|   if (!mStylePadding->GetPadding(padding)) {
 | |
|     nsStyleCoord left, right;
 | |
| 
 | |
|     // We have to compute the left and right values
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetLeftUnit()) {
 | |
|       padding.left = 0;  // just ignore
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetLeftUnit(),
 | |
|                              mStylePadding->mPadding.GetLeft(left),
 | |
|                              padding.left);
 | |
|     }
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetRightUnit()) {
 | |
|       padding.right = 0;  // just ignore
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetRightUnit(),
 | |
|                              mStylePadding->mPadding.GetRight(right),
 | |
|                              padding.right);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // See if the style system can provide us the margin directly
 | |
|   if (!mStyleMargin->GetMargin(margin)) {
 | |
|     nsStyleCoord left, right;
 | |
| 
 | |
|     // We have to compute the left and right values
 | |
|     if ((eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) ||
 | |
|         (eStyleUnit_Inherit == mStyleMargin->mMargin.GetLeftUnit())) {
 | |
|       margin.left = 0;  // just ignore
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStyleMargin->mMargin.GetLeftUnit(),
 | |
|                              mStyleMargin->mMargin.GetLeft(left),
 | |
|                              margin.left);
 | |
|     }
 | |
|     if ((eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) ||
 | |
|         (eStyleUnit_Inherit == mStyleMargin->mMargin.GetRightUnit())) {
 | |
|       margin.right = 0;  // just ignore
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStyleMargin->mMargin.GetRightUnit(),
 | |
|                              mStyleMargin->mMargin.GetRight(right),
 | |
|                              margin.right);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return padding.left + padding.right + border.left + border.right +
 | |
|          margin.left + margin.right;
 | |
| }
 | |
| 
 | |
| static void
 | |
| GetPlaceholderOffset(nsIFrame* aPlaceholderFrame,
 | |
|                      nsIFrame* aBlockFrame,
 | |
|                      nsPoint&  aOffset)
 | |
| {
 | |
|   aPlaceholderFrame->GetOrigin(aOffset);
 | |
| 
 | |
|   // Convert the placeholder position to the coordinate space of the block
 | |
|   // frame that contains it
 | |
|   nsIFrame* parent;
 | |
|   aPlaceholderFrame->GetParent(&parent);
 | |
|   while (parent && (parent != aBlockFrame)) {
 | |
|     nsPoint origin;
 | |
| 
 | |
|     parent->GetOrigin(origin);
 | |
|     aOffset += origin;
 | |
|     parent->GetParent(&parent);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static nsIFrame*
 | |
| FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame)
 | |
| {
 | |
|   nsIFrame* result = aDescendantFrame;
 | |
| 
 | |
|   while (result) {
 | |
|     nsIFrame* parent;
 | |
|     
 | |
|     result->GetParent(&parent);
 | |
|     if (parent == aParent) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // The frame is not an immediate child of aParent so walk up another level
 | |
|     result = parent;
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // Calculate the hypothetical box that the element would have if it were in
 | |
| // the flow. The values returned are relative to the padding edge of the
 | |
| // absolute containing block
 | |
| void
 | |
| nsHTMLReflowState::CalculateHypotheticalBox(nsIPresContext*    aPresContext,
 | |
|                                             nsIFrame*          aPlaceholderFrame,
 | |
|                                             nsIFrame*          aBlockFrame,
 | |
|                                             nsMargin&          aBlockContentArea,
 | |
|                                             nsIFrame*          aAbsoluteContainingBlockFrame,
 | |
|                                             nsHypotheticalBox& aHypotheticalBox)
 | |
| {
 | |
|   // If it's a replaced element and it has a 'auto' value for 'width', see if we
 | |
|   // can get the intrinsic size. This will allow us to exactly determine both the
 | |
|   // left and right edges
 | |
|   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
 | |
|   nsSize      intrinsicSize;
 | |
|   PRBool      knowIntrinsicSize = PR_FALSE;
 | |
|   if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
 | |
|     // See if we can get the intrinsic size of the element
 | |
|     knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize);
 | |
|   }
 | |
| 
 | |
|   // See if we can calculate what the box width would have been if the
 | |
|   // element had been in the flow
 | |
|   nscoord boxWidth;
 | |
|   PRBool  knowBoxWidth = PR_FALSE;
 | |
|   if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mDisplay) &&
 | |
|       !NS_FRAME_IS_REPLACED(mFrameType)) {
 | |
|     // For non-replaced inline-level elements the 'width' property doesn't apply,
 | |
|     // so we don't know what the width would have been without reflowing it
 | |
| 
 | |
|   } else {
 | |
|     // It's either a replaced inline-level element or a block-level element
 | |
|     nscoord horizBorderPaddingMargin;
 | |
| 
 | |
|     // Determine the total amount of horizontal border/padding/margin that
 | |
|     // the element would have had if it had been in the flow. Note that we
 | |
|     // ignore any 'auto' and 'inherit' values
 | |
|     horizBorderPaddingMargin = CalculateHorizBorderPaddingMargin(aBlockContentArea.right -
 | |
|                                                                  aBlockContentArea.left);
 | |
| 
 | |
|     if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
 | |
|       // It's a replaced element with an 'auto' width so the box width is
 | |
|       // its intrinsic size plus any border/padding/margin
 | |
|       if (knowIntrinsicSize) {
 | |
|         boxWidth = intrinsicSize.width + horizBorderPaddingMargin;
 | |
|         knowBoxWidth = PR_TRUE;
 | |
|       }
 | |
| 
 | |
|     } else if ((eStyleUnit_Inherit == widthUnit) || (eStyleUnit_Auto == widthUnit)) {
 | |
|       // The box width is the containing block width
 | |
|       boxWidth = aBlockContentArea.right - aBlockContentArea.left;
 | |
|       knowBoxWidth = PR_TRUE;
 | |
|     
 | |
|     } else {
 | |
|       // We need to compute it. It's important we do this, because if it's
 | |
|       // percentage based this computed value may be different from the comnputed
 | |
|       // value calculated using the absolute containing block width
 | |
|       ComputeHorizontalValue(aBlockContentArea.right - aBlockContentArea.left,
 | |
|                              widthUnit, mStylePosition->mWidth, boxWidth);
 | |
|       boxWidth += horizBorderPaddingMargin;
 | |
|       knowBoxWidth = PR_TRUE;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   // Get the 'direction' of the block
 | |
|   const nsStyleVisibility* blockVis;
 | |
|   aBlockFrame->GetStyleData(eStyleStruct_Visibility, (const nsStyleStruct*&)blockVis);
 | |
| 
 | |
|   // How we determine the hypothetical box depends on whether the element
 | |
|   // would have been inline-level or block-level
 | |
|   if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mDisplay) {
 | |
|     nsPoint placeholderOffset;
 | |
| 
 | |
|     // Get the placeholder x-offset and y-offset in the coordinate
 | |
|     // space of the block frame that contains it
 | |
|     GetPlaceholderOffset(aPlaceholderFrame, aBlockFrame, placeholderOffset);
 | |
| 
 | |
|     // The y-offset is the baseline of where the text would be if it were
 | |
|     // in the flow. We need the top position and not the baseline position
 | |
|     nsIFontMetrics*     metrics;
 | |
|     const nsStyleFont*  font;
 | |
| 
 | |
|     frame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&)font);
 | |
|     aPresContext->GetMetricsFor(font->mFont, &metrics);
 | |
|     if (metrics) {
 | |
|       nscoord   ascent;
 | |
| 
 | |
|       // Adjust the y-offset up by the font ascent. That will translate from
 | |
|       // the baseline to the top of where the text would be
 | |
|       metrics->GetMaxAscent(ascent);
 | |
|       placeholderOffset.y -= ascent;
 | |
|       NS_RELEASE(metrics);
 | |
|     }
 | |
|     aHypotheticalBox.mTop = placeholderOffset.y;
 | |
| 
 | |
|     // To determine the left and right offsets we need to look at the block's 'direction'
 | |
|     if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
 | |
|       // The placeholder represents the left edge of the hypothetical box
 | |
|       aHypotheticalBox.mLeft = placeholderOffset.x;
 | |
|       aHypotheticalBox.mLeftIsExact = PR_TRUE;
 | |
| 
 | |
|       if (knowBoxWidth) {
 | |
|         aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
 | |
|         aHypotheticalBox.mRightIsExact = PR_TRUE;
 | |
|       } else {
 | |
|         // We can't compute the right edge because we don't know the desired
 | |
|         // width. So instead use the right content edge of the block parent,
 | |
|         // but remember it's not exact
 | |
|         aHypotheticalBox.mRight = aBlockContentArea.right;
 | |
|         aHypotheticalBox.mRightIsExact = PR_FALSE;
 | |
|       }
 | |
|       
 | |
|     } else {
 | |
|       // The placeholder represents the right edge of the hypothetical box
 | |
|       aHypotheticalBox.mRight = placeholderOffset.x;
 | |
|       aHypotheticalBox.mRightIsExact = PR_TRUE;
 | |
|       
 | |
|       if (knowBoxWidth) {
 | |
|         aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
 | |
|         aHypotheticalBox.mLeftIsExact = PR_TRUE;
 | |
|       } else {
 | |
|         // We can't compute the left edge because we don't know the desired
 | |
|         // width. So instead use the left content edge of the block parent,
 | |
|         // but remember it's not exact
 | |
|         aHypotheticalBox.mLeft = aBlockContentArea.left;
 | |
|         aHypotheticalBox.mLeftIsExact = PR_FALSE;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     // The element would have been block-level which means it would be below
 | |
|     // the line containing the placeholder frame
 | |
|     if (aBlockFrame) {
 | |
|       nsIFrame*   blockChild;
 | |
|       nsBlockFrame::line_iterator lineBox;
 | |
|       PRBool      isFloater;
 | |
|       nsBlockFrame* blockFrame = NS_STATIC_CAST(nsBlockFrame*, aBlockFrame);
 | |
| 
 | |
|       // We need the immediate child of the block frame, and that may not be
 | |
|       // the placeholder frame
 | |
|       blockChild = FindImmediateChildOf(aBlockFrame, aPlaceholderFrame);
 | |
|       if (blockFrame->FindLineFor(blockChild, &isFloater, &lineBox)) {
 | |
|         // The top of the hypothetical box is just below the line containing
 | |
|         // the placeholder
 | |
|         aHypotheticalBox.mTop = lineBox->mBounds.YMost();
 | |
|       } else {
 | |
|         nsPoint placeholderOffset;
 | |
|         
 | |
|         // Just use the placeholder's y-offset
 | |
|         GetPlaceholderOffset(aPlaceholderFrame, aBlockFrame, placeholderOffset);
 | |
|         aHypotheticalBox.mTop = placeholderOffset.y;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // To determine the left and right offsets we need to look at the block's 'direction'
 | |
|     if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
 | |
|       aHypotheticalBox.mLeft = aBlockContentArea.left;
 | |
|       aHypotheticalBox.mLeftIsExact = PR_TRUE;
 | |
| 
 | |
|       // If we know the box width then we can determine the right edge
 | |
|       if (knowBoxWidth) {
 | |
|         aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
 | |
|         aHypotheticalBox.mRightIsExact = PR_TRUE;
 | |
|       } else {
 | |
|         // We can't compute the right edge because we don't know the intrinsic
 | |
|         // width yet. So instead use the right content edge of the block parent,
 | |
|         // but remember it's not exact
 | |
|         aHypotheticalBox.mRight = aBlockContentArea.right;
 | |
|         aHypotheticalBox.mRightIsExact = PR_FALSE;
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       aHypotheticalBox.mRight = aBlockContentArea.right;
 | |
|       aHypotheticalBox.mRightIsExact = PR_TRUE;
 | |
| 
 | |
|       // If we know the box width then we can determine the left edge
 | |
|       if (knowBoxWidth) {
 | |
|         aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
 | |
|         aHypotheticalBox.mLeftIsExact = PR_TRUE;
 | |
|       } else {
 | |
|         // We can't compute the left edge because we don't know the intrinsic
 | |
|         // width yet. So instead use the left content edge of the block parent,
 | |
|         // but remember it's not exact
 | |
|         aHypotheticalBox.mLeft = aBlockContentArea.left;
 | |
|         aHypotheticalBox.mLeftIsExact = PR_FALSE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // The current coordinate space is that of the nearest block to the placeholder.
 | |
|   // Convert to the coordinate space of the absolute containing block
 | |
|   if (aBlockFrame != aAbsoluteContainingBlockFrame) {
 | |
|     nsIFrame* parent = aBlockFrame;
 | |
|     do {
 | |
|       nsPoint origin;
 | |
| 
 | |
|       parent->GetOrigin(origin);
 | |
|       aHypotheticalBox.mLeft += origin.x;
 | |
|       aHypotheticalBox.mRight += origin.x;
 | |
|       aHypotheticalBox.mTop += origin.y;
 | |
| 
 | |
|       // Move up the tree one level
 | |
|       parent->GetParent(&parent);
 | |
|     } while (parent && (parent != aAbsoluteContainingBlockFrame));
 | |
|   }
 | |
| 
 | |
|   // The specified offsets are relative to the absolute containing block's padding
 | |
|   // edge, and our current values are relative to the border edge so translate
 | |
|   nsMargin              border;
 | |
|   const nsStyleBorder* borderStyle;
 | |
| 
 | |
|   aAbsoluteContainingBlockFrame->GetStyleData(eStyleStruct_Border, (const nsStyleStruct*&)borderStyle);
 | |
|   if (!borderStyle->GetBorder(border)) {
 | |
|     NS_NOTYETIMPLEMENTED("percentage border");
 | |
|   }
 | |
|   aHypotheticalBox.mLeft -= border.left;
 | |
|   aHypotheticalBox.mRight -= border.left;
 | |
|   aHypotheticalBox.mTop -= border.top;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::InitAbsoluteConstraints(nsIPresContext* aPresContext,
 | |
|                                            const nsHTMLReflowState* cbrs,
 | |
|                                            nscoord containingBlockWidth,
 | |
|                                            nscoord containingBlockHeight)
 | |
| {
 | |
|   NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
 | |
|                   "containing block height must be constrained");
 | |
| 
 | |
|   // Get the placeholder frame
 | |
|   nsIFrame*     placeholderFrame;
 | |
|   nsCOMPtr<nsIPresShell> presShell;
 | |
|   aPresContext->GetShell(getter_AddRefs(presShell));
 | |
| 
 | |
|   presShell->GetPlaceholderFrameFor(frame, &placeholderFrame);
 | |
|   NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");
 | |
| 
 | |
|   // Find the nearest containing block frame to the placeholder frame,
 | |
|   // and return its content area left, top, right, and bottom edges
 | |
|   nsMargin  blockContentArea;
 | |
|   nsIFrame* blockFrame = GetNearestContainingBlock(placeholderFrame,
 | |
|                                                    blockContentArea);
 | |
|   
 | |
|   // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
 | |
|   // 'auto', then compute the hypothetical box of where the element would
 | |
|   // have been if it had been in the flow
 | |
|   nsHypotheticalBox hypotheticalBox;
 | |
|   if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
 | |
|        (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
 | |
|       ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
 | |
|        (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {
 | |
| 
 | |
|     CalculateHypotheticalBox(aPresContext, placeholderFrame, blockFrame,
 | |
|                              blockContentArea, cbrs->frame, hypotheticalBox);
 | |
|   }
 | |
| 
 | |
|   // Initialize the 'left' and 'right' computed offsets
 | |
|   // XXX Handle new 'static-position' value...
 | |
|   PRBool        leftIsAuto = PR_FALSE, rightIsAuto = PR_FALSE;
 | |
|   nsStyleCoord  coord;
 | |
|   if (eStyleUnit_Inherit == mStylePosition->mOffset.GetLeftUnit()) {
 | |
|     mComputedOffsets.left = cbrs->mComputedOffsets.left;
 | |
|   } else if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
 | |
|     mComputedOffsets.left = 0;
 | |
|     leftIsAuto = PR_TRUE;
 | |
|   } else {
 | |
|     ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetLeftUnit(),
 | |
|                            mStylePosition->mOffset.GetLeft(coord),
 | |
|                            mComputedOffsets.left);
 | |
|   }
 | |
|   if (eStyleUnit_Inherit == mStylePosition->mOffset.GetRightUnit()) {
 | |
|     mComputedOffsets.right = cbrs->mComputedOffsets.right;
 | |
|   } else if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
 | |
|     mComputedOffsets.right = 0;
 | |
|     rightIsAuto = PR_TRUE;
 | |
|   } else {
 | |
|     ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetRightUnit(),
 | |
|                            mStylePosition->mOffset.GetRight(coord),
 | |
|                            mComputedOffsets.right);
 | |
|   }
 | |
| 
 | |
|   // When the CSS2 spec refers to direction it means the containing block's
 | |
|   // direction and not the direction of the absolutely positioned element itself
 | |
|   PRUint8 direction = cbrs->mStyleVisibility->mDirection;
 | |
| 
 | |
|   // Initialize the 'width' computed value
 | |
|   nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
 | |
|   PRBool      widthIsAuto = (eStyleUnit_Auto == widthUnit);
 | |
|   if (!widthIsAuto) {
 | |
|     if (eStyleUnit_Inherit == widthUnit) {
 | |
|       // The inherited value comes from the parent and not the absolute
 | |
|       // containing block
 | |
|       mComputedWidth = blockContentArea.right - blockContentArea.left;
 | |
| 
 | |
|     } else {
 | |
|       // Use the specified value for the computed width
 | |
|       ComputeHorizontalValue(containingBlockWidth, widthUnit,
 | |
|                              mStylePosition->mWidth, mComputedWidth);
 | |
|     }
 | |
| 
 | |
|     AdjustComputedWidth();
 | |
|   }
 | |
| 
 | |
|   // See if none of 'left', 'width', and 'right', is 'auto'
 | |
|   if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
 | |
|     // See whether we're over-constrained
 | |
|     PRInt32 availBoxSpace = containingBlockWidth - mComputedOffsets.left - mComputedOffsets.right;
 | |
|     PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.left -
 | |
|                                 mComputedBorderPadding.right;
 | |
| 
 | |
|     if (availContentSpace < mComputedWidth) {
 | |
|       // We're over-constrained so use 'direction' to dictate which value to
 | |
|       // ignore
 | |
|       if (NS_STYLE_DIRECTION_LTR == direction) {
 | |
|         // Ignore the specified value for 'right'
 | |
|         mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
 | |
|           mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right;
 | |
|       } else {
 | |
|         // Ignore the specified value for 'left'
 | |
|         mComputedOffsets.left = containingBlockWidth - mComputedBorderPadding.left -
 | |
|           mComputedWidth - mComputedBorderPadding.right - mComputedOffsets.right;
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       // Calculate any 'auto' margin values
 | |
|       PRBool  marginLeftIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit());
 | |
|       PRBool  marginRightIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit());
 | |
|       PRInt32 availMarginSpace = availContentSpace - mComputedWidth;
 | |
| 
 | |
|       if (marginLeftIsAuto) {
 | |
|         if (marginRightIsAuto) {
 | |
|           // Both 'margin-left' and 'margin-right' are 'auto', so they get
 | |
|           // equal values
 | |
|           mComputedMargin.left = availMarginSpace / 2;
 | |
|           mComputedMargin.right = availMarginSpace - mComputedMargin.left;
 | |
|         } else {
 | |
|           // Just 'margin-left' is 'auto'
 | |
|           mComputedMargin.left = availMarginSpace - mComputedMargin.right;
 | |
|         }
 | |
|       } else {
 | |
|         // Just 'margin-right' is 'auto'
 | |
|         mComputedMargin.right = availMarginSpace - mComputedMargin.left;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     // See if all three of 'left', 'width', and 'right', are 'auto'
 | |
|     if (leftIsAuto && widthIsAuto && rightIsAuto) {
 | |
|       // Use the 'direction' to dictate whether 'left' or 'right' is
 | |
|       // treated like 'static-position'
 | |
|       if (NS_STYLE_DIRECTION_LTR == direction) {
 | |
|         if (hypotheticalBox.mLeftIsExact) {
 | |
|           mComputedOffsets.left = hypotheticalBox.mLeft;
 | |
|           leftIsAuto = PR_FALSE;
 | |
|         } else {
 | |
|           // Well, we don't know 'left' so we have to use 'right' and
 | |
|           // then solve for 'left'
 | |
|           mComputedOffsets.right = hypotheticalBox.mRight;
 | |
|           rightIsAuto = PR_FALSE;
 | |
|         }
 | |
|       } else {
 | |
|         if (hypotheticalBox.mRightIsExact) {
 | |
|           mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
 | |
|           rightIsAuto = PR_FALSE;
 | |
|         } else {
 | |
|           // Well, we don't know 'right' so we have to use 'left' and
 | |
|           // then solve for 'right'
 | |
|           mComputedOffsets.left = hypotheticalBox.mLeft;
 | |
|           leftIsAuto = PR_FALSE;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // At this point we know that at least one of 'left', 'width', and 'right'
 | |
|     // is 'auto', but not all three. Examine the various combinations
 | |
|     if (widthIsAuto) {
 | |
|       if (leftIsAuto || rightIsAuto) {
 | |
|         if (NS_FRAME_IS_REPLACED(mFrameType)) {
 | |
|           // For a replaced element we use the intrinsic size
 | |
|           mComputedWidth = NS_INTRINSICSIZE;
 | |
|         } else {
 | |
|           // The width is shrink-to-fit but constrained by the containing block width
 | |
|           mComputedWidth = NS_SHRINKWRAPWIDTH;
 | |
|           
 | |
|           PRInt32 maxWidth = containingBlockWidth - mComputedOffsets.left -
 | |
|                              mComputedMargin.left - mComputedBorderPadding.left -
 | |
|                              mComputedBorderPadding.right - mComputedMargin.right -
 | |
|                              mComputedOffsets.right;
 | |
|           if (maxWidth <= 0) {
 | |
|             maxWidth = 1;
 | |
|           }
 | |
|           if (mComputedMaxWidth > maxWidth) {
 | |
|             mComputedMaxWidth = maxWidth;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (leftIsAuto) {
 | |
|           mComputedOffsets.left = NS_AUTOOFFSET;   // solve for 'left'
 | |
|         } else {
 | |
|           mComputedOffsets.right = NS_AUTOOFFSET;  // solve for 'right'
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         // Only 'width' is 'auto' so just solve for 'width'
 | |
|         mComputedWidth = containingBlockWidth - mComputedOffsets.left -
 | |
|           mComputedMargin.left - mComputedBorderPadding.left -
 | |
|           mComputedBorderPadding.right -
 | |
|           mComputedMargin.right - mComputedOffsets.right;
 | |
| 
 | |
|         AdjustComputedWidth();
 | |
| 
 | |
|         // XXX If the direction is rtl then we need to reevaluate left...
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       // Either 'left' or 'right' or both is 'auto'
 | |
|       if (leftIsAuto && rightIsAuto) {
 | |
|         // Use the 'direction' to dictate whether 'left' or 'right' is treated like
 | |
|         // 'static-position'
 | |
|         if (NS_STYLE_DIRECTION_LTR == direction) {
 | |
|           if (hypotheticalBox.mLeftIsExact) {
 | |
|             mComputedOffsets.left = hypotheticalBox.mLeft;
 | |
|             leftIsAuto = PR_FALSE;
 | |
|           } else {
 | |
|             // Well, we don't know 'left' so we have to use 'right' and
 | |
|             // then solve for 'left'
 | |
|             mComputedOffsets.right = hypotheticalBox.mRight;
 | |
|             rightIsAuto = PR_FALSE;
 | |
|           }
 | |
|         } else {
 | |
|           if (hypotheticalBox.mRightIsExact) {
 | |
|             mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
 | |
|             rightIsAuto = PR_FALSE;
 | |
|           } else {
 | |
|             // Well, we don't know 'right' so we have to use 'left' and
 | |
|             // then solve for 'right'
 | |
|             mComputedOffsets.left = hypotheticalBox.mLeft;
 | |
|             leftIsAuto = PR_FALSE;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (leftIsAuto) {
 | |
|         // Solve for 'left'
 | |
|         mComputedOffsets.left = containingBlockWidth - mComputedMargin.left -
 | |
|           mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right - 
 | |
|           mComputedMargin.right - mComputedOffsets.right;
 | |
| 
 | |
|       } else if (rightIsAuto) {
 | |
|         // Solve for 'right'
 | |
|         mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
 | |
|           mComputedMargin.left - mComputedBorderPadding.left - mComputedWidth -
 | |
|           mComputedBorderPadding.right - mComputedMargin.right;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Initialize the 'top' and 'bottom' computed offsets
 | |
|   nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
 | |
|   PRBool      topIsAuto = PR_FALSE, bottomIsAuto = PR_FALSE;
 | |
|   if (eStyleUnit_Inherit == mStylePosition->mOffset.GetTopUnit()) {
 | |
|     mComputedOffsets.top = cbrs->mComputedOffsets.top;
 | |
|   } else if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
 | |
|     mComputedOffsets.top = 0;
 | |
|     topIsAuto = PR_TRUE;
 | |
|   } else {
 | |
|     nsStyleCoord c;
 | |
|     ComputeVerticalValue(containingBlockHeight,
 | |
|                          mStylePosition->mOffset.GetTopUnit(),
 | |
|                          mStylePosition->mOffset.GetTop(c),
 | |
|                          mComputedOffsets.top);
 | |
|   }
 | |
|   if (eStyleUnit_Inherit == mStylePosition->mOffset.GetBottomUnit()) {
 | |
|     mComputedOffsets.bottom = cbrs->mComputedOffsets.bottom;
 | |
|   } else if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
 | |
|     mComputedOffsets.bottom = 0;        
 | |
|     bottomIsAuto = PR_TRUE;
 | |
|   } else {
 | |
|     nsStyleCoord c;
 | |
|     ComputeVerticalValue(containingBlockHeight,
 | |
|                          mStylePosition->mOffset.GetBottomUnit(),
 | |
|                          mStylePosition->mOffset.GetBottom(c),
 | |
|                          mComputedOffsets.bottom);
 | |
|   }
 | |
| 
 | |
|   // Initialize the 'height' computed value
 | |
|   PRBool  heightIsAuto = (eStyleUnit_Auto == heightUnit);
 | |
|   if (!heightIsAuto) {
 | |
|     if (eStyleUnit_Inherit == heightUnit) {
 | |
|       // The inherited value comes from the parent and not the absolute
 | |
|       // containing block
 | |
|       mComputedHeight = blockContentArea.bottom - blockContentArea.top;
 | |
| 
 | |
|     } else {
 | |
|       // Use the specified value for the computed height
 | |
|       // XXX Handle 'inherit'. The inherited value comes from the parent
 | |
|       // and not the containing block
 | |
|       ComputeVerticalValue(containingBlockHeight, heightUnit,
 | |
|                            mStylePosition->mHeight, mComputedHeight);
 | |
|     }
 | |
| 
 | |
|     AdjustComputedHeight();
 | |
|   }
 | |
| 
 | |
|   // See if none of 'top', 'height', and 'bottom', is 'auto'
 | |
|   if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
 | |
|     // See whether we're over-constrained
 | |
|     PRInt32 availBoxSpace = containingBlockHeight - mComputedOffsets.top - mComputedOffsets.bottom;
 | |
|     PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.top -
 | |
|                                 mComputedBorderPadding.bottom;
 | |
| 
 | |
|     if (availContentSpace < mComputedHeight) {
 | |
|       // We're over-constrained so ignore the specified value for 'bottom'
 | |
|       mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
 | |
|         mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom;
 | |
| 
 | |
|     } else {
 | |
|       // Calculate any 'auto' margin values
 | |
|       PRBool  marginTopIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit());
 | |
|       PRBool  marginBottomIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit());
 | |
|       PRInt32 availMarginSpace = availContentSpace - mComputedHeight;
 | |
| 
 | |
|       if (marginTopIsAuto) {
 | |
|         if (marginBottomIsAuto) {
 | |
|           // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
 | |
|           // equal values
 | |
|           mComputedMargin.top = availMarginSpace / 2;
 | |
|           mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
 | |
|         } else {
 | |
|           // Just 'margin-top' is 'auto'
 | |
|           mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
 | |
|         }
 | |
|       } else {
 | |
|         // Just 'margin-bottom' is 'auto'
 | |
|         mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     // See if all three of 'top', 'height', and 'bottom', are 'auto'
 | |
|     if (topIsAuto && heightIsAuto && bottomIsAuto) {
 | |
|       // Treat 'top' like 'static-position'
 | |
|       mComputedOffsets.top = hypotheticalBox.mTop;
 | |
|       topIsAuto = PR_FALSE;
 | |
|     }
 | |
| 
 | |
|     // At this point we know that at least one of 'top', 'height', and 'bottom'
 | |
|     // is 'auto', but not all three. Examine the various combinations
 | |
|     if (heightIsAuto) {
 | |
|       if (topIsAuto || bottomIsAuto) {
 | |
|         if (NS_FRAME_IS_REPLACED(mFrameType)) {
 | |
|           // For a replaced element we use the intrinsic size
 | |
|           mComputedHeight = NS_INTRINSICSIZE;
 | |
|         } else {
 | |
|           // The height is based on the content
 | |
|           mComputedHeight = NS_AUTOHEIGHT;
 | |
|         }
 | |
| 
 | |
|         if (topIsAuto) {
 | |
|           mComputedOffsets.top = NS_AUTOOFFSET;     // solve for 'top'
 | |
|         } else {
 | |
|           mComputedOffsets.bottom = NS_AUTOOFFSET;  // solve for 'bottom'
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         // Only 'height' is 'auto' so just solve for 'height'
 | |
|         mComputedHeight = containingBlockHeight - mComputedOffsets.top -
 | |
|           mComputedMargin.top - mComputedBorderPadding.top -
 | |
|           mComputedBorderPadding.bottom -
 | |
|           mComputedMargin.bottom - mComputedOffsets.bottom;
 | |
| 
 | |
|         AdjustComputedHeight();
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       // Either 'top' or 'bottom' or both is 'auto'
 | |
|       if (topIsAuto && bottomIsAuto) {
 | |
|         // Treat 'top' like 'static-position'
 | |
|         mComputedOffsets.top = hypotheticalBox.mTop;
 | |
|         topIsAuto = PR_FALSE;
 | |
|       }
 | |
| 
 | |
|       if (topIsAuto) {
 | |
|         // Solve for 'top'
 | |
|         mComputedOffsets.top = containingBlockHeight - mComputedMargin.top -
 | |
|           mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom - 
 | |
|           mComputedMargin.bottom - mComputedOffsets.bottom;
 | |
| 
 | |
|       } else if (bottomIsAuto) {
 | |
|         // Solve for 'bottom'
 | |
|         mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
 | |
|           mComputedMargin.top - mComputedBorderPadding.top - mComputedHeight -
 | |
|           mComputedBorderPadding.bottom - mComputedMargin.bottom;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static PRBool
 | |
| IsInitialContainingBlock(nsIFrame* aFrame)
 | |
| {
 | |
|   nsIContent* content;
 | |
|   PRBool      result = PR_FALSE;
 | |
| 
 | |
|   aFrame->GetContent(&content);
 | |
|   if (content) {
 | |
|     nsIContent* parentContent;
 | |
| 
 | |
|     content->GetParent(parentContent);
 | |
|     if (!parentContent) {
 | |
|       // The containing block corresponds to the document element so it's
 | |
|       // the initial containing block
 | |
|       result = PR_TRUE;
 | |
|     }
 | |
|     NS_IF_RELEASE(parentContent);
 | |
|   }
 | |
|   NS_IF_RELEASE(content);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nscoord 
 | |
| GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
 | |
| {
 | |
|   nscoord result = 0;
 | |
|   if (!aReflowState) return result;
 | |
| 
 | |
|   // zero auto margins
 | |
|   nsMargin margin = aReflowState->mComputedMargin;
 | |
|   if (NS_AUTOMARGIN == margin.top) 
 | |
|     margin.top = 0;
 | |
|   if (NS_AUTOMARGIN == margin.bottom) 
 | |
|     margin.bottom = 0;
 | |
| 
 | |
|   result += margin.top + margin.bottom;
 | |
|   result += aReflowState->mComputedBorderPadding.top + 
 | |
|             aReflowState->mComputedBorderPadding.bottom;
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /* Get the height based on the viewport of the containing block specified 
 | |
|  * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
 | |
|  * This will walk up the chain of containing blocks looking for a computed height
 | |
|  * until it finds the canvas frame, or it encounters a frame that is not a block
 | |
|  * or and area frame. This handles compatibility with IE (see bug 85016)
 | |
|  *
 | |
|  *  When the argument aRestrictToFirstLevel is TRUE, we stop looking after the first parent 
 | |
|  *  block or area frame. When FALSE, we look all the way up the frame tree, through nested 
 | |
|  *  blocks and area frames, and always find a real height. This is needed for percentage-height
 | |
|  *  images in unconstrained blocks, like DIVs (see bugzilla bug 85016)
 | |
|  */
 | |
| nscoord
 | |
| CalcQuirkContainingBlockHeight(const nsHTMLReflowState& aReflowState,
 | |
|                                PRBool aRestrictToFirstLevel)
 | |
| {
 | |
|   nsHTMLReflowState* firstBlockRS = nsnull; // a candidate for body frame
 | |
|   nsHTMLReflowState* firstAreaRS  = nsnull; // a candidate for html frame
 | |
|   nscoord result = 0;
 | |
| 
 | |
|   const nsHTMLReflowState* rs = &aReflowState;
 | |
|   for (; rs; rs = (nsHTMLReflowState *)(rs->parentReflowState)) { 
 | |
|     nsCOMPtr<nsIAtom> frameType;
 | |
|     rs->frame->GetFrameType(getter_AddRefs(frameType));
 | |
|     // if the ancestor is auto height then skip it and continue up if it 
 | |
|     // is the first block/area frame and possibly the body/html
 | |
|     if (nsLayoutAtoms::blockFrame == frameType.get()) {
 | |
|       // special hack for bug 50695, skip form frames
 | |
|       nsIFrame* formFrame;
 | |
|       if (NS_OK == rs->frame->QueryInterface(NS_GET_IID(nsIFormManager), (void **)&formFrame)) {
 | |
|         continue;
 | |
|       }
 | |
|       if (aRestrictToFirstLevel && firstBlockRS) {
 | |
|         break;
 | |
|       }
 | |
|       firstBlockRS = (nsHTMLReflowState*)rs;
 | |
|       if (NS_AUTOHEIGHT == rs->mComputedHeight) {
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|     else if (nsLayoutAtoms::areaFrame == frameType.get()) {
 | |
|       if (aRestrictToFirstLevel && firstAreaRS) {
 | |
|         break;
 | |
|       }
 | |
|       firstAreaRS = (nsHTMLReflowState*)rs;
 | |
|       if (NS_AUTOHEIGHT == rs->mComputedHeight) {
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
|     else if (nsLayoutAtoms::canvasFrame == frameType.get()) {
 | |
|       // Use scroll frames' computed height if we have one, this will
 | |
|       // allow us to get viewport height for native scrollbars.
 | |
|       nsHTMLReflowState* scrollState = (nsHTMLReflowState *)rs->parentReflowState;
 | |
|       nsCOMPtr<nsIAtom> scrollFrameType;
 | |
|       scrollState->frame->GetFrameType(getter_AddRefs(scrollFrameType));
 | |
|       if (nsLayoutAtoms::scrollFrame == scrollFrameType.get()) {
 | |
|         rs = scrollState;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // if the ancestor has a computed height, it is the percent base
 | |
|     result = rs->mComputedHeight;
 | |
|     // if unconstrained - don't sutract borders - would result in huge height
 | |
|     if (NS_AUTOHEIGHT == result) return result;
 | |
| 
 | |
|     // if we got to the canvas frame, then subtract out 
 | |
|     // margin/border/padding for the BODY and HTML elements
 | |
|     if (nsLayoutAtoms::canvasFrame == frameType.get()) {
 | |
| 
 | |
|       result -= GetVerticalMarginBorderPadding(firstBlockRS); 
 | |
|       result -= GetVerticalMarginBorderPadding(firstAreaRS); 
 | |
| 
 | |
| #ifdef DEBUG
 | |
|       // make sure the Area is the HTML and the Block is the BODY
 | |
|       nsCOMPtr<nsIContent> frameContent;
 | |
|       nsCOMPtr<nsIAtom> contentTag;
 | |
|       if(firstBlockRS) {
 | |
|         firstBlockRS->frame->GetContent(getter_AddRefs(frameContent));
 | |
|         if (frameContent) {
 | |
|           frameContent->GetTag(*getter_AddRefs(contentTag));
 | |
|           NS_ASSERTION(contentTag.get() == nsHTMLAtoms::body, "block is not BODY");
 | |
|         }
 | |
|       }
 | |
|       if(firstAreaRS) {
 | |
|         firstAreaRS->frame->GetContent(getter_AddRefs(frameContent));
 | |
|         if (frameContent) {
 | |
|           frameContent->GetTag(*getter_AddRefs(contentTag));
 | |
|           NS_ASSERTION(contentTag.get() == nsHTMLAtoms::html, "Area frame is not HTML element");
 | |
|         }
 | |
|       }
 | |
| #endif
 | |
|       
 | |
|     }
 | |
|     // if we got to the html frame, then subtract out 
 | |
|     // margin/border/padding for the BODY element
 | |
|     else if (nsLayoutAtoms::areaFrame == frameType.get()) {
 | |
|       // make sure it is the body
 | |
|       nsCOMPtr<nsIAtom> fType;
 | |
|       rs->parentReflowState->frame->GetFrameType(getter_AddRefs(fType));
 | |
|       if (nsLayoutAtoms::canvasFrame == fType.get()) {
 | |
|         result -= GetVerticalMarginBorderPadding(firstBlockRS);
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| // Called by InitConstraints() to compute the containing block rectangle for
 | |
| // the element. Handles the special logic for absolutely positioned elements
 | |
| void
 | |
| nsHTMLReflowState::ComputeContainingBlockRectangle(nsIPresContext*          aPresContext,
 | |
|                                                    const nsHTMLReflowState* aContainingBlockRS,
 | |
|                                                    nscoord&                 aContainingBlockWidth,
 | |
|                                                    nscoord&                 aContainingBlockHeight)
 | |
| {
 | |
|   // Unless the element is absolutely positioned, the containing block is
 | |
|   // formed by the content edge of the nearest block-level ancestor
 | |
|   aContainingBlockWidth = aContainingBlockRS->mComputedWidth;
 | |
|   aContainingBlockHeight = aContainingBlockRS->mComputedHeight;
 | |
|   
 | |
|   if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
 | |
|     // See if the ancestor is block-level or inline-level
 | |
|     if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
 | |
|       // The CSS2 spec says that if the ancestor is inline-level, the containing
 | |
|       // block depends on the 'direction' property of the ancestor. For direction
 | |
|       // 'ltr', it's the top and left of the content edges of the first box and
 | |
|       // the bottom and right content edges of the last box
 | |
|       //
 | |
|       // XXX This is a pain because it isn't top-down and it requires that we've
 | |
|       // completely reflowed the ancestor. It also isn't clear what happens when
 | |
|       // a relatively positioned ancestor is split across pages. So instead use
 | |
|       // the computed width and height of the nearest block-level ancestor
 | |
|       const nsHTMLReflowState*  cbrs = aContainingBlockRS;
 | |
|       while (cbrs) {
 | |
|         nsCSSFrameType  type = NS_FRAME_GET_TYPE(cbrs->mFrameType);
 | |
|         if ((NS_CSS_FRAME_TYPE_BLOCK == type) ||
 | |
|             (NS_CSS_FRAME_TYPE_FLOATING == type) ||
 | |
|             (NS_CSS_FRAME_TYPE_ABSOLUTE == type)) {
 | |
| 
 | |
|           aContainingBlockWidth = cbrs->mComputedWidth;
 | |
|           aContainingBlockHeight = cbrs->mComputedHeight;
 | |
| 
 | |
|           if (NS_CSS_FRAME_TYPE_ABSOLUTE == type) {
 | |
|             aContainingBlockWidth += cbrs->mComputedPadding.left +
 | |
|                                      cbrs->mComputedPadding.right;
 | |
|             aContainingBlockHeight += cbrs->mComputedPadding.top +
 | |
|                                       cbrs->mComputedPadding.bottom;
 | |
|           }
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         cbrs = (const nsHTMLReflowState*)cbrs->parentReflowState;   // XXX cast
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       // If the ancestor is block-level, the containing block is formed by the
 | |
|       // padding edge of the ancestor
 | |
|       aContainingBlockWidth += aContainingBlockRS->mComputedPadding.left +
 | |
|                                aContainingBlockRS->mComputedPadding.right;
 | |
| 
 | |
|       // If the containing block is the initial containing block and it has a
 | |
|       // height that depends on its content, then use the viewport height instead.
 | |
|       // This gives us a reasonable value against which to compute percentage
 | |
|       // based heights and to do bottom relative positioning
 | |
|       if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
 | |
|           IsInitialContainingBlock(aContainingBlockRS->frame)) {
 | |
| 
 | |
|         // Use the viewport height as the containing block height
 | |
|         const nsHTMLReflowState* rs = aContainingBlockRS->parentReflowState;
 | |
|         while (rs) {
 | |
|           aContainingBlockHeight = rs->mComputedHeight;
 | |
|           rs = rs->parentReflowState;
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         aContainingBlockHeight += aContainingBlockRS->mComputedPadding.top +
 | |
|                                   aContainingBlockRS->mComputedPadding.bottom;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     // If this is an unconstrained reflow, then reset the containing block
 | |
|     // width to NS_UNCONSTRAINEDSIZE. This way percentage based values have
 | |
|     // no effect
 | |
|     if (NS_UNCONSTRAINEDSIZE == availableWidth) {
 | |
|       aContainingBlockWidth = NS_UNCONSTRAINEDSIZE;
 | |
|     }
 | |
|     // an element in quirks mode gets a containing block based on the viewport (less  
 | |
|     // body margins, border, padding) if the element is a child of the body.
 | |
|     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
 | |
|       nsCompatibility mode;
 | |
|       aPresContext->GetCompatibilityMode(&mode);
 | |
|       if (eCompatibility_NavQuirks == mode) {
 | |
|         aContainingBlockHeight = CalcQuirkContainingBlockHeight(*aContainingBlockRS, PR_TRUE);
 | |
|         // NOTE: passing PR_TRUE for the aRestrictToFirstLevel argument, to restrict the search
 | |
|         //       for the containing block height to only the immediate parent block or area
 | |
|         //       frame. In the case that we need to go further, we would need to pass PR_TRUE
 | |
|         //       and take the performance hit. This is generally only needed if the frame being reflowed
 | |
|         //       has percentage height and is in a shrink-wrapping container 
 | |
|         //       (see the special-case call in InitConstraints)
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Prefs callback to pick up changes
 | |
| static int PR_CALLBACK PrefsChanged(const char *aPrefName, void *instance)
 | |
| {
 | |
|     PRBool boolPref;
 | |
|     nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
 | |
|     if (prefs && NS_SUCCEEDED(prefs->GetBoolPref("browser.blink_allowed", 
 | |
|                                                  &boolPref)))
 | |
|         sBlinkIsAllowed = boolPref;
 | |
|     return 0; /* PREF_OK */
 | |
| }
 | |
| 
 | |
| // Check to see if |text-decoration: blink| is allowed.  The first time
 | |
| // called, register the callback and then force-load the pref.  After that,
 | |
| // just use the cached value.
 | |
| static PRBool BlinkIsAllowed(void)
 | |
| {
 | |
|     if (!sBlinkPrefIsLoaded) {
 | |
|         // Set up a listener and check the initial value
 | |
|         nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
 | |
|         if (prefs) 
 | |
|             prefs->RegisterCallback("browser.blink_allowed", PrefsChanged, 
 | |
|                                     nsnull);
 | |
|         PrefsChanged(nsnull, nsnull);
 | |
|         sBlinkPrefIsLoaded = PR_TRUE;
 | |
|     }
 | |
|     return sBlinkIsAllowed;
 | |
| }
 | |
| 
 | |
| // XXX refactor this code to have methods for each set of properties
 | |
| // we are computing: width,height,line-height; margin; offsets
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext,
 | |
|                                    nscoord         aContainingBlockWidth,
 | |
|                                    nscoord         aContainingBlockHeight)
 | |
| {
 | |
|   // If this is the root frame, then set the computed width and
 | |
|   // height equal to the available space
 | |
|   if (nsnull == parentReflowState) {
 | |
|     mComputedWidth = availableWidth;
 | |
|     mComputedHeight = availableHeight;
 | |
|     mComputedMargin.SizeTo(0, 0, 0, 0);
 | |
|     mComputedPadding.SizeTo(0, 0, 0, 0);
 | |
|     mComputedBorderPadding.SizeTo(0, 0, 0, 0);
 | |
|     mComputedOffsets.SizeTo(0, 0, 0, 0);
 | |
|     mComputedMinWidth = mComputedMinHeight = 0;
 | |
|     mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
 | |
|   } else {
 | |
|     // Get the containing block reflow state
 | |
|     const nsHTMLReflowState* cbrs =
 | |
|       GetContainingBlockReflowState(parentReflowState);
 | |
|     NS_ASSERTION(nsnull != cbrs, "no containing block");
 | |
| 
 | |
|     // If we weren't given a containing block width and height, then
 | |
|     // compute one
 | |
|     if (aContainingBlockWidth == -1) {
 | |
|       ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, 
 | |
|                                       aContainingBlockHeight);
 | |
|     }
 | |
| 
 | |
|     // See if the element is relatively positioned
 | |
|     if (NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
 | |
|       ComputeRelativeOffsets(cbrs, aContainingBlockWidth, aContainingBlockHeight);
 | |
|     } else {
 | |
|       // Initialize offsets to 0
 | |
|       mComputedOffsets.SizeTo(0, 0, 0, 0);
 | |
|     }
 | |
| 
 | |
| #if 0
 | |
|     nsFrame::ListTag(stdout, frame); printf(": cb=");
 | |
|     nsFrame::ListTag(stdout, cbrs->frame); printf(" size=%d,%d\n", aContainingBlockWidth, aContainingBlockHeight);
 | |
| #endif
 | |
| 
 | |
|     // See if the containing block height is based on the size of its
 | |
|     // content
 | |
|     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
 | |
|       // See if the containing block is a scrolled frame, i.e. its
 | |
|       // parent is a scroll frame. The presence of the intervening
 | |
|       // frame (that the scroll frame scrolls) needs to be hidden from
 | |
|       // the containingBlockHeight calcuation.
 | |
|       if (cbrs->parentReflowState) {
 | |
|         nsIFrame* f = cbrs->parentReflowState->frame;
 | |
|         nsCOMPtr<nsIAtom>  fType;
 | |
|         f->GetFrameType(getter_AddRefs(fType));
 | |
|         if (nsLayoutAtoms::scrollFrame == fType.get()) {
 | |
|           // Use the scroll frame's computed height instead
 | |
|           aContainingBlockHeight =
 | |
|             ((nsHTMLReflowState*)cbrs->parentReflowState)->mComputedHeight;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Compute margins from the specified margin style information. These
 | |
|     // become the default computed values, and may be adjusted below
 | |
|     // XXX fix to provide 0,0 for the top&bottom margins for
 | |
|     // inline-non-replaced elements
 | |
|     ComputeMargin(aContainingBlockWidth, cbrs);
 | |
|     ComputePadding(aContainingBlockWidth, cbrs);
 | |
|     if (!mStyleBorder->GetBorder(mComputedBorderPadding)) {
 | |
|       // CSS2 has no percentage borders
 | |
|       mComputedBorderPadding.SizeTo(0, 0, 0, 0);
 | |
|     }
 | |
|     mComputedBorderPadding += mComputedPadding;
 | |
| 
 | |
|     nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
 | |
|     nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
 | |
| 
 | |
|     nsCOMPtr<nsIAtom>  frameType;
 | |
|     frame->GetFrameType(getter_AddRefs(frameType));
 | |
| 
 | |
|     // Check for a percentage based width and an unconstrained containing
 | |
|     // block width
 | |
|     if (eStyleUnit_Percent == widthUnit) {
 | |
|       if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
 | |
|         widthUnit = eStyleUnit_Auto;
 | |
|       }
 | |
|     }
 | |
|     // Check for a percentage based height and a containing block height
 | |
|     // that depends on the content height
 | |
|     if (eStyleUnit_Percent == heightUnit) {
 | |
|       if (NS_AUTOHEIGHT == aContainingBlockHeight) {
 | |
|         // this if clause enables %-height on replaced inline frames,
 | |
|         // such as images.  See bug 54119.  The else clause "heightUnit = eStyleUnit_Auto;"
 | |
|         // used to be called exclusively.
 | |
|         if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
 | |
|           // Get the containing block reflow state
 | |
|           const nsHTMLReflowState* cbrs =
 | |
|             GetContainingBlockReflowState(parentReflowState);
 | |
|           NS_ASSERTION(nsnull != cbrs, "no containing block");
 | |
|           nsCompatibility mode;
 | |
|           aPresContext->GetCompatibilityMode(&mode);
 | |
|           // in quirks mode, get the cb height using the special quirk method
 | |
|           if (eCompatibility_NavQuirks == mode) {
 | |
|             aContainingBlockHeight = CalcQuirkContainingBlockHeight(*cbrs, PR_FALSE);
 | |
|             // NOTE: since here we really do NEED the computed height of the containing block,
 | |
|             //       we pass PR_FALSE for the aRestrictToFirstLevel argument, allowing the method
 | |
|             //       to walk up the frame tree arbitrarily far to find a real height. This is NOT
 | |
|             //       default behavior, since it is an additional performance hit and is not usually 
 | |
|             //       necessary (see other call in ComputeContainingBlockRectangle). 
 | |
|             //       This is an IE emulation for %-height images - see bug 85016
 | |
|           }
 | |
|           // in standard mode, use the cb height.  if it's "auto", as will be the case
 | |
|           // by default in BODY, use auto height as per CSS2 spec.
 | |
|           else 
 | |
|           {
 | |
|             if (NS_AUTOHEIGHT != cbrs->mComputedHeight)
 | |
|               aContainingBlockHeight = cbrs->mComputedHeight;
 | |
|             else
 | |
|               heightUnit = eStyleUnit_Auto;
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           // default to interpreting the height like 'auto'
 | |
|           heightUnit = eStyleUnit_Auto;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Calculate the computed values for min and max properties
 | |
|     ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
 | |
| 
 | |
|     // Calculate the computed width and height. This varies by frame type
 | |
|     if ((NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) ||
 | |
|         (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_FLOATING) == mFrameType)) {
 | |
|       // Inline replaced element and floating replaced element are basically
 | |
|       // treated the same. First calculate the computed width
 | |
|       if (eStyleUnit_Inherit == widthUnit) {
 | |
|         mComputedWidth = aContainingBlockWidth;
 | |
|       } else if (eStyleUnit_Auto == widthUnit) {
 | |
|         // A specified value of 'auto' uses the element's intrinsic width
 | |
|         mComputedWidth = NS_INTRINSICSIZE;
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
 | |
|                                mStylePosition->mWidth,
 | |
|                                mComputedWidth);
 | |
|       }
 | |
| 
 | |
|       AdjustComputedWidth();
 | |
| 
 | |
|       // Now calculate the computed height
 | |
|       if (eStyleUnit_Inherit == heightUnit) {
 | |
|         mComputedHeight = aContainingBlockHeight;
 | |
|       } else if (eStyleUnit_Auto == heightUnit) {
 | |
|         // A specified value of 'auto' uses the element's intrinsic height
 | |
|         mComputedHeight = NS_INTRINSICSIZE;
 | |
|       } else {
 | |
|         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
 | |
|                              mStylePosition->mHeight,
 | |
|                              mComputedHeight);
 | |
|       }
 | |
| 
 | |
|       AdjustComputedHeight();
 | |
| 
 | |
|     } else if (NS_CSS_FRAME_TYPE_FLOATING == mFrameType) {
 | |
|       // Floating non-replaced element. First calculate the computed width
 | |
|       if (eStyleUnit_Inherit == widthUnit) {
 | |
|         mComputedWidth = aContainingBlockWidth;
 | |
|       } else if (eStyleUnit_Auto == widthUnit) {
 | |
|         if ((NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) &&
 | |
|             (eStyleUnit_Percent == mStylePosition->mWidth.GetUnit())) {
 | |
|           // The element has a percentage width, but since the containing
 | |
|           // block width is unconstrained we set 'widthUnit' to 'auto'
 | |
|           // above. However, we want the element to be unconstrained, too
 | |
|           mComputedWidth = NS_UNCONSTRAINEDSIZE;
 | |
| 
 | |
|         } else if (NS_STYLE_DISPLAY_TABLE == mStyleDisplay->mDisplay) {
 | |
|           // It's an outer table because an inner table is not positioned
 | |
|           // shrink wrap its width since the outer table is anonymous
 | |
|           mComputedWidth = NS_SHRINKWRAPWIDTH;
 | |
| 
 | |
|         } else {
 | |
|           // The CSS2 spec says the computed width should be 0; however, that's
 | |
|           // not what Nav and IE do and even the spec doesn't really want that
 | |
|           // to happen.
 | |
|           //
 | |
|           // Instead, have the element shrink wrap its width
 | |
|           mComputedWidth = NS_SHRINKWRAPWIDTH;
 | |
| 
 | |
|           // Limnit the width to the containing block width
 | |
|           nscoord widthFromCB = aContainingBlockWidth - 
 | |
|                   mComputedBorderPadding.left - mComputedBorderPadding.right -
 | |
|                   mComputedMargin.left - mComputedMargin.right;
 | |
|           if (mComputedMaxWidth > widthFromCB) {
 | |
|             mComputedMaxWidth = widthFromCB;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
 | |
|                                mStylePosition->mWidth,
 | |
|                                mComputedWidth);
 | |
|       }
 | |
| 
 | |
|       // Take into account minimum and maximum sizes
 | |
|       AdjustComputedWidth();
 | |
| 
 | |
|       // Now calculate the computed height
 | |
|       if (eStyleUnit_Inherit == heightUnit) {
 | |
|         mComputedHeight = aContainingBlockHeight;
 | |
|       } else if (eStyleUnit_Auto == heightUnit) {
 | |
|         mComputedHeight = NS_AUTOHEIGHT;  // let it choose its height
 | |
|       } else {
 | |
|         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
 | |
|                              mStylePosition->mHeight,
 | |
|                              mComputedHeight);
 | |
|       }
 | |
| 
 | |
|       AdjustComputedHeight();
 | |
|     
 | |
|     } else if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
 | |
|       // Internal table elements. The rules vary depending on the type.
 | |
|       // Calculate the computed width
 | |
|       PRBool rowOrRowGroup = PR_FALSE;
 | |
|       if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
 | |
|           (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
 | |
|         // 'width' property doesn't apply to table rows and row groups
 | |
|         widthUnit = eStyleUnit_Auto;
 | |
|         rowOrRowGroup = PR_TRUE;
 | |
|       }
 | |
| 
 | |
|       if (eStyleUnit_Inherit == widthUnit) {
 | |
|         mComputedWidth = aContainingBlockWidth;
 | |
|       } else if (eStyleUnit_Auto == widthUnit) {
 | |
|         mComputedWidth = availableWidth;
 | |
| 
 | |
|         if ((mComputedWidth != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
 | |
|           // Internal table elements don't have margins. Only tables and
 | |
|           // cells have border and padding
 | |
|           mComputedWidth -= mComputedBorderPadding.left +
 | |
|             mComputedBorderPadding.right;
 | |
|         }
 | |
|       
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
 | |
|                                mStylePosition->mWidth,
 | |
|                                mComputedWidth);
 | |
|       }
 | |
| 
 | |
|       // Calculate the computed height
 | |
|       if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
 | |
|           (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
 | |
|         // 'height' property doesn't apply to table columns and column groups
 | |
|         heightUnit = eStyleUnit_Auto;
 | |
|       }
 | |
|       if (eStyleUnit_Inherit == heightUnit) {
 | |
|         mComputedHeight = aContainingBlockHeight;
 | |
|       } else if (eStyleUnit_Auto == heightUnit) {
 | |
|         mComputedHeight = NS_AUTOHEIGHT;
 | |
|       } else {
 | |
|         ComputeVerticalValue(aContainingBlockHeight, heightUnit,
 | |
|                              mStylePosition->mHeight,
 | |
|                              mComputedHeight);
 | |
|       }
 | |
| 
 | |
|       // Doesn't apply to table elements
 | |
|       mComputedMinWidth = mComputedMinHeight = 0;
 | |
|       mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
 | |
| 
 | |
|     } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
 | |
|       // XXX not sure if this belongs here or somewhere else - cwk
 | |
|       // an nsHTMLFrameInnerFrame doesn't get a placeholder frame, the nsHTMLFrameOuterFrame does
 | |
|       nsIAtom* targetFrameType;
 | |
|       frame->GetFrameType(&targetFrameType);
 | |
|       if (nsLayoutAtoms::htmlFrameInnerFrame != targetFrameType) {
 | |
|         InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
 | |
|                                 aContainingBlockHeight);
 | |
|       }
 | |
|       NS_IF_RELEASE(targetFrameType);
 | |
|     } else if (NS_CSS_FRAME_TYPE_INLINE == mFrameType) {
 | |
|       // Inline non-replaced elements do not have computed widths or heights
 | |
|       // XXX add this check to HaveFixedContentHeight/Width too
 | |
|       mComputedWidth = NS_UNCONSTRAINEDSIZE;
 | |
|       mComputedHeight = NS_UNCONSTRAINEDSIZE;
 | |
|       mComputedMargin.top = 0;
 | |
|       mComputedMargin.bottom = 0;
 | |
|       mComputedMinWidth = mComputedMinHeight = 0;
 | |
|       mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
 | |
|     } else {
 | |
|       ComputeBlockBoxData(aPresContext, cbrs, widthUnit, heightUnit,
 | |
|                           aContainingBlockWidth,
 | |
|                           aContainingBlockHeight);
 | |
|     }
 | |
|   }
 | |
|   // Check for blinking text and permission to display it
 | |
|   mBlinks = (parentReflowState && parentReflowState->mBlinks);
 | |
|   if (!mBlinks && BlinkIsAllowed()) {
 | |
|     const nsStyleTextReset* st;
 | |
|     frame->GetStyleData(eStyleStruct_TextReset,
 | |
|                         (const nsStyleStruct*&)st);
 | |
|     mBlinks = (st->mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Compute the box data for block and block-replaced elements in the
 | |
| // normal flow.
 | |
| void
 | |
| nsHTMLReflowState::ComputeBlockBoxData(nsIPresContext* aPresContext,
 | |
|                                        const nsHTMLReflowState* cbrs,
 | |
|                                        nsStyleUnit aWidthUnit,
 | |
|                                        nsStyleUnit aHeightUnit,
 | |
|                                        nscoord aContainingBlockWidth,
 | |
|                                        nscoord aContainingBlockHeight)
 | |
| {
 | |
|   // Compute the content width
 | |
|   if (eStyleUnit_Auto == aWidthUnit) {
 | |
|     if (NS_FRAME_IS_REPLACED(mFrameType)) {
 | |
|       // Block-level replaced element in the flow. A specified value of 
 | |
|       // 'auto' uses the element's intrinsic width (CSS2 10.3.4)
 | |
|       mComputedWidth = NS_INTRINSICSIZE;
 | |
|     } else {
 | |
|       // Block-level non-replaced element in the flow. 'auto' values
 | |
|       // for margin-left and margin-right become 0, and the sum of the
 | |
|       // areas must equal the width of the content-area of the parent
 | |
|       // element.
 | |
|       if (NS_UNCONSTRAINEDSIZE == availableWidth) {
 | |
|         // During pass1 table reflow, auto side margin values are
 | |
|         // uncomputable (== 0).
 | |
|         mComputedWidth = NS_UNCONSTRAINEDSIZE;
 | |
|       } else if (NS_SHRINKWRAPWIDTH == aContainingBlockWidth) {
 | |
|         // The containing block should shrink wrap its width, so have
 | |
|         // the child block do the same
 | |
|         mComputedWidth = NS_UNCONSTRAINEDSIZE;
 | |
| 
 | |
|         // Let its content area be as wide as the containing block's max width
 | |
|         // minus any margin and border/padding
 | |
|         nscoord maxWidth = cbrs->mComputedMaxWidth - mComputedMargin.left -
 | |
|                            mComputedBorderPadding.left - mComputedMargin.right -
 | |
|                            mComputedBorderPadding.right;
 | |
| 
 | |
|         if (maxWidth < mComputedMaxWidth) {
 | |
|           mComputedMaxWidth = maxWidth;
 | |
|         }
 | |
| 
 | |
|       } else {
 | |
|         // tables act like replaced elements regarding mComputedWidth 
 | |
|         nsCOMPtr<nsIAtom> fType;
 | |
|         frame->GetFrameType(getter_AddRefs(fType));
 | |
|         if (nsLayoutAtoms::tableOuterFrame == fType.get()) {
 | |
|           mComputedWidth = 0; // XXX temp fix for trees
 | |
|         } else if ((nsLayoutAtoms::tableFrame == fType.get()) ||
 | |
|                    (nsLayoutAtoms::tableCaptionFrame == fType.get())) {
 | |
|           mComputedWidth = NS_SHRINKWRAPWIDTH;
 | |
|           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
 | |
|             mComputedMargin.left = NS_AUTOMARGIN;
 | |
|           }
 | |
|           if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
 | |
|             mComputedMargin.right = NS_AUTOMARGIN;
 | |
|           }
 | |
|         } else {
 | |
|           mComputedWidth = availableWidth - mComputedMargin.left -
 | |
|             mComputedMargin.right - mComputedBorderPadding.left -
 | |
|             mComputedBorderPadding.right;
 | |
|         }
 | |
| 
 | |
|         // Take into account any min and max values
 | |
|         if (mComputedWidth > mComputedMaxWidth) {
 | |
|           // Use 'max-width' as the value for 'width'
 | |
|           mComputedWidth = mComputedMaxWidth;
 | |
|         } else if (mComputedWidth < mComputedMinWidth) {
 | |
|           // Use 'min-width' as the value for 'width'
 | |
|           mComputedWidth = mComputedMinWidth;
 | |
|         }
 | |
| 
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (eStyleUnit_Inherit == aWidthUnit) {
 | |
|       // Use parent element's width. Note that if its width was
 | |
|       // 'inherit', then it already did this so we don't need to
 | |
|       // recurse upwards.
 | |
|       //
 | |
|       // We use the containing block's width here for the "parent"
 | |
|       // elements width, because we want to skip over any intervening
 | |
|       // inline elements (since width doesn't apply to them).
 | |
|       if (NS_UNCONSTRAINEDSIZE != aContainingBlockWidth) {
 | |
|         mComputedWidth = aContainingBlockWidth;
 | |
|       }
 | |
|       else {
 | |
|         mComputedWidth = NS_UNCONSTRAINEDSIZE;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth, aWidthUnit,
 | |
|                              mStylePosition->mWidth, mComputedWidth);
 | |
|     }
 | |
| 
 | |
|     AdjustComputedWidth(); 
 | |
| 
 | |
|     // Now that we have the computed-width, compute the side margins
 | |
|     CalculateBlockSideMargins(cbrs->mComputedWidth, mComputedWidth);
 | |
|   }
 | |
| 
 | |
|   // Compute the content height
 | |
|   if (eStyleUnit_Inherit == aHeightUnit) {
 | |
|     // Use parent elements height (note that if its height was inherit
 | |
|     // then it already did this so we don't need to recurse upwards).
 | |
|     //
 | |
|     // We use the containing blocks height here for the "parent"
 | |
|     // elements height because we want to skip over any interveening
 | |
|     // inline elements (since height doesn't apply to them).
 | |
|     if (NS_UNCONSTRAINEDSIZE != aContainingBlockHeight) {
 | |
|       mComputedHeight = aContainingBlockHeight;
 | |
|     }
 | |
|     else {
 | |
|       mComputedHeight = NS_UNCONSTRAINEDSIZE;
 | |
|     }
 | |
|   } else if (eStyleUnit_Auto == aHeightUnit) {
 | |
|     if (NS_FRAME_IS_REPLACED(mFrameType)) {
 | |
|       // For replaced elements use the intrinsic size for "auto"
 | |
|       mComputedHeight = NS_INTRINSICSIZE;
 | |
|     } else {
 | |
|       // For non-replaced elements auto means unconstrained
 | |
|       mComputedHeight = NS_UNCONSTRAINEDSIZE;
 | |
|     }
 | |
|   } else {
 | |
|     ComputeVerticalValue(aContainingBlockHeight, aHeightUnit,
 | |
|                          mStylePosition->mHeight, mComputedHeight);
 | |
|   }
 | |
|   AdjustComputedHeight();
 | |
| }
 | |
| 
 | |
| // This code enforces section 10.3.3 of the CSS2 spec for this formula:
 | |
| //
 | |
| // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
 | |
| //   'padding-right' + 'border-right-width' + 'margin-right'
 | |
| //   = width of containing block 
 | |
| //
 | |
| // Note: the width unit is not auto when this is called
 | |
| void
 | |
| nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
 | |
|                                              nscoord aComputedWidth)
 | |
| {
 | |
|   // We can only provide values for auto side margins in a constrained
 | |
|   // reflow. For unconstrained reflow there is no effective width to
 | |
|   // compute against...
 | |
|   if ((NS_UNCONSTRAINEDSIZE == aComputedWidth) ||
 | |
|       (NS_UNCONSTRAINEDSIZE == aAvailWidth)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nscoord sum = mComputedMargin.left + mComputedBorderPadding.left +
 | |
|     aComputedWidth + mComputedBorderPadding.right + mComputedMargin.right;
 | |
|   if (sum == aAvailWidth) {
 | |
|     // The sum is already correct
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Determine the left and right margin values. The width value
 | |
|   // remains constant while we do this.
 | |
|   PRBool isAutoLeftMargin =
 | |
|     eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
 | |
|   PRBool isAutoRightMargin =
 | |
|     eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
 | |
| 
 | |
|   // Calculate how much space is available for margins
 | |
|   nscoord availMarginSpace = aAvailWidth - aComputedWidth -
 | |
|     mComputedBorderPadding.left - mComputedBorderPadding.right;
 | |
| 
 | |
|   if ((mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE) ||
 | |
|       (mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION))  {
 | |
|     // Special rules for tables. In general, tables will stick to the
 | |
|     // left edge when they are too large otherwise they behave like
 | |
|     // blocks.
 | |
| 
 | |
|     // the borderpadding should not influence the margin relative to the 
 | |
|     // outertable frame  
 | |
|     availMarginSpace = aAvailWidth - aComputedWidth;
 | |
| 
 | |
|     if (availMarginSpace < 0) {
 | |
|       // Whoops - the TABLE element is too large for the available
 | |
|       // space. In this case use the "direction" property to pin the
 | |
|       // element to the left or right side. Note that we look at the
 | |
|       // parent's direction since the parent will be placing this
 | |
|       // element.
 | |
|       mComputedMargin.left = 0;
 | |
|       mComputedMargin.right = 0;
 | |
|       const nsHTMLReflowState* prs = (const nsHTMLReflowState*)
 | |
|         parentReflowState;
 | |
|       if (prs && (NS_STYLE_DIRECTION_RTL == prs->mStyleVisibility->mDirection)) {
 | |
|         mComputedMargin.left = availMarginSpace;
 | |
|       }
 | |
|       isAutoLeftMargin = isAutoRightMargin = PR_FALSE;
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     // The css2 spec clearly defines how block elements should be have
 | |
|     // in section 10.3.3.
 | |
|     if (!isAutoLeftMargin && !isAutoRightMargin) {
 | |
|       // Neither margin is 'auto' so we're over constrained. Use the
 | |
|       // 'direction' property of the parent to tell which margin to
 | |
|       // ignore
 | |
|       const nsHTMLReflowState* prs = (const nsHTMLReflowState*)
 | |
|         parentReflowState;
 | |
|       if (prs) {
 | |
| 
 | |
|         // First check if there is an HTML alignment that we should honor
 | |
|         if ((prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) ||
 | |
|             (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT))
 | |
|         {
 | |
|           isAutoLeftMargin = PR_TRUE;
 | |
|           isAutoRightMargin =
 | |
|             (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER);
 | |
|         } else
 | |
|         // Otherwise apply the CSS rules
 | |
|         if (NS_STYLE_DIRECTION_LTR == prs->mStyleVisibility->mDirection) {
 | |
|           // The specified value of margin-right is ignored (== forced
 | |
|           // to auto)
 | |
|           isAutoRightMargin = PR_TRUE;
 | |
|         }
 | |
|         else {
 | |
|           isAutoLeftMargin = PR_TRUE;
 | |
|         }
 | |
|       }
 | |
|       else {
 | |
|         // No parent reflow state -- assume direction is ltr
 | |
|         isAutoRightMargin = PR_TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Logic which is common to blocks and tables
 | |
|   if (isAutoLeftMargin) {
 | |
|     if (isAutoRightMargin) {
 | |
|       // Both margins are 'auto' so their computed values are equal
 | |
|       mComputedMargin.left = availMarginSpace / 2;
 | |
|       mComputedMargin.right = availMarginSpace - mComputedMargin.left;
 | |
|     } else {
 | |
|       mComputedMargin.left = availMarginSpace - mComputedMargin.right;
 | |
|     }
 | |
|   } else if (isAutoRightMargin) {
 | |
|     mComputedMargin.right = availMarginSpace - mComputedMargin.left;
 | |
|   }
 | |
| }
 | |
| 
 | |
| PRBool
 | |
| nsHTMLReflowState::UseComputedHeight()
 | |
| {
 | |
|   static PRBool useComputedHeight = PR_FALSE;
 | |
| 
 | |
| #if defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS)
 | |
|   static PRBool firstTime = 1;
 | |
|   if (firstTime) {
 | |
|     if (getenv("GECKO_USE_COMPUTED_HEIGHT")) {
 | |
|       useComputedHeight = PR_TRUE;
 | |
|     }
 | |
|     firstTime = 0;
 | |
|   }
 | |
| #endif
 | |
|   return useComputedHeight;
 | |
| }
 | |
| 
 | |
| static nsIStyleContext*
 | |
| GetNonInheritedLineHeightStyleContext(nsIStyleContext* aStyleContext)
 | |
| {
 | |
|   nsIStyleContext* parentSC;
 | |
|   parentSC = aStyleContext->GetParent();
 | |
|   if (parentSC) {
 | |
|     const nsStyleText* text = (const nsStyleText*)
 | |
|       parentSC->GetStyleData(eStyleStruct_Text);
 | |
|     if (eStyleUnit_Inherit == text->mLineHeight.GetUnit()) {
 | |
|       nsIStyleContext* sc = GetNonInheritedLineHeightStyleContext(parentSC);
 | |
|       NS_RELEASE(parentSC);
 | |
|       return sc;
 | |
|     }
 | |
|   }
 | |
|   return parentSC;
 | |
| }
 | |
| 
 | |
| static nscoord
 | |
| ComputeLineHeight(nsIRenderingContext* aRenderingContext,
 | |
|                   nsIStyleContext* aStyleContext)
 | |
| {
 | |
|   NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");
 | |
| 
 | |
|   nscoord lineHeight = -1;
 | |
| 
 | |
|   const nsStyleText* text = (const nsStyleText*)
 | |
|     aStyleContext->GetStyleData(eStyleStruct_Text);
 | |
|   const nsStyleFont* font = (const nsStyleFont*)
 | |
|     aStyleContext->GetStyleData(eStyleStruct_Font);
 | |
|   const nsStyleVisibility* vis = 
 | |
|       (const nsStyleVisibility*)aStyleContext->GetStyleData(eStyleStruct_Visibility);
 | |
|   
 | |
|   nsStyleUnit unit = text->mLineHeight.GetUnit();
 | |
|   if (eStyleUnit_Inherit == unit) {
 | |
|     // Inherit parents line-height value
 | |
|     nsCOMPtr<nsIStyleContext> parentSC =
 | |
|       getter_AddRefs(GetNonInheritedLineHeightStyleContext(aStyleContext));
 | |
|     if (parentSC) {
 | |
|       text = (const nsStyleText*) parentSC->GetStyleData(eStyleStruct_Text);
 | |
|       unit = text->mLineHeight.GetUnit();
 | |
|       if (eStyleUnit_Percent == unit) {
 | |
|         // For percent, we inherit the computed value so update the
 | |
|         // font to use the parent's font not our font.
 | |
|         font = (const nsStyleFont*) parentSC->GetStyleData(eStyleStruct_Font);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (eStyleUnit_Coord == unit) {
 | |
|     // For length values just use the pre-computed value
 | |
|     lineHeight = text->mLineHeight.GetCoordValue();
 | |
|   }
 | |
|   else {
 | |
|     // For "normal", factor or percentage units the computed value of
 | |
|     // the line-height property is found by multiplying the factor by
 | |
|     // the font's <b>actual</b> em height. For "normal" we use the
 | |
|     // font's normal line height (em height + leading).
 | |
|     nscoord emHeight = -1;
 | |
|     nscoord normalLineHeight = -1;
 | |
|     nsCOMPtr<nsIDeviceContext> deviceContext;
 | |
|     aRenderingContext->GetDeviceContext(*getter_AddRefs(deviceContext));
 | |
|     nsCOMPtr<nsIAtom> langGroup;
 | |
|     if (vis->mLanguage) {
 | |
|       vis->mLanguage->GetLanguageGroup(getter_AddRefs(langGroup));
 | |
|     }
 | |
|     nsCOMPtr<nsIFontMetrics> fm;
 | |
|     deviceContext->GetMetricsFor(font->mFont, langGroup, *getter_AddRefs(fm));
 | |
| #ifdef NEW_FONT_HEIGHT_APIS
 | |
|     if (fm) {
 | |
|       fm->GetEmHeight(emHeight);
 | |
|       fm->GetNormalLineHeight(normalLineHeight);
 | |
|     }
 | |
| #endif
 | |
|     float factor;
 | |
|     if (eStyleUnit_Factor == unit) {
 | |
|       factor = text->mLineHeight.GetFactorValue();
 | |
|     }
 | |
|     else if (eStyleUnit_Percent == unit) {
 | |
|       factor = text->mLineHeight.GetPercentValue();
 | |
|     }
 | |
|     else {
 | |
|       factor = (((float) normalLineHeight) / PR_MAX(1, emHeight));
 | |
|     }
 | |
| 
 | |
|     // Note: we normally use the actual font height for computing the
 | |
|     // line-height raw value from the style context. On systems where
 | |
|     // they disagree the actual font height is more appropriate. This
 | |
|     // little hack lets us override that behavior to allow for more
 | |
|     // precise layout in the face of imprecise fonts.
 | |
|     if (nsHTMLReflowState::UseComputedHeight()) {
 | |
|       emHeight = font->mFont.size;
 | |
|     }
 | |
| 
 | |
|     lineHeight = NSToCoordRound(factor * emHeight);
 | |
|   }
 | |
| 
 | |
|   return lineHeight;
 | |
| }
 | |
| 
 | |
| nscoord
 | |
| nsHTMLReflowState::CalcLineHeight(nsIPresContext* aPresContext,
 | |
|                                   nsIRenderingContext* aRenderingContext,
 | |
|                                   nsIFrame* aFrame)
 | |
| {
 | |
|   nscoord lineHeight = -1;
 | |
|   nsCOMPtr<nsIStyleContext> sc;
 | |
|   aFrame->GetStyleContext(getter_AddRefs(sc));
 | |
|   if (sc) {
 | |
|     lineHeight = ComputeLineHeight(aRenderingContext, sc);
 | |
|   }
 | |
|   if (lineHeight < 0) {
 | |
|     // Negative line-heights are not allowed by the spec. Translate
 | |
|     // them into "normal" when found.
 | |
|     const nsStyleFont* font = (const nsStyleFont*)
 | |
|       sc->GetStyleData(eStyleStruct_Font);
 | |
|     if (UseComputedHeight()) {
 | |
|       lineHeight = font->mFont.size;
 | |
|     }
 | |
|     else {
 | |
|       aRenderingContext->SetFont(font->mFont);
 | |
|       nsCOMPtr<nsIFontMetrics> fm;
 | |
|       aRenderingContext->GetFontMetrics(*getter_AddRefs(fm));
 | |
| #ifdef NEW_FONT_HEIGHT_APIS
 | |
|       if (fm) {
 | |
|         fm->GetNormalLineHeight(lineHeight);
 | |
|       }
 | |
| #endif
 | |
|     }
 | |
|   }
 | |
|   return lineHeight;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputeHorizontalValue(nscoord aContainingBlockWidth,
 | |
|                                           nsStyleUnit aUnit,
 | |
|                                           const nsStyleCoord& aCoord,
 | |
|                                           nscoord& aResult)
 | |
| {
 | |
|   NS_PRECONDITION(eStyleUnit_Inherit != aUnit, "unexpected unit");
 | |
|   aResult = 0;
 | |
|   if (eStyleUnit_Percent == aUnit) {
 | |
|     if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
 | |
|       aResult = 0;
 | |
|     } else {
 | |
|       float pct = aCoord.GetPercentValue();
 | |
|       aResult = NSToCoordFloor(aContainingBlockWidth * pct);
 | |
|     }
 | |
|   
 | |
|   } else if (eStyleUnit_Coord == aUnit) {
 | |
|     aResult = aCoord.GetCoordValue();
 | |
|   }
 | |
|   else if (eStyleUnit_Chars == aUnit) {
 | |
|     if ((nsnull == rendContext) || (nsnull == frame)) {
 | |
|       // We can't compute it without a rendering context or frame, so
 | |
|       // pretend its zero...
 | |
|     }
 | |
|     else {
 | |
|       const nsStyleFont* font;
 | |
|       frame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&) font);
 | |
| 
 | |
|       const nsStyleVisibility* vis; 
 | |
|       frame->GetStyleData(eStyleStruct_Visibility, (const nsStyleStruct*&)vis);
 | |
| 
 | |
|       nsCOMPtr<nsIDeviceContext> deviceContext;
 | |
|       rendContext->GetDeviceContext(*getter_AddRefs(deviceContext));
 | |
|       nsCOMPtr<nsIAtom> langGroup;
 | |
|       if (vis->mLanguage) {
 | |
|         vis->mLanguage->GetLanguageGroup(getter_AddRefs(langGroup));
 | |
|       }
 | |
|       nsCOMPtr<nsIFontMetrics> fm;
 | |
|       deviceContext->GetMetricsFor(font->mFont, langGroup, *getter_AddRefs(fm));
 | |
|       rendContext->SetFont(fm);
 | |
|       nscoord fontWidth;
 | |
|       rendContext->GetWidth('M', fontWidth);
 | |
|       aResult = aCoord.GetIntValue() * fontWidth;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputeVerticalValue(nscoord aContainingBlockHeight,
 | |
|                                         nsStyleUnit aUnit,
 | |
|                                         const nsStyleCoord& aCoord,
 | |
|                                         nscoord& aResult)
 | |
| {
 | |
|   NS_PRECONDITION(eStyleUnit_Inherit != aUnit, "unexpected unit");
 | |
|   aResult = 0;
 | |
|   if (eStyleUnit_Percent == aUnit) {
 | |
|     // Verify no one is trying to calculate a percentage based height against
 | |
|     // a height that's shrink wrapping to its content. In that case they should
 | |
|     // treat the specified value like 'auto'
 | |
|     NS_ASSERTION(NS_AUTOHEIGHT != aContainingBlockHeight, "unexpected containing block height");
 | |
|     if (NS_AUTOHEIGHT!=aContainingBlockHeight)
 | |
|     {
 | |
|       float pct = aCoord.GetPercentValue();
 | |
|       aResult = NSToCoordFloor(aContainingBlockHeight * pct);
 | |
|     }
 | |
|     else {  // safest thing to do for an undefined height is to make it 0
 | |
|       aResult = 0;
 | |
|     }
 | |
| 
 | |
|   } else if (eStyleUnit_Coord == aUnit) {
 | |
|     aResult = aCoord.GetCoordValue();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputeMargin(nscoord aContainingBlockWidth,
 | |
|                                  const nsHTMLReflowState* aContainingBlockRS)
 | |
| {
 | |
|   // If style style can provide us the margin directly, then use it.
 | |
|   if (!mStyleMargin->GetMargin(mComputedMargin)) {
 | |
|     // We have to compute the value
 | |
|     if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
 | |
|       mComputedMargin.left = 0;
 | |
|       mComputedMargin.right = 0;
 | |
| 
 | |
|       if (eStyleUnit_Coord == mStyleMargin->mMargin.GetLeftUnit()) {
 | |
|         nsStyleCoord left;
 | |
|         
 | |
|         mStyleMargin->mMargin.GetLeft(left),
 | |
|         mComputedMargin.left = left.GetCoordValue();
 | |
|       }
 | |
|       if (eStyleUnit_Coord == mStyleMargin->mMargin.GetRightUnit()) {
 | |
|         nsStyleCoord right;
 | |
|         
 | |
|         mStyleMargin->mMargin.GetRight(right),
 | |
|         mComputedMargin.right = right.GetCoordValue();
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       nsStyleCoord left, right;
 | |
| 
 | |
|       if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetLeftUnit()) {
 | |
|         mComputedMargin.left = aContainingBlockRS->mComputedMargin.left;
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                                mStyleMargin->mMargin.GetLeftUnit(),
 | |
|                                mStyleMargin->mMargin.GetLeft(left),
 | |
|                                mComputedMargin.left);
 | |
|       }
 | |
|       if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetRightUnit()) {
 | |
|         mComputedMargin.right = aContainingBlockRS->mComputedMargin.right;
 | |
|       } else {
 | |
|         ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                                mStyleMargin->mMargin.GetRightUnit(),
 | |
|                                mStyleMargin->mMargin.GetRight(right),
 | |
|                                mComputedMargin.right);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const nsHTMLReflowState* rs2 = GetPageBoxReflowState(parentReflowState);
 | |
|     nsStyleCoord top, bottom;
 | |
|     if (nsnull != rs2) {
 | |
|       // According to the CSS2 spec, margin percentages are
 | |
|       // calculated with respect to the *height* of the containing
 | |
|       // block when in a paginated context.
 | |
|       if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetTopUnit()) {
 | |
|         mComputedMargin.top = aContainingBlockRS->mComputedMargin.top;
 | |
|       } else {
 | |
|         ComputeVerticalValue(rs2->mComputedHeight,
 | |
|                              mStyleMargin->mMargin.GetTopUnit(),
 | |
|                              mStyleMargin->mMargin.GetTop(top),
 | |
|                              mComputedMargin.top);
 | |
|       }
 | |
|       if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetBottomUnit()) {
 | |
|         mComputedMargin.bottom = aContainingBlockRS->mComputedMargin.bottom;
 | |
|       } else {
 | |
|         ComputeVerticalValue(rs2->mComputedHeight,
 | |
|                              mStyleMargin->mMargin.GetBottomUnit(),
 | |
|                              mStyleMargin->mMargin.GetBottom(bottom),
 | |
|                              mComputedMargin.bottom);
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       // According to the CSS2 spec, margin percentages are
 | |
|       // calculated with respect to the *width* of the containing
 | |
|       // block, even for margin-top and margin-bottom.
 | |
|       if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
 | |
|         mComputedMargin.top = 0;
 | |
|         mComputedMargin.bottom = 0;
 | |
| 
 | |
|       } else {
 | |
|         if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetTopUnit()) {
 | |
|           mComputedMargin.top = aContainingBlockRS->mComputedMargin.top;
 | |
|         } else {
 | |
|           ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                                  mStyleMargin->mMargin.GetTopUnit(),
 | |
|                                  mStyleMargin->mMargin.GetTop(top),
 | |
|                                  mComputedMargin.top);
 | |
|         }
 | |
|         if (eStyleUnit_Inherit == mStyleMargin->mMargin.GetBottomUnit()) {
 | |
|           mComputedMargin.bottom = aContainingBlockRS->mComputedMargin.bottom;
 | |
|         } else {
 | |
|           ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                                  mStyleMargin->mMargin.GetBottomUnit(),
 | |
|                                  mStyleMargin->mMargin.GetBottom(bottom),
 | |
|                                  mComputedMargin.bottom);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputePadding(nscoord aContainingBlockWidth,
 | |
|                                   const nsHTMLReflowState* aContainingBlockRS)
 | |
| 
 | |
| {
 | |
|   // If style can provide us the padding directly, then use it.
 | |
|   if (!mStylePadding->GetPadding(mComputedPadding)) {
 | |
|     // We have to compute the value
 | |
|     nsStyleCoord left, right, top, bottom;
 | |
| 
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetLeftUnit()) {
 | |
|       mComputedPadding.left = aContainingBlockRS->mComputedPadding.left;
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetLeftUnit(),
 | |
|                              mStylePadding->mPadding.GetLeft(left),
 | |
|                              mComputedPadding.left);
 | |
|     }
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetRightUnit()) {
 | |
|       mComputedPadding.right = aContainingBlockRS->mComputedPadding.right;
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetRightUnit(),
 | |
|                              mStylePadding->mPadding.GetRight(right),
 | |
|                              mComputedPadding.right);
 | |
|     }
 | |
| 
 | |
|     // According to the CSS2 spec, percentages are calculated with respect to
 | |
|     // containing block width for padding-top and padding-bottom
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetTopUnit()) {
 | |
|       mComputedPadding.top = aContainingBlockRS->mComputedPadding.top;
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetTopUnit(),
 | |
|                              mStylePadding->mPadding.GetTop(top),
 | |
|                              mComputedPadding.top);
 | |
|     }
 | |
|     if (eStyleUnit_Inherit == mStylePadding->mPadding.GetBottomUnit()) {
 | |
|       mComputedPadding.bottom = aContainingBlockRS->mComputedPadding.bottom;
 | |
|     } else {
 | |
|       ComputeHorizontalValue(aContainingBlockWidth,
 | |
|                              mStylePadding->mPadding.GetBottomUnit(),
 | |
|                              mStylePadding->mPadding.GetBottom(bottom),
 | |
|                              mComputedPadding.bottom);
 | |
|     }
 | |
|   }
 | |
|   // a table row/col group, row/col doesn't have padding
 | |
|   if (frame) {
 | |
|     nsCOMPtr<nsIAtom> frameType;
 | |
|     frame->GetFrameType(getter_AddRefs(frameType));
 | |
|     if ((nsLayoutAtoms::tableRowGroupFrame == frameType.get()) ||
 | |
|         (nsLayoutAtoms::tableColGroupFrame == frameType.get()) ||
 | |
|         (nsLayoutAtoms::tableRowFrame      == frameType.get()) ||
 | |
|         (nsLayoutAtoms::tableColFrame      == frameType.get())) {
 | |
|       mComputedPadding.top    = 0;
 | |
|       mComputedPadding.right  = 0;
 | |
|       mComputedPadding.bottom = 0;
 | |
|       mComputedPadding.left   = 0;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
 | |
|                                        nscoord aContainingBlockHeight,
 | |
|                                        const nsHTMLReflowState* aContainingBlockRS)
 | |
| {
 | |
|   nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
 | |
|   if (eStyleUnit_Inherit == minWidthUnit) {
 | |
|     mComputedMinWidth = aContainingBlockRS->mComputedMinWidth;
 | |
|   } else {
 | |
|     ComputeHorizontalValue(aContainingBlockWidth, minWidthUnit,
 | |
|                            mStylePosition->mMinWidth, mComputedMinWidth);
 | |
|   }
 | |
|   nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
 | |
|   if (eStyleUnit_Inherit == maxWidthUnit) {
 | |
|     mComputedMaxWidth = aContainingBlockRS->mComputedMaxWidth;
 | |
|   } else if (eStyleUnit_Null == maxWidthUnit) {
 | |
|     // Specified value of 'none'
 | |
|     mComputedMaxWidth = NS_UNCONSTRAINEDSIZE;  // no limit
 | |
|   } else {
 | |
|     ComputeHorizontalValue(aContainingBlockWidth, maxWidthUnit,
 | |
|                            mStylePosition->mMaxWidth, mComputedMaxWidth);
 | |
|   }
 | |
| 
 | |
|   // If the computed value of 'min-width' is greater than the value of
 | |
|   // 'max-width', 'max-width' is set to the value of 'min-width'
 | |
|   if (mComputedMinWidth > mComputedMaxWidth) {
 | |
|     mComputedMaxWidth = mComputedMinWidth;
 | |
|   }
 | |
| 
 | |
|   nsStyleUnit minHeightUnit = mStylePosition->mMinHeight.GetUnit();
 | |
|   if (eStyleUnit_Inherit == minHeightUnit) {
 | |
|     mComputedMinHeight = aContainingBlockRS->mComputedMinHeight;
 | |
|   } else {
 | |
|     // Check for percentage based values and a containing block height that
 | |
|     // depends on the content height. Treat them like 'auto'
 | |
|     if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
 | |
|         (eStyleUnit_Percent == minHeightUnit)) {
 | |
|       mComputedMinHeight = 0;
 | |
|     } else {
 | |
|       ComputeVerticalValue(aContainingBlockHeight, minHeightUnit,
 | |
|                            mStylePosition->mMinHeight, mComputedMinHeight);
 | |
|     }
 | |
|   }
 | |
|   nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
 | |
|   if (eStyleUnit_Inherit == maxHeightUnit) {
 | |
|     mComputedMaxHeight = aContainingBlockRS->mComputedMaxHeight;
 | |
|   } else if (eStyleUnit_Null == maxHeightUnit) {
 | |
|     // Specified value of 'none'
 | |
|     mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;  // no limit
 | |
|   } else {
 | |
|     // Check for percentage based values and a containing block height that
 | |
|     // depends on the content height. Treat them like 'auto'
 | |
|     if ((NS_AUTOHEIGHT == aContainingBlockHeight) && 
 | |
|         (eStyleUnit_Percent == maxHeightUnit)) {
 | |
|       mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
 | |
|     } else {
 | |
|       ComputeVerticalValue(aContainingBlockHeight, maxHeightUnit,
 | |
|                            mStylePosition->mMaxHeight, mComputedMaxHeight);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the computed value of 'min-height' is greater than the value of
 | |
|   // 'max-height', 'max-height' is set to the value of 'min-height'
 | |
|   if (mComputedMinHeight > mComputedMaxHeight) {
 | |
|     mComputedMaxHeight = mComputedMinHeight;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void nsHTMLReflowState::AdjustComputedHeight(void)
 | |
| {
 | |
|   // only do the math if the height  is not a symbolic value
 | |
|   if (mComputedHeight != NS_UNCONSTRAINEDSIZE) {
 | |
|     NS_ASSERTION(mComputedHeight>=0, "Negative Height Input - very bad");
 | |
| 
 | |
|     // Factor in any minimum and maximum size information
 | |
|     if (mComputedHeight > mComputedMaxHeight) {
 | |
|       mComputedHeight = mComputedMaxHeight;
 | |
|     } else if (mComputedHeight < mComputedMinHeight) {
 | |
|       mComputedHeight = mComputedMinHeight;
 | |
|     }
 | |
| 
 | |
|     if (mComputedHeight > 0) {
 | |
|       NS_ASSERTION(mComputedHeight>0, "Negative Height Input - very bad");
 | |
|       // remove extra padding/border if box-sizing property is set
 | |
|       switch (mStylePosition->mBoxSizing) {
 | |
|         case NS_STYLE_BOX_SIZING_PADDING : {
 | |
|           mComputedHeight -= mComputedPadding.top + mComputedPadding.bottom;
 | |
|           break;
 | |
|         }
 | |
|         case NS_STYLE_BOX_SIZING_BORDER : {
 | |
|           mComputedHeight -= mComputedBorderPadding.top + mComputedBorderPadding.bottom;
 | |
|         }
 | |
|         default : break;
 | |
|       }
 | |
|     }
 | |
|     // NOTE: this next assertion was firing for HR frames sometimes - why?
 | |
|     // NS_ASSERTION(mComputedHeight>=0, "Negative Height Result- very bad");
 | |
|     // if it did go bozo, set to 0
 | |
|     if(mComputedHeight<0) mComputedHeight = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsHTMLReflowState::AdjustComputedWidth(void)
 | |
| {
 | |
|   // only do the math if the width is not a symbolic value
 | |
|   if (mComputedWidth != NS_UNCONSTRAINEDSIZE) {
 | |
|     NS_ASSERTION(mComputedWidth>=0, "Negative Width Input - very bad");
 | |
| 
 | |
|     // Factor in any minimum and maximum size information
 | |
|     if (mComputedWidth > mComputedMaxWidth) {
 | |
|       mComputedWidth = mComputedMaxWidth;
 | |
|     } else if (mComputedWidth < mComputedMinWidth) {
 | |
|       mComputedWidth = mComputedMinWidth;
 | |
|     }
 | |
|     NS_ASSERTION(mComputedWidth>=0, "Negative Width Result - very bad");
 | |
|     // if it did go bozo, set to 0
 | |
|     if(mComputedWidth<0) mComputedWidth = 0;
 | |
| 
 | |
|     if (mComputedWidth > 0) {
 | |
|       NS_ASSERTION(mComputedWidth>=0, "Negative Width Input - very bad");
 | |
|       // remove extra padding/border if box-sizing property is set
 | |
|       switch (mStylePosition->mBoxSizing) {
 | |
|         case NS_STYLE_BOX_SIZING_PADDING : {
 | |
|           mComputedWidth -= mComputedPadding.left + mComputedPadding.right;
 | |
|           break;
 | |
|         }
 | |
|         case NS_STYLE_BOX_SIZING_BORDER : {
 | |
|           mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right;
 | |
|         }
 | |
|         default : break;
 | |
|       }
 | |
|     }
 | |
|     // NOTE: the next assertion was firing in the table regression tests - why? 
 | |
|     //       need to look into this
 | |
|     // NS_ASSERTION(mComputedWidth>=0, "Negative Width Result - very bad");
 | |
|     // if it did go bozo, set to 0
 | |
|     if(mComputedWidth<0) mComputedWidth = 0;
 | |
|   }
 | |
| }
 |