void nsTableCellFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); if (aReflowInput.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(aReflowInput); aStatus = NS_FRAME_COMPLETE; WritingMode wm = aReflowInput.GetWritingMode(); LogicalSize availSize(wm, aReflowInput.AvailableISize(), aReflowInput.AvailableBSize()); LogicalMargin borderPadding = aReflowInput.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; } ReflowOutput kidSize(wm, aDesiredSize.mFlags); kidSize.ClearSize(); SetPriorAvailISize(aReflowInput.AvailableISize()); nsIFrame* firstKid = mFrames.FirstChild(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); nsTableFrame* tableFrame = GetTableFrame(); if (aReflowInput.mFlags.mSpecialBSizeReflow) { const_cast<ReflowInput&>(aReflowInput). SetComputedBSize(BSize(wm) - borderPadding.BStartEnd(wm)); DISPLAY_REFLOW_CHANGE(); } else if (aPresContext->IsPaginated()) { nscoord computedUnpaginatedBSize = CalcUnpaginatedBSize((nsTableCellFrame&)*this, *tableFrame, borderPadding.BStartEnd(wm)); if (computedUnpaginatedBSize > 0) { const_cast<ReflowInput&>(aReflowInput).SetComputedBSize(computedUnpaginatedBSize); DISPLAY_REFLOW_CHANGE(); } } else { SetHasPctOverBSize(false); } WritingMode kidWM = firstKid->GetWritingMode(); ReflowInput kidReflowInput(aPresContext, aReflowInput, 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 (!aReflowInput.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. kidReflowInput.mPercentBSizeObserver = this; } // Don't propagate special bsize reflow state to our kids kidReflowInput.mFlags.mSpecialBSizeReflow = false; if (aReflowInput.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. kidReflowInput.SetBResize(true); } nsSize containerSize = aReflowInput.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, kidReflowInput, 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, &kidReflowInput, 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 (aReflowInput.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 == aReflowInput.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, aReflowInput, aDesiredSize); }
void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); NS_PRECONDITION(aReflowInput.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, aReflowInput, ocBounds, 0, ocStatus); } //------------ Handle Incremental Reflow ----------------- bool reflowInner; bool reflowLegend; nsIFrame* legend = GetLegend(); nsIFrame* inner = GetInner(); if (aReflowInput.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 = aReflowInput.ComputedSizeWithPadding(innerWM); LogicalSize legendAvailSize = aReflowInput.ComputedSizeWithPadding(legendWM); innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) = NS_UNCONSTRAINEDSIZE; NS_ASSERTION(!inner || nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext, inner, nsLayoutUtils::MIN_ISIZE) <= innerAvailSize.ISize(innerWM), "Bogus availSize.ISize; should be bigger"); NS_ASSERTION(!legend || nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext, legend, nsLayoutUtils::MIN_ISIZE) <= legendAvailSize.ISize(legendWM), "Bogus availSize.ISize; should be bigger"); // get our border and padding LogicalMargin border = aReflowInput.ComputedLogicalBorderPadding() - aReflowInput.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<ReflowInput> legendReflowInput; if (legend) { legendReflowInput.emplace(aPresContext, aReflowInput, legend, legendAvailSize); } if (reflowLegend) { ReflowOutput legendDesiredSize(aReflowInput); // 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, *legendReflowInput, 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, legendReflowInput.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) { ReflowInput kidReflowInput(aPresContext, aReflowInput, inner, innerAvailSize, nullptr, ReflowInput::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowInput.Init(aPresContext, nullptr, nullptr, &aReflowInput.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 (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowInput.SetComputedBSize( std::max(0, aReflowInput.ComputedBSize() - mLegendSpace)); } if (aReflowInput.ComputedMinBSize() > 0) { kidReflowInput.ComputedMinBSize() = std::max(0, aReflowInput.ComputedMinBSize() - mLegendSpace); } if (aReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) { kidReflowInput.ComputedMaxBSize() = std::max(0, aReflowInput.ComputedMaxBSize() - mLegendSpace); } ReflowOutput kidDesiredSize(kidReflowInput, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowInput.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, kidReflowInput, 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, &kidReflowInput, 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 = aReflowInput.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, aReflowInput.ComputedLogicalPadding()); // If the inner content rect is larger than the legend, we can align the // legend. if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) { // NOTE legend @align values are: left/right/center/top/bottom. // GetLogicalAlign converts left/right to start/end for the given WM. // @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign int32_t align = static_cast<nsLegendFrame*> (legend->GetContentInsertionFrame())->GetLogicalAlign(wm); switch (align) { case NS_STYLE_TEXT_ALIGN_END: mLegendRect.IStart(wm) = innerContentRect.IEnd(wm) - mLegendRect.ISize(wm); break; case NS_STYLE_TEXT_ALIGN_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; case NS_STYLE_TEXT_ALIGN_START: case NS_STYLE_VERTICAL_ALIGN_TOP: case NS_STYLE_VERTICAL_ALIGN_BOTTOM: mLegendRect.IStart(wm) = innerContentRect.IStart(wm); break; default: MOZ_ASSERT_UNREACHABLE("unexpected GetLogicalAlign value"); } } 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) + aReflowInput.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 = legendReflowInput->ComputedLogicalOffsets(). ConvertTo(wm, legendReflowInput->GetWritingMode()); ReflowInput::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, aReflowInput, aStatus); InvalidateFrame(); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); }