/* virtual */ void nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // Although a ruby text container may have continuations, returning // NS_FRAME_COMPLETE here is still safe, since its parent, ruby frame, // ignores the status, and continuations of the ruby base container // will take care of our continuations. aStatus = NS_FRAME_COMPLETE; WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); nscoord minBCoord = nscoord_MAX; nscoord maxBCoord = nscoord_MIN; // The container size is not yet known, so we use a dummy (0, 0) size. // The block-dir position will be corrected below after containerSize // is finalized. const nsSize dummyContainerSize; for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsIFrame* child = e.get(); MOZ_ASSERT(child->GetType() == nsGkAtoms::rubyTextFrame); LogicalRect rect = child->GetLogicalRect(lineWM, dummyContainerSize); LogicalMargin margin = child->GetLogicalUsedMargin(lineWM); nscoord blockStart = rect.BStart(lineWM) - margin.BStart(lineWM); minBCoord = std::min(minBCoord, blockStart); nscoord blockEnd = rect.BEnd(lineWM) + margin.BEnd(lineWM); maxBCoord = std::max(maxBCoord, blockEnd); } LogicalSize size(lineWM, mISize, 0); if (!mFrames.IsEmpty()) { if (MOZ_UNLIKELY(minBCoord > maxBCoord)) { // XXX When bug 765861 gets fixed, this warning should be upgraded. NS_WARNING("bad block coord"); minBCoord = maxBCoord = 0; } size.BSize(lineWM) = maxBCoord - minBCoord; nsSize containerSize = size.GetPhysicalSize(lineWM); for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsIFrame* child = e.get(); // We reflowed the child with a dummy container size, as the true size // was not yet known at that time. LogicalPoint pos = child->GetLogicalPosition(lineWM, dummyContainerSize); // Adjust block position to account for minBCoord, // then reposition child based on the true container width. pos.B(lineWM) -= minBCoord; // Relative positioning hasn't happened yet. // So MovePositionBy should not be used here. child->SetPosition(lineWM, pos, containerSize); nsContainerFrame::PlaceFrameView(child); } } aDesiredSize.SetSize(lineWM, size); }
nsresult nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect, WritingMode aWM, const nsSize& aContainerSize) { CHECK_BLOCK_AND_LINE_DIR(aWM); NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!"); NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!"); FloatInfo info(aFloatFrame, mLineLeft, mBlockStart, aMarginRect, aWM, aContainerSize); // Set mLeftBEnd and mRightBEnd. if (HasAnyFloats()) { FloatInfo &tail = mFloats[mFloats.Length() - 1]; info.mLeftBEnd = tail.mLeftBEnd; info.mRightBEnd = tail.mRightBEnd; } else { info.mLeftBEnd = nscoord_MIN; info.mRightBEnd = nscoord_MIN; } StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM); MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right, "Unexpected float style!"); nscoord& sideBEnd = floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd; nscoord thisBEnd = info.BEnd(); if (thisBEnd > sideBEnd) sideBEnd = thisBEnd; if (!mFloats.AppendElement(info)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; }
nsresult nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect, WritingMode aWM, nscoord aContainerWidth) { NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!"); NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!"); FloatInfo info(aFloatFrame, aWM, aMarginRect + mOffset); // Set mLeftBEnd and mRightBEnd. if (HasAnyFloats()) { FloatInfo &tail = mFloats[mFloats.Length() - 1]; info.mLeftBEnd = tail.mLeftBEnd; info.mRightBEnd = tail.mRightBEnd; } else { info.mLeftBEnd = nscoord_MIN; info.mRightBEnd = nscoord_MIN; } uint8_t floatStyle = aFloatFrame->StyleDisplay()->mFloats; NS_ASSERTION(floatStyle == NS_STYLE_FLOAT_LEFT || floatStyle == NS_STYLE_FLOAT_RIGHT, "unexpected float"); nscoord& sideBEnd = ((floatStyle == NS_STYLE_FLOAT_LEFT) == aWM.IsBidiLTR()) ? info.mLeftBEnd : info.mRightBEnd; nscoord thisBEnd = info.mRect.BEnd(aWM); if (thisBEnd > sideBEnd) sideBEnd = thisBEnd; if (!mFloats.AppendElement(info)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; }
LogicalRect nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat, nscoord aContainerWidth) { LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerWidth); void* storedRegion = aFloat->Properties().Get(FloatRegionProperty()); if (storedRegion) { nsMargin margin = *static_cast<nsMargin*>(storedRegion); region.Inflate(aWM, LogicalMargin(aWM, margin)); } return region; }
nscoord nsFloatManager::GetLowestFloatTop(WritingMode aWM, nscoord aContainerWidth) const { if (mPushedLeftFloatPastBreak || mPushedRightFloatPastBreak) { return nscoord_MAX; } if (!HasAnyFloats()) { return nscoord_MIN; } FloatInfo fi = mFloats[mFloats.Length() - 1]; LogicalRect rect = fi.mRect.ConvertTo(aWM, fi.mWritingMode, aContainerWidth); LogicalPoint offset = mOffset.ConvertTo(aWM, mWritingMode, 0); return rect.BStart(aWM) - offset.B(aWM); }
LogicalRect nsGridContainerFrame::ContainingBlockForAbsPos( const WritingMode& aWM, const GridArea& aArea, const nsTArray<TrackSize>& aColSizes, const nsTArray<TrackSize>& aRowSizes, const LogicalPoint& aGridOrigin, const LogicalRect& aGridCB) const { nscoord i = aGridCB.IStart(aWM); nscoord b = aGridCB.BStart(aWM); nscoord iSize = aGridCB.ISize(aWM); nscoord bSize = aGridCB.BSize(aWM); aArea.mCols.ToPositionAndLengthForAbsPos(aColSizes, aGridOrigin.I(aWM), &i, &iSize); aArea.mRows.ToPositionAndLengthForAbsPos(aRowSizes, aGridOrigin.B(aWM), &b, &bSize); return LogicalRect(aWM, i, b, iSize, bSize); }
/** * Restore information about floats into the float manager for an * incremental reflow, and simultaneously push the floats by * |aDeltaBCoord|, which is the amount |aLine| was pushed relative to its * parent. The recovery of state is one of the things that makes * incremental reflow O(N^2) and this state should really be kept * around, attached to the frame tree. */ void nsBlockReflowState::RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord) { WritingMode wm = mReflowState.GetWritingMode(); if (aLine->HasFloats()) { // Place the floats into the space-manager again. Also slide // them, just like the regular frames on the line. nsFloatCache* fc = aLine->GetFirstFloat(); while (fc) { nsIFrame* floatFrame = fc->mFloat; if (aDeltaBCoord != 0) { floatFrame->MovePositionBy(nsPoint(0, aDeltaBCoord)); nsContainerFrame::PositionFrameView(floatFrame); nsContainerFrame::PositionChildViews(floatFrame); } #ifdef DEBUG if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { nscoord tI, tB; mFloatManager->GetTranslation(tI, tB); nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI, tB, mFloatManagerI, mFloatManagerB); nsFrame::ListTag(stdout, floatFrame); LogicalRect region = nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()); printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n", aDeltaBCoord, region.IStart(wm), region.BStart(wm), region.ISize(wm), region.BSize(wm)); } #endif mFloatManager->AddFloat(floatFrame, nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()), wm, ContainerSize()); fc = fc->Next(); } } else if (aLine->IsBlock()) { nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager, wm, ContainerSize()); } }
static nscoord GetBEndMarginClone(nsIFrame* aFrame, nsRenderingContext* aRenderingContext, const LogicalRect& aContentArea, WritingMode aWritingMode) { if (aFrame->StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_CLONE) { nsCSSOffsetState os(aFrame, aRenderingContext, aContentArea.Width(aWritingMode)); return os.ComputedLogicalMargin(). ConvertTo(aWritingMode, aFrame->GetWritingMode()).BEnd(aWritingMode); } return 0; }
static nscoord GetBEndMarginClone(nsIFrame* aFrame, nsRenderingContext* aRenderingContext, const LogicalRect& aContentArea, WritingMode aWritingMode) { if (aFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone) { SizeComputationInput os(aFrame, aRenderingContext, aWritingMode, aContentArea.ISize(aWritingMode)); return os.ComputedLogicalMargin(). ConvertTo(aWritingMode, aFrame->GetWritingMode()).BEnd(aWritingMode); } return 0; }
void nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat, const LogicalRect& aRegion, const nsSize& aContainerSize) { nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize); nsRect rect = aFloat->GetRect(); FrameProperties props = aFloat->Properties(); if (region.IsEqualEdges(rect)) { props.Delete(FloatRegionProperty()); } else { nsMargin* storedMargin = props.Get(FloatRegionProperty()); if (!storedMargin) { storedMargin = new nsMargin(); props.Set(FloatRegionProperty(), storedMargin); } *storedMargin = region - rect; } }
void nsBlockReflowState::ComputeReplacedBlockOffsetsForFloats( nsIFrame* aFrame, const LogicalRect& aFloatAvailableSpace, nscoord& aIStartResult, nscoord& aIEndResult) const { WritingMode wm = mReflowState.GetWritingMode(); // The frame is clueless about the float manager and therefore we // only give it free space. An example is a table frame - the // tables do not flow around floats. // However, we can let its margins intersect floats. NS_ASSERTION(aFloatAvailableSpace.IStart(wm) >= mContentArea.IStart(wm), "bad avail space rect inline-coord"); NS_ASSERTION(aFloatAvailableSpace.ISize(wm) == 0 || aFloatAvailableSpace.IEnd(wm) <= mContentArea.IEnd(wm), "bad avail space rect inline-size"); nscoord iStartOffset, iEndOffset; if (aFloatAvailableSpace.ISize(wm) == mContentArea.ISize(wm)) { // We don't need to compute margins when there are no floats around. iStartOffset = 0; iEndOffset = 0; } else { LogicalMargin frameMargin(wm); nsCSSOffsetState os(aFrame, mReflowState.rendContext, wm, mContentArea.ISize(wm)); frameMargin = os.ComputedLogicalMargin().ConvertTo(wm, aFrame->GetWritingMode()); nscoord iStartFloatIOffset = aFloatAvailableSpace.IStart(wm) - mContentArea.IStart(wm); iStartOffset = std::max(iStartFloatIOffset, frameMargin.IStart(wm)) - frameMargin.IStart(wm); iStartOffset = std::max(iStartOffset, 0); // in case of negative margin nscoord iEndFloatIOffset = mContentArea.IEnd(wm) - aFloatAvailableSpace.IEnd(wm); iEndOffset = std::max(iEndFloatIOffset, frameMargin.IEnd(wm)) - frameMargin.IEnd(wm); iEndOffset = std::max(iEndOffset, 0); // in case of negative margin } aIStartResult = iStartOffset; aIEndResult = iEndOffset; }
nsresult nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect, WritingMode aWM, const nsSize& aContainerSize) { CHECK_BLOCK_DIR(aWM); NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!"); NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!"); FloatInfo info(aFloatFrame, aMarginRect.LineLeft(aWM, aContainerSize) + mLineLeft, aMarginRect.BStart(aWM) + mBlockStart, aMarginRect.ISize(aWM), aMarginRect.BSize(aWM)); // Set mLeftBEnd and mRightBEnd. if (HasAnyFloats()) { FloatInfo &tail = mFloats[mFloats.Length() - 1]; info.mLeftBEnd = tail.mLeftBEnd; info.mRightBEnd = tail.mRightBEnd; } else { info.mLeftBEnd = nscoord_MIN; info.mRightBEnd = nscoord_MIN; } uint8_t floatStyle = aFloatFrame->StyleDisplay()->mFloats; NS_ASSERTION(floatStyle == NS_STYLE_FLOAT_LEFT || floatStyle == NS_STYLE_FLOAT_RIGHT, "unexpected float"); nscoord& sideBEnd = floatStyle == NS_STYLE_FLOAT_LEFT ? info.mLeftBEnd : info.mRightBEnd; nscoord thisBEnd = info.BEnd(); if (thisBEnd > sideBEnd) sideBEnd = thisBEnd; if (!mFloats.AppendElement(info)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; }
void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) { /* It's the 'border-collapse' on the table that matters */ LogicalMargin borderPadding = GetLogicalUsedBorderAndPadding(aWM); nscoord bStartInset = borderPadding.BStart(aWM); nscoord bEndInset = borderPadding.BEnd(aWM); uint8_t verticalAlignFlags = GetVerticalAlign(); nscoord bSize = BSize(aWM); nsIFrame* firstKid = mFrames.FirstChild(); nsSize containerSize = mRect.Size(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has " "an inner cell frame"); LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize); nscoord childBSize = kidRect.BSize(aWM); // Vertically align the child nscoord kidBStart = 0; switch (verticalAlignFlags) { case NS_STYLE_VERTICAL_ALIGN_BASELINE: // Align the baselines of the child frame with the baselines of // other children in the same row which have 'vertical-align: baseline' kidBStart = bStartInset + aMaxAscent - GetCellBaseline(); break; case NS_STYLE_VERTICAL_ALIGN_TOP: // Align the top of the child frame with the top of the content area, kidBStart = bStartInset; break; case NS_STYLE_VERTICAL_ALIGN_BOTTOM: // Align the bottom of the child frame with the bottom of the content area, kidBStart = bSize - childBSize - bEndInset; break; default: case NS_STYLE_VERTICAL_ALIGN_MIDDLE: // Align the middle of the child frame with the middle of the content area, kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2; } // if the content is larger than the cell bsize, align from bstart kidBStart = std::max(0, kidBStart); if (kidBStart != kidRect.BStart(aWM)) { // Invalidate at the old position first firstKid->InvalidateFrameSubtree(); } firstKid->SetPosition(aWM, LogicalPoint(aWM, kidRect.IStart(aWM), kidBStart), containerSize); nsHTMLReflowMetrics desiredSize(aWM); desiredSize.SetSize(aWM, GetLogicalSize(aWM)); nsRect overflow(nsPoint(0,0), GetSize()); overflow.Inflate(GetBorderOverflow()); desiredSize.mOverflowAreas.SetAllTo(overflow); ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid); FinishAndStoreOverflow(&desiredSize); if (kidBStart != kidRect.BStart(aWM)) { // Make sure any child views are correctly positioned. We know the inner table // cell won't have a view nsContainerFrame::PositionChildViews(firstKid); // Invalidate new overflow rect firstKid->InvalidateFrameSubtree(); } if (HasView()) { nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(), desiredSize.VisualOverflow(), 0); } }
// Compute the amount of available space for reflowing a block frame // at the current Y coordinate. This method assumes that // GetAvailableSpace has already been called. void nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame, const nsStyleDisplay* aDisplay, const nsFlowAreaRect& aFloatAvailableSpace, bool aBlockAvoidsFloats, LogicalRect& aResult) { #ifdef REALLY_NOISY_REFLOW printf("CBAS frame=%p has floats %d\n", aFrame, aFloatAvailableSpace.mHasFloats); #endif WritingMode wm = mReflowState.GetWritingMode(); aResult.BStart(wm) = mBCoord; aResult.BSize(wm) = GetFlag(BRS_UNCONSTRAINEDBSIZE) ? NS_UNCONSTRAINEDSIZE : mReflowState.AvailableBSize() - mBCoord - GetBEndMarginClone(aFrame, mReflowState.rendContext, mContentArea, wm); // mBCoord might be greater than mBEndEdge if the block's top margin pushes // it off the page/column. Negative available height can confuse other code // and is nonsense in principle. // XXX Do we really want this condition to be this restrictive (i.e., // more restrictive than it used to be)? The |else| here is allowed // by the CSS spec, but only out of desperation given implementations, // and the behavior it leads to is quite undesirable (it can cause // things to become extremely narrow when they'd fit quite well a // little bit lower). Should the else be a quirk or something that // applies to a specific set of frame classes and no new ones? // If we did that, then for those frames where the condition below is // true but nsBlockFrame::BlockCanIntersectFloats is false, // nsBlockFrame::ISizeToClearPastFloats would need to use the // shrink-wrap formula, max(MIN_ISIZE, min(avail width, PREF_ISIZE)) // rather than just using MIN_ISIZE. NS_ASSERTION(nsBlockFrame::BlockCanIntersectFloats(aFrame) == !aBlockAvoidsFloats, "unexpected replaced width"); if (!aBlockAvoidsFloats) { if (aFloatAvailableSpace.mHasFloats) { // Use the float-edge property to determine how the child block // will interact with the float. const nsStyleBorder* borderStyle = aFrame->StyleBorder(); switch (borderStyle->mFloatEdge) { default: case NS_STYLE_FLOAT_EDGE_CONTENT: // content and only content does runaround of floats // The child block will flow around the float. Therefore // give it all of the available space. aResult.IStart(wm) = mContentArea.IStart(wm); aResult.ISize(wm) = mContentArea.ISize(wm); break; case NS_STYLE_FLOAT_EDGE_MARGIN: { // The child block's margins should be placed adjacent to, // but not overlap the float. aResult.IStart(wm) = aFloatAvailableSpace.mRect.IStart(wm); aResult.ISize(wm) = aFloatAvailableSpace.mRect.ISize(wm); } break; } } else { // Since there are no floats present the float-edge property // doesn't matter therefore give the block element all of the // available space since it will flow around the float itself. aResult.IStart(wm) = mContentArea.IStart(wm); aResult.ISize(wm) = mContentArea.ISize(wm); } } else { nscoord iStartOffset, iEndOffset; ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace.mRect, iStartOffset, iEndOffset); aResult.IStart(wm) = mContentArea.IStart(wm) + iStartOffset; aResult.ISize(wm) = mContentArea.ISize(wm) - iStartOffset - iEndOffset; } #ifdef REALLY_NOISY_REFLOW printf(" CBAS: result %d %d %d %d\n", aResult.IStart(wm), aResult.BStart(wm), aResult.ISize(wm), aResult.BSize(wm)); #endif }
void nsGridContainerFrame::ReflowChildren(GridItemCSSOrderIterator& aIter, const LogicalRect& aContentArea, const nsTArray<TrackSize>& aColSizes, const nsTArray<TrackSize>& aRowSizes, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { WritingMode wm = aReflowState.GetWritingMode(); const LogicalPoint gridOrigin(aContentArea.Origin(wm)); const nscoord gridWidth = aContentArea.Width(wm); nsPresContext* pc = PresContext(); for (; !aIter.AtEnd(); aIter.Next()) { nsIFrame* child = *aIter; GridArea* area = GetGridAreaForChild(child); MOZ_ASSERT(area && area->IsDefinite()); LogicalRect cb = ContainingBlockFor(wm, *area, aColSizes, aRowSizes); cb += gridOrigin; nsHTMLReflowState childRS(pc, aReflowState, child, cb.Size(wm)); const LogicalMargin margin = childRS.ComputedLogicalMargin(); if (childRS.ComputedBSize() == NS_AUTOHEIGHT) { // XXX the start of an align-self:stretch impl. Needs min-/max-bsize // clamping though, and check the prop value is actually 'stretch'! LogicalMargin bp = childRS.ComputedLogicalBorderPadding(); bp.ApplySkipSides(child->GetLogicalSkipSides()); nscoord bSize = cb.BSize(wm) - bp.BStartEnd(wm) - margin.BStartEnd(wm); childRS.SetComputedBSize(std::max(bSize, 0)); } LogicalPoint childPos = cb.Origin(wm); childPos.I(wm) += margin.IStart(wm); childPos.B(wm) += margin.BStart(wm); nsHTMLReflowMetrics childSize(childRS); nsReflowStatus childStatus; ReflowChild(child, pc, childSize, childRS, wm, childPos, gridWidth, 0, childStatus); FinishReflowChild(child, pc, childSize, &childRS, wm, childPos, gridWidth, 0); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); // XXX deal with 'childStatus' not being COMPLETE } if (IsAbsoluteContainer()) { nsFrameList children(GetChildList(GetAbsoluteListID())); if (!children.IsEmpty()) { LogicalMargin pad(aReflowState.ComputedLogicalPadding()); pad.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); // 'gridOrigin' is the origin of the grid (the start of the first track), // with respect to the grid container's padding-box (CB). const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm)); const LogicalRect gridCB(wm, 0, 0, aContentArea.ISize(wm) + pad.IStartEnd(wm), aContentArea.BSize(wm) + pad.BStartEnd(wm)); for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { nsIFrame* child = e.get(); GridArea* area = GetGridAreaForChild(child); MOZ_ASSERT(area); LogicalRect itemCB(ContainingBlockForAbsPos(wm, *area, aColSizes, aRowSizes, gridOrigin, gridCB)); // nsAbsoluteContainingBlock::Reflow uses physical coordinates. nsRect* cb = static_cast<nsRect*>(child->Properties().Get( GridItemContainingBlockRect())); if (!cb) { cb = new nsRect; child->Properties().Set(GridItemContainingBlockRect(), cb); } *cb = itemCB.GetPhysicalRect(wm, gridWidth); } // This rect isn't used at all for layout so we use it to optimize // away the virtual GetType() call in the callee in most cases. // @see nsAbsoluteContainingBlock::Reflow nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0); GetAbsoluteContainingBlock()->Reflow(this, pc, aReflowState, aStatus, dummyRect, true, true, true, // XXX could be optimized &aDesiredSize.mOverflowAreas); } } }
nsFlowAreaRect nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBOffset, BandInfoType aInfoType, nscoord aBSize, LogicalRect aContentArea, SavedState* aState, nscoord aContainerWidth) const { NS_ASSERTION(aBSize >= 0, "unexpected max block size"); NS_ASSERTION(aContentArea.ISize(aWM) >= 0, "unexpected content area inline size"); LogicalPoint offset = mOffset.ConvertTo(aWM, mWritingMode, 0); nscoord blockStart = aBOffset + offset.B(aWM); if (blockStart < nscoord_MIN) { NS_WARNING("bad value"); blockStart = nscoord_MIN; } // Determine the last float that we should consider. uint32_t floatCount; if (aState) { // Use the provided state. floatCount = aState->mFloatInfoCount; NS_ABORT_IF_FALSE(floatCount <= mFloats.Length(), "bad state"); } else { // Use our current state. floatCount = mFloats.Length(); } // If there are no floats at all, or we're below the last one, return // quickly. if (floatCount == 0 || (mFloats[floatCount-1].mLeftBEnd <= blockStart && mFloats[floatCount-1].mRightBEnd <= blockStart)) { return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBOffset, aContentArea.ISize(aWM), aBSize, false); } nscoord blockEnd; if (aBSize == nscoord_MAX) { // This warning (and the two below) are possible to hit on pages // with really large objects. NS_WARN_IF_FALSE(aInfoType == BAND_FROM_POINT, "bad height"); blockEnd = nscoord_MAX; } else { blockEnd = blockStart + aBSize; if (blockEnd < blockStart || blockEnd > nscoord_MAX) { NS_WARNING("bad value"); blockEnd = nscoord_MAX; } } nscoord inlineStart = offset.I(aWM) + aContentArea.IStart(aWM); nscoord inlineEnd = offset.I(aWM) + aContentArea.IEnd(aWM); if (inlineEnd < inlineStart) { NS_WARNING("bad value"); inlineEnd = inlineStart; } // Walk backwards through the floats until we either hit the front of // the list or we're above |blockStart|. bool haveFloats = false; for (uint32_t i = floatCount; i > 0; --i) { const FloatInfo &fi = mFloats[i-1]; if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) { // There aren't any more floats that could intersect this band. break; } if (fi.mRect.IsEmpty()) { // For compatibility, ignore floats with empty rects, even though it // disagrees with the spec. (We might want to fix this in the // future, though.) continue; } LogicalRect rect = fi.mRect.ConvertTo(aWM, fi.mWritingMode, aContainerWidth); nscoord floatBStart = rect.BStart(aWM); nscoord floatBEnd = rect.BEnd(aWM); if (blockStart < floatBStart && aInfoType == BAND_FROM_POINT) { // This float is below our band. Shrink our band's height if needed. if (floatBStart < blockEnd) { blockEnd = floatBStart; } } // If blockStart == blockEnd (which happens only with WIDTH_WITHIN_HEIGHT), // we include floats that begin at our 0-height vertical area. We // need to to this to satisfy the invariant that a // WIDTH_WITHIN_HEIGHT call is at least as narrow on both sides as a // BAND_WITHIN_POINT call beginning at its blockStart. else if (blockStart < floatBEnd && (floatBStart < blockEnd || (floatBStart == blockEnd && blockStart == blockEnd))) { // This float is in our band. // Shrink our band's height if needed. if (floatBEnd < blockEnd && aInfoType == BAND_FROM_POINT) { blockEnd = floatBEnd; } // Shrink our band's width if needed. if ((fi.mFrame->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) == aWM.IsBidiLTR()) { // A left float in an ltr block or a right float in an rtl block nscoord inlineEndEdge = rect.IEnd(aWM); if (inlineEndEdge > inlineStart) { inlineStart = inlineEndEdge; // Only set haveFloats to true if the float is inside our // containing block. This matches the spec for what some // callers want and disagrees for other callers, so we should // probably provide better information at some point. haveFloats = true; } } else { // A left float in an rtl block or a right float in an ltr block nscoord inlineStartEdge = rect.IStart(aWM); if (inlineStartEdge < inlineEnd) { inlineEnd = inlineStartEdge; // See above. haveFloats = true; } } } } nscoord blockSize = (blockEnd == nscoord_MAX) ? nscoord_MAX : (blockEnd - blockStart); return nsFlowAreaRect(aWM, inlineStart - offset.I(aWM), blockStart - offset.B(aWM), inlineEnd - inlineStart, blockSize, haveFloats); }
bool nsBlockReflowState::FlowAndPlaceFloat(nsIFrame* aFloat) { WritingMode wm = mReflowState.GetWritingMode(); // Save away the Y coordinate before placing the float. We will // restore mBCoord at the end after placing the float. This is // necessary because any adjustments to mBCoord during the float // placement are for the float only, not for any non-floating // content. AutoRestore<nscoord> restoreBCoord(mBCoord); // FIXME: Should give AutoRestore a getter for the value to avoid this. const nscoord saveBCoord = mBCoord; // Grab the float's display information const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay(); // The float's old region, so we can propagate damage. LogicalRect oldRegion = nsFloatManager::GetRegionFor(wm, aFloat, ContainerSize()); // 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. mBCoord = std::max(mFloatManager->GetLowestFloatTop(), mBCoord); // 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? mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm)); } // Get the band of available space nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(mBCoord); LogicalRect adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat); NS_ASSERTION(aFloat->GetParent() == mBlock, "Float frame has wrong parent"); nsCSSOffsetState offsets(aFloat, mReflowState.rendContext, wm, mReflowState.ComputedISize()); nscoord floatMarginISize = FloatMarginISize(mReflowState, adjustedAvailableSpace.ISize(wm), aFloat, offsets); LogicalMargin floatMargin(wm); // computed margin LogicalMargin floatOffsets(wm); nsReflowStatus reflowStatus; // If it's a floating first-letter, we need to reflow it before we // know how wide it is (since we don't compute which letters are part // of the first letter until reflow!). // We also need to do this early reflow if FloatMarginISize returned // an unconstrained inline-size, which can occur if the float had an // orthogonal writing mode and 'auto' block-size (in its mode). bool earlyFloatReflow = aFloat->GetType() == nsGkAtoms::letterFrame || floatMarginISize == NS_UNCONSTRAINEDSIZE; if (earlyFloatReflow) { mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, floatOffsets, false, reflowStatus); floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm); NS_ASSERTION(NS_FRAME_IS_COMPLETE(reflowStatus), "letter frames and orthogonal floats with auto block-size " "shouldn't break, and if they do now, then they're breaking " "at the wrong point"); } // 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). uint8_t floatStyle = floatDisplay->PhysicalFloats(wm); NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floatStyle) || (NS_STYLE_FLOAT_RIGHT == floatStyle), "invalid float type"); // Can the float fit here? bool keepFloatOnSameLine = false; // Are we required to place at least part of the float because we're // at the top of the page (to avoid an infinite loop of pushing and // breaking). bool mustPlaceFloat = mReflowState.mFlags.mIsTopOfPage && IsAdjacentWithTop(); for (;;) { if (mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE && floatAvailableSpace.mRect.BSize(wm) <= 0 && !mustPlaceFloat) { // No space, nowhere to put anything. PushFloatPastBreak(aFloat); return false; } if (CanPlaceFloat(floatMarginISize, floatAvailableSpace)) { // We found an appropriate place. break; } // Nope. try to advance to the next band. if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || eCompatibility_NavQuirks != mPresContext->CompatibilityMode() ) { mBCoord += floatAvailableSpace.mRect.BSize(wm); if (adjustedAvailableSpace.BSize(wm) != NS_UNCONSTRAINEDSIZE) { adjustedAvailableSpace.BSize(wm) -= floatAvailableSpace.mRect.BSize(wm); } floatAvailableSpace = GetFloatAvailableSpace(mBCoord); } else { // This quirk matches the one in nsBlockFrame::AdjustFloatAvailableSpace // 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 = nullptr; while (fc) { if (fc->mFloat == aFloat) { break; } prevFrame = fc->mFloat; 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 = 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 mBCoord += floatAvailableSpace.mRect.BSize(wm); // To match nsBlockFrame::AdjustFloatAvailableSpace, we have to // get a new width for the new band. floatAvailableSpace = GetFloatAvailableSpace(mBCoord); adjustedAvailableSpace = mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat); floatMarginISize = FloatMarginISize(mReflowState, adjustedAvailableSpace.ISize(wm), aFloat, offsets); } mustPlaceFloat = false; } // 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 inline and block dir coordinates to the float. We don't use // LineLeft() and LineRight() here, because we would only have to // convert the result back into this block's writing mode. LogicalPoint floatPos(wm); bool leftFloat = NS_STYLE_FLOAT_LEFT == floatStyle; if (leftFloat == wm.IsBidiLTR()) { floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm); } else { if (!keepFloatOnSameLine) { floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize; } 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 floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm); } } // 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.) floatPos.B(wm) = std::max(mBCoord, ContentBStart()); // Reflow the float after computing its vertical position so it knows // where to break. if (!earlyFloatReflow) { bool pushedDown = mBCoord != saveBCoord; mBlock->ReflowFloat(*this, adjustedAvailableSpace, aFloat, floatMargin, floatOffsets, pushedDown, reflowStatus); } if (aFloat->GetPrevInFlow()) floatMargin.BStart(wm) = 0; if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) floatMargin.BEnd(wm) = 0; // In the case that we're in columns and not splitting floats, we need // to check here that the float's height fit, and if it didn't, bail. // (controlled by the pref "layout.float-fragments-inside-column.enabled") // // Likewise, if none of the float fit, and it needs to be pushed in // its entirety to the next page (NS_FRAME_IS_TRUNCATED or // NS_INLINE_IS_BREAK_BEFORE), we need to do the same. if ((ContentBSize() != NS_UNCONSTRAINEDSIZE && !GetFlag(BRS_FLOAT_FRAGMENTS_INSIDE_COLUMN_ENABLED) && adjustedAvailableSpace.BSize(wm) == NS_UNCONSTRAINEDSIZE && !mustPlaceFloat && aFloat->BSize(wm) + floatMargin.BStartEnd(wm) > ContentBEnd() - floatPos.B(wm)) || NS_FRAME_IS_TRUNCATED(reflowStatus) || NS_INLINE_IS_BREAK_BEFORE(reflowStatus)) { PushFloatPastBreak(aFloat); return false; } // We can't use aFloat->ShouldAvoidBreakInside(mReflowState) here since // its mIsTopOfPage may be true even though the float isn't at the // top when floatPos.B(wm) > 0. if (ContentBSize() != NS_UNCONSTRAINEDSIZE && !mustPlaceFloat && (!mReflowState.mFlags.mIsTopOfPage || floatPos.B(wm) > 0) && NS_STYLE_PAGE_BREAK_AVOID == aFloat->StyleDisplay()->mBreakInside && (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus) || aFloat->BSize(wm) + floatMargin.BStartEnd(wm) > ContentBEnd() - floatPos.B(wm)) && !aFloat->GetPrevInFlow()) { PushFloatPastBreak(aFloat); return false; } // Calculate the actual origin of the float frame's border rect // relative to the parent block; the margin must be added in // to get the border rect LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm), floatMargin.BStart(wm) + floatPos.B(wm)); // If float is relatively positioned, factor that in as well nsHTMLReflowState::ApplyRelativePositioning(aFloat, wm, floatOffsets, &origin, ContainerSize()); // 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. bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin; if (moved) { aFloat->SetPosition(wm, origin, ContainerSize()); nsContainerFrame::PositionFrameView(aFloat); nsContainerFrame::PositionChildViews(aFloat); } // Update the float combined area state // XXX Floats should really just get invalidated here if necessary mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() + aFloat->GetPosition()); // Place the float in the float manager // calculate region LogicalRect region = nsFloatManager::CalculateRegionFor(wm, aFloat, floatMargin, ContainerSize()); // if the float split, then take up all of the vertical height if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) && (NS_UNCONSTRAINEDSIZE != ContentBSize())) { region.BSize(wm) = std::max(region.BSize(wm), ContentBSize() - floatPos.B(wm)); } DebugOnly<nsresult> rv = mFloatManager->AddFloat(aFloat, region, wm, ContainerSize()); MOZ_ASSERT(NS_SUCCEEDED(rv), "bad float placement"); // store region nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize()); // If the float's dimensions have changed, note the damage in the // float manager. if (!region.IsEqualEdges(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 blockStart = std::min(region.BStart(wm), oldRegion.BStart(wm)); nscoord blockEnd = std::max(region.BEnd(wm), oldRegion.BEnd(wm)); mFloatManager->IncludeInDamage(wm, blockStart, blockEnd); } if (!NS_FRAME_IS_FULLY_COMPLETE(reflowStatus)) { mBlock->SplitFloat(*this, aFloat, reflowStatus); } else { MOZ_ASSERT(!aFloat->GetNextInFlow()); } #ifdef NOISY_FLOATMANAGER nscoord tI, tB; mFloatManager->GetTranslation(tI, tB); nsIFrame::ListTag(stdout, mBlock); printf(": FlowAndPlaceFloat: AddFloat: tIB=%d,%d (%d,%d) {%d,%d,%d,%d}\n", tI, tB, mFloatManagerI, mFloatManagerB, region.IStart(wm), region.BStart(wm), region.ISize(wm), region.BSize(wm)); #endif #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsRect r = aFloat->GetRect(); nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("placed float: "); nsFrame::ListTag(stdout, aFloat); printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); } #endif return true; }
void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsRubyBaseContainerFrame* aBaseContainer, nsReflowStatus& aStatus) { WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowInput.AvailableISize(), aReflowInput.AvailableBSize()); WritingMode rubyWM = GetWritingMode(); NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM), "Ruby frame writing-mode shouldn't be orthogonal to its line"); AutoRubyTextContainerArray textContainers(aBaseContainer); const uint32_t rtcCount = textContainers.Length(); ReflowOutput baseMetrics(aReflowInput); bool pushedFrame; aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus, &baseMetrics, pushedFrame); if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) { if (aBaseContainer != mFrames.FirstChild()) { // Some segments may have been reflowed before, hence it is not // a break-before for the ruby container. aStatus = NS_INLINE_LINE_BREAK_AFTER(NS_FRAME_NOT_COMPLETE); PushChildren(aBaseContainer, aBaseContainer->GetPrevSibling()); aReflowInput.mLineLayout->SetDirtyNextLine(); } // This base container is not placed at all, we can skip all // text containers paired with it. return; } if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // It always promise that if the status is incomplete, there is a // break occurs. Break before has been processed above. However, // it is possible that break after happens with the frame reflow // completed. It happens if there is a force break at the end. MOZ_ASSERT(NS_INLINE_IS_BREAK_AFTER(aStatus)); // Find the previous sibling which we will // insert new continuations after. nsIFrame* lastChild; if (rtcCount > 0) { lastChild = textContainers.LastElement(); } else { lastChild = aBaseContainer; } // Create continuations for the base container nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer); // newBaseContainer is null if there are existing next-in-flows. // We only need to move and push if there were not. if (newBaseContainer) { // Move the new frame after all the text containers mFrames.RemoveFrame(newBaseContainer); mFrames.InsertFrame(nullptr, lastChild, newBaseContainer); // Create continuations for text containers nsIFrame* newLastChild = newBaseContainer; for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]); MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist " "if the corresponding rbc does not"); mFrames.RemoveFrame(newTextContainer); mFrames.InsertFrame(nullptr, newLastChild, newTextContainer); newLastChild = newTextContainer; } } if (lastChild != mFrames.LastChild()) { // Always push the next frame after the last child in this segment. // It is possible that we pulled it back before our next-in-flow // drain our overflow. PushChildren(lastChild->GetNextSibling(), lastChild); aReflowInput.mLineLayout->SetDirtyNextLine(); } } else { // If the ruby base container is reflowed completely, the line // layout will remove the next-in-flows of that frame. But the // line layout is not aware of the ruby text containers, hence // it is necessary to remove them here. for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* nextRTC = textContainers[i]->GetNextInFlow(); if (nextRTC) { nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true); } } } nscoord segmentISize = baseMetrics.ISize(lineWM); const nsSize dummyContainerSize; LogicalRect baseRect = aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize); // We need to position our rtc frames on one side or the other of the // base container's rect, using a coordinate space that's relative to // the ruby frame. Right now, the base container's rect's block-axis // position is relative to the block container frame containing the // lines, so we use 0 instead. (i.e. we assume that the base container // is adjacent to the ruby frame's block-start edge.) // XXX We may need to add border/padding here. See bug 1055667. baseRect.BStart(lineWM) = 0; // The rect for offsets of text containers. LogicalRect offsetRect = baseRect; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; WritingMode rtcWM = textContainer->GetWritingMode(); nsReflowStatus textReflowStatus; ReflowOutput textMetrics(aReflowInput); ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer, availSize.ConvertTo(rtcWM, lineWM)); // FIXME We probably shouldn't be using the same nsLineLayout for // the text containers. But it should be fine now as we are // not actually using this line layout to reflow something, // but just read the writing mode from it. textReflowInput.mLineLayout = aReflowInput.mLineLayout; textContainer->Reflow(aPresContext, textMetrics, textReflowInput, textReflowStatus); // Ruby text containers always return NS_FRAME_COMPLETE even when // they have continuations, because the breaking has already been // handled when reflowing the base containers. NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE, "Ruby text container must not break itself inside"); // The metrics is initialized with reflow state of this ruby frame, // hence the writing-mode is tied to rubyWM instead of rtcWM. LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM); textContainer->SetSize(lineWM, size); nscoord reservedISize = RubyUtils::GetReservedISize(textContainer); segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize); uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition; MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER || rubyPosition == NS_STYLE_RUBY_POSITION_UNDER); Maybe<LogicalSide> side; if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver)); } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder)); } else { // XXX inter-character support in bug 1055672 MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position"); } LogicalPoint position(lineWM); if (side.isSome()) { if (side.value() == eLogicalSideBStart) { offsetRect.BStart(lineWM) -= size.BSize(lineWM); offsetRect.BSize(lineWM) += size.BSize(lineWM); position = offsetRect.Origin(lineWM); } else if (side.value() == eLogicalSideBEnd) { position = offsetRect.Origin(lineWM) + LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM)); offsetRect.BSize(lineWM) += size.BSize(lineWM); } else { MOZ_ASSERT_UNREACHABLE("???"); } } // Using a dummy container-size here, so child positioning may not be // correct. We will fix it in nsLineLayout after the whole line is // reflowed. FinishReflowChild(textContainer, aPresContext, textMetrics, &textReflowInput, lineWM, position, dummyContainerSize, 0); } MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM), "Annotations should only be placed on the block directions"); nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM); if (deltaISize <= 0) { RubyUtils::ClearReservedISize(aBaseContainer); } else { RubyUtils::SetReservedISize(aBaseContainer, deltaISize); aReflowInput.mLineLayout->AdvanceICoord(deltaISize); } // Set block leadings of the base container nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM); nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM); // XXX When bug 765861 gets fixed, this warning should be upgraded. NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0, "Leadings should be non-negative (because adding " "ruby annotation can only increase the size)"); mBStartLeading = std::max(mBStartLeading, startLeading); mBEndLeading = std::max(mBEndLeading, endLeading); }
void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE, "Should have a precomputed inline-size!"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- bool reflowInner; bool reflowLegend; nsIFrame* legend = GetLegend(); nsIFrame* inner = GetInner(); if (aReflowState.ShouldReflowAllKids()) { reflowInner = inner != nullptr; reflowLegend = legend != nullptr; } else { reflowInner = inner && NS_SUBTREE_DIRTY(inner); reflowLegend = legend && NS_SUBTREE_DIRTY(legend); } // We don't allow fieldsets to break vertically. If we did, we'd // need logic here to push and pull overflow frames. // Since we're not applying our padding in this frame, we need to add it here // to compute the available width for our children. WritingMode wm = GetWritingMode(); WritingMode innerWM = inner ? inner->GetWritingMode() : wm; WritingMode legendWM = legend ? legend->GetWritingMode() : wm; LogicalSize innerAvailSize = aReflowState.ComputedSizeWithPadding(innerWM); LogicalSize legendAvailSize = aReflowState.ComputedSizeWithPadding(legendWM); innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) = NS_UNCONSTRAINEDSIZE; NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, inner, nsLayoutUtils::MIN_ISIZE) <= innerAvailSize.ISize(innerWM), "Bogus availSize.ISize; should be bigger"); NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext, legend, nsLayoutUtils::MIN_ISIZE) <= legendAvailSize.ISize(legendWM), "Bogus availSize.ISize; should be bigger"); // get our border and padding LogicalMargin border = aReflowState.ComputedLogicalBorderPadding() - aReflowState.ComputedLogicalPadding(); // Figure out how big the legend is if there is one. // get the legend's margin LogicalMargin legendMargin(wm); // reflow the legend only if needed Maybe<nsHTMLReflowState> legendReflowState; if (legend) { legendReflowState.emplace(aPresContext, aReflowState, legend, legendAvailSize); } if (reflowLegend) { nsHTMLReflowMetrics legendDesiredSize(aReflowState); // We'll move the legend to its proper place later, so the position // and containerSize passed here are unimportant. const nsSize dummyContainerSize; ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.Width(), legendDesiredSize.Height()); #endif // figure out the legend's rectangle legendMargin = legend->GetLogicalUsedMargin(wm); mLegendRect = LogicalRect(wm, 0, 0, legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm), legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm)); nscoord oldSpace = mLegendSpace; mLegendSpace = 0; if (mLegendRect.BSize(wm) > border.BStart(wm)) { // center the border on the legend mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm); } else { mLegendRect.BStart(wm) = (border.BStart(wm) - mLegendRect.BSize(wm)) / 2; } // if the legend space changes then we need to reflow the // content area as well. if (mLegendSpace != oldSpace && inner) { reflowInner = true; } FinishReflowChild(legend, aPresContext, legendDesiredSize, legendReflowState.ptr(), wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME); } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. legendMargin = legend->GetLogicalUsedMargin(wm); } // This containerSize is incomplete as yet: it does not include the size // of the |inner| frame itself. nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) + border.Size(wm)).GetPhysicalSize(wm); // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); // Our child is "height:100%" but we actually want its height to be reduced // by the amount of content-height the legend is eating up, unless our // height is unconstrained (in which case the child's will be too). if (aReflowState.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.SetComputedBSize( std::max(0, aReflowState.ComputedBSize() - mLegendSpace)); } if (aReflowState.ComputedMinBSize() > 0) { kidReflowState.ComputedMinBSize() = std::max(0, aReflowState.ComputedMinBSize() - mLegendSpace); } if (aReflowState.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowState.ComputedMaxBSize() = std::max(0, aReflowState.ComputedMaxBSize() - mLegendSpace); } nsHTMLReflowMetrics kidDesiredSize(kidReflowState, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace); // We don't know the correct containerSize until we have reflowed |inner|, // so we use a dummy value for now; FinishReflowChild will fix the position // if necessary. const nsSize dummyContainerSize; ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, wm, pt, dummyContainerSize, 0, aStatus); // Update containerSize to account for size of the inner frame, so that // FinishReflowChild can position it correctly. containerSize += kidDesiredSize.PhysicalSize(); FinishReflowChild(inner, aPresContext, kidDesiredSize, &kidReflowState, wm, pt, containerSize, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); } else if (inner) { // |inner| didn't need to be reflowed but we do need to include its size // in containerSize. containerSize += inner->GetSize(); } LogicalRect contentRect(wm); if (inner) { // We don't support margins on inner, so our content rect is just the // inner's border-box. (We don't really care about container size at this // point, as we'll figure out the actual positioning later.) contentRect = inner->GetLogicalRect(wm, containerSize); } // Our content rect must fill up the available width LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm); if (availSize.ISize(wm) > contentRect.ISize(wm)) { contentRect.ISize(wm) = innerAvailSize.ISize(wm); } if (legend) { // The legend is positioned inline-wards within the inner's content rect // (so that padding on the fieldset affects the legend position). LogicalRect innerContentRect = contentRect; innerContentRect.Deflate(wm, aReflowState.ComputedLogicalPadding()); // If the inner content rect is larger than the legend, we can align the // legend. if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) { int32_t align = static_cast<nsLegendFrame*> (legend->GetContentInsertionFrame())->GetAlign(); if (!wm.IsBidiLTR()) { if (align == NS_STYLE_TEXT_ALIGN_LEFT || align == NS_STYLE_TEXT_ALIGN_MOZ_LEFT) { align = NS_STYLE_TEXT_ALIGN_END; } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT || align == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) { align = NS_STYLE_TEXT_ALIGN_DEFAULT; } } switch (align) { case NS_STYLE_TEXT_ALIGN_END: mLegendRect.IStart(wm) = innerContentRect.IEnd(wm) - mLegendRect.ISize(wm); break; case NS_STYLE_TEXT_ALIGN_CENTER: case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: // Note: rounding removed; there doesn't seem to be any need mLegendRect.IStart(wm) = innerContentRect.IStart(wm) + (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2; break; default: mLegendRect.IStart(wm) = innerContentRect.IStart(wm); break; } } else { // otherwise make place for the legend mLegendRect.IStart(wm) = innerContentRect.IStart(wm); innerContentRect.ISize(wm) = mLegendRect.ISize(wm); contentRect.ISize(wm) = mLegendRect.ISize(wm) + aReflowState.ComputedLogicalPadding().IStartEnd(wm); } // place the legend LogicalRect actualLegendRect = mLegendRect; actualLegendRect.Deflate(wm, legendMargin); LogicalPoint actualLegendPos(actualLegendRect.Origin(wm)); // Note that legend's writing mode may be different from the fieldset's, // so we need to convert offsets before applying them to it (bug 1134534). LogicalMargin offsets = legendReflowState->ComputedLogicalOffsets(). ConvertTo(wm, legendReflowState->GetWritingMode()); nsHTMLReflowState::ApplyRelativePositioning(legend, wm, offsets, &actualLegendPos, containerSize); legend->SetPosition(wm, actualLegendPos, containerSize); nsContainerFrame::PositionFrameView(legend); nsContainerFrame::PositionChildViews(legend); } // Return our size and our result. LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm), mLegendSpace + border.BStartEnd(wm) + (inner ? inner->BSize(wm) : 0)); aDesiredSize.SetSize(wm, finalSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (legend) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend); } if (inner) { ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner); } // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); InvalidateFrame(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
DrawResult nsFieldSetFrame::PaintBorderBackground( nsDisplayListBuilder* aBuilder, nsRenderingContext& aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect) { // if the border is smaller than the legend. Move the border down // to be centered on the legend. // FIXME: This means border-radius clamping is incorrect; we should // override nsIFrame::GetBorderRadii. WritingMode wm = GetWritingMode(); nsRect rect = VisualBorderRectRelativeToSelf(); nscoord off = wm.IsVertical() ? rect.x : rect.y; rect += aPt; nsPresContext* presContext = PresContext(); uint32_t bgFlags = aBuilder->GetBackgroundPaintFlags(); PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages() ? PaintBorderFlags::SYNC_DECODE_IMAGES : PaintBorderFlags(); DrawResult result = nsCSSRendering::PaintBackground(presContext, aRenderingContext, this, aDirtyRect, rect, bgFlags); nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext, this, rect, aDirtyRect); if (nsIFrame* legend = GetLegend()) { css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart); nscoord legendBorderWidth = StyleBorder()->GetComputedBorderWidth(legendSide); // Use the rect of the legend frame, not mLegendRect, so we draw our // border under the legend's inline-start and -end margins. LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size()); // Compute clipRect using logical coordinates, so that the legend space // will be clipped out of the appropriate physical side depending on mode. LogicalRect clipRect = LogicalRect(wm, rect, rect.Size()); DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); gfxContext* gfx = aRenderingContext.ThebesContext(); int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); // draw inline-start portion of the block-start side of the border clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm); clipRect.BSize(wm) = legendBorderWidth; gfx->Save(); gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); result &= nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext, borderFlags); gfx->Restore(); // draw inline-end portion of the block-start side of the border clipRect = LogicalRect(wm, rect, rect.Size()); clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm); clipRect.IStart(wm) = legendRect.IEnd(wm); clipRect.BSize(wm) = legendBorderWidth; gfx->Save(); gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); result &= nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext, borderFlags); gfx->Restore(); // draw remainder of the border (omitting the block-start side) clipRect = LogicalRect(wm, rect, rect.Size()); clipRect.BStart(wm) += legendBorderWidth; clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth); gfx->Save(); gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); result &= nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext, borderFlags); gfx->Restore(); } else { result &= nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, nsRect(aPt, mRect.Size()), mStyleContext, borderFlags); } return result; }
nsFloatManager::FloatInfo::FloatInfo(nsIFrame* aFrame, nscoord aLineLeft, nscoord aBlockStart, const LogicalRect& aMarginRect, WritingMode aWM, const nsSize& aContainerSize) : mFrame(aFrame) , mRect(aMarginRect.LineLeft(aWM, aContainerSize) + aLineLeft, aMarginRect.BStart(aWM) + aBlockStart, aMarginRect.ISize(aWM), aMarginRect.BSize(aWM)) { MOZ_COUNT_CTOR(nsFloatManager::FloatInfo); const StyleShapeOutside& shapeOutside = mFrame->StyleDisplay()->mShapeOutside; if (shapeOutside.GetType() == StyleShapeSourceType::Box) { // Initialize shape-box reference rect. LogicalRect rect = aMarginRect; switch (shapeOutside.GetReferenceBox()) { case StyleShapeOutsideShapeBox::Content: rect.Deflate(aWM, mFrame->GetLogicalUsedPadding(aWM)); MOZ_FALLTHROUGH; case StyleShapeOutsideShapeBox::Padding: rect.Deflate(aWM, mFrame->GetLogicalUsedBorder(aWM)); MOZ_FALLTHROUGH; case StyleShapeOutsideShapeBox::Border: rect.Deflate(aWM, mFrame->GetLogicalUsedMargin(aWM)); break; case StyleShapeOutsideShapeBox::Margin: // Do nothing. rect is already a margin rect. break; case StyleShapeOutsideShapeBox::NoBox: MOZ_ASSERT_UNREACHABLE("Why don't we have a shape-box?"); break; } mShapeBoxRect.emplace(rect.LineLeft(aWM, aContainerSize) + aLineLeft, rect.BStart(aWM) + aBlockStart, rect.ISize(aWM), rect.BSize(aWM)); } }
nsFlowAreaRect nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBCoord, nscoord aBSize, BandInfoType aBandInfoType, ShapeType aShapeType, LogicalRect aContentArea, SavedState* aState, const nsSize& aContainerSize) const { CHECK_BLOCK_AND_LINE_DIR(aWM); NS_ASSERTION(aBSize >= 0, "unexpected max block size"); NS_ASSERTION(aContentArea.ISize(aWM) >= 0, "unexpected content area inline size"); nscoord blockStart = aBCoord + mBlockStart; if (blockStart < nscoord_MIN) { NS_WARNING("bad value"); blockStart = nscoord_MIN; } // Determine the last float that we should consider. uint32_t floatCount; if (aState) { // Use the provided state. floatCount = aState->mFloatInfoCount; MOZ_ASSERT(floatCount <= mFloats.Length(), "bad state"); } else { // Use our current state. floatCount = mFloats.Length(); } // If there are no floats at all, or we're below the last one, return // quickly. if (floatCount == 0 || (mFloats[floatCount-1].mLeftBEnd <= blockStart && mFloats[floatCount-1].mRightBEnd <= blockStart)) { return nsFlowAreaRect(aWM, aContentArea.IStart(aWM), aBCoord, aContentArea.ISize(aWM), aBSize, false); } nscoord blockEnd; if (aBSize == nscoord_MAX) { // This warning (and the two below) are possible to hit on pages // with really large objects. NS_WARNING_ASSERTION(aBandInfoType == BandInfoType::BandFromPoint, "bad height"); blockEnd = nscoord_MAX; } else { blockEnd = blockStart + aBSize; if (blockEnd < blockStart || blockEnd > nscoord_MAX) { NS_WARNING("bad value"); blockEnd = nscoord_MAX; } } nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize); nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize); if (lineRight < lineLeft) { NS_WARNING("bad value"); lineRight = lineLeft; } // Walk backwards through the floats until we either hit the front of // the list or we're above |blockStart|. bool haveFloats = false; for (uint32_t i = floatCount; i > 0; --i) { const FloatInfo &fi = mFloats[i-1]; if (fi.mLeftBEnd <= blockStart && fi.mRightBEnd <= blockStart) { // There aren't any more floats that could intersect this band. break; } if (fi.IsEmpty()) { // For compatibility, ignore floats with empty rects, even though it // disagrees with the spec. (We might want to fix this in the // future, though.) continue; } nscoord floatBStart = fi.BStart(aShapeType); nscoord floatBEnd = fi.BEnd(aShapeType); if (blockStart < floatBStart && aBandInfoType == BandInfoType::BandFromPoint) { // This float is below our band. Shrink our band's height if needed. if (floatBStart < blockEnd) { blockEnd = floatBStart; } } // If blockStart == blockEnd (which happens only with WidthWithinHeight), // we include floats that begin at our 0-height vertical area. We // need to do this to satisfy the invariant that a // WidthWithinHeight call is at least as narrow on both sides as a // BandFromPoint call beginning at its blockStart. else if (blockStart < floatBEnd && (floatBStart < blockEnd || (floatBStart == blockEnd && blockStart == blockEnd))) { // This float is in our band. // Shrink our band's width if needed. StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM); // When aBandInfoType is BandFromPoint, we're only intended to // consider a point along the y axis rather than a band. const nscoord bandBlockEnd = aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd; if (floatStyle == StyleFloat::Left) { // A left float nscoord lineRightEdge = fi.LineRight(aWM, aShapeType, blockStart, bandBlockEnd); if (lineRightEdge > lineLeft) { lineLeft = lineRightEdge; // Only set haveFloats to true if the float is inside our // containing block. This matches the spec for what some // callers want and disagrees for other callers, so we should // probably provide better information at some point. haveFloats = true; } } else { // A right float nscoord lineLeftEdge = fi.LineLeft(aWM, aShapeType, blockStart, bandBlockEnd); if (lineLeftEdge < lineRight) { lineRight = lineLeftEdge; // See above. haveFloats = true; } } // Shrink our band's height if needed. if (floatBEnd < blockEnd && aBandInfoType == BandInfoType::BandFromPoint) { blockEnd = floatBEnd; } } } nscoord blockSize = (blockEnd == nscoord_MAX) ? nscoord_MAX : (blockEnd - blockStart); // convert back from LineLeft/Right to IStart nscoord inlineStart = aWM.IsBidiLTR() ? lineLeft - mLineLeft : mLineLeft - lineRight + LogicalSize(aWM, aContainerSize).ISize(aWM); return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart, lineRight - lineLeft, blockSize, haveFloats); }