diff --git a/mozilla/layout/base/public/nsHTMLReflowState.h b/mozilla/layout/base/public/nsHTMLReflowState.h index 1ceaf6b16d4..c31377197ba 100644 --- a/mozilla/layout/base/public/nsHTMLReflowState.h +++ b/mozilla/layout/base/public/nsHTMLReflowState.h @@ -91,59 +91,66 @@ typedef PRUint32 nsCSSFrameType; #define NS_FRAME_GET_TYPE(_ft) \ ((_ft) & ~NS_CSS_FRAME_TYPE_REPLACED) -#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE -#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE -#define NS_AUTOMARGIN NS_UNCONSTRAINEDSIZE +#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE +#define NS_SHRINKWRAPWIDTH NS_UNCONSTRAINEDSIZE +#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE +#define NS_AUTOMARGIN NS_UNCONSTRAINEDSIZE /** - * Reflow state passed to a frame during reflow. The reflow states are linked - * together. The availableWidth and availableHeight represent the available - * space in which to reflow your frame, and are computed based on the parent - * frame's computed width and computed height. The available space is the total - * space including margins, border, padding, and content area. A value of - * NS_UNCONSTRAINEDSIZE means you can choose whatever size you want + * Reflow state passed to a frame during reflow. * - * @see #Reflow() + * @see nsIFrame#Reflow() */ struct nsHTMLReflowState { - // pointer to parent's reflow state + // the reflow states are linked together. this is the pointer to the + // parent's reflow state const nsHTMLReflowState* parentReflowState; // the frame being reflowed - nsIFrame* frame; + nsIFrame* frame; // the reason for the reflow - nsReflowReason reason; + nsReflowReason reason; - // the reflow command. only set for eReflowReason_Incremental - nsIReflowCommand* reflowCommand; + // the reflow command. only set for a reflow reason of eReflowReason_Incremental + nsIReflowCommand* reflowCommand; - // the available space in which to reflow + // the available space in which to reflow the frame. The space represents the + // amount of room for the frame's border, padding, and content area (not the + // margin area. The parent frame deals with the child frame's margins). The + // frame size you choose should fit within the available space. + // A value of NS_UNCONSTRAINEDSIZE for the available height means you can + // choose whatever size you want. In galley mode the available height is always + // NS_UNCONSTRAINEDSIZE, and only page mode involves a constrained height nscoord availableWidth, availableHeight; // rendering context to use for measurement nsIRenderingContext* rendContext; // is the current context at the top of a page? - PRPackedBool isTopOfPage; + PRPackedBool isTopOfPage; // The type of frame, from css's perspective. This value is // initialized by the Init method below. nsCSSFrameType mFrameType; + // pointer to the space manager associated with this area nsISpaceManager* mSpaceManager; // LineLayout object (only for inline reflow; set to NULL otherwise) nsLineLayout* mLineLayout; - // The computed width specifies the frame's content width, and it does not - // apply to inline non-replaced elements + // The computed width specifies the frame's content area width, and it does + // not apply to inline non-replaced elements // // For replaced inline frames, a value of NS_INTRINSICSIZE means you should // use your intrinsic width as the computed width // // For block-level frames, the computed width is based on the width of the - // containing block, the margin/border/padding areas, and the min/max width + // containing block, the margin/border/padding areas, and the min/max width. + // A value of NS_SHRINKWRAPWIDTH means that you should choose a width based + // on your content. The width may be as large as the specified maximum width + // (see mComputedMaxWidth). nscoord mComputedWidth; // The computed height specifies the frame's content height, and it does diff --git a/mozilla/layout/generic/nsBlockFrame.cpp b/mozilla/layout/generic/nsBlockFrame.cpp index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/generic/nsBlockFrame.cpp +++ b/mozilla/layout/generic/nsBlockFrame.cpp @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/generic/nsBlockReflowContext.cpp b/mozilla/layout/generic/nsBlockReflowContext.cpp index c2f025bf805..5fbfe4f2f6c 100644 --- a/mozilla/layout/generic/nsBlockReflowContext.cpp +++ b/mozilla/layout/generic/nsBlockReflowContext.cpp @@ -358,6 +358,63 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, } } + // If the block is shrink wrapping its width, then see if we have percentage + // based margins. If so, we can calculate them now that we know the shrink + // wrap width + if (NS_SHRINKWRAPWIDTH == reflowState.mComputedWidth) { + nscoord boxWidth = mMetrics.width; + float leftPct = 0.0; + float rightPct = 0.0; + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetLeftUnit()) { + nsStyleCoord leftCoord; + + reflowState.mStyleSpacing->mMargin.GetLeft(leftCoord); + leftPct = leftCoord.GetPercentValue(); + + } else { + boxWidth += mMargin.left; + } + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetRightUnit()) { + nsStyleCoord rightCoord; + + reflowState.mStyleSpacing->mMargin.GetRight(rightCoord); + rightPct = rightCoord.GetPercentValue(); + + } else { + boxWidth += mMargin.right; + } + + // The total shrink wrap width "sww" is calculated by the expression: + // sww = bw + (mp * sww) + // where "bw" is the box width (frame width plus margins that aren't percentage + // based) and "mp" are the total margin percentages (i.e., the left percentage + // value plus the right percentage value) + // Solving for "sww" gives us: + // sww = bw / (1 - mp) + // Note that this is only well defined for "mp" less than 100% + float marginPct = leftPct + rightPct; + if (marginPct >= 1.0) { + // Ignore the right percentage and just use the left percentage + // XXX Pay attention to direction property... + marginPct = leftPct; + rightPct = 0.0; + } + + if ((marginPct > 0.0) && (marginPct < 1.0)) { + double shrinkWrapWidth = float(boxWidth) / (1.0 - marginPct); + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetLeftUnit()) { + mMargin.left = NSToCoordFloor(shrinkWrapWidth * leftPct); + mX += mMargin.left; + } + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetRightUnit()) { + mMargin.right = NSToCoordFloor(shrinkWrapWidth * rightPct); + } + } + } + return rv; } diff --git a/mozilla/layout/generic/nsBlockReflowState.cpp b/mozilla/layout/generic/nsBlockReflowState.cpp index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/generic/nsBlockReflowState.cpp +++ b/mozilla/layout/generic/nsBlockReflowState.cpp @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/generic/nsBlockReflowState.h b/mozilla/layout/generic/nsBlockReflowState.h index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/generic/nsBlockReflowState.h +++ b/mozilla/layout/generic/nsBlockReflowState.h @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/generic/nsFirstLetterFrame.cpp b/mozilla/layout/generic/nsFirstLetterFrame.cpp index 7a040b2b1be..98faa026c9a 100644 --- a/mozilla/layout/generic/nsFirstLetterFrame.cpp +++ b/mozilla/layout/generic/nsFirstLetterFrame.cpp @@ -238,8 +238,10 @@ nsFirstLetterFrame::Reflow(nsIPresContext* aPresContext, else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowState.mLineLayout; + PRBool pushedFrame; + ll->BeginSpan(this, &aReflowState, bp.left, availSize.width); - ll->ReflowFrame(kid, &nextRCFrame, aReflowStatus, &aMetrics); + ll->ReflowFrame(kid, &nextRCFrame, aReflowStatus, &aMetrics, pushedFrame); nsSize size; ll->EndSpan(this, size, aMetrics.maxElementSize); } diff --git a/mozilla/layout/generic/nsHTMLReflowState.cpp b/mozilla/layout/generic/nsHTMLReflowState.cpp index db583a00731..745b8d39145 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.cpp +++ b/mozilla/layout/generic/nsHTMLReflowState.cpp @@ -998,8 +998,23 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, if (eStyleUnit_Inherit == widthUnit) { mComputedWidth = aContainingBlockWidth; } else if (eStyleUnit_Auto == widthUnit) { + // XXX TROY. Once all the frame classes have been converted to the handle + // NS_SHRINKWRAPWIDTH. then we should switch to the new code... +#if 0 + // Have it shrink wrap the width + mComputedWidth = NS_SHRINKWRAPWIDTH; + + // If there's no specified maximum width, then use 1/3 of the containing + // block width + if (NS_UNCONSTRAINEDSIZE == mComputedMaxWidth) { + if (NS_UNCONSTRAINEDSIZE != aContainingBlockWidth) { + mComputedMaxWidth = aContainingBlockWidth / 3; + } + } +#else // A specified value of 'auto' becomes a computed width of 0 mComputedWidth = 0; +#endif } else { ComputeHorizontalValue(aContainingBlockWidth, widthUnit, mStylePosition->mWidth, @@ -1007,19 +1022,21 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } // Take into account minimum and maximum sizes - if (mComputedWidth > mComputedMaxWidth) { - mComputedWidth = mComputedMaxWidth; - } else if (mComputedWidth < mComputedMinWidth) { - mComputedWidth = mComputedMinWidth; - } - - // See what edge the width applies to (the default is the content - // edge) - if ((mComputedWidth > 0) && (mComputedWidth != NS_UNCONSTRAINEDSIZE)) { - if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { - mComputedWidth -= mComputedPadding.left + mComputedPadding.right; - } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { - mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right; + if (mComputedWidth != NS_SHRINKWRAPWIDTH) { + if (mComputedWidth > mComputedMaxWidth) { + mComputedWidth = mComputedMaxWidth; + } else if (mComputedWidth < mComputedMinWidth) { + mComputedWidth = mComputedMinWidth; + } + + // See what edge the width applies to (the default is the content + // edge) + if (mComputedWidth > 0) { + if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { + mComputedWidth -= mComputedPadding.left + mComputedPadding.right; + } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { + mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right; + } } } @@ -1035,15 +1052,15 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } // Take into account minimum and maximum sizes - if (mComputedHeight > mComputedMaxHeight) { - mComputedHeight = mComputedMaxHeight; - } else if (mComputedHeight < mComputedMinHeight) { - mComputedHeight = mComputedMinHeight; - } - - // See what edge the height applies to (the default is the content - // edge) if (mComputedHeight != NS_AUTOHEIGHT) { + if (mComputedHeight > mComputedMaxHeight) { + mComputedHeight = mComputedMaxHeight; + } else if (mComputedHeight < mComputedMinHeight) { + mComputedHeight = mComputedMinHeight; + } + + // See what edge the height applies to (the default is the content + // edge) if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { mComputedHeight -= mComputedPadding.top + mComputedPadding.bottom; } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { @@ -1150,6 +1167,21 @@ nsHTMLReflowState::ComputeBlockBoxData(nsIPresContext* aPresContext, // 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 { mComputedWidth = availableWidth - mComputedMargin.left - mComputedMargin.right - mComputedBorderPadding.left - @@ -1546,6 +1578,19 @@ nsHTMLReflowState::ComputeMargin(nscoord aContainingBlockWidth, mComputedMargin.left = 0; mComputedMargin.right = 0; + if (eStyleUnit_Coord == mStyleSpacing->mMargin.GetLeftUnit()) { + nsStyleCoord left; + + mStyleSpacing->mMargin.GetLeft(left), + mComputedMargin.left = left.GetCoordValue(); + } + if (eStyleUnit_Coord == mStyleSpacing->mMargin.GetRightUnit()) { + nsStyleCoord right; + + mStyleSpacing->mMargin.GetRight(right), + mComputedMargin.right = right.GetCoordValue(); + } + } else { nsStyleCoord left, right; diff --git a/mozilla/layout/generic/nsHTMLReflowState.h b/mozilla/layout/generic/nsHTMLReflowState.h index 1ceaf6b16d4..c31377197ba 100644 --- a/mozilla/layout/generic/nsHTMLReflowState.h +++ b/mozilla/layout/generic/nsHTMLReflowState.h @@ -91,59 +91,66 @@ typedef PRUint32 nsCSSFrameType; #define NS_FRAME_GET_TYPE(_ft) \ ((_ft) & ~NS_CSS_FRAME_TYPE_REPLACED) -#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE -#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE -#define NS_AUTOMARGIN NS_UNCONSTRAINEDSIZE +#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE +#define NS_SHRINKWRAPWIDTH NS_UNCONSTRAINEDSIZE +#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE +#define NS_AUTOMARGIN NS_UNCONSTRAINEDSIZE /** - * Reflow state passed to a frame during reflow. The reflow states are linked - * together. The availableWidth and availableHeight represent the available - * space in which to reflow your frame, and are computed based on the parent - * frame's computed width and computed height. The available space is the total - * space including margins, border, padding, and content area. A value of - * NS_UNCONSTRAINEDSIZE means you can choose whatever size you want + * Reflow state passed to a frame during reflow. * - * @see #Reflow() + * @see nsIFrame#Reflow() */ struct nsHTMLReflowState { - // pointer to parent's reflow state + // the reflow states are linked together. this is the pointer to the + // parent's reflow state const nsHTMLReflowState* parentReflowState; // the frame being reflowed - nsIFrame* frame; + nsIFrame* frame; // the reason for the reflow - nsReflowReason reason; + nsReflowReason reason; - // the reflow command. only set for eReflowReason_Incremental - nsIReflowCommand* reflowCommand; + // the reflow command. only set for a reflow reason of eReflowReason_Incremental + nsIReflowCommand* reflowCommand; - // the available space in which to reflow + // the available space in which to reflow the frame. The space represents the + // amount of room for the frame's border, padding, and content area (not the + // margin area. The parent frame deals with the child frame's margins). The + // frame size you choose should fit within the available space. + // A value of NS_UNCONSTRAINEDSIZE for the available height means you can + // choose whatever size you want. In galley mode the available height is always + // NS_UNCONSTRAINEDSIZE, and only page mode involves a constrained height nscoord availableWidth, availableHeight; // rendering context to use for measurement nsIRenderingContext* rendContext; // is the current context at the top of a page? - PRPackedBool isTopOfPage; + PRPackedBool isTopOfPage; // The type of frame, from css's perspective. This value is // initialized by the Init method below. nsCSSFrameType mFrameType; + // pointer to the space manager associated with this area nsISpaceManager* mSpaceManager; // LineLayout object (only for inline reflow; set to NULL otherwise) nsLineLayout* mLineLayout; - // The computed width specifies the frame's content width, and it does not - // apply to inline non-replaced elements + // The computed width specifies the frame's content area width, and it does + // not apply to inline non-replaced elements // // For replaced inline frames, a value of NS_INTRINSICSIZE means you should // use your intrinsic width as the computed width // // For block-level frames, the computed width is based on the width of the - // containing block, the margin/border/padding areas, and the min/max width + // containing block, the margin/border/padding areas, and the min/max width. + // A value of NS_SHRINKWRAPWIDTH means that you should choose a width based + // on your content. The width may be as large as the specified maximum width + // (see mComputedMaxWidth). nscoord mComputedWidth; // The computed height specifies the frame's content height, and it does diff --git a/mozilla/layout/generic/nsInlineFrame.cpp b/mozilla/layout/generic/nsInlineFrame.cpp index 326470af89b..e29c4c8ace2 100644 --- a/mozilla/layout/generic/nsInlineFrame.cpp +++ b/mozilla/layout/generic/nsInlineFrame.cpp @@ -486,7 +486,9 @@ nsInlineFrame::ReflowInlineFrame(nsIPresContext* aPresContext, { nsLineLayout* lineLayout = aReflowState.mLineLayout; PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); - nsresult rv = lineLayout->ReflowFrame(aFrame, &irs.mNextRCFrame, aStatus); + PRBool pushedFrame; + nsresult rv = lineLayout->ReflowFrame(aFrame, &irs.mNextRCFrame, aStatus, + nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } diff --git a/mozilla/layout/generic/nsLineBox.h b/mozilla/layout/generic/nsLineBox.h index c51e26224d2..8cfcfc15190 100644 --- a/mozilla/layout/generic/nsLineBox.h +++ b/mozilla/layout/generic/nsLineBox.h @@ -189,6 +189,7 @@ public: return mFlags.mTrimmed; } + // mHasPercentageChild bit void SetHasPercentageChild(PRBool aOn) { mFlags.mHasPercentageChild = aOn; } @@ -196,6 +197,14 @@ public: return mFlags.mHasPercentageChild; } + // mLineWrapped bit + void SetLineWrapped(PRBool aOn) { + mFlags.mLineWrapped = aOn; + } + PRBool IsLineWrapped() const { + return mFlags.mLineWrapped; + } + // mChildCount value PRInt32 GetChildCount() const { return (PRInt32) mFlags.mChildCount; @@ -299,10 +308,11 @@ public: PRUint32 mImpactedByFloater : 1; PRUint32 mTrimmed : 1; PRUint32 mHasPercentageChild : 1; + PRUint32 mLineWrapped: 1; PRUint32 mBreakType : 4; - PRUint32 mChildCount : 23; + PRUint32 mChildCount : 22; }; struct ExtraData { diff --git a/mozilla/layout/generic/nsLineLayout.cpp b/mozilla/layout/generic/nsLineLayout.cpp index 66765a848c5..a9b38e47bd6 100644 --- a/mozilla/layout/generic/nsLineLayout.cpp +++ b/mozilla/layout/generic/nsLineLayout.cpp @@ -800,8 +800,12 @@ nsresult nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsIFrame** aNextRCFrame, nsReflowStatus& aReflowStatus, - nsHTMLReflowMetrics* aMetrics) + nsHTMLReflowMetrics* aMetrics, + PRBool& aPushedFrame) { + // Initialize OUT parameter + aPushedFrame = PR_FALSE; + PerFrameData* pfd; nsresult rv = NewPerFrameData(&pfd); if (NS_FAILED(rv)) { @@ -1132,6 +1136,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, } else { PushFrame(aFrame); + aPushedFrame = PR_TRUE; } } else { @@ -2354,8 +2359,10 @@ nsLineLayout::TrimTrailingWhiteSpace() return 0 != deltaWidth; } -void -nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) +PRBool +nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, + PRBool aAllowJustify, + PRBool aShrinkWrapWidth) { PerSpanData* psd = mRootSpan; nscoord availWidth = psd->mRightEdge; @@ -2365,7 +2372,7 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) nsFrame::ListTag(stdout, mBlockReflowState->frame); printf(": skipping horizontal alignment in pass1 table reflow\n"); #endif - return; + return PR_TRUE; } availWidth -= psd->mLeftEdge; nscoord remainingWidth = availWidth - aLineBounds.width; @@ -2412,6 +2419,12 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) break; } if (0 != dx) { + // If we need to move the frames but we're shrink wrapping, then + // we need to wait until the final width is known + if (aShrinkWrapWidth) { + return PR_FALSE; + } + PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { pfd->mBounds.x += dx; @@ -2426,6 +2439,9 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) psd->mChangedFrameDirection = PR_TRUE; /* Assume that all frames have been right aligned.*/ + if (aShrinkWrapWidth) { + return PR_FALSE; + } PerFrameData* pfd = psd->mFirstFrame; PRUint32 maxX = psd->mRightEdge; while (nsnull != pfd) { @@ -2436,6 +2452,8 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) } } } + + return PR_TRUE; } void diff --git a/mozilla/layout/generic/nsLineLayout.h b/mozilla/layout/generic/nsLineLayout.h index bf6b985bfea..7e472515c53 100644 --- a/mozilla/layout/generic/nsLineLayout.h +++ b/mozilla/layout/generic/nsLineLayout.h @@ -89,10 +89,13 @@ public: PRBool IsZeroHeight(); + // Reflows the frame and returns the reflow status. aPushedFrame is PR_TRUE + // if the frame is pushed to the next line because it doesn't fit nsresult ReflowFrame(nsIFrame* aFrame, nsIFrame** aNextRCFrame, nsReflowStatus& aReflowStatus, - nsHTMLReflowMetrics* aMetrics = nsnull); + nsHTMLReflowMetrics* aMetrics, + PRBool& aPushedFrame); nscoord GetCarriedOutBottomMargin() const { return mCarriedOutBottomMargin; @@ -110,7 +113,9 @@ public: PRBool TrimTrailingWhiteSpace(); - void HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify); + PRBool HorizontalAlignFrames(nsRect& aLineBounds, + PRBool aAllowJustify, + PRBool aShrinkWrapWidth); void RelativePositionFrames(nsRect& aCombinedArea); diff --git a/mozilla/layout/html/base/src/nsBlockFrame.cpp b/mozilla/layout/html/base/src/nsBlockFrame.cpp index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/html/base/src/nsBlockFrame.cpp +++ b/mozilla/layout/html/base/src/nsBlockFrame.cpp @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp index c2f025bf805..5fbfe4f2f6c 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowContext.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowContext.cpp @@ -358,6 +358,63 @@ nsBlockReflowContext::ReflowBlock(nsIFrame* aFrame, } } + // If the block is shrink wrapping its width, then see if we have percentage + // based margins. If so, we can calculate them now that we know the shrink + // wrap width + if (NS_SHRINKWRAPWIDTH == reflowState.mComputedWidth) { + nscoord boxWidth = mMetrics.width; + float leftPct = 0.0; + float rightPct = 0.0; + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetLeftUnit()) { + nsStyleCoord leftCoord; + + reflowState.mStyleSpacing->mMargin.GetLeft(leftCoord); + leftPct = leftCoord.GetPercentValue(); + + } else { + boxWidth += mMargin.left; + } + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetRightUnit()) { + nsStyleCoord rightCoord; + + reflowState.mStyleSpacing->mMargin.GetRight(rightCoord); + rightPct = rightCoord.GetPercentValue(); + + } else { + boxWidth += mMargin.right; + } + + // The total shrink wrap width "sww" is calculated by the expression: + // sww = bw + (mp * sww) + // where "bw" is the box width (frame width plus margins that aren't percentage + // based) and "mp" are the total margin percentages (i.e., the left percentage + // value plus the right percentage value) + // Solving for "sww" gives us: + // sww = bw / (1 - mp) + // Note that this is only well defined for "mp" less than 100% + float marginPct = leftPct + rightPct; + if (marginPct >= 1.0) { + // Ignore the right percentage and just use the left percentage + // XXX Pay attention to direction property... + marginPct = leftPct; + rightPct = 0.0; + } + + if ((marginPct > 0.0) && (marginPct < 1.0)) { + double shrinkWrapWidth = float(boxWidth) / (1.0 - marginPct); + + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetLeftUnit()) { + mMargin.left = NSToCoordFloor(shrinkWrapWidth * leftPct); + mX += mMargin.left; + } + if (eStyleUnit_Percent == reflowState.mStyleSpacing->mMargin.GetRightUnit()) { + mMargin.right = NSToCoordFloor(shrinkWrapWidth * rightPct); + } + } + } + return rv; } diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.cpp b/mozilla/layout/html/base/src/nsBlockReflowState.cpp index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.cpp +++ b/mozilla/layout/html/base/src/nsBlockReflowState.cpp @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/html/base/src/nsBlockReflowState.h b/mozilla/layout/html/base/src/nsBlockReflowState.h index 1eef663b005..f4021a10da9 100644 --- a/mozilla/layout/html/base/src/nsBlockReflowState.h +++ b/mozilla/layout/html/base/src/nsBlockReflowState.h @@ -272,7 +272,8 @@ public: nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics); + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot); ~nsBlockReflowState(); @@ -449,6 +450,10 @@ public: PRBool mUnconstrainedHeight; + PRBool mShrinkWrapWidth; + + PRBool mNeedResizeReflow; + // The content area to reflow child frames within. The x/y // coordinates are known to be mBorderPadding.left and // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE @@ -564,7 +569,8 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsIPresContext* aPresContext, nsBlockFrame* aFrame, - const nsHTMLReflowMetrics& aMetrics) + const nsHTMLReflowMetrics& aMetrics, + PRBool aBlockMarginRoot) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), @@ -574,8 +580,17 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mNextRCFrame(nsnull), mPrevBottomMargin(0), mFreeLineList(nsnull), - mLineNumber(0) + mLineNumber(0), + mNeedResizeReflow(PR_FALSE) { + if (aBlockMarginRoot) { + mIsTopMarginRoot = PR_TRUE; + mIsBottomMarginRoot = PR_TRUE; + } + if (mIsTopMarginRoot) { + mApplyTopMargin = PR_TRUE; + } + mSpaceManager = aReflowState.mSpaceManager; // Translate into our content area and then save the @@ -593,6 +608,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, // Compute content area width (the content area is inside the border // and padding) mUnconstrainedWidth = PR_FALSE; + mShrinkWrapWidth = PR_FALSE; if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) { mContentArea.width = aReflowState.mComputedWidth; } @@ -601,6 +617,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, mContentArea.width = NS_UNCONSTRAINEDSIZE; mUnconstrainedWidth = PR_TRUE; } + else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { + // Choose a width based on the content (shrink wrap width) up + // to the maximum width + mContentArea.width = aReflowState.mComputedMaxWidth; + mShrinkWrapWidth = PR_TRUE; + } else { nscoord lr = borderPadding.left + borderPadding.right; mContentArea.width = aReflowState.availableWidth - lr; @@ -1423,14 +1445,8 @@ nsBlockFrame::Reflow(nsIPresContext* aPresContext, reflowState.mSpaceManager = spaceManager.get(); } - nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics); - if (NS_BLOCK_MARGIN_ROOT & mState) { - state.mIsTopMarginRoot = PR_TRUE; - state.mIsBottomMarginRoot = PR_TRUE; - } - if (state.mIsTopMarginRoot) { - state.mApplyTopMargin = PR_TRUE; - } + nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); if (eReflowReason_Resize != aReflowState.reason) { RenumberLists(); @@ -1792,7 +1808,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, // contents or we fluff out to the maximum block width. Note: // We always shrink wrap when given an unconstrained width. if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) && - !aState.mUnconstrainedWidth && + !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth && !compact) { // Set our width to the max width if we aren't already that // wide. Note that the max-width has nothing to do with our @@ -1826,7 +1842,7 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, nscoord computedMaxWidth = aReflowState.mComputedMaxWidth + borderPadding.left + borderPadding.right; if (computedWidth > computedMaxWidth) { - computedWidth = aReflowState.mComputedMaxWidth; + computedWidth = computedMaxWidth; } } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { @@ -1837,6 +1853,47 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, } } aMetrics.width = computedWidth; + + // If we're shrink wrapping, then now that we know our final width we + // need to do horizontal alignment of the inline lines and make sure + // blocks are correctly sized and positioned. Any lines that need + // final adjustment will have been marked as dirty + if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) { + // If the parent reflow state is also shrink wrap width, then + // we don't need to do this, because it will reflow us after it + // calculates the final width + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } + + if (!parentIsShrinkWrapWidth) { + nsHTMLReflowState reflowState(aReflowState); + + reflowState.mComputedWidth = aMetrics.width - borderPadding.left - + borderPadding.right; + reflowState.reason = eReflowReason_Resize; + reflowState.mSpaceManager->ClearRegions(); + + nscoord oldDesiredWidth = aMetrics.width; + nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics, + NS_BLOCK_MARGIN_ROOT & mState); + ReflowDirtyLines(state); + aState.mY = state.mY; + NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width"); + } + } + } + + if (aState.mShrinkWrapWidth) { + PRBool parentIsShrinkWrapWidth = PR_FALSE; + if (aReflowState.parentReflowState) { + if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) { + parentIsShrinkWrapWidth = PR_TRUE; + } + } } // Compute final height @@ -2182,8 +2239,14 @@ nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) nsLineBox* line = mLines; if (tryAndSkipLines) { // The line's bounds are relative to the border edge of the frame - nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left + - aState.mReflowState.mComputedWidth; + nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left; + + if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) { + newAvailWidth += aState.mReflowState.mComputedMaxWidth; + } else { + newAvailWidth += aState.mReflowState.mComputedWidth; + } + #ifdef DEBUG if (gNoisyReflow) { IndentBy(stdout, gNoiseIndent); @@ -2685,6 +2748,7 @@ nsBlockFrame::ReflowLine(nsBlockReflowState& aState, } } else { + aLine->SetLineWrapped(PR_FALSE); rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); // We don't really know what changed in the line, so use the union @@ -3295,6 +3359,21 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets, &collapsedBottomMargin, aLine->mBounds, combinedArea); + if (aState.mShrinkWrapWidth) { + // Mark the line as block so once we known the final shrink wrap width + // we can reflow the block to the correct size + // XXX We don't always need to do this... + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + + // Add the right margin to the line's bounnds. That way it will be taken into + // account when we compute our shrink wrap size + nscoord marginRight = brc.GetMargin().right; + if (marginRight != NS_UNCONSTRAINEDSIZE) { + aLine->mBounds.width += marginRight; + } + + } aLine->SetCombinedArea(combinedArea); if (*aKeepReflowGoing) { // Some of the child block fit @@ -3703,8 +3782,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, // Reflow the inline frame nsReflowStatus frameReflowStatus; + PRBool pushedFrame; nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame, - frameReflowStatus); + frameReflowStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } @@ -3752,6 +3832,13 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + + // If we're splitting the line because the frame didn't fit and it + // was pushed, then mark the line as having word wrapped. We need to + // know that if we're shrink wrapping our width + if (pushedFrame) { + aLine->SetLineWrapped(PR_TRUE); + } } } else { @@ -3770,6 +3857,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, if (NS_FAILED(rv)) { return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); } // Split line, but after the frame just reflowed @@ -3799,6 +3888,9 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, return rv; } + // Remember that the line has wrapped + aLine->SetLineWrapped(PR_TRUE); + // If we are reflowing the first letter frame then don't split the // line and don't stop the line reflow... PRBool splitLine = !reflowingFirstLetter; @@ -3988,6 +4080,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, } nsSize maxElementSize; aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize); + // See if we're shrink wrapping the width + if (aState.mShrinkWrapWidth) { + // When determining the line's width we also need to include any + // right floaters that impact us. This represents the shrink wrap + // width of the line + if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) { + NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state"); + aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost(); + } + } #ifdef DEBUG { static nscoord lastHeight = 0; @@ -4016,8 +4118,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #else PRBool allowJustify = PR_FALSE; #endif - aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify); + + PRBool successful; nsRect combinedArea; + successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify, + aState.mShrinkWrapWidth); + if (!successful) { + // Mark the line dirty and then later once we've determined the width + // we can do the horizontal alignment + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } aLineLayout.RelativePositionFrames(combinedArea); aLine->SetCombinedArea(combinedArea); if (addedBullet) { @@ -4113,17 +4224,17 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif CombineRects(aState.mFloaterCombinedArea, lineCombinedArea); - if (aState.mUnconstrainedWidth && aState.mHaveRightFloaters) { - // We are reflowing in an unconstrained situation and have some - // right floaters. They were placed at the infinite right edge + if (aState.mHaveRightFloaters && + (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) { + // We are reflowing in an unconstrained situation or shrink wrapping and + // have some right floaters. They were placed at the infinite right edge // which will cause the combined area to be unusable. // - // To solve this issue, we pretend that the right floaters ended - // up just past the end of the line. Note that the right floater - // combined area we computed as we were going will have as its X - // coordinate the left most edge of all the right - // floaters. Therefore, to accomplish our goal all we do is set - // that X value to the lines XMost value. + // To solve this issue, we pretend that the right floaters ended up just + // past the end of the line. Note that the right floater combined area + // we computed as we were going will have as its X coordinate the left + // most edge of all the right floaters. Therefore, to accomplish our goal + // all we do is set that X value to the lines XMost value. #ifdef NOISY_COMBINED_AREA printf(" ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n", aState.mRightFloaterCombinedArea.x, @@ -4134,6 +4245,13 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState, #endif aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost(); CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea); + + if (aState.mShrinkWrapWidth) { + // Mark the line dirty so we come back and re-place the floater once + // the shrink wrap width is determined + aLine->MarkDirty(); + aState.mNeedResizeReflow = PR_TRUE; + } } aLine->SetCombinedArea(lineCombinedArea); #ifdef NOISY_COMBINED_AREA @@ -4239,7 +4357,13 @@ nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState, printf(": line=%p xmost=%d\n", aLine, xmost); } #endif - if (xmost > aState.mKidXMost) { + // If we're shrink wrapping our width and the line was wrapped, + // then make sure we take up all of the available width + if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) { + aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width; + + } + else if (xmost > aState.mKidXMost) { aState.mKidXMost = xmost; } } @@ -5228,7 +5352,7 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, #ifdef DEBUG nsresult rv = #endif - mSpaceManager->AddRectRegion(floater, region); + mSpaceManager->AddRectRegion(floater, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement"); } @@ -5276,13 +5400,12 @@ nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache, nsRect combinedArea = aFloaterCache->mCombinedArea; combinedArea.x += x; combinedArea.y += y; - if (!isLeftFloater && mUnconstrainedWidth) { - // When we are placing a right floater in an unconstrained - // situation we don't apply it to the floater combined area - // immediately. Otherwise we end up with an infinitely wide - // combined area. Instead, we save it away in - // mRightFloaterCombinedArea so that later on when we know the - // width of a line we can compute a better value. + if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) { + // When we are placing a right floater in an unconstrained situation or + // when shrink wrapping, we don't apply it to the floater combined area + // immediately. Otherwise we end up with an infinitely wide combined + // area. Instead, we save it away in mRightFloaterCombinedArea so that + // later on when we know the width of a line we can compute a better value. if (!mHaveRightFloaters) { mRightFloaterCombinedArea = combinedArea; mHaveRightFloaters = PR_TRUE; diff --git a/mozilla/layout/html/base/src/nsFirstLetterFrame.cpp b/mozilla/layout/html/base/src/nsFirstLetterFrame.cpp index 7a040b2b1be..98faa026c9a 100644 --- a/mozilla/layout/html/base/src/nsFirstLetterFrame.cpp +++ b/mozilla/layout/html/base/src/nsFirstLetterFrame.cpp @@ -238,8 +238,10 @@ nsFirstLetterFrame::Reflow(nsIPresContext* aPresContext, else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowState.mLineLayout; + PRBool pushedFrame; + ll->BeginSpan(this, &aReflowState, bp.left, availSize.width); - ll->ReflowFrame(kid, &nextRCFrame, aReflowStatus, &aMetrics); + ll->ReflowFrame(kid, &nextRCFrame, aReflowStatus, &aMetrics, pushedFrame); nsSize size; ll->EndSpan(this, size, aMetrics.maxElementSize); } diff --git a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp index db583a00731..745b8d39145 100644 --- a/mozilla/layout/html/base/src/nsHTMLReflowState.cpp +++ b/mozilla/layout/html/base/src/nsHTMLReflowState.cpp @@ -998,8 +998,23 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, if (eStyleUnit_Inherit == widthUnit) { mComputedWidth = aContainingBlockWidth; } else if (eStyleUnit_Auto == widthUnit) { + // XXX TROY. Once all the frame classes have been converted to the handle + // NS_SHRINKWRAPWIDTH. then we should switch to the new code... +#if 0 + // Have it shrink wrap the width + mComputedWidth = NS_SHRINKWRAPWIDTH; + + // If there's no specified maximum width, then use 1/3 of the containing + // block width + if (NS_UNCONSTRAINEDSIZE == mComputedMaxWidth) { + if (NS_UNCONSTRAINEDSIZE != aContainingBlockWidth) { + mComputedMaxWidth = aContainingBlockWidth / 3; + } + } +#else // A specified value of 'auto' becomes a computed width of 0 mComputedWidth = 0; +#endif } else { ComputeHorizontalValue(aContainingBlockWidth, widthUnit, mStylePosition->mWidth, @@ -1007,19 +1022,21 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } // Take into account minimum and maximum sizes - if (mComputedWidth > mComputedMaxWidth) { - mComputedWidth = mComputedMaxWidth; - } else if (mComputedWidth < mComputedMinWidth) { - mComputedWidth = mComputedMinWidth; - } - - // See what edge the width applies to (the default is the content - // edge) - if ((mComputedWidth > 0) && (mComputedWidth != NS_UNCONSTRAINEDSIZE)) { - if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { - mComputedWidth -= mComputedPadding.left + mComputedPadding.right; - } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { - mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right; + if (mComputedWidth != NS_SHRINKWRAPWIDTH) { + if (mComputedWidth > mComputedMaxWidth) { + mComputedWidth = mComputedMaxWidth; + } else if (mComputedWidth < mComputedMinWidth) { + mComputedWidth = mComputedMinWidth; + } + + // See what edge the width applies to (the default is the content + // edge) + if (mComputedWidth > 0) { + if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { + mComputedWidth -= mComputedPadding.left + mComputedPadding.right; + } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { + mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right; + } } } @@ -1035,15 +1052,15 @@ nsHTMLReflowState::InitConstraints(nsIPresContext* aPresContext, } // Take into account minimum and maximum sizes - if (mComputedHeight > mComputedMaxHeight) { - mComputedHeight = mComputedMaxHeight; - } else if (mComputedHeight < mComputedMinHeight) { - mComputedHeight = mComputedMinHeight; - } - - // See what edge the height applies to (the default is the content - // edge) if (mComputedHeight != NS_AUTOHEIGHT) { + if (mComputedHeight > mComputedMaxHeight) { + mComputedHeight = mComputedMaxHeight; + } else if (mComputedHeight < mComputedMinHeight) { + mComputedHeight = mComputedMinHeight; + } + + // See what edge the height applies to (the default is the content + // edge) if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_PADDING) { mComputedHeight -= mComputedPadding.top + mComputedPadding.bottom; } else if (mStylePosition->mBoxSizing == NS_STYLE_BOX_SIZING_BORDER) { @@ -1150,6 +1167,21 @@ nsHTMLReflowState::ComputeBlockBoxData(nsIPresContext* aPresContext, // 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 { mComputedWidth = availableWidth - mComputedMargin.left - mComputedMargin.right - mComputedBorderPadding.left - @@ -1546,6 +1578,19 @@ nsHTMLReflowState::ComputeMargin(nscoord aContainingBlockWidth, mComputedMargin.left = 0; mComputedMargin.right = 0; + if (eStyleUnit_Coord == mStyleSpacing->mMargin.GetLeftUnit()) { + nsStyleCoord left; + + mStyleSpacing->mMargin.GetLeft(left), + mComputedMargin.left = left.GetCoordValue(); + } + if (eStyleUnit_Coord == mStyleSpacing->mMargin.GetRightUnit()) { + nsStyleCoord right; + + mStyleSpacing->mMargin.GetRight(right), + mComputedMargin.right = right.GetCoordValue(); + } + } else { nsStyleCoord left, right; diff --git a/mozilla/layout/html/base/src/nsInlineFrame.cpp b/mozilla/layout/html/base/src/nsInlineFrame.cpp index 326470af89b..e29c4c8ace2 100644 --- a/mozilla/layout/html/base/src/nsInlineFrame.cpp +++ b/mozilla/layout/html/base/src/nsInlineFrame.cpp @@ -486,7 +486,9 @@ nsInlineFrame::ReflowInlineFrame(nsIPresContext* aPresContext, { nsLineLayout* lineLayout = aReflowState.mLineLayout; PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); - nsresult rv = lineLayout->ReflowFrame(aFrame, &irs.mNextRCFrame, aStatus); + PRBool pushedFrame; + nsresult rv = lineLayout->ReflowFrame(aFrame, &irs.mNextRCFrame, aStatus, + nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } diff --git a/mozilla/layout/html/base/src/nsLineBox.h b/mozilla/layout/html/base/src/nsLineBox.h index c51e26224d2..8cfcfc15190 100644 --- a/mozilla/layout/html/base/src/nsLineBox.h +++ b/mozilla/layout/html/base/src/nsLineBox.h @@ -189,6 +189,7 @@ public: return mFlags.mTrimmed; } + // mHasPercentageChild bit void SetHasPercentageChild(PRBool aOn) { mFlags.mHasPercentageChild = aOn; } @@ -196,6 +197,14 @@ public: return mFlags.mHasPercentageChild; } + // mLineWrapped bit + void SetLineWrapped(PRBool aOn) { + mFlags.mLineWrapped = aOn; + } + PRBool IsLineWrapped() const { + return mFlags.mLineWrapped; + } + // mChildCount value PRInt32 GetChildCount() const { return (PRInt32) mFlags.mChildCount; @@ -299,10 +308,11 @@ public: PRUint32 mImpactedByFloater : 1; PRUint32 mTrimmed : 1; PRUint32 mHasPercentageChild : 1; + PRUint32 mLineWrapped: 1; PRUint32 mBreakType : 4; - PRUint32 mChildCount : 23; + PRUint32 mChildCount : 22; }; struct ExtraData { diff --git a/mozilla/layout/html/base/src/nsLineLayout.cpp b/mozilla/layout/html/base/src/nsLineLayout.cpp index 66765a848c5..a9b38e47bd6 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.cpp +++ b/mozilla/layout/html/base/src/nsLineLayout.cpp @@ -800,8 +800,12 @@ nsresult nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsIFrame** aNextRCFrame, nsReflowStatus& aReflowStatus, - nsHTMLReflowMetrics* aMetrics) + nsHTMLReflowMetrics* aMetrics, + PRBool& aPushedFrame) { + // Initialize OUT parameter + aPushedFrame = PR_FALSE; + PerFrameData* pfd; nsresult rv = NewPerFrameData(&pfd); if (NS_FAILED(rv)) { @@ -1132,6 +1136,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame, } else { PushFrame(aFrame); + aPushedFrame = PR_TRUE; } } else { @@ -2354,8 +2359,10 @@ nsLineLayout::TrimTrailingWhiteSpace() return 0 != deltaWidth; } -void -nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) +PRBool +nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, + PRBool aAllowJustify, + PRBool aShrinkWrapWidth) { PerSpanData* psd = mRootSpan; nscoord availWidth = psd->mRightEdge; @@ -2365,7 +2372,7 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) nsFrame::ListTag(stdout, mBlockReflowState->frame); printf(": skipping horizontal alignment in pass1 table reflow\n"); #endif - return; + return PR_TRUE; } availWidth -= psd->mLeftEdge; nscoord remainingWidth = availWidth - aLineBounds.width; @@ -2412,6 +2419,12 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) break; } if (0 != dx) { + // If we need to move the frames but we're shrink wrapping, then + // we need to wait until the final width is known + if (aShrinkWrapWidth) { + return PR_FALSE; + } + PerFrameData* pfd = psd->mFirstFrame; while (nsnull != pfd) { pfd->mBounds.x += dx; @@ -2426,6 +2439,9 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) psd->mChangedFrameDirection = PR_TRUE; /* Assume that all frames have been right aligned.*/ + if (aShrinkWrapWidth) { + return PR_FALSE; + } PerFrameData* pfd = psd->mFirstFrame; PRUint32 maxX = psd->mRightEdge; while (nsnull != pfd) { @@ -2436,6 +2452,8 @@ nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify) } } } + + return PR_TRUE; } void diff --git a/mozilla/layout/html/base/src/nsLineLayout.h b/mozilla/layout/html/base/src/nsLineLayout.h index bf6b985bfea..7e472515c53 100644 --- a/mozilla/layout/html/base/src/nsLineLayout.h +++ b/mozilla/layout/html/base/src/nsLineLayout.h @@ -89,10 +89,13 @@ public: PRBool IsZeroHeight(); + // Reflows the frame and returns the reflow status. aPushedFrame is PR_TRUE + // if the frame is pushed to the next line because it doesn't fit nsresult ReflowFrame(nsIFrame* aFrame, nsIFrame** aNextRCFrame, nsReflowStatus& aReflowStatus, - nsHTMLReflowMetrics* aMetrics = nsnull); + nsHTMLReflowMetrics* aMetrics, + PRBool& aPushedFrame); nscoord GetCarriedOutBottomMargin() const { return mCarriedOutBottomMargin; @@ -110,7 +113,9 @@ public: PRBool TrimTrailingWhiteSpace(); - void HorizontalAlignFrames(nsRect& aLineBounds, PRBool aAllowJustify); + PRBool HorizontalAlignFrames(nsRect& aLineBounds, + PRBool aAllowJustify, + PRBool aShrinkWrapWidth); void RelativePositionFrames(nsRect& aCombinedArea);