/** * Place below-current-line floats. */ PRBool nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRBool aForceFit) { nsFloatCache* fc = aList.Head(); while (fc) { { #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("placing bcl float: "); nsFrame::ListTag(stdout, fc->mPlaceholder->GetOutOfFlowFrame()); printf("\n"); } #endif // Place the float PRBool isLeftFloat; nsReflowStatus reflowStatus; PRBool placed = FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus, aForceFit); NS_ASSERTION(placed || !aForceFit, "If we're in force-fit mode, we should have placed the float"); if (!placed || (NS_FRAME_IS_TRUNCATED(reflowStatus) && !aForceFit)) { // return before processing all of the floats, since the line will be pushed. return PR_FALSE; } else if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) { // Create a continuation for the incomplete float and its placeholder. nsresult rv = mBlock->SplitPlaceholder(*this, fc->mPlaceholder); if (NS_FAILED(rv)) return PR_FALSE; } else { // XXX We could deal with truncated frames better by breaking before // the associated placeholder NS_WARN_IF_FALSE(!NS_FRAME_IS_TRUNCATED(reflowStatus), "This situation currently leads to data not printing"); // Float is complete. We need to delete any leftover placeholders now. nsIFrame* nextPlaceholder = fc->mPlaceholder->GetNextInFlow(); if (nextPlaceholder) { nsHTMLContainerFrame* parent = static_cast<nsHTMLContainerFrame*>(nextPlaceholder->GetParent()); parent->DeleteNextInFlowChild(mPresContext, nextPlaceholder); } } } fc = fc->Next(); } return PR_TRUE; }
nsresult nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aReflowStatus, nscoord aContainingBlockWidth, nscoord aContainingBlockHeight, PRBool aConstrainHeight, PRBool aCBWidthChanged, PRBool aCBHeightChanged, nsOverflowAreas* aOverflowAreas) { nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; PRBool reflowAll = aReflowState.ShouldReflowAllKids(); nsIFrame* kidFrame; nsOverflowContinuationTracker tracker(aPresContext, aDelegatingFrame, PR_TRUE); for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { PRBool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) || FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged); if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) { // Reflow the frame nsReflowStatus kidStatus = NS_FRAME_COMPLETE; ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState, aContainingBlockWidth, aContainingBlockHeight, aConstrainHeight, kidFrame, kidStatus, aOverflowAreas); nsIFrame* nextFrame = kidFrame->GetNextInFlow(); if (!NS_FRAME_IS_FULLY_COMPLETE(kidStatus)) { // Need a continuation if (!nextFrame) { nsresult rv = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, aDelegatingFrame, &nextFrame); NS_ENSURE_SUCCESS(rv, rv); } // Add it as an overflow container. //XXXfr This is a hack to fix some of our printing dataloss. // See bug 154892. Not sure how to do it "right" yet; probably want // to keep continuations within an nsAbsoluteContainingBlock eventually. tracker.Insert(nextFrame, kidStatus); NS_MergeReflowStatusInto(&reflowStatus, kidStatus); } else { // Delete any continuations if (nextFrame) { tracker.Finish(kidFrame); static_cast<nsContainerFrame*>(nextFrame->GetParent()) ->DeleteNextInFlowChild(aPresContext, nextFrame, PR_TRUE); } } } else { tracker.Skip(kidFrame, reflowStatus); if (aOverflowAreas) { aDelegatingFrame->ConsiderChildOverflow(*aOverflowAreas, kidFrame); } } // Make a CheckForInterrupt call, here, not just HasPendingInterrupt. That // will make sure that we end up reflowing aDelegatingFrame in cases when // one of our kids interrupted. Otherwise we'd set the dirty or // dirty-children bit on the kid in the condition below, and then when // reflow completes and we go to mark dirty bits on all ancestors of that // kid we'll immediately bail out, because the kid already has a dirty bit. // In particular, we won't set any dirty bits on aDelegatingFrame, so when // the following reflow happens we won't reflow the kid in question. This // might be slightly suboptimal in cases where |kidFrame| itself did not // interrupt, since we'll trigger a reflow of it too when it's not strictly // needed. But the logic to not do that is enough more complicated, and // the case enough of an edge case, that this is probably better. if (kidNeedsReflow && aPresContext->CheckForInterrupt(aDelegatingFrame)) { if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) { kidFrame->AddStateBits(NS_FRAME_IS_DIRTY); } else { kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); } } } // Abspos frames can't cause their parent to be incomplete, // only overflow incomplete. if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) NS_FRAME_SET_OVERFLOW_INCOMPLETE(reflowStatus); NS_MergeReflowStatusInto(&aReflowStatus, reflowStatus); return NS_OK; }
PRBool nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus, PRBool aForceFit) { aReflowStatus = NS_FRAME_COMPLETE; // Save away the Y coordinate before placing the float. We will // restore mY at the end after placing the float. This is // necessary because any adjustments to mY during the float // placement are for the float only, not for any non-floating // content. nscoord saveY = mY; nsPlaceholderFrame* placeholder = aFloatCache->mPlaceholder; nsIFrame* floatFrame = placeholder->GetOutOfFlowFrame(); // Grab the float's display information const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay(); // The float's old region, so we can propagate damage. nsRect oldRegion = aFloatCache->mRegion; // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't // ``above'' another float that preceded it in the flow. mY = NS_MAX(mSpaceManager->GetLowestRegionTop() + BorderPadding().top, mY); // See if the float should clear any preceding floats... // XXX We need to mark this float somehow so that it gets reflowed // when floats are inserted before it. if (NS_STYLE_CLEAR_NONE != floatDisplay->mBreakType) { // XXXldb Does this handle vertical margins correctly? mY = ClearFloats(mY, floatDisplay->mBreakType); } // Get the band of available space GetAvailableSpace(mY, aForceFit); NS_ASSERTION(floatFrame->GetParent() == mBlock, "Float frame has wrong parent"); // Reflow the float nsMargin floatMargin; mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus); #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsRect region = floatFrame->GetRect(); nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("flowed float: "); nsFrame::ListTag(stdout, floatFrame); printf(" (%d,%d,%d,%d)\n", region.x, region.y, region.width, region.height); } #endif nsSize floatSize = floatFrame->GetSize() + nsSize(floatMargin.LeftRight(), floatMargin.TopBottom()); // Find a place to place the float. The CSS2 spec doesn't want // floats overlapping each other or sticking out of the containing // block if possible (CSS2 spec section 9.5.1, see the rule list). NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) || (NS_STYLE_FLOAT_RIGHT == floatDisplay->mFloats), "invalid float type"); // Can the float fit here? PRBool keepFloatOnSameLine = PR_FALSE; while (!CanPlaceFloat(floatSize, floatDisplay->mFloats, aForceFit)) { if (mAvailSpaceRect.height <= 0) { // No space, nowhere to put anything. mY = saveY; return PR_FALSE; } // Nope. try to advance to the next band. if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) { mY += mAvailSpaceRect.height; GetAvailableSpace(mY, aForceFit); } else { // This quirk matches the one in nsBlockFrame::ReflowFloat // IE handles float tables in a very special way // see if the previous float is also a table and has "align" nsFloatCache* fc = mCurrentLineFloats.Head(); nsIFrame* prevFrame = nsnull; while (fc) { if (fc->mPlaceholder->GetOutOfFlowFrame() == floatFrame) { break; } prevFrame = fc->mPlaceholder->GetOutOfFlowFrame(); fc = fc->Next(); } if(prevFrame) { //get the frame type if (nsGkAtoms::tableOuterFrame == prevFrame->GetType()) { //see if it has "align=" // IE makes a difference between align and he float property nsIContent* content = prevFrame->GetContent(); if (content) { // we're interested only if previous frame is align=left // IE messes things up when "right" (overlapping frames) if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::align, NS_LITERAL_STRING("left"), eIgnoreCase)) { keepFloatOnSameLine = PR_TRUE; // don't advance to next line (IE quirkie behaviour) // it breaks rule CSS2/9.5.1/1, but what the hell // since we cannot evangelize the world break; } } } } // the table does not fit anymore in this line so advance to next band mY += mAvailSpaceRect.height; GetAvailableSpace(mY, aForceFit); // reflow the float again now since we have more space // XXXldb We really don't need to Reflow in a loop, we just need // to ComputeSize in a loop (once ComputeSize depends on // availableWidth, which should make this work again). mBlock->ReflowFloat(*this, placeholder, floatMargin, aReflowStatus); // Get the floats bounding box and margin information floatSize = floatFrame->GetSize() + nsSize(floatMargin.LeftRight(), floatMargin.TopBottom()); } } // If the float is continued, it will get the same absolute x value as its prev-in-flow // We don't worry about the geometry of the prev in flow, let the continuation // place and size itself as required. // Assign an x and y coordinate to the float. Note that the x,y // coordinates are computed <b>relative to the translation in the // spacemanager</b> which means that the impacted region will be // <b>inside</b> the border/padding area. PRBool isLeftFloat; nscoord floatX, floatY; if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { isLeftFloat = PR_TRUE; floatX = mAvailSpaceRect.x; } else { isLeftFloat = PR_FALSE; if (!keepFloatOnSameLine) { floatX = mAvailSpaceRect.XMost() - floatSize.width; } else { // this is the IE quirk (see few lines above) // the table is kept in the same line: don't let it overlap the // previous float floatX = mAvailSpaceRect.x; } } *aIsLeftFloat = isLeftFloat; const nsMargin& borderPadding = BorderPadding(); floatY = mY - borderPadding.top; if (floatY < 0) { // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not // be higher than the top of its containing block." (Since the // containing block is the content edge of the block box, this // means the margin edge of the float can't be higher than the // content edge of the block that contains it.) floatY = 0; } // Place the float in the space manager // if the float split, then take up all of the vertical height if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE != mContentArea.height)) { floatSize.height = PR_MAX(floatSize.height, mContentArea.height - floatY); } nsRect region(floatX, floatY, floatSize.width, floatSize.height); // Don't send rectangles with negative margin-box width or height to // the space manager; it can't deal with them. if (region.width < 0) { // Preserve the right margin-edge for left floats and the left // margin-edge for right floats if (isLeftFloat) { region.x = region.XMost(); } region.width = 0; } if (region.height < 0) { region.height = 0; } #ifdef DEBUG nsresult rv = #endif mSpaceManager->AddRectRegion(floatFrame, region); NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad float placement"); // Save away the floats region in the spacemanager, after making // it relative to the containing block's frame instead of relative // to the spacemanager translation (which is inset by the // border+padding). // XXX Maybe RecoverFloats should calc/add in the borderPadding itself? // It's kind of confusing to have the spacemanager translation be different // depending on what stage of reflow we're in. aFloatCache->mRegion = region + nsPoint(borderPadding.left, borderPadding.top); // If the float's dimensions have changed, note the damage in the // space manager. if (aFloatCache->mRegion != oldRegion) { // XXXwaterson conservative: we could probably get away with noting // less damage; e.g., if only height has changed, then only note the // area into which the float has grown or from which the float has // shrunk. nscoord top = NS_MIN(region.y, oldRegion.y); nscoord bottom = NS_MAX(region.YMost(), oldRegion.YMost()); mSpaceManager->IncludeInDamage(top, bottom); } #ifdef NOISY_SPACEMANAGER nscoord tx, ty; mSpaceManager->GetTranslation(tx, ty); nsFrame::ListTag(stdout, mBlock); printf(": FlowAndPlaceFloat: AddRectRegion: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n", tx, ty, mSpaceManagerX, mSpaceManagerY, aFloatCache->mRegion.x, aFloatCache->mRegion.y, aFloatCache->mRegion.width, aFloatCache->mRegion.height); #endif // Calculate the actual origin of the float frame's border rect // relative to the parent block; floatX/Y must be converted from space-manager // coordinates to parent coordinates, and the margin must be added in // to get the border rect nsPoint origin(borderPadding.left + floatMargin.left + floatX, borderPadding.top + floatMargin.top + floatY); // If float is relatively positioned, factor that in as well origin += floatFrame->GetRelativeOffset(floatDisplay); // Position the float and make sure and views are properly // positioned. We need to explicitly position its child views as // well, since we're moving the float after flowing it. floatFrame->SetPosition(origin); nsContainerFrame::PositionFrameView(floatFrame); nsContainerFrame::PositionChildViews(floatFrame); // Update the float combined area state nsRect combinedArea = floatFrame->GetOverflowRect() + origin; // XXX Floats should really just get invalidated here if necessary mFloatCombinedArea.UnionRect(combinedArea, mFloatCombinedArea); // Now restore mY mY = saveY; #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsRect r = floatFrame->GetRect(); nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("placed float: "); nsFrame::ListTag(stdout, floatFrame); printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); } #endif return PR_TRUE; }
nsresult nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsIFrame* aFrame, nsReflowStatus& aStatus) { nsLineLayout* lineLayout = aReflowState.mLineLayout; PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); PRBool pushedFrame; nsresult rv = lineLayout->ReflowFrame(aFrame, aStatus, nsnull, pushedFrame); if (NS_FAILED(rv)) { return rv; } if (NS_INLINE_IS_BREAK(aStatus)) { if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) { if (aFrame != mFrames.FirstChild()) { // Change break-before status into break-after since we have // already placed at least one child frame. This preserves the // break-type so that it can be propagated upward. aStatus = NS_FRAME_NOT_COMPLETE | NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER | (aStatus & NS_INLINE_BREAK_TYPE_MASK); PushFrames(aPresContext, aFrame, irs.mPrevFrame); } else { // Preserve reflow status when breaking-before our first child // and propagate it upward without modification. // Note: if we're lazily setting the frame pointer for our child // frames, then we need to set it now. Don't return and leave the // remaining child frames in our child list with the wrong parent // frame pointer... if (irs.mSetParentPointer) { if (irs.mLineContainer && irs.mLineContainer->GetPrevContinuation()) { ReparentFloatsForInlineChild(irs.mLineContainer, aFrame->GetNextSibling(), PR_TRUE); } for (nsIFrame* f = aFrame->GetNextSibling(); f; f = f->GetNextSibling()) { f->SetParent(this); } } } } else { // Break-after if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { nsIFrame* newFrame; rv = CreateNextInFlow(aPresContext, this, aFrame, newFrame); if (NS_FAILED(rv)) { return rv; } } nsIFrame* nextFrame = aFrame->GetNextSibling(); if (nextFrame) { NS_FRAME_SET_INCOMPLETE(aStatus); PushFrames(aPresContext, nextFrame, aFrame); } else if (nsnull != GetNextInFlow()) { // We must return an incomplete status if there are more child // frames remaining in a next-in-flow that follows this frame. nsInlineFrame* nextInFlow = (nsInlineFrame*) GetNextInFlow(); while (nsnull != nextInFlow) { if (nextInFlow->mFrames.NotEmpty()) { NS_FRAME_SET_INCOMPLETE(aStatus); break; } nextInFlow = (nsInlineFrame*) nextInFlow->GetNextInFlow(); } } } } else if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { if (nsGkAtoms::placeholderFrame == aFrame->GetType()) { nsBlockReflowState* blockRS = lineLayout->mBlockRS; blockRS->mBlock->SplitPlaceholder(*blockRS, aFrame); // Allow the parent to continue reflowing aStatus = NS_FRAME_COMPLETE; } else { nsIFrame* newFrame; rv = CreateNextInFlow(aPresContext, this, aFrame, newFrame); if (NS_FAILED(rv)) { return rv; } if (!reflowingFirstLetter) { nsIFrame* nextFrame = aFrame->GetNextSibling(); if (nextFrame) { PushFrames(aPresContext, nextFrame, aFrame); } } } } return rv; }
nsresult nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, InlineReflowState& irs, nsHTMLReflowMetrics& aMetrics, nsReflowStatus& aStatus) { nsresult rv = NS_OK; aStatus = NS_FRAME_COMPLETE; nsLineLayout* lineLayout = aReflowState.mLineLayout; PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection); nscoord leftEdge = 0; // Don't offset by our start borderpadding if we have a prev continuation or // if we're in the last part of an {ib} split. if (!GetPrevContinuation() && !nsLayoutUtils::FrameIsInLastPartOfIBSplit(this)) { leftEdge = ltr ? aReflowState.mComputedBorderPadding.left : aReflowState.mComputedBorderPadding.right; } nscoord availableWidth = aReflowState.availableWidth; NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE, "should no longer use available widths"); // Subtract off left and right border+padding from availableWidth availableWidth -= leftEdge; availableWidth -= ltr ? aReflowState.mComputedBorderPadding.right : aReflowState.mComputedBorderPadding.left; lineLayout->BeginSpan(this, &aReflowState, leftEdge, leftEdge + availableWidth); // First reflow our current children nsIFrame* frame = mFrames.FirstChild(); PRBool done = PR_FALSE; while (nsnull != frame) { PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); // Check if we should lazily set the child frame's parent pointer if (irs.mSetParentPointer) { PRBool havePrevBlock = irs.mLineContainer && irs.mLineContainer->GetPrevContinuation(); // If our block is the first in flow, then any floats under the pulled // frame must already belong to our block. if (havePrevBlock) { // This has to happen before we update frame's parent; we need to // know frame's ancestry under its old block. // The blockChildren.ContainsFrame check performed by // ReparentFloatsForInlineChild here may be slow, but we can't // easily avoid it because we don't know where 'frame' originally // came from. If we really really have to optimize this we could // cache whether frame->GetParent() is under its containing blocks // overflowList or not. ReparentFloatsForInlineChild(irs.mLineContainer, frame, PR_FALSE); } frame->SetParent(this); // We also need to check if frame has a next-in-flow. If it does, then set // its parent frame pointer, too. Otherwise, if we reflow frame and it's // complete we'll fail when deleting its next-in-flow which is no longer // needed. This scenario doesn't happen often, but it can happen nsIFrame* nextInFlow = frame->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { // Since we only do lazy setting of parent pointers for the frame's // initial reflow, this frame can't have a next-in-flow. That means // the continuing child frame must be in our child list as well. If // not, then something is wrong NS_ASSERTION(mFrames.ContainsFrame(nextInFlow), "unexpected flow"); if (havePrevBlock) { ReparentFloatsForInlineChild(irs.mLineContainer, nextInFlow, PR_FALSE); } nextInFlow->SetParent(this); } // Fix the parent pointer for ::first-letter child frame next-in-flows, // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042). nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame); if (realFrame->GetType() == nsGkAtoms::letterFrame) { nsIFrame* child = realFrame->GetFirstChild(nsnull); if (child) { NS_ASSERTION(child->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); nsIFrame* nextInFlow = child->GetNextInFlow(); for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(nextInFlow->GetType() == nsGkAtoms::textFrame, "unexpected frame type"); if (mFrames.ContainsFrame(nextInFlow)) { nextInFlow->SetParent(this); } else { #ifdef DEBUG // Once we find a next-in-flow that isn't ours none of the // remaining next-in-flows should be either. for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) { NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow), "unexpected letter frame flow"); } #endif break; } } } } } rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_FAILED(rv)) { done = PR_TRUE; break; } if (NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { done = PR_TRUE; break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } // Attempt to pull frames from our next-in-flow until we can't if (!done && (nsnull != GetNextInFlow())) { while (!done) { PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK(); PRBool isComplete; if (!frame) { // Could be non-null if we pulled a first-letter frame and // it created a continuation, since we don't push those. frame = PullOneFrame(aPresContext, irs, &isComplete); } #ifdef NOISY_PUSHING printf("%p pulled up %p\n", this, frame); #endif if (nsnull == frame) { if (!isComplete) { aStatus = NS_FRAME_NOT_COMPLETE; } break; } rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus); if (NS_FAILED(rv)) { done = PR_TRUE; break; } if (NS_INLINE_IS_BREAK(aStatus) || (!reflowingFirstLetter && NS_FRAME_IS_NOT_COMPLETE(aStatus))) { done = PR_TRUE; break; } irs.mPrevFrame = frame; frame = frame->GetNextSibling(); } } #ifdef DEBUG if (NS_FRAME_IS_COMPLETE(aStatus)) { // We can't be complete AND have overflow frames! NS_ASSERTION(!GetOverflowFrames(), "whoops"); } #endif // If after reflowing our children they take up no area then make // sure that we don't either. // // Note: CSS demands that empty inline elements still affect the // line-height calculations. However, continuations of an inline // that are empty we force to empty so that things like collapsed // whitespace in an inline element don't affect the line-height. aMetrics.width = lineLayout->EndSpan(this); // Compute final width. // Make sure to not include our start border and padding if we have a prev // continuation or if we're in the last part of an {ib} split. if (!GetPrevContinuation() && !nsLayoutUtils::FrameIsInLastPartOfIBSplit(this)) { aMetrics.width += ltr ? aReflowState.mComputedBorderPadding.left : aReflowState.mComputedBorderPadding.right; } /* * We want to only apply the end border and padding if we're the last * continuation and not in the first part of an {ib} split. To be the last * continuation we have to be complete (so that we won't get a next-in-flow) * and have no non-fluid continuations on our continuation chain. */ if (NS_FRAME_IS_COMPLETE(aStatus) && !GetLastInFlow()->GetNextContinuation() && !nsLayoutUtils::FrameIsInFirstPartOfIBSplit(this)) { aMetrics.width += ltr ? aReflowState.mComputedBorderPadding.right : aReflowState.mComputedBorderPadding.left; } nsLayoutUtils::SetFontFromStyle(aReflowState.rendContext, mStyleContext); nsCOMPtr<nsIFontMetrics> fm; aReflowState.rendContext->GetFontMetrics(*getter_AddRefs(fm)); if (fm) { // Compute final height of the frame. // // Do things the standard css2 way -- though it's hard to find it // in the css2 spec! It's actually found in the css1 spec section // 4.4 (you will have to read between the lines to really see // it). // // The height of our box is the sum of our font size plus the top // and bottom border and padding. The height of children do not // affect our height. fm->GetMaxAscent(aMetrics.ascent); fm->GetHeight(aMetrics.height); } else { NS_WARNING("Cannot get font metrics - defaulting sizes to 0"); aMetrics.ascent = aMetrics.height = 0; } aMetrics.ascent += aReflowState.mComputedBorderPadding.top; aMetrics.height += aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; // For now our overflow area is zero. The real value will be // computed in |nsLineLayout::RelativePositionFrames|. aMetrics.mOverflowArea.SetRect(0, 0, 0, 0); #ifdef NOISY_FINAL_SIZE ListTag(stdout); printf(": metrics=%d,%d ascent=%d\n", aMetrics.width, aMetrics.height, aMetrics.ascent); #endif return rv; }