// static LogicalRect nsFloatManager::CalculateRegionFor(WritingMode aWM, nsIFrame* aFloat, const LogicalMargin& aMargin, const nsSize& aContainerSize) { // We consider relatively positioned frames at their original position. LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(), aFloat->GetSize()), aContainerSize); // Float region includes its margin region.Inflate(aWM, aMargin); // Don't store rectangles with negative margin-box width or height in // the float manager; it can't deal with them. if (region.ISize(aWM) < 0) { // Preserve the right margin-edge for left floats and the left // margin-edge for right floats const nsStyleDisplay* display = aFloat->StyleDisplay(); StyleFloat floatStyle = display->PhysicalFloats(aWM); if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) { region.IStart(aWM) = region.IEnd(aWM); } region.ISize(aWM) = 0; } if (region.BSize(aWM) < 0) { region.BSize(aWM) = 0; } return region; }
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; }
/* virtual */ LogicalSize nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { LogicalSize result = nsContainerFrame::ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags); // XXX The code below doesn't make sense if the caller's writing mode // is orthogonal to this frame's. Not sure yet what should happen then; // for now, just bail out. if (aWM.IsVertical() != GetWritingMode().IsVertical()) { return result; } // Fieldsets never shrink below their min width. // If we're a container for font size inflation, then shrink // wrapping inside of us should not apply font size inflation. AutoMaybeDisableFontInflation an(this); nscoord minISize = GetMinISize(aRenderingContext); if (minISize > result.ISize(aWM)) { result.ISize(aWM) = minISize; } return result; }
nscoord nsSubDocumentFrame::GetIntrinsicBSize() { // <frame> processing does not use this routine, only <iframe> NS_ASSERTION(IsInline(), "Shouldn't have been called"); if (mContent->IsXULElement()) { return 0; } NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr, "Intrinsic bsize should come from the embedded document."); // Use size of 300px x 150px, for compatibility with IE, and per CSS2.1 draft. WritingMode wm = GetWritingMode(); return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 300 : 150); }
nscoord nsSubDocumentFrame::GetIntrinsicISize() { if (!IsInline()) { return 0; // HTML <frame> has no useful intrinsic isize } if (mContent->IsXULElement()) { return 0; // XUL <iframe> and <browser> have no useful intrinsic isize } NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr, "Intrinsic isize should come from the embedded document."); // We must be an HTML <iframe>. Default to size of 300px x 150px, for IE // compat (and per CSS2.1 draft). WritingMode wm = GetWritingMode(); return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 150 : 300); }
/* returns a Direction struct containing the horizontal and vertical direction */ nsResizerFrame::Direction nsResizerFrame::GetDirection() { static const nsIContent::AttrValuesArray strings[] = { &nsGkAtoms::topleft, &nsGkAtoms::top, &nsGkAtoms::topright, &nsGkAtoms::left, &nsGkAtoms::right, &nsGkAtoms::bottomleft, &nsGkAtoms::bottom, &nsGkAtoms::bottomright, &nsGkAtoms::bottomstart, &nsGkAtoms::bottomend, nullptr }; static const Direction directions[] = { {-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}, {-1, 1}, {1, 1} }; if (!GetContent()) { return directions[0]; // default: topleft } int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir, strings, eCaseMatters); if (index < 0) { return directions[0]; // default: topleft } if (index >= 8) { // Directions 8 and higher are RTL-aware directions and should reverse the // horizontal component if RTL. WritingMode wm = GetWritingMode(); if (!(wm.IsVertical() ? wm.IsVerticalLR() : wm.IsBidiLTR())) { Direction direction = directions[index]; direction.mHorizontal *= -1; return direction; } } return directions[index]; }
void nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nscoord aWidth, nscoord aHeight) { // Aim to fill the whole size of the document, not only so we // can act as a background in print preview but also handle overflow // in child page frames correctly. // Use availableISize so we don't cause a needless horizontal scrollbar. WritingMode wm = aReflowInput.GetWritingMode(); nscoord scaledWidth = aWidth * PresContext()->GetPrintPreviewScale(); nscoord scaledHeight = aHeight * PresContext()->GetPrintPreviewScale(); nscoord scaledISize = (wm.IsVertical() ? scaledHeight : scaledWidth); nscoord scaledBSize = (wm.IsVertical() ? scaledWidth : scaledHeight); aDesiredSize.ISize(wm) = std::max(scaledISize, aReflowInput.AvailableISize()); aDesiredSize.BSize(wm) = std::max(scaledBSize, aReflowInput.ComputedBSize()); }
/* virtual */ nscoord nsSVGOuterSVGFrame::GetPrefISize(nsRenderingContext *aRenderingContext) { nscoord result; DISPLAY_PREF_WIDTH(this, result); SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent); WritingMode wm = GetWritingMode(); const nsSVGLength2& isize = wm.IsVertical() ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT] : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; if (isize.IsPercentage()) { // It looks like our containing block's isize may depend on our isize. In // that case our behavior is undefined according to CSS 2.1 section 10.3.2. // As a last resort, we'll fall back to returning zero. result = nscoord(0); // Returning zero may be unhelpful, however, as it leads to unexpected // disappearance of %-sized SVGs in orthogonal contexts, where our // containing block wants to shrink-wrap. So let's look for an ancestor // with non-zero size in this dimension, and use that as a (somewhat // arbitrary) result instead. nsIFrame *parent = GetParent(); while (parent) { nscoord parentISize = parent->GetLogicalSize(wm).ISize(wm); if (parentISize > 0 && parentISize != NS_UNCONSTRAINEDSIZE) { result = parentISize; break; } parent = parent->GetParent(); } } else { result = nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValue(svg)); if (result < 0) { result = nscoord(0); } } return result; }
nscoord nsSubDocumentFrame::GetIntrinsicISize() { if (!IsInline()) { return 0; // HTML <frame> has no useful intrinsic isize } if (mContent->IsXULElement()) { return 0; // XUL <iframe> and <browser> have no useful intrinsic isize } NS_ASSERTION(ObtainIntrinsicSizeFrame() == nullptr, "Intrinsic isize should come from the embedded document."); // We must be an HTML <iframe>. Default to size of 300px x 150px, for IE // compat (and per CSS2.1 draft). // This depends on the applied styles, which the comments in nsLeafFrame.h // say it should not, but we know it cannot change during the lifetime of // the frame because changing writing-mode leads to frame reconstruction. WritingMode wm = GetWritingMode(); return nsPresContext::CSSPixelsToAppUnits(wm.IsVertical() ? 150 : 300); }
nscoord nsFloatManager::ClearFloats(WritingMode aWM, nscoord aBCoord, uint8_t aBreakType, nscoord aContainerWidth, uint32_t aFlags) const { if (!(aFlags & DONT_CLEAR_PUSHED_FLOATS) && ClearContinues(aBreakType)) { return nscoord_MAX; } if (!HasAnyFloats()) { return aBCoord; } LogicalPoint offset = mOffset.ConvertTo(aWM, mWritingMode, 0); nscoord blockEnd = aBCoord + offset.B(aWM); const FloatInfo &tail = mFloats[mFloats.Length() - 1]; switch (aBreakType) { case NS_STYLE_CLEAR_BOTH: blockEnd = std::max(blockEnd, tail.mLeftBEnd); blockEnd = std::max(blockEnd, tail.mRightBEnd); break; case NS_STYLE_CLEAR_LEFT: blockEnd = std::max(blockEnd, aWM.IsBidiLTR() ? tail.mLeftBEnd : tail.mRightBEnd); break; case NS_STYLE_CLEAR_RIGHT: blockEnd = std::max(blockEnd, aWM.IsBidiLTR() ? tail.mRightBEnd : tail.mLeftBEnd); break; default: // Do nothing break; } blockEnd -= offset.B(aWM); return blockEnd; }
int32_t nsLegendFrame::GetLogicalAlign(WritingMode aCBWM) { int32_t intValue = NS_STYLE_TEXT_ALIGN_START; nsGenericHTMLElement* content = nsGenericHTMLElement::FromContent(mContent); if (content) { const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::align); if (attr && attr->Type() == nsAttrValue::eEnum) { intValue = attr->GetEnumValue(); switch (intValue) { case NS_STYLE_TEXT_ALIGN_LEFT: intValue = aCBWM.IsBidiLTR() ? NS_STYLE_TEXT_ALIGN_START : NS_STYLE_TEXT_ALIGN_END; break; case NS_STYLE_TEXT_ALIGN_RIGHT: intValue = aCBWM.IsBidiLTR() ? NS_STYLE_TEXT_ALIGN_END : NS_STYLE_TEXT_ALIGN_START; break; } } } return intValue; }
LogicalSize nsProgressFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, bool aShrinkWrap) { const WritingMode wm = GetWritingMode(); LogicalSize autoSize(wm); autoSize.BSize(wm) = autoSize.ISize(wm) = NSToCoordRound(StyleFont()->mFont.size * nsLayoutUtils::FontSizeInflationFor(this)); // 1em if (ResolvedOrientationIsVertical() == wm.IsVertical()) { autoSize.ISize(wm) *= 10; // 10em } else { autoSize.BSize(wm) *= 10; // 10em } return autoSize.ConvertTo(aWM, wm); }
nscoord nsFormControlFrame::GetLogicalBaseline(WritingMode aWritingMode) const { NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty"); // Treat radio buttons and checkboxes as having an intrinsic baseline // at the block-end of the control (use the block-end content edge rather // than the margin edge). // For "inverted" lines (typically in writing-mode:vertical-lr), use the // block-start end instead. return aWritingMode.IsLineInverted() ? GetLogicalUsedBorderAndPadding(aWritingMode).BStart(aWritingMode) : BSize(aWritingMode) - GetLogicalUsedBorderAndPadding(aWritingMode).BEnd(aWritingMode); }
nscoord nsTableWrapperFrame::ChildShrinkWrapISize(nsRenderingContext* aRenderingContext, nsIFrame* aChildFrame, WritingMode aWM, LogicalSize aCBSize, nscoord aAvailableISize, nscoord* aMarginResult) const { AutoMaybeDisableFontInflation an(aChildFrame); // For the caption frame, child's WM may differ from the table's main WM. WritingMode childWM = aChildFrame->GetWritingMode(); SizeComputationInput offsets(aChildFrame, aRenderingContext, aWM, aCBSize.ISize(aWM)); LogicalSize marginSize = offsets.ComputedLogicalMargin().Size(childWM).ConvertTo(aWM, childWM); LogicalSize paddingSize = offsets.ComputedLogicalPadding().Size(childWM).ConvertTo(aWM, childWM); LogicalSize bpSize = offsets.ComputedLogicalBorderPadding().Size(childWM).ConvertTo(aWM, childWM); // Shrink-wrap aChildFrame by default, except if we're a stretched grid item. auto flags = ComputeSizeFlags::eShrinkWrap; auto parent = GetParent(); nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr; bool isGridItem = (parentFrameType == nsGkAtoms::gridContainerFrame && !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); if (MOZ_UNLIKELY(isGridItem) && !StyleMargin()->HasInlineAxisAuto(aWM)) { auto inlineAxisAlignment = aWM.IsOrthogonalTo(parent->GetWritingMode()) ? StylePosition()->UsedAlignSelf(parent->StyleContext()) : StylePosition()->UsedJustifySelf(parent->StyleContext()); if (inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL || inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) { flags = nsIFrame::ComputeSizeFlags::eDefault; } } LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, marginSize, bpSize - paddingSize, paddingSize, flags); if (aMarginResult) { *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(aWM); } return size.ISize(aWM) + marginSize.ISize(aWM) + bpSize.ISize(aWM); }
LogicalSize nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, bool aShrinkWrap) { float inflation = nsLayoutUtils::FontSizeInflationFor(this); LogicalSize autoSize(aWM); nsresult rv = CalcIntrinsicSize(aRenderingContext, aWM, autoSize, inflation); if (NS_FAILED(rv)) { // What now? autoSize.SizeTo(aWM, 0, 0); } #ifdef DEBUG // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width else { const nsStyleCoord& inlineStyleCoord = aWM.IsVertical() ? StylePosition()->mHeight : StylePosition()->mWidth; if (inlineStyleCoord.GetUnit() == eStyleUnit_Auto) { LogicalSize ancestorAutoSize = nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aShrinkWrap); // Disabled when there's inflation; see comment in GetPrefSize. MOZ_ASSERT(inflation != 1.0f || ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM), "Incorrect size computed by ComputeAutoSize?"); } } #endif return autoSize; }
bool nsColumnSetFrame::ReflowChildren(ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aCarriedOutBEndMargin, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); bool isRTL = !wm.IsBidiLTR(); bool shrinkingBSizeOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceBSize > aConfig.mColMaxBSize; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceBSize=%d, mColMaxBSize=%d, RTL=%d\n" " mBalanceColCount=%d, mColISize=%d, mColGap=%d\n", mLastBalanceBSize, aConfig.mColMaxBSize, isRTL, aConfig.mBalanceColCount, aConfig.mColISize, aConfig.mColGap); #endif DrainOverflowColumns(); const bool colBSizeChanged = mLastBalanceBSize != aConfig.mColMaxBSize; if (colBSizeChanged) { mLastBalanceBSize = aConfig.mColMaxBSize; // XXX Seems like this could fire if incremental reflow pushed the column set // down so we reflow incrementally with a different available height. // We need a way to do an incremental reflow and be sure availableHeight // changes are taken account of! Right now I think block frames with absolute // children might exit early. //NS_ASSERTION(aKidReason != eReflowReason_Incremental, // "incremental reflow should not have changed the balance height"); } // get our border and padding LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding(); borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowInput)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); LogicalPoint childOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); // In vertical-rl mode, columns will not be correctly placed if the // reflowInput's ComputedWidth() is UNCONSTRAINED (in which case we'll get // a containerSize.width of zero here). In that case, the column positions // will be adjusted later, after our correct contentSize is known. nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained(); // For RTL, since the columns might not fill the frame exactly, we // need to account for the slop. Otherwise we'll waste time moving the // columns by some tiny amount // XXX when all of layout is converted to logical coordinates, we // probably won't need to do this hack any more. For now, we // confine it to the legacy horizontal-rl case if (!isVertical && isRTL) { nscoord availISize = aReflowInput.AvailableISize(); if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowInput.ComputedISize(); } if (availISize != NS_INTRINSICSIZE) { childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) - availISize; #ifdef DEBUG_roc printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm)); #endif } } int columnCount = 0; int contentBEnd = 0; bool reflowNext = false; while (child) { // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't // skip if the next column is dirty, because the next column's first line(s) // might be pullable back to this column. We can't skip if it's the last child // because we need to obtain the bottom margin. We can't skip // if this is the last column and we're supposed to assign unbounded // height to it, because that could change the available height from // the last time we reflowed it and we should try to pull all the // content from its next sibling. (Note that it might be the last // column, but not be the last child because the desired number of columns // has changed.) bool skipIncremental = !aReflowInput.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(child) && child->GetNextSibling() && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) && !NS_SUBTREE_DIRTY(child->GetNextSibling()); // If we need to pull up content from the prev-in-flow then this is not just // a height shrink. The prev in flow will have set the dirty bit. // Check the overflow rect YMost instead of just the child's content height. The child // may have overflowing content that cares about the available height boundary. // (It may also have overflowing content that doesn't care about the available height // boundary, but if so, too bad, this optimization is defeated.) // We want scrollable overflow here since this is a calculation that // affects layout. bool skipResizeBSizeShrink = false; if (shrinkingBSizeOnly) { switch (wm.GetBlockDir()) { case WritingMode::eBlockTB: if (child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockLR: if (child->GetScrollableOverflowRect().XMost() <= aConfig.mColMaxBSize) { skipResizeBSizeShrink = true; } break; case WritingMode::eBlockRL: // XXX not sure how to handle this, so for now just don't attempt // the optimization break; default: NS_NOTREACHED("unknown block direction"); break; } } nscoord childContentBEnd = 0; if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(child, childOrigin, wm, containerSize); // If this is the last frame then make sure we get the right status nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { aStatus = mLastFrameStatus; } childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); #ifdef DEBUG_roc printf("*** Skipping child #%d %p (incremental %d, resize block-size shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeBSizeShrink, aStatus); #endif } else { LogicalSize availSize(wm, aConfig.mColISize, aConfig.mColMaxBSize); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.BSize(wm) = GetAvailableContentBSize(aReflowInput); } LogicalSize computedSize = aReflowInput.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); ReflowInput kidReflowInput(PresContext(), aReflowInput, child, availSize, &kidCBSize); kidReflowInput.mFlags.mIsTopOfPage = true; kidReflowInput.mFlags.mTableIsSplittable = false; kidReflowInput.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; // We need to reflow any float placeholders, even if our column height // hasn't changed. kidReflowInput.mFlags.mMustReflowPlaceholders = !colBSizeChanged; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,availSize.BSize(wm)); #endif // Note if the column's next in flow is not being changed by this incremental reflow. // This may allow the current column to avoid trying to pull lines from the next column. if (child->GetNextSibling() && !(GetStateBits() & NS_FRAME_IS_DIRTY) && !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { kidReflowInput.mFlags.mNextInFlowUntouched = true; } ReflowOutput kidDesiredSize(wm, aDesiredSize.mFlags); // XXX it would be cool to consult the float manager for the // previous block to figure out the region of floats from the // previous column that extend into this column, and subtract // that region from the new float manager. So you could stick a // really big float in the first column and text in following // columns would flow around it. // Reflow the frame LogicalPoint origin(wm, childOrigin.I(wm) + kidReflowInput.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowInput.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowInput, wm, origin, containerSize, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, &kidReflowInput, wm, childOrigin, containerSize, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxBSize) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingBSize = std::max(childContentBEnd, aColData.mMaxOverflowingBSize); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBEnd = std::max(contentBEnd, childContentBEnd); aColData.mLastBSize = childContentBEnd; aColData.mSumBSize += childContentBEnd; // Build a continuation column if necessary nsIFrame* kidNextInFlow = child->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); child = nullptr; break; } else { ++columnCount; // Make sure that the column has a next-in-flow. If not, we must // create one to hold the overflowing stuff, even if we're just // going to put it on our overflow list and let *our* // next in flow handle it. if (!kidNextInFlow) { NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "We have to create a continuation, but the block doesn't want us to reflow it?"); // We need to create a continuing column kidNextInFlow = CreateNextInFlow(child); } // Make sure we reflow a next-in-flow when it switches between being // normal or overflow container if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = true; kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } if ((contentBEnd > aReflowInput.ComputedMaxBSize() || contentBEnd > aReflowInput.ComputedBSize()) && aConfig.mBalanceColCount < INT32_MAX) { // We overflowed vertically, but have not exceeded the number of // columns. We're going to go into overflow columns now, so balancing // no longer applies. aColData.mHasExcessBSize = true; } if (columnCount >= aConfig.mBalanceColCount) { // No more columns allowed here. Stop. aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); // Move any of our leftover columns to our overflow list. Our // next-in-flow will eventually pick them up. const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); if (continuationColumns.NotEmpty()) { SetOverflowFrames(continuationColumns); } child = nullptr; break; } } if (PresContext()->HasPendingInterrupt()) { // Stop the loop now while |child| still points to the frame that bailed // out. We could keep going here and condition a bunch of the code in // this loop on whether there's an interrupt, or even just keep going and // trying to reflow the blocks (even though we know they'll interrupt // right after their first line), but stopping now is conceptually the // simplest (and probably fastest) thing. break; } // Advance to the next column child = child->GetNextSibling(); if (child) { childOrigin.I(wm) += aConfig.mColISize + aConfig.mColGap; #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.icoord = %d\n", childOrigin.I(wm)); #endif } } if (PresContext()->CheckForInterrupt(this) && (GetStateBits() & NS_FRAME_IS_DIRTY)) { // Mark all our kids starting with |child| dirty // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, // because we might have interrupted while reflowing |child|, and since // we're about to add a dirty bit to |child| we need to make sure that // |this| is scheduled to have dirty bits marked on it and its ancestors. // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll // bail out immediately, since it'll already have a dirty bit. for (; child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } aColData.mMaxBSize = contentBEnd; LogicalSize contentSize = LogicalSize(wm, contentRect.Size()); contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd); mLastFrameStatus = aStatus; // Apply computed and min/max values if (aConfig.mComputedBSize != NS_INTRINSICSIZE) { if (aReflowInput.AvailableBSize() != NS_INTRINSICSIZE) { contentSize.BSize(wm) = std::min(contentSize.BSize(wm), aConfig.mComputedBSize); } else { contentSize.BSize(wm) = aConfig.mComputedBSize; } } else { // We add the "consumed" block-size back in so that we're applying // constraints to the correct bSize value, then subtract it again // after we've finished with the min/max calculation. This prevents us from // having a last continuation that is smaller than the min bSize. but which // has prev-in-flows, trigger a larger bSize than actually required. contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(contentSize.BSize(wm), aConfig.mConsumedBSize); } if (aReflowInput.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowInput.ComputedISize(); } else { contentSize.ISize(wm) = aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm)); } contentSize.ISize(wm) += borderPadding.IStartEnd(wm); contentSize.BSize(wm) += borderPadding.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); // In vertical-rl mode, make a second pass if necessary to reposition the // columns with the correct container width. (In other writing modes, // correct containerSize was not required for column positioning so we don't // need this fixup.) if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) { const nsSize finalContainerSize = aDesiredSize.PhysicalSize(); for (nsIFrame* child : mFrames) { // Get the logical position as set previously using a provisional or // dummy containerSize, and reset with the correct container size. child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize), finalContainerSize); } } #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); }
bool nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, bool aCBWidthChanged, bool aCBHeightChanged) { const nsStylePosition* pos = f->StylePosition(); // See if f's position might have changed because it depends on a // placeholder's position // This can happen in the following cases: // 1) Vertical positioning. "top" must be auto and "bottom" must be auto // (otherwise the vertical position is completely determined by // whichever of them is not auto and the height). // 2) Horizontal positioning. "left" must be auto and "right" must be auto // (otherwise the horizontal position is completely determined by // whichever of them is not auto and the width). // See nsHTMLReflowState::InitAbsoluteConstraints -- these are the // only cases when we call CalculateHypotheticalBox(). if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto && pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) || (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto && pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) { return true; } if (!aCBWidthChanged && !aCBHeightChanged) { // skip getting style data return false; } const nsStylePadding* padding = f->StylePadding(); const nsStyleMargin* margin = f->StyleMargin(); WritingMode wm = f->GetWritingMode(); if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) { // See if f's inline-size might have changed. // If margin-inline-start/end, padding-inline-start/end, // inline-size, min/max-inline-size are all lengths, 'none', or enumerated, // then our frame isize does not depend on the parent isize. // Note that borders never depend on the parent isize. // XXX All of the enumerated values except -moz-available are ok too. if (pos->ISizeDependsOnContainer(wm) || pos->MinISizeDependsOnContainer(wm) || pos->MaxISizeDependsOnContainer(wm) || !IsFixedPaddingSize(padding->mPadding.GetIStart(wm)) || !IsFixedPaddingSize(padding->mPadding.GetIEnd(wm))) { return true; } // See if f's position might have changed. If we're RTL then the // rules are slightly different. We'll assume percentage or auto // margins will always induce a dependency on the size if (!IsFixedMarginSize(margin->mMargin.GetIStart(wm)) || !IsFixedMarginSize(margin->mMargin.GetIEnd(wm))) { return true; } if (!wm.IsBidiLTR()) { // Note that even if 'istart' is a length, our position can // still depend on the containing block isze, because if // 'iend' is also a length we will discard 'istart' and be // positioned relative to the containing block iend edge. // 'istart' length and 'iend' auto is the only combination // we can be sure of. if (!IsFixedOffset(pos->mOffset.GetIStart(wm)) || pos->mOffset.GetIEndUnit(wm) != eStyleUnit_Auto) { return true; } } else { if (!IsFixedOffset(pos->mOffset.GetIStart(wm))) { return true; } } } if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) { // See if f's block-size might have changed. // If margin-block-start/end, padding-block-start/end, // min-block-size, and max-block-size are all lengths or 'none', // and bsize is a length or bsize and bend are auto and bstart is not auto, // then our frame bsize does not depend on the parent bsize. // Note that borders never depend on the parent bsize. if ((pos->BSizeDependsOnContainer(wm) && !(pos->BSize(wm).GetUnit() == eStyleUnit_Auto && pos->mOffset.GetBEndUnit(wm) == eStyleUnit_Auto && pos->mOffset.GetBStartUnit(wm) != eStyleUnit_Auto)) || pos->MinBSizeDependsOnContainer(wm) || pos->MaxBSizeDependsOnContainer(wm) || !IsFixedPaddingSize(padding->mPadding.GetBStart(wm)) || !IsFixedPaddingSize(padding->mPadding.GetBEnd(wm))) { return true; } // See if f's position might have changed. if (!IsFixedMarginSize(margin->mMargin.GetBStart(wm)) || !IsFixedMarginSize(margin->mMargin.GetBEnd(wm))) { return true; } if (!IsFixedOffset(pos->mOffset.GetBStart(wm))) { return true; } } return false; }
void nsColumnSetFrame::PaintColumnRule(nsRenderingContext* aCtx, const nsRect& aDirtyRect, const nsPoint& aPt) { nsIFrame* child = mFrames.FirstChild(); if (!child) return; // no columns nsIFrame* nextSibling = child->GetNextSibling(); if (!nextSibling) return; // 1 column only - this means no gap to draw on WritingMode wm = GetWritingMode(); bool isVertical = wm.IsVertical(); bool isRTL = !wm.IsBidiLTR(); const nsStyleColumn* colStyle = StyleColumn(); uint8_t ruleStyle; // Per spec, inset => ridge and outset => groove if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_INSET) ruleStyle = NS_STYLE_BORDER_STYLE_RIDGE; else if (colStyle->mColumnRuleStyle == NS_STYLE_BORDER_STYLE_OUTSET) ruleStyle = NS_STYLE_BORDER_STYLE_GROOVE; else ruleStyle = colStyle->mColumnRuleStyle; nsPresContext* presContext = PresContext(); nscoord ruleWidth = colStyle->GetComputedColumnRuleWidth(); if (!ruleWidth) return; nscolor ruleColor = GetVisitedDependentColor(eCSSProperty_column_rule_color); // In order to re-use a large amount of code, we treat the column rule as a border. // We create a new border style object and fill in all the details of the column rule as // the left border. PaintBorder() does all the rendering for us, so we not // only save an enormous amount of code but we'll support all the line styles that // we support on borders! nsStyleBorder border(presContext); Sides skipSides; if (isVertical) { border.SetBorderWidth(eSideTop, ruleWidth); border.SetBorderStyle(eSideTop, ruleStyle); border.mBorderTopColor = StyleComplexColor::FromColor(ruleColor); skipSides |= mozilla::eSideBitsLeftRight; skipSides |= mozilla::eSideBitsBottom; } else { border.SetBorderWidth(eSideLeft, ruleWidth); border.SetBorderStyle(eSideLeft, ruleStyle); border.mBorderLeftColor = StyleComplexColor::FromColor(ruleColor); skipSides |= mozilla::eSideBitsTopBottom; skipSides |= mozilla::eSideBitsRight; } // Get our content rect as an absolute coordinate, not relative to // our parent (which is what the X and Y normally is) nsRect contentRect = GetContentRect() - GetRect().TopLeft() + aPt; nsSize ruleSize = isVertical ? nsSize(contentRect.width, ruleWidth) : nsSize(ruleWidth, contentRect.height); while (nextSibling) { // The frame tree goes RTL in RTL. // The |prevFrame| and |nextFrame| frames here are the visually preceding // (left/above) and following (right/below) frames, not in logical writing- // mode direction. nsIFrame* prevFrame = isRTL ? nextSibling : child; nsIFrame* nextFrame = isRTL ? child : nextSibling; // Each child frame's position coordinates is actually relative to this // nsColumnSetFrame. // linePt will be at the top-left edge to paint the line. nsPoint linePt; if (isVertical) { nscoord edgeOfPrev = prevFrame->GetRect().YMost() + aPt.y; nscoord edgeOfNext = nextFrame->GetRect().Y() + aPt.y; linePt = nsPoint(contentRect.x, (edgeOfPrev + edgeOfNext - ruleSize.height) / 2); } else { nscoord edgeOfPrev = prevFrame->GetRect().XMost() + aPt.x; nscoord edgeOfNext = nextFrame->GetRect().X() + aPt.x; linePt = nsPoint((edgeOfPrev + edgeOfNext - ruleSize.width) / 2, contentRect.y); } nsRect lineRect(linePt, ruleSize); // Assert that we're not drawing a border-image here; if we were, we // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns. MOZ_ASSERT(border.mBorderImageSource.GetType() == eStyleImageType_Null); Unused << nsCSSRendering::PaintBorderWithStyleBorder(presContext, *aCtx, this, aDirtyRect, lineRect, border, StyleContext(), PaintBorderFlags::SYNC_DECODE_IMAGES, skipSides); child = nextSibling; nextSibling = nextSibling->GetNextSibling(); } }
void nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { bool vertical = ResolvedOrientationIsVertical(); WritingMode wm = aBarFrame->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, availSize); nscoord size = vertical ? aReflowState.ComputedHeight() : aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; double position = static_cast<HTMLProgressElement*>(mContent)->Position(); // Force the bar's size to match the current progress. // When indeterminate, the progress' size will be 100%. if (position >= 0.0) { size *= position; } if (!vertical && (wm.IsVertical() ? wm.IsVerticalRL() : !wm.IsBidiLTR())) { xoffset += aReflowState.ComputedWidth() - size; } // The bar size is fixed in these cases: // - the progress position is determined: the bar size is fixed according // to it's value. // - the progress position is indeterminate and the bar appearance should be // shown as native: the bar size is forced to 100%. // Otherwise (when the progress is indeterminate and the bar appearance isn't // native), the bar size isn't fixed and can be set by the author. if (position != -1 || ShouldUseNativeStyle()) { if (vertical) { // We want the bar to begin at the bottom. yoffset += aReflowState.ComputedHeight() - size; size -= reflowState.ComputedPhysicalMargin().TopBottom() + reflowState.ComputedPhysicalBorderPadding().TopBottom(); size = std::max(size, 0); reflowState.SetComputedHeight(size); } else { size -= reflowState.ComputedPhysicalMargin().LeftRight() + reflowState.ComputedPhysicalBorderPadding().LeftRight(); size = std::max(size, 0); reflowState.SetComputedWidth(size); } } else if (vertical) { // For vertical progress bars, we need to position the bar specificly when // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) // because aReflowState.ComputedHeight() - size == 0. yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight(); } xoffset += reflowState.ComputedPhysicalMargin().left; yoffset += reflowState.ComputedPhysicalMargin().top; nsHTMLReflowMetrics barDesiredSize(aReflowState); ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, xoffset, yoffset, 0); }
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); }
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); }
void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus); // Grab overflow list DrainOverflowFrames(aPresContext); nsIFrame* kid = mFrames.FirstChild(); // Setup reflow state for our child WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize availSize = aReflowInput.AvailableSize(); const LogicalMargin& bp = aReflowInput.ComputedLogicalBorderPadding(); NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE, "should no longer use unconstrained inline size"); availSize.ISize(wm) -= bp.IStartEnd(wm); if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { availSize.BSize(wm) -= bp.BStartEnd(wm); } WritingMode lineWM = aMetrics.GetWritingMode(); ReflowOutput kidMetrics(lineWM); // Reflow the child if (!aReflowInput.mLineLayout) { // When there is no lineLayout provided, we provide our own. The // only time that the first-letter-frame is not reflowing in a // line context is when its floating. WritingMode kidWritingMode = WritingModeForLine(wm, kid); LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm); ReflowInput rs(aPresContext, aReflowInput, kid, kidAvailSize); nsLineLayout ll(aPresContext, nullptr, &aReflowInput, nullptr, nullptr); ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm), availSize.ISize(wm), NS_UNCONSTRAINEDSIZE, false, true, kidWritingMode, nsSize(aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); rs.mLineLayout = ≪ ll.SetInFirstLetter(true); ll.SetFirstLetterStyleOK(true); kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus); ll.EndLineReflow(); ll.SetInFirstLetter(false); // In the floating first-letter case, we need to set this ourselves; // nsLineLayout::BeginSpan will set it in the other case mBaseline = kidMetrics.BlockStartAscent(); // Place and size the child and update the output metrics LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM); kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm), convertedSize.ISize(wm), convertedSize.BSize(wm))); kid->FinishAndStoreOverflow(&kidMetrics); kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); convertedSize.ISize(wm) += bp.IStartEnd(wm); convertedSize.BSize(wm) += bp.BStartEnd(wm); aMetrics.SetSize(wm, convertedSize); aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); // Ensure that the overflow rect contains the child textframe's // overflow rect. // Note that if this is floating, the overline/underline drawable // area is in the overflow rect of the child textframe. aMetrics.UnionOverflowAreasWithDesiredBounds(); ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); FinishAndStoreOverflow(&aMetrics); } else { // Pretend we are a span and reflow the child frame nsLineLayout* ll = aReflowInput.mLineLayout; bool pushedFrame; ll->SetInFirstLetter( mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); ll->BeginSpan(this, &aReflowInput, bp.IStart(wm), availSize.ISize(wm), &mBaseline); ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame); NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(), "we're assuming we can mix sizes between lineWM and wm " "since we shouldn't have orthogonal writing modes within " "a line."); aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm); ll->SetInFirstLetter(false); if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) { aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() + bp.BStart(wm)); aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm); } else { nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm); } } if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { // Create a continuation or remove existing continuations based on // the reflow completion status. if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { if (aReflowInput.mLineLayout) { aReflowInput.mLineLayout->SetFirstLetterStyleOK(false); } nsIFrame* kidNextInFlow = kid->GetNextInFlow(); if (kidNextInFlow) { // Remove all of the childs next-in-flows kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); } } else { // Create a continuation for the child frame if it doesn't already // have one. if (!IsFloating()) { CreateNextInFlow(kid); // And then push it to our overflow list const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid); if (overflow.NotEmpty()) { SetOverflowFrames(overflow); } } else if (!kid->GetNextInFlow()) { // For floating first letter frames (if a continuation wasn't already // created for us) we need to put the continuation with the rest of the // text that the first letter frame was made out of. nsIFrame* continuation; CreateContinuationForFloatingParent(aPresContext, kid, &continuation, true); } } } NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowInput, aMetrics); }
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); }
void BRFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("BRFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize finalSize(wm); finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks // mode by nsLineLayout::VerticalAlignFrames . // However, it's not always 0. See below. finalSize.ISize(wm) = 0; aMetrics.SetBlockStartAscent(0); // Only when the BR is operating in a line-layout situation will it // behave like a BR. Additionally, we suppress breaks from BR inside // of ruby frames. To determine if we're inside ruby, we have to rely // on the *parent's* ShouldSuppressLineBreak() method, instead of our // own, because we may have custom "display" value that makes our // ShouldSuppressLineBreak() return false. nsLineLayout* ll = aReflowInput.mLineLayout; if (ll && !GetParent()->StyleContext()->ShouldSuppressLineBreak()) { // Note that the compatibility mode check excludes AlmostStandards // mode, since this is the inline box model. See bug 161691. if ( ll->LineIsEmpty() || aPresContext->CompatibilityMode() == eCompatibility_FullStandards ) { // The line is logically empty; any whitespace is trimmed away. // // If this frame is going to terminate the line we know // that nothing else will go on the line. Therefore, in this // case, we provide some height for the BR frame so that it // creates some vertical whitespace. It's necessary to use the // line-height rather than the font size because the // quirks-mode fix that doesn't apply the block's min // line-height makes this necessary to make BR cause a line // of the full line-height // We also do this in strict mode because BR should act like a // normal inline frame. That line-height is used is important // here for cases where the line-height is less than 1. RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetInflatedFontMetricsForFrame(this); if (fm) { nscoord logicalHeight = aReflowInput.CalcLineHeight(); finalSize.BSize(wm) = logicalHeight; aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline( fm, logicalHeight, wm.IsLineInverted())); } else { aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0); } // XXX temporary until I figure out a better solution; see the // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY // if the width is zero. // XXX This also fixes bug 10036! // Warning: nsTextControlFrame::CalculateSizeStandard depends on // the following line, see bug 228752. // The code below in AddInlinePrefISize also adds 1 appunit to width finalSize.ISize(wm) = 1; } // Return our reflow status uint32_t breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm); if (NS_STYLE_CLEAR_NONE == breakType) { breakType = NS_STYLE_CLEAR_LINE; } aStatus = NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER | NS_INLINE_MAKE_BREAK_TYPE(breakType); ll->SetLineEndsInBR(true); } else { aStatus = NS_FRAME_COMPLETE; } aMetrics.SetSize(wm, finalSize); aMetrics.SetOverflowAreasToDesiredBounds(); mAscent = aMetrics.BlockStartAscent(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics); }
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; }
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); }
/* virtual */ bool nsInlineFrame::IsSelfEmpty() { #if 0 // I used to think inline frames worked this way, but it seems they // don't. At least not in our codebase. if (GetPresContext()->CompatibilityMode() == eCompatibility_FullStandards) { return false; } #endif const nsStyleMargin* margin = StyleMargin(); const nsStyleBorder* border = StyleBorder(); const nsStylePadding* padding = StylePadding(); // Block-start and -end ignored, since they shouldn't affect things, but this // doesn't really match with nsLineLayout.cpp's setting of // ZeroEffectiveSpanBox, anymore, so what should this really be? WritingMode wm = GetWritingMode(); bool haveStart, haveEnd; // Initially set up haveStart and haveEnd in terms of visual (LTR/TTB) // coordinates; we'll exchange them later if bidi-RTL is in effect to // get logical start and end flags. if (wm.IsVertical()) { haveStart = border->GetComputedBorderWidth(eSideTop) != 0 || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) || !IsMarginZero(margin->mMargin.GetTop()); haveEnd = border->GetComputedBorderWidth(eSideBottom) != 0 || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom()) || !IsMarginZero(margin->mMargin.GetBottom()); } else { haveStart = border->GetComputedBorderWidth(eSideLeft) != 0 || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetLeft()) || !IsMarginZero(margin->mMargin.GetLeft()); haveEnd = border->GetComputedBorderWidth(eSideRight) != 0 || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetRight()) || !IsMarginZero(margin->mMargin.GetRight()); } if (haveStart || haveEnd) { // We skip this block and return false for box-decoration-break:clone since // in that case all the continuations will have the border/padding/margin. if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) && StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice) { // When direction=rtl, we need to consider logical rather than visual // start and end, so swap the flags. if (!wm.IsBidiLTR()) { Swap(haveStart, haveEnd); } // For ib-split frames, ignore things we know we'll skip in GetSkipSides. // XXXbz should we be doing this for non-ib-split frames too, in a more // general way? // Get the first continuation eagerly, as a performance optimization, to // avoid having to get it twice.. nsIFrame* firstCont = FirstContinuation(); return (!haveStart || firstCont->FrameIsNonFirstInIBSplit()) && (!haveEnd || firstCont->FrameIsNonLastInIBSplit()); } return false; } return true; }
/* static */ nsRect nsCaret::GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset, nscoord* aBidiIndicatorSize) { nsPoint framePos(0, 0); nsRect rect; nsresult rv = aFrame->GetPointFromOffset(aFrameOffset, &framePos); if (NS_FAILED(rv)) { if (aBidiIndicatorSize) { *aBidiIndicatorSize = 0; } return rect; } nsIFrame* frame = aFrame->GetContentInsertionFrame(); if (!frame) { frame = aFrame; } NS_ASSERTION(!(frame->GetStateBits() & NS_FRAME_IN_REFLOW), "We should not be in the middle of reflow"); nscoord baseline = frame->GetCaretBaseline(); nscoord ascent = 0, descent = 0; RefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm), nsLayoutUtils::FontSizeInflationFor(aFrame)); NS_ASSERTION(fm, "We should be able to get the font metrics"); if (fm) { ascent = fm->MaxAscent(); descent = fm->MaxDescent(); } nscoord height = ascent + descent; WritingMode wm = aFrame->GetWritingMode(); bool vertical = wm.IsVertical(); if (vertical) { if (wm.IsLineInverted()) { framePos.x = baseline - descent; } else { framePos.x = baseline - ascent; } } else { framePos.y = baseline - ascent; } Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height); rect = nsRect(framePos, vertical ? nsSize(height, caretMetrics.mCaretWidth) : nsSize(caretMetrics.mCaretWidth, height)); // Clamp the inline-position to be within our scroll frame. If we don't, then // it clips us, and we don't appear at all. See bug 335560. nsIFrame *scrollFrame = nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame); if (scrollFrame) { // First, use the scrollFrame to get at the scrollable view that we're in. nsIScrollableFrame *sf = do_QueryFrame(scrollFrame); nsIFrame *scrolled = sf->GetScrolledFrame(); nsRect caretInScroll = rect + aFrame->GetOffsetTo(scrolled); // Now see if the caret extends beyond the view's bounds. If it does, // then snap it back, put it as close to the edge as it can. if (vertical) { nscoord overflow = caretInScroll.YMost() - scrolled->GetVisualOverflowRectRelativeToSelf().height; if (overflow > 0) { rect.y -= overflow; } } else { nscoord overflow = caretInScroll.XMost() - scrolled->GetVisualOverflowRectRelativeToSelf().width; if (overflow > 0) { rect.x -= overflow; } } } if (aBidiIndicatorSize) { *aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize; } return rect; }
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 nsCaret::ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, nsRect* aCaretRect, nsRect* aHookRect) { NS_ASSERTION(aFrame, "Should have a frame here"); WritingMode wm = aFrame->GetWritingMode(); bool isVertical = wm.IsVertical(); nscoord bidiIndicatorSize; *aCaretRect = GetGeometryForFrame(aFrame, aFrameOffset, &bidiIndicatorSize); // on RTL frames the right edge of mCaretRect must be equal to framePos const nsStyleVisibility* vis = aFrame->StyleVisibility(); if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { if (isVertical) { aCaretRect->y -= aCaretRect->height; } else { aCaretRect->x -= aCaretRect->width; } } // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction aHookRect->SetEmpty(); if (!IsBidiUI()) { return; } bool isCaretRTL; nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard(); // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the // keyboard direction, or the user has no right-to-left keyboard // installed, so we never draw the hook. if (bidiKeyboard && NS_SUCCEEDED(bidiKeyboard->IsLangRTL(&isCaretRTL))) { // If keyboard language is RTL, draw the hook on the left; if LTR, to the right // The height of the hook rectangle is the same as the width of the caret // rectangle. if (isVertical) { bool isSidewaysLR = wm.IsVerticalLR() && !wm.IsLineInverted(); if (isSidewaysLR) { aHookRect->SetRect(aCaretRect->x + bidiIndicatorSize, aCaretRect->y + (!isCaretRTL ? bidiIndicatorSize * -1 : aCaretRect->height), aCaretRect->height, bidiIndicatorSize); } else { aHookRect->SetRect(aCaretRect->XMost() - bidiIndicatorSize, aCaretRect->y + (isCaretRTL ? bidiIndicatorSize * -1 : aCaretRect->height), aCaretRect->height, bidiIndicatorSize); } } else { aHookRect->SetRect(aCaretRect->x + (isCaretRTL ? bidiIndicatorSize * -1 : aCaretRect->width), aCaretRect->y + bidiIndicatorSize, bidiIndicatorSize, aCaretRect->width); } } }