/** * Attempt to place the block frame within the available space. If * it fits, apply horizontal positioning (CSS 10.3.3), collapse * margins (CSS2 8.3.1). Also apply relative positioning. */ bool nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, bool aForceFit, nsLineBox* aLine, nsCollapsingMargin& aBottomMarginResult, nsRect& aInFlowBounds, nsOverflowAreas& aOverflowAreas, nsReflowStatus aReflowStatus) { // Compute collapsed bottom margin value. if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { aBottomMarginResult = mMetrics.mCarriedOutBottomMargin; aBottomMarginResult.Include(aReflowState.mComputedMargin.bottom); } else { // The used bottom-margin is set to zero above a break. aBottomMarginResult.Zero(); } nscoord x = mX; nscoord y = mY; nscoord backupContainingBlockAdvance = 0; // Check whether the block's bottom margin collapses with its top // margin. See CSS 2.1 section 8.3.1; those rules seem to match // nsBlockFrame::IsEmpty(). Any such block must have zero height so // check that first. Note that a block can have clearance and still // have adjoining top/bottom margins, because the clearance goes // above the top margin. // Mark the frame as non-dirty; it has been reflowed (or we wouldn't // be here), and we don't want to assert in CachedIsEmpty() mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY); bool empty = 0 == mMetrics.height && aLine->CachedIsEmpty(); if (empty) { // Collapse the bottom margin with the top margin that was already // applied. aBottomMarginResult.Include(mTopMargin); #ifdef NOISY_VERTICAL_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": "); nsFrame::ListTag(stdout, mFrame); printf(" -- collapsing top & bottom margin together; y=%d spaceY=%d\n", y, mSpace.y); #endif // Section 8.3.1 of CSS 2.1 says that blocks with adjoining // top/bottom margins whose top margin collapses with their // parent's top margin should have their top border-edge at the // top border-edge of their parent. We actually don't have to do // anything special to make this happen. In that situation, // nsBlockFrame::ShouldApplyTopMargin will have returned false, // and mTopMargin and aClearance will have been zero in // ReflowBlock. // If we did apply our top margin, but now we're collapsing it // into the bottom margin, we need to back up the containing // block's y-advance by our top margin so that it doesn't get // counted twice. Note that here we're allowing the line's bounds // to become different from the block's position; we do this // because the containing block will place the next line at the // line's YMost, and it must place the next line at a different // point from where this empty block will be. backupContainingBlockAdvance = mTopMargin.get(); } // See if the frame fit. If it's the first frame or empty then it // always fits. If the height is unconstrained then it always fits, // even if there's some sort of integer overflow that makes y + // mMetrics.height appear to go beyond the available height. if (!empty && !aForceFit && mSpace.height != NS_UNCONSTRAINEDSIZE) { nscoord yMost = y - backupContainingBlockAdvance + mMetrics.height; if (yMost > mSpace.YMost()) { // didn't fit, we must acquit. mFrame->DidReflow(mPresContext, &aReflowState, NS_FRAME_REFLOW_FINISHED); return false; } } aInFlowBounds = nsRect(x, y - backupContainingBlockAdvance, mMetrics.width, mMetrics.height); // Apply CSS relative positioning const nsStyleDisplay* styleDisp = mFrame->GetStyleDisplay(); if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) { x += aReflowState.mComputedOffsets.left; y += aReflowState.mComputedOffsets.top; } // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, &aReflowState, mMetrics, x, y, 0); aOverflowAreas = mMetrics.mOverflowAreas + nsPoint(x, y); return true; }
/** * Attempt to place the block frame within the available space. If * it fits, apply inline-dir ("horizontal") positioning (CSS 10.3.3), * collapse margins (CSS2 8.3.1). Also apply relative positioning. */ bool nsBlockReflowContext::PlaceBlock(const nsHTMLReflowState& aReflowState, bool aForceFit, nsLineBox* aLine, nsCollapsingMargin& aBEndMarginResult, nsOverflowAreas& aOverflowAreas, nsReflowStatus aReflowStatus) { // Compute collapsed block-end margin value. WritingMode wm = aReflowState.GetWritingMode(); WritingMode parentWM = mMetrics.GetWritingMode(); if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { aBEndMarginResult = mMetrics.mCarriedOutBEndMargin; aBEndMarginResult.Include(aReflowState.ComputedLogicalMargin(). ConvertTo(parentWM, wm).BEnd(parentWM)); } else { // The used block-end-margin is set to zero before a break. aBEndMarginResult.Zero(); } nscoord backupContainingBlockAdvance = 0; // Check whether the block's block-end margin collapses with its block-start // margin. See CSS 2.1 section 8.3.1; those rules seem to match // nsBlockFrame::IsEmpty(). Any such block must have zero block-size so // check that first. Note that a block can have clearance and still // have adjoining block-start/end margins, because the clearance goes // above the block-start margin. // Mark the frame as non-dirty; it has been reflowed (or we wouldn't // be here), and we don't want to assert in CachedIsEmpty() mFrame->RemoveStateBits(NS_FRAME_IS_DIRTY); bool empty = 0 == mMetrics.BSize(parentWM) && aLine->CachedIsEmpty(); if (empty) { // Collapse the block-end margin with the block-start margin that was // already applied. aBEndMarginResult.Include(mBStartMargin); #ifdef NOISY_BLOCKDIR_MARGINS printf(" "); nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": "); nsFrame::ListTag(stdout, mFrame); printf(" -- collapsing block start & end margin together; BStart=%d spaceBStart=%d\n", mBCoord, mSpace.BStart(mWritingMode)); #endif // Section 8.3.1 of CSS 2.1 says that blocks with adjoining // "top/bottom" (i.e. block-start/end) margins whose top margin collapses // with their parent's top margin should have their top border-edge at the // top border-edge of their parent. We actually don't have to do // anything special to make this happen. In that situation, // nsBlockFrame::ShouldApplyBStartMargin will have returned false, // and mBStartMargin and aClearance will have been zero in // ReflowBlock. // If we did apply our block-start margin, but now we're collapsing it // into the block-end margin, we need to back up the containing // block's bCoord-advance by our block-start margin so that it doesn't get // counted twice. Note that here we're allowing the line's bounds // to become different from the block's position; we do this // because the containing block will place the next line at the // line's BEnd, and it must place the next line at a different // point from where this empty block will be. backupContainingBlockAdvance = mBStartMargin.get(); } // See if the frame fit. If it's the first frame or empty then it // always fits. If the block-size is unconstrained then it always fits, // even if there's some sort of integer overflow that makes bCoord + // mMetrics.BSize() appear to go beyond the available block size. if (!empty && !aForceFit && mSpace.BSize(mWritingMode) != NS_UNCONSTRAINEDSIZE) { nscoord bEnd = mBCoord - backupContainingBlockAdvance + mMetrics.BSize(mWritingMode); if (bEnd > mSpace.BEnd(mWritingMode)) { // didn't fit, we must acquit. mFrame->DidReflow(mPresContext, &aReflowState, nsDidReflowStatus::FINISHED); return false; } } aLine->SetBounds(mWritingMode, mICoord, mBCoord - backupContainingBlockAdvance, mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode), mContainerSize); WritingMode frameWM = mFrame->GetWritingMode(); LogicalPoint logPos = LogicalPoint(mWritingMode, mICoord, mBCoord). ConvertTo(frameWM, mWritingMode, mContainerSize - mMetrics.PhysicalSize()); // ApplyRelativePositioning in right-to-left writing modes needs to // know the updated frame width mFrame->SetSize(mWritingMode, mMetrics.Size(mWritingMode)); aReflowState.ApplyRelativePositioning(&logPos, mContainerSize); // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics, &aReflowState, frameWM, logPos, mContainerSize, 0); aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition(); return true; }