nscoord nsColumnSetFrame::GetAvailableContentBSize(const nsHTMLReflowState& aReflowState) { if (aReflowState.AvailableBSize() == NS_INTRINSICSIZE) { return NS_INTRINSICSIZE; } WritingMode wm = aReflowState.GetWritingMode(); LogicalMargin bp = aReflowState.ComputedLogicalBorderPadding(); bp.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); bp.BEnd(wm) = aReflowState.ComputedLogicalBorderPadding().BEnd(wm); return std::max(0, aReflowState.AvailableBSize() - bp.BStartEnd(wm)); }
void nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, const nsRect& aContainingBlock, AbsPosReflowFlags aFlags, nsIFrame* aKidFrame, nsReflowStatus& aStatus, nsOverflowAreas* aOverflowAreas) { #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent); printf("abs pos "); if (aKidFrame) { nsAutoString name; aKidFrame->GetFrameName(name); printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); } char width[16]; char height[16]; PrettyUC(aReflowState.AvailableWidth(), width, 16); PrettyUC(aReflowState.AvailableHeight(), height, 16); printf(" a=%s,%s ", width, height); PrettyUC(aReflowState.ComputedWidth(), width, 16); PrettyUC(aReflowState.ComputedHeight(), height, 16); printf("c=%s,%s \n", width, height); } AutoNoisyIndenter indent(nsBlockFrame::gNoisy); #endif // DEBUG WritingMode wm = aKidFrame->GetWritingMode(); LogicalSize logicalCBSize(wm, aContainingBlock.Size()); nscoord availISize = logicalCBSize.ISize(wm); if (availISize == -1) { NS_ASSERTION(aReflowState.ComputedSize(wm).ISize(wm) != NS_UNCONSTRAINEDSIZE, "Must have a useful inline-size _somewhere_"); availISize = aReflowState.ComputedSizeWithPadding(wm).ISize(wm); } uint32_t rsFlags = 0; if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) { // When a grid container generates the abs.pos. CB for a *child* then // the static-position is the CB origin (i.e. of the grid area rect). // https://drafts.csswg.org/css-grid/#static-position nsIFrame* placeholder = aPresContext->PresShell()->GetPlaceholderFrameFor(aKidFrame); if (placeholder && placeholder->GetParent() == aDelegatingFrame) { rsFlags |= nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN; } } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame, LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE), &logicalCBSize, rsFlags); // Get the border values WritingMode outerWM = aReflowState.GetWritingMode(); const LogicalMargin border(outerWM, aReflowState.mStyleBorder->GetComputedBorder()); const LogicalMargin margin = kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm); bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE) && (aFlags & AbsPosReflowFlags::eConstrainHeight) // Don't split if told not to (e.g. for fixed frames) && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame) //XXX we don't handle splitting frames for inline absolute containing blocks yet && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <= aReflowState.AvailableBSize()); // Don't split things below the fold. (Ideally we shouldn't *have* // anything totally below the fold, but we can't position frames // across next-in-flow breaks yet. if (constrainBSize) { kidReflowState.AvailableBSize() = aReflowState.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) - kidReflowState.ComputedLogicalMargin().BStart(wm); if (NS_AUTOOFFSET != kidReflowState.ComputedLogicalOffsets().BStart(wm)) { kidReflowState.AvailableBSize() -= kidReflowState.ComputedLogicalOffsets().BStart(wm); } } // Do the reflow nsHTMLReflowMetrics kidDesiredSize(kidReflowState); aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus); const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm); LogicalMargin offsets = kidReflowState.ComputedLogicalOffsets().ConvertTo(outerWM, wm); // If we're solving for start in either inline or block direction, // then compute it now that we know the dimensions. if ((NS_AUTOOFFSET == offsets.IStart(outerWM)) || (NS_AUTOOFFSET == offsets.BStart(outerWM))) { if (-1 == logicalCBSize.ISize(wm)) { // Get the containing block width/height logicalCBSize = kidReflowState.ComputeContainingBlockRectangle(aPresContext, &aReflowState); } if (NS_AUTOOFFSET == offsets.IStart(outerWM)) { NS_ASSERTION(NS_AUTOOFFSET != offsets.IEnd(outerWM), "Can't solve for both start and end"); offsets.IStart(outerWM) = logicalCBSize.ConvertTo(outerWM, wm).ISize(outerWM) - offsets.IEnd(outerWM) - margin.IStartEnd(outerWM) - kidSize.ISize(outerWM); } if (NS_AUTOOFFSET == offsets.BStart(outerWM)) { offsets.BStart(outerWM) = logicalCBSize.ConvertTo(outerWM, wm).BSize(outerWM) - offsets.BEnd(outerWM) - margin.BStartEnd(outerWM) - kidSize.BSize(outerWM); } kidReflowState.SetComputedLogicalOffsets(offsets.ConvertTo(wm, outerWM)); } // Position the child relative to our padding edge LogicalRect rect(outerWM, border.IStart(outerWM) + offsets.IStart(outerWM) + margin.IStart(outerWM), border.BStart(outerWM) + offsets.BStart(outerWM) + margin.BStart(outerWM), kidSize.ISize(outerWM), kidSize.BSize(outerWM)); nsRect r = rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) + border.Size(outerWM).GetPhysicalSize(outerWM)); // Offset the frame rect by the given origin of the absolute containing block. // If the frame is auto-positioned on both sides of an axis, it will be // positioned based on its containing block and we don't need to offset // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)). if (aContainingBlock.TopLeft() != nsPoint(0, 0)) { const nsStyleSides& offsets = kidReflowState.mStylePosition->mOffset; if (!(offsets.GetLeftUnit() == eStyleUnit_Auto && offsets.GetRightUnit() == eStyleUnit_Auto) || (rsFlags & nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN)) { r.x += aContainingBlock.x; } if (!(offsets.GetTopUnit() == eStyleUnit_Auto && offsets.GetBottomUnit() == eStyleUnit_Auto) || (rsFlags & nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN)) { r.y += aContainingBlock.y; } } aKidFrame->SetRect(r); nsView* view = aKidFrame->GetView(); if (view) { // Size and position the view and set its opacity, visibility, content // transparency, and clip nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, kidDesiredSize.VisualOverflow()); } else { nsContainerFrame::PositionChildViews(aKidFrame); } aKidFrame->DidReflow(aPresContext, &kidReflowState, nsDidReflowStatus::FINISHED); #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1); printf("abs pos "); if (aKidFrame) { nsAutoString name; aKidFrame->GetFrameName(name); printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); } printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame), r.x, r.y, r.width, r.height); } #endif if (aOverflowAreas) { aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft()); } }
void nsTableCellFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); if (aReflowState.mFlags.mSpecialBSizeReflow) { FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); } // see if a special bsize reflow needs to occur due to having a pct height nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowState); aStatus = NS_FRAME_COMPLETE; WritingMode wm = aReflowState.GetWritingMode(); LogicalSize availSize(wm, aReflowState.AvailableISize(), aReflowState.AvailableBSize()); LogicalMargin borderPadding = aReflowState.ComputedLogicalPadding(); LogicalMargin border = GetBorderWidth(wm); borderPadding += border; // reduce available space by insets, if we're in a constrained situation availSize.ISize(wm) -= borderPadding.IStartEnd(wm); if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) { availSize.BSize(wm) -= borderPadding.BStartEnd(wm); } // Try to reflow the child into the available space. It might not // fit or might need continuing. if (availSize.BSize(wm) < 0) { availSize.BSize(wm) = 1; } nsHTMLReflowMetrics kidSize(wm, aDesiredSize.mFlags); kidSize.ClearSize(); SetPriorAvailISize(aReflowState.AvailableISize()); nsIFrame* firstKid = mFrames.FirstChild(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); nsTableFrame* tableFrame = GetTableFrame(); if (aReflowState.mFlags.mSpecialBSizeReflow) { const_cast<nsHTMLReflowState&>(aReflowState). SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm)); DISPLAY_REFLOW_CHANGE(); } else if (aPresContext->IsPaginated()) { nscoord computedUnpaginatedBSize = CalcUnpaginatedBSize(aPresContext, (nsTableCellFrame&)*this, *tableFrame, borderPadding.BStartEnd(wm)); if (computedUnpaginatedBSize > 0) { const_cast<nsHTMLReflowState&>(aReflowState).SetComputedBSize(computedUnpaginatedBSize); DISPLAY_REFLOW_CHANGE(); } } else { SetHasPctOverBSize(false); } WritingMode kidWM = firstKid->GetWritingMode(); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, availSize.ConvertTo(kidWM, wm)); // Don't be a percent height observer if we're in the middle of // special-bsize reflow, in case we get an accidental NotifyPercentBSize() // call (which we shouldn't honor during special-bsize reflow) if (!aReflowState.mFlags.mSpecialBSizeReflow) { // mPercentBSizeObserver is for children of cells in quirks mode, // but only those than are tables in standards mode. NeedsToObserve // will determine how far this is propagated to descendants. kidReflowState.mPercentBSizeObserver = this; } // Don't propagate special bsize reflow state to our kids kidReflowState.mFlags.mSpecialBSizeReflow = false; if (aReflowState.mFlags.mSpecialBSizeReflow || FirstInFlow()->HasAnyStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { // We need to force the kid to have mBResize set if we've had a // special reflow in the past, since the non-special reflow needs to // resize back to what it was without the special bsize reflow. kidReflowState.SetBResize(true); } nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained(); LogicalPoint kidOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); nsRect origRect = firstKid->GetRect(); nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); bool firstReflow = firstKid->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, wm, kidOrigin, containerSize, 0, aStatus); if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it //XXX should paginate overflow as overflow, but not in this patch (bug 379349) NS_FRAME_SET_INCOMPLETE(aStatus); printf("Set table cell incomplete %p\n", static_cast<void*>(this)); } // XXXbz is this invalidate actually needed, really? if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { InvalidateFrameSubtree(); } #ifdef DEBUG DebugCheckChildSize(firstKid, kidSize); #endif // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode // see testcase "emptyCells.html" nsIFrame* prevInFlow = GetPrevInFlow(); bool isEmpty; if (prevInFlow) { isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty(); } else { isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid); } SetContentEmpty(isEmpty); // Place the child FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowState, wm, kidOrigin, containerSize, 0); nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, firstReflow); // first, compute the bsize which can be set w/o being restricted by // available bsize LogicalSize cellSize(wm); cellSize.BSize(wm) = kidSize.BSize(wm); if (NS_UNCONSTRAINEDSIZE != cellSize.BSize(wm)) { cellSize.BSize(wm) += borderPadding.BStartEnd(wm); } // next determine the cell's isize cellSize.ISize(wm) = kidSize.ISize(wm); // at this point, we've factored in the cell's style attributes // factor in border and padding if (NS_UNCONSTRAINEDSIZE != cellSize.ISize(wm)) { cellSize.ISize(wm) += borderPadding.IStartEnd(wm); } // set the cell's desired size and max element size aDesiredSize.SetSize(wm, cellSize); // the overflow area will be computed when BlockDirAlignChild() gets called if (aReflowState.mFlags.mSpecialBSizeReflow) { if (aDesiredSize.BSize(wm) > BSize(wm)) { // set a bit indicating that the pct bsize contents exceeded // the height that they could honor in the pass 2 reflow SetHasPctOverBSize(true); } if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableBSize()) { aDesiredSize.BSize(wm) = BSize(wm); } } // If our parent is in initial reflow, it'll handle invalidating our // entire overflow rect. if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { InvalidateFrame(); } // remember the desired size for this reflow SetDesiredSize(aDesiredSize); // Any absolutely-positioned children will get reflowed in // nsFrame::FixupPositionedTableParts in another pass, so propagate our // dirtiness to them before our parent clears our dirty bits. PushDirtyBitToAbsoluteFrames(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }
/* virtual */ void nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; if (!aReflowState.mLineLayout) { NS_ASSERTION( aReflowState.mLineLayout, "No line layout provided to RubyBaseContainerFrame reflow method."); return; } MoveOverflowToChildList(); // Ask text containers to drain overflows AutoRubyTextContainerArray textContainers(this); const uint32_t rtcCount = textContainers.Length(); for (uint32_t i = 0; i < rtcCount; i++) { textContainers[i]->MoveOverflowToChildList(); } WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowState.AvailableISize(), aReflowState.AvailableBSize()); // We have a reflow state and a line layout for each RTC. // They are conceptually the state of the RTCs, but we don't actually // reflow those RTCs in this code. These two arrays are holders of // the reflow states and line layouts. // Since there are pointers refer to reflow states and line layouts, // it is necessary to guarantee that they won't be moved. For this // reason, they are wrapped in UniquePtr here. nsAutoTArray<UniquePtr<nsHTMLReflowState>, RTC_ARRAY_SIZE> reflowStates; nsAutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts; reflowStates.SetCapacity(rtcCount); lineLayouts.SetCapacity(rtcCount); // Begin the line layout for each ruby text container in advance. bool hasSpan = false; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; if (textContainer->IsSpanContainer()) { hasSpan = true; } nsHTMLReflowState* reflowState = new nsHTMLReflowState( aPresContext, *aReflowState.parentReflowState, textContainer, availSize.ConvertTo(textContainer->GetWritingMode(), lineWM)); reflowStates.AppendElement(reflowState); nsLineLayout* lineLayout = new nsLineLayout(aPresContext, reflowState->mFloatManager, reflowState, nullptr, aReflowState.mLineLayout); lineLayout->SetSuppressLineWrap(true); lineLayouts.AppendElement(lineLayout); // Line number is useless for ruby text // XXX nullptr here may cause problem, see comments for // nsLineLayout::mBlockRS and nsLineLayout::AddFloat lineLayout->Init(nullptr, reflowState->CalcLineHeight(), -1); reflowState->mLineLayout = lineLayout; // Border and padding are suppressed on ruby text containers. // If the writing mode is vertical-rl, the horizontal position of // rt frames will be updated when reflowing this text container, // hence leave container size 0 here for now. lineLayout->BeginLineReflow(0, 0, reflowState->ComputedISize(), NS_UNCONSTRAINEDSIZE, false, false, lineWM, nsSize(0, 0)); lineLayout->AttachRootFrameToBaseLineLayout(); } aReflowState.mLineLayout->BeginSpan(this, &aReflowState, 0, aReflowState.AvailableISize(), &mBaseline); bool allowInitialLineBreak, allowLineBreak; GetIsLineBreakAllowed(this, aReflowState.mLineLayout->LineIsBreakable(), &allowInitialLineBreak, &allowLineBreak); nscoord isize = 0; // Reflow columns excluding any span ReflowState reflowState = { allowInitialLineBreak, allowLineBreak && !hasSpan, textContainers, aReflowState, reflowStates }; isize = ReflowColumns(reflowState, aStatus); DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this); aDesiredSize.ISize(lineWM) = isize; // When there are no frames inside the ruby base container, EndSpan // will return 0. However, in this case, the actual width of the // container could be non-zero because of non-empty ruby annotations. // XXX When bug 765861 gets fixed, this warning should be upgraded. NS_WARN_IF_FALSE(NS_INLINE_IS_BREAK(aStatus) || isize == lineSpanSize || mFrames.IsEmpty(), "bad isize"); // If there exists any span, the columns must either be completely // reflowed, or be not reflowed at all. MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || NS_FRAME_IS_COMPLETE(aStatus) || !hasSpan); if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && NS_FRAME_IS_COMPLETE(aStatus) && hasSpan) { // Reflow spans ReflowState reflowState = { false, false, textContainers, aReflowState, reflowStates }; nscoord spanISize = ReflowSpans(reflowState); isize = std::max(isize, spanISize); } for (uint32_t i = 0; i < rtcCount; i++) { // It happens before the ruby text container is reflowed, and that // when it is reflowed, it will just use this size. nsRubyTextContainerFrame* textContainer = textContainers[i]; nsLineLayout* lineLayout = lineLayouts[i].get(); RubyUtils::ClearReservedISize(textContainer); nscoord rtcISize = lineLayout->GetCurrentICoord(); // Only span containers and containers with collapsed annotations // need reserving isize. For normal ruby text containers, their // children will be expanded properly. We only need to expand their // own size. if (!textContainer->IsSpanContainer()) { rtcISize = isize; } else if (isize > rtcISize) { RubyUtils::SetReservedISize(textContainer, isize - rtcISize); } lineLayout->VerticalAlignLine(); textContainer->SetISize(rtcISize); lineLayout->EndLineReflow(); } // Border and padding are suppressed on ruby base container, // create a fake borderPadding for setting BSize. WritingMode frameWM = aReflowState.GetWritingMode(); LogicalMargin borderPadding(frameWM); nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, borderPadding, lineWM, frameWM); }
void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsRubyBaseContainerFrame* aBaseContainer, nsReflowStatus& aStatus) { WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowState.AvailableISize(), aReflowState.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(); nsHTMLReflowMetrics baseMetrics(aReflowState); bool pushedFrame; aReflowState.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()); aReflowState.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); aReflowState.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; nsHTMLReflowMetrics textMetrics(aReflowState); nsHTMLReflowState textReflowState(aPresContext, aReflowState, 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. textReflowState.mLineLayout = aReflowState.mLineLayout; textContainer->Reflow(aPresContext, textMetrics, textReflowState, 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, &textReflowState, 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); aReflowState.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_WARN_IF_FALSE(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); }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedBSize) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mContentArea(aReflowState.GetWritingMode()), mPushedFloats(nullptr), mOverflowTracker(nullptr), mBorderPadding(mReflowState.ComputedLogicalBorderPadding()), mPrevBEndMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedBSize(aConsumedBSize) { if (!sFloatFragmentsInsideColumnPrefCached) { sFloatFragmentsInsideColumnPrefCached = true; Preferences::AddBoolVarCache(&sFloatFragmentsInsideColumnEnabled, "layout.float-fragments-inside-column.enabled"); } SetFlag(BRS_FLOAT_FRAGMENTS_INSIDE_COLUMN_ENABLED, sFloatFragmentsInsideColumnEnabled); WritingMode wm = aReflowState.GetWritingMode(); SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); nsIFrame::LogicalSides logicalSkipSides = aFrame->GetLogicalSkipSides(&aReflowState); mBorderPadding.ApplySkipSides(logicalSkipSides); // Note that mContainerSize is the physical size, needed to // convert logical block-coordinates in vertical-rl writing mode // (measured from a RHS origin) to physical coordinates within the // containing block. // If aReflowState doesn't have a constrained ComputedWidth(), we set // mContainerSize.width to zero, which means lines will be positioned // (physically) incorrectly; we will fix them up at the end of // nsBlockFrame::Reflow, after we know the total block-size of the // frame. mContainerSize.width = aReflowState.ComputedWidth(); if (mContainerSize.width == NS_UNCONSTRAINEDSIZE) { mContainerSize.width = 0; } mContainerSize.width += mBorderPadding.LeftRight(wm); // For now at least, we don't do that fix-up for mContainerHeight. // It's only used in nsBidiUtils::ReorderFrames for vertical rtl // writing modes, which aren't fully supported for the time being. mContainerSize.height = aReflowState.ComputedHeight() + mBorderPadding.TopBottom(wm); if ((aBStartMarginRoot && !logicalSkipSides.BStart()) || 0 != mBorderPadding.BStart(wm)) { SetFlag(BRS_ISBSTARTMARGINROOT, true); SetFlag(BRS_APPLYBSTARTMARGIN, true); } if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) || 0 != mBorderPadding.BEnd(wm)) { SetFlag(BRS_ISBENDMARGINROOT, true); } if (aBlockNeedsFloatManager) { SetFlag(BRS_FLOAT_MGR, true); } mFloatManager = aReflowState.mFloatManager; NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerI, mFloatManagerB); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } mReflowStatus = NS_FRAME_COMPLETE; mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedISize(), "have unconstrained width; this should only result " "from very large sizes, not attempts at intrinsic " "width calculation"); mContentArea.ISize(wm) = aReflowState.ComputedISize(); // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize()) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBEndEdge = aReflowState.AvailableBSize() - mBorderPadding.BEnd(wm); mContentArea.BSize(wm) = std::max(0, mBEndEdge - mBorderPadding.BStart(wm)); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDBSIZE, true); mContentArea.BSize(wm) = mBEndEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.IStart(wm) = mBorderPadding.IStart(wm); mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsPresContext* aPresContext, nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot, bool aBlockNeedsFloatManager, nscoord aConsumedBSize) : mBlock(aFrame), mPresContext(aPresContext), mReflowState(aReflowState), mContentArea(aReflowState.GetWritingMode()), mPushedFloats(nullptr), mOverflowTracker(nullptr), mBorderPadding(mReflowState.ComputedLogicalBorderPadding()), mPrevBEndMargin(), mLineNumber(0), mFlags(0), mFloatBreakType(NS_STYLE_CLEAR_NONE), mConsumedBSize(aConsumedBSize) { WritingMode wm = aReflowState.GetWritingMode(); SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr); SetFlag(BRS_ISOVERFLOWCONTAINER, IS_TRUE_OVERFLOW_CONTAINER(aFrame)); nsIFrame::LogicalSides logicalSkipSides = aFrame->GetLogicalSkipSides(&aReflowState); mBorderPadding.ApplySkipSides(logicalSkipSides); // Note that mContainerWidth is the physical width! mContainerWidth = aReflowState.ComputedWidth() + mBorderPadding.LeftRight(wm); if ((aBStartMarginRoot && !logicalSkipSides.BStart()) || 0 != mBorderPadding.BStart(wm)) { SetFlag(BRS_ISBSTARTMARGINROOT, true); SetFlag(BRS_APPLYBSTARTMARGIN, true); } if ((aBEndMarginRoot && !logicalSkipSides.BEnd()) || 0 != mBorderPadding.BEnd(wm)) { SetFlag(BRS_ISBENDMARGINROOT, true); } if (aBlockNeedsFloatManager) { SetFlag(BRS_FLOAT_MGR, true); } mFloatManager = aReflowState.mFloatManager; NS_ASSERTION(mFloatManager, "FloatManager should be set in nsBlockReflowState" ); if (mFloatManager) { // Save the coordinate system origin for later. mFloatManager->GetTranslation(mFloatManagerX, mFloatManagerY); mFloatManager->PushState(&mFloatManagerStateBefore); // never popped } mReflowStatus = NS_FRAME_COMPLETE; mNextInFlow = static_cast<nsBlockFrame*>(mBlock->GetNextInFlow()); NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowState.ComputedISize(), "have unconstrained width; this should only result from " "very large sizes, not attempts at intrinsic width " "calculation"); mContentArea.ISize(wm) = aReflowState.ComputedISize(); // Compute content area height. Unlike the width, if we have a // specified style height we ignore it since extra content is // managed by the "overflow" property. When we don't have a // specified style height then we may end up limiting our height if // the availableHeight is constrained (this situation occurs when we // are paginated). if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableBSize()) { // We are in a paginated situation. The bottom edge is just inside // the bottom border and padding. The content area height doesn't // include either border or padding edge. mBEndEdge = aReflowState.AvailableBSize() - mBorderPadding.BEnd(wm); mContentArea.BSize(wm) = std::max(0, mBEndEdge - mBorderPadding.BStart(wm)); } else { // When we are not in a paginated situation then we always use // an constrained height. SetFlag(BRS_UNCONSTRAINEDBSIZE, true); mContentArea.BSize(wm) = mBEndEdge = NS_UNCONSTRAINEDSIZE; } mContentArea.IStart(wm) = mBorderPadding.IStart(wm); mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); mPrevChild = nullptr; mCurrentLine = aFrame->end_lines(); mMinLineHeight = aReflowState.CalcLineHeight(); }
void nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace, bool aApplyBStartMargin, nsCollapsingMargin& aPrevMargin, nscoord aClearance, bool aIsAdjacentWithBStart, nsLineBox* aLine, nsHTMLReflowState& aFrameRS, nsReflowStatus& aFrameReflowStatus, nsBlockReflowState& aState) { mFrame = aFrameRS.frame; mWritingMode = aState.mReflowState.GetWritingMode(); mContainerSize = aState.ContainerSize(); mSpace = aSpace; if (!aIsAdjacentWithBStart) { aFrameRS.mFlags.mIsTopOfPage = false; // make sure this is cleared } if (aApplyBStartMargin) { mBStartMargin = aPrevMargin; #ifdef NOISY_BLOCKDIR_MARGINS nsFrame::ListTag(stdout, mOuterReflowState.frame); printf(": reflowing "); nsFrame::ListTag(stdout, mFrame); printf(" margin => %d, clearance => %d\n", mBStartMargin.get(), aClearance); #endif // Adjust the available size if it's constrained so that the // child frame doesn't think it can reflow into its margin area. if (mWritingMode.IsOrthogonalTo(mFrame->GetWritingMode())) { if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableISize()) { aFrameRS.AvailableISize() -= mBStartMargin.get() + aClearance; } } else { if (NS_UNCONSTRAINEDSIZE != aFrameRS.AvailableBSize()) { aFrameRS.AvailableBSize() -= mBStartMargin.get() + aClearance; } } } nscoord tI = 0, tB = 0; // The values of x and y do not matter for floats, so don't bother // calculating them. Floats are guaranteed to have their own float // manager, so tI and tB don't matter. mICoord and mBCoord don't // matter becacuse they are only used in PlaceBlock, which is not used // for floats. if (aLine) { // Compute inline/block coordinate where reflow will begin. Use the // rules from 10.3.3 to determine what to apply. At this point in the // reflow auto inline-start/end margins will have a zero value. WritingMode frameWM = aFrameRS.GetWritingMode(); LogicalMargin usedMargin = aFrameRS.ComputedLogicalMargin().ConvertTo(mWritingMode, frameWM); mICoord = mSpace.IStart(mWritingMode) + usedMargin.IStart(mWritingMode); mBCoord = mSpace.BStart(mWritingMode) + mBStartMargin.get() + aClearance; LogicalRect space(mWritingMode, mICoord, mBCoord, mSpace.ISize(mWritingMode) - usedMargin.IStartEnd(mWritingMode), mSpace.BSize(mWritingMode) - usedMargin.BStartEnd(mWritingMode)); tI = space.LineLeft(mWritingMode, mContainerSize); tB = mBCoord; if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0) aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart(); } #ifdef DEBUG mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef); mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef); #endif mOuterReflowState.mFloatManager->Translate(tI, tB); mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus); mOuterReflowState.mFloatManager->Translate(-tI, -tB); #ifdef DEBUG if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) { if (CRAZY_SIZE(mMetrics.ISize(mWritingMode)) || CRAZY_SIZE(mMetrics.BSize(mWritingMode))) { printf("nsBlockReflowContext: "); nsFrame::ListTag(stdout, mFrame); printf(" metrics=%d,%d!\n", mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode)); } if ((mMetrics.ISize(mWritingMode) == nscoord(0xdeadbeef)) || (mMetrics.BSize(mWritingMode) == nscoord(0xdeadbeef))) { printf("nsBlockReflowContext: "); nsFrame::ListTag(stdout, mFrame); printf(" didn't set i/b %d,%d!\n", mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode)); } } #endif if (!mFrame->HasOverflowAreas()) { mMetrics.SetOverflowAreasToDesiredBounds(); } if (!NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus) || (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { // If frame is complete and has a next-in-flow, we need to delete // them now. Do not do this when a break-before is signaled because // the frame is going to get reflowed again (and may end up wanting // a next-in-flow where it ends up), unless it is an out of flow frame. if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) { nsIFrame* kidNextInFlow = mFrame->GetNextInFlow(); if (nullptr != kidNextInFlow) { // Remove all of the childs next-in-flows. Make sure that we ask // the right parent to do the removal (it's possible that the // parent is not this because we are executing pullup code). // Floats will eventually be removed via nsBlockFrame::RemoveFloat // which detaches the placeholder from the float. nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame); kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true); } } } }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, 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 = aReflowState.ComputedLogicalBorderPadding(); borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); 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 // reflowState'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 = aReflowState.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 = aReflowState.AvailableISize(); if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowState.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 = !aReflowState.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(aReflowState); } LogicalSize computedSize = aReflowState.ComputedSize(wm); if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); LogicalSize kidCBSize(wm, availSize.ISize(wm), computedSize.BSize(wm)); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, &kidCBSize); kidReflowState.mFlags.mIsTopOfPage = true; kidReflowState.mFlags.mTableIsSplittable = false; kidReflowState.mFlags.mIsColumnBalancing = aConfig.mBalanceColCount < INT32_MAX; // We need to reflow any float placeholders, even if our column height // hasn't changed. kidReflowState.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)) { kidReflowState.mFlags.mNextInFlowUntouched = true; } nsHTMLReflowMetrics 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) + kidReflowState.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowState.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, 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, &kidReflowState, 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 > aReflowState.ComputedMaxBSize() || contentBEnd > aReflowState.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 (aReflowState.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) = aReflowState.ApplyMinMaxBSize(contentSize.BSize(wm), aConfig.mConsumedBSize); } if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { contentSize.ISize(wm) = aReflowState.ComputedISize(); } else { contentSize.ISize(wm) = aReflowState.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); }
void nsColumnSetFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { MarkInReflow(); // Don't support interruption in columns nsPresContext::InterruptPreventer noInterrupts(aPresContext); DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; // Our children depend on our block-size if we have a fixed block-size. if (aReflowState.ComputedBSize() != NS_AUTOHEIGHT) { AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } else { RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } #ifdef DEBUG nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList)); for (; !oc.AtEnd(); oc.Next()) { MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get())); } nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList)); for (; !eoc.AtEnd(); eoc.Next()) { MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get())); } #endif nsOverflowAreas ocBounds; nsReflowStatus ocStatus = NS_FRAME_COMPLETE; if (GetPrevInFlow()) { ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- // If inline size is unconstrained, set aForceAuto to true to allow // the columns to expand in the inline direction. (This typically // happens in orthogonal flows where the inline direction is the // container's block direction). ReflowConfig config = ChooseColumnStrategy(aReflowState, aReflowState.ComputedISize() == NS_UNCONSTRAINEDSIZE); // If balancing, then we allow the last column to grow to unbounded // height during the first reflow. This gives us a way to estimate // what the average column height should be, because we can measure // the heights of all the columns and sum them up. But don't do this // if we have a next in flow because we don't want to suck all its // content back here and then have to push it out again! nsIFrame* nextInFlow = GetNextInFlow(); bool unboundedLastColumn = config.mIsBalancing && !nextInFlow; nsCollapsingMargin carriedOutBottomMargin; ColumnBalanceData colData; colData.mHasExcessBSize = false; bool feasible = ReflowColumns(aDesiredSize, aReflowState, aStatus, config, unboundedLastColumn, &carriedOutBottomMargin, colData); // If we're not balancing, then we're already done, since we should have // reflown all of our children, and there is no need for a binary search to // determine proper column height. if (config.mIsBalancing && !aPresContext->HasPendingInterrupt()) { FindBestBalanceBSize(aReflowState, aPresContext, config, colData, aDesiredSize, carriedOutBottomMargin, unboundedLastColumn, feasible, aStatus); } if (aPresContext->HasPendingInterrupt() && aReflowState.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { // In this situation, we might be lying about our reflow status, because // our last kid (the one that got interrupted) was incomplete. Fix that. aStatus = NS_FRAME_COMPLETE; } NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) || aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE, "Column set should be complete if the available block-size is unconstrained"); // Merge overflow container bounds and status. aDesiredSize.mOverflowAreas.UnionWith(ocBounds); NS_MergeReflowStatusInto(&aStatus, ocStatus); FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false); aDesiredSize.mCarriedOutBEndMargin = carriedOutBottomMargin; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); }