зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1731541 - Implement text-wrap: balance for nsBlockFrame reflow. r=emilio
A simple form of balance for short blocks, implemented by incrementally reducing the effective inline-size used during line-breaking, up to the point where an extra line would be created. This fails the test text-wrap-balance-line-clamp-001.html, but it's unclear to me if that test is correct (see https://github.com/w3c/csswg-drafts/issues/9310). If we do want the behavior expected by that test, an additional patch to handle the interaction with line-clamp will be required. Depends on D187543 Differential Revision: https://phabricator.services.mozilla.com/D187544
This commit is contained in:
Родитель
0428bf09b5
Коммит
bd93e1361b
|
@ -27,17 +27,16 @@
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::layout;
|
using namespace mozilla::layout;
|
||||||
|
|
||||||
BlockReflowState::BlockReflowState(const ReflowInput& aReflowInput,
|
BlockReflowState::BlockReflowState(
|
||||||
nsPresContext* aPresContext,
|
const ReflowInput& aReflowInput, nsPresContext* aPresContext,
|
||||||
nsBlockFrame* aFrame, bool aBStartMarginRoot,
|
nsBlockFrame* aFrame, bool aBStartMarginRoot, bool aBEndMarginRoot,
|
||||||
bool aBEndMarginRoot,
|
bool aBlockNeedsFloatManager, const nscoord aConsumedBSize,
|
||||||
bool aBlockNeedsFloatManager,
|
const nscoord aEffectiveContentBoxBSize, const nscoord aInset)
|
||||||
const nscoord aConsumedBSize,
|
|
||||||
const nscoord aEffectiveContentBoxBSize)
|
|
||||||
: mBlock(aFrame),
|
: mBlock(aFrame),
|
||||||
mPresContext(aPresContext),
|
mPresContext(aPresContext),
|
||||||
mReflowInput(aReflowInput),
|
mReflowInput(aReflowInput),
|
||||||
mContentArea(aReflowInput.GetWritingMode()),
|
mContentArea(aReflowInput.GetWritingMode()),
|
||||||
|
mInsetForBalance(aInset),
|
||||||
mPushedFloats(nullptr),
|
mPushedFloats(nullptr),
|
||||||
mOverflowTracker(nullptr),
|
mOverflowTracker(nullptr),
|
||||||
mBorderPadding(
|
mBorderPadding(
|
||||||
|
|
|
@ -98,7 +98,8 @@ class BlockReflowState {
|
||||||
nsBlockFrame* aFrame, bool aBStartMarginRoot,
|
nsBlockFrame* aFrame, bool aBStartMarginRoot,
|
||||||
bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
|
bool aBEndMarginRoot, bool aBlockNeedsFloatManager,
|
||||||
const nscoord aConsumedBSize,
|
const nscoord aConsumedBSize,
|
||||||
const nscoord aEffectiveContentBoxBSize);
|
const nscoord aEffectiveContentBoxBSize,
|
||||||
|
const nscoord aInset = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the available reflow space (the area not occupied by floats)
|
* Get the available reflow space (the area not occupied by floats)
|
||||||
|
@ -243,9 +244,9 @@ class BlockReflowState {
|
||||||
// the block.
|
// the block.
|
||||||
|
|
||||||
// The block frame that is using this object
|
// The block frame that is using this object
|
||||||
nsBlockFrame* mBlock;
|
nsBlockFrame* const mBlock;
|
||||||
|
|
||||||
nsPresContext* mPresContext;
|
nsPresContext* const mPresContext;
|
||||||
|
|
||||||
const ReflowInput& mReflowInput;
|
const ReflowInput& mReflowInput;
|
||||||
|
|
||||||
|
@ -301,6 +302,10 @@ class BlockReflowState {
|
||||||
return mContentArea.Size(wm).ConvertTo(aWM, wm);
|
return mContentArea.Size(wm).ConvertTo(aWM, wm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Amount of inset to apply during line-breaking, used by text-wrap:balance
|
||||||
|
// to adjust line-breaks for more consistent lengths throughout the block.
|
||||||
|
nscoord mInsetForBalance;
|
||||||
|
|
||||||
// Physical size. Use only for physical <-> logical coordinate conversion.
|
// Physical size. Use only for physical <-> logical coordinate conversion.
|
||||||
nsSize mContainerSize;
|
nsSize mContainerSize;
|
||||||
const nsSize& ContainerSize() const { return mContainerSize; }
|
const nsSize& ContainerSize() const { return mContainerSize; }
|
||||||
|
@ -345,7 +350,7 @@ class BlockReflowState {
|
||||||
|
|
||||||
// mBlock's computed logical border+padding with pre-reflow skip sides applied
|
// mBlock's computed logical border+padding with pre-reflow skip sides applied
|
||||||
// (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides).
|
// (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides).
|
||||||
LogicalMargin mBorderPadding;
|
const LogicalMargin mBorderPadding;
|
||||||
|
|
||||||
// The overflow areas of all floats placed so far
|
// The overflow areas of all floats placed so far
|
||||||
OverflowAreas mFloatOverflowAreas;
|
OverflowAreas mFloatOverflowAreas;
|
||||||
|
@ -383,7 +388,7 @@ class BlockReflowState {
|
||||||
// placed, since we're on a nowrap context.
|
// placed, since we're on a nowrap context.
|
||||||
nsTArray<nsIFrame*> mNoWrapFloats;
|
nsTArray<nsIFrame*> mNoWrapFloats;
|
||||||
|
|
||||||
nscoord mMinLineHeight;
|
const nscoord mMinLineHeight;
|
||||||
|
|
||||||
int32_t mLineNumber;
|
int32_t mLineNumber;
|
||||||
|
|
||||||
|
|
|
@ -1414,6 +1414,16 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, some lines may be reflowed. Blow away any saved line cursor
|
||||||
|
// because we may invalidate the nondecreasing
|
||||||
|
// overflowArea.InkOverflow().y/yMost invariant, and we may even
|
||||||
|
// delete the line with the line cursor.
|
||||||
|
ClearLineCursors();
|
||||||
|
|
||||||
// See comment below about oldSize. Use *only* for the
|
// See comment below about oldSize. Use *only* for the
|
||||||
// abs-pos-containing-block-size-change optimization!
|
// abs-pos-containing-block-size-change optimization!
|
||||||
nsSize oldSize = GetSize();
|
nsSize oldSize = GetSize();
|
||||||
|
@ -1430,16 +1440,316 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
autoFloatManager.CreateFloatManager(aPresContext);
|
autoFloatManager.CreateFloatManager(aPresContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK, some lines may be reflowed. Blow away any saved line cursor
|
if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
|
||||||
// because we may invalidate the nondecreasing
|
PresContext()->BidiEnabled()) {
|
||||||
// overflowArea.InkOverflow().y/yMost invariant, and we may even
|
static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
|
||||||
// delete the line with the line cursor.
|
|
||||||
ClearLineCursors();
|
|
||||||
|
|
||||||
if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether to apply text-wrap: balance behavior.
|
||||||
|
bool tryBalance = StyleText()->mTextWrap == StyleTextWrap::Balance &&
|
||||||
|
!GetPrevContinuation();
|
||||||
|
|
||||||
|
// Target number of lines in the block while balancing; negative if no
|
||||||
|
// balancing is being done.
|
||||||
|
int32_t balanceTarget = -1;
|
||||||
|
|
||||||
|
// Helpers for text-wrap: balance implementation:
|
||||||
|
|
||||||
|
// Count the number of lines in the mLines list, but return -1 instead if the
|
||||||
|
// count is going to exceed aLimit.
|
||||||
|
auto countLinesUpTo = [&](int32_t aLimit) -> int32_t {
|
||||||
|
int32_t n = 0;
|
||||||
|
for (auto iter = mLines.begin(); iter != mLines.end(); ++iter) {
|
||||||
|
if (++n > aLimit) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
|
||||||
|
// "balancing" is implemented by shortening the effective inline-size of the
|
||||||
|
// lines, so that content will tend to be pushed down to fill later lines of
|
||||||
|
// the block. `balanceInset` is the current amount of "inset" to apply, and
|
||||||
|
// `balanceStep` is the increment to adjust it by for the next iteration.
|
||||||
|
nscoord balanceStep = 0;
|
||||||
|
|
||||||
|
// text-wrap: balance loop, executed only once if balancing is not required.
|
||||||
|
nsReflowStatus reflowStatus;
|
||||||
|
TrialReflowState trialState(consumedBSize, effectiveContentBoxBSize,
|
||||||
|
needFloatManager);
|
||||||
|
while (true) {
|
||||||
|
// Save the initial floatManager state for repeated trial reflows.
|
||||||
|
// We'll restore (and re-save) the initial state each time we repeat the
|
||||||
|
// reflow.
|
||||||
|
nsFloatManager::SavedState floatManagerState;
|
||||||
|
aReflowInput.mFloatManager->PushState(&floatManagerState);
|
||||||
|
|
||||||
|
aMetrics = ReflowOutput(aMetrics.GetWritingMode());
|
||||||
|
reflowStatus =
|
||||||
|
TrialReflow(aPresContext, aMetrics, aReflowInput, trialState);
|
||||||
|
|
||||||
|
// Do we need to start a `text-wrap: balance` iteration?
|
||||||
|
if (tryBalance) {
|
||||||
|
tryBalance = false;
|
||||||
|
// Don't try to balance an incomplete block.
|
||||||
|
if (!reflowStatus.IsFullyComplete()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
balanceTarget =
|
||||||
|
countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
|
||||||
|
if (balanceTarget < 2) {
|
||||||
|
// If there are less than 2 lines, or the number exceeds the limit,
|
||||||
|
// no balancing is needed; just break from the balance loop.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Initialize the amount of inset to try, and the iteration step size.
|
||||||
|
balanceStep = aReflowInput.ComputedISize() / balanceTarget;
|
||||||
|
trialState.ResetForBalance(balanceStep);
|
||||||
|
balanceStep /= 2;
|
||||||
|
|
||||||
|
// Restore initial floatManager state for a new trial with updated inset.
|
||||||
|
aReflowInput.mFloatManager->PopState(&floatManagerState);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in the process of a balance operation, check whether we've
|
||||||
|
// inset by too much and either increase or reduce the inset for the next
|
||||||
|
// iteration.
|
||||||
|
if (balanceStep > 0) {
|
||||||
|
int32_t numLines =
|
||||||
|
countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
|
||||||
|
if (reflowStatus.IsFullyComplete() && numLines == balanceTarget) {
|
||||||
|
trialState.ResetForBalance(balanceStep);
|
||||||
|
} else {
|
||||||
|
trialState.ResetForBalance(-balanceStep);
|
||||||
|
}
|
||||||
|
balanceStep /= 2;
|
||||||
|
|
||||||
|
aReflowInput.mFloatManager->PopState(&floatManagerState);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we were attempting to balance, check whether the final iteration was
|
||||||
|
// successful, and if not, back up by one step.
|
||||||
|
if (balanceTarget >= 0) {
|
||||||
|
int32_t numLines =
|
||||||
|
countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
|
||||||
|
if (reflowStatus.IsFullyComplete() && numLines == balanceTarget) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
trialState.ResetForBalance(-1);
|
||||||
|
|
||||||
|
aReflowInput.mFloatManager->PopState(&floatManagerState);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, no balancing was required, so just exit; we don't
|
||||||
|
// reset (pop) the floatManager state because this is the reflow we're
|
||||||
|
// going to keep. So the saved state is just dropped.
|
||||||
|
break;
|
||||||
|
} // End of text-wrap: balance retry loop
|
||||||
|
|
||||||
|
// If the block direction is right-to-left, we need to update the bounds of
|
||||||
|
// lines that were placed relative to mContainerSize during reflow, as
|
||||||
|
// we typically do not know the true container size until we've reflowed all
|
||||||
|
// its children. So we use a dummy mContainerSize during reflow (see
|
||||||
|
// BlockReflowState's constructor) and then fix up the positions of the
|
||||||
|
// lines here, once the final block size is known.
|
||||||
|
//
|
||||||
|
// Note that writing-mode:vertical-rl is the only case where the block
|
||||||
|
// logical direction progresses in a negative physical direction, and
|
||||||
|
// therefore block-dir coordinate conversion depends on knowing the width
|
||||||
|
// of the coordinate space in order to translate between the logical and
|
||||||
|
// physical origins.
|
||||||
|
if (aReflowInput.GetWritingMode().IsVerticalRL()) {
|
||||||
|
nsSize containerSize = aMetrics.PhysicalSize();
|
||||||
|
nscoord deltaX = containerSize.width - trialState.mContainerWidth;
|
||||||
|
if (deltaX != 0) {
|
||||||
|
// We compute our lines and markers' overflow areas later in
|
||||||
|
// ComputeOverflowAreas(), so we don't need to adjust their overflow areas
|
||||||
|
// here.
|
||||||
|
const nsPoint physicalDelta(deltaX, 0);
|
||||||
|
for (auto& line : Lines()) {
|
||||||
|
UpdateLineContainerSize(&line, containerSize);
|
||||||
|
}
|
||||||
|
trialState.mFcBounds.Clear();
|
||||||
|
for (nsIFrame* f : mFloats) {
|
||||||
|
f->MovePositionBy(physicalDelta);
|
||||||
|
ConsiderChildOverflow(trialState.mFcBounds, f);
|
||||||
|
}
|
||||||
|
nsFrameList* markerList = GetOutsideMarkerList();
|
||||||
|
if (markerList) {
|
||||||
|
for (nsIFrame* f : *markerList) {
|
||||||
|
f->MovePositionBy(physicalDelta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nsFrameList* overflowContainers = GetOverflowContainers()) {
|
||||||
|
trialState.mOcBounds.Clear();
|
||||||
|
for (nsIFrame* f : *overflowContainers) {
|
||||||
|
f->MovePositionBy(physicalDelta);
|
||||||
|
ConsiderChildOverflow(trialState.mOcBounds, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aMetrics.SetOverflowAreasToDesiredBounds();
|
||||||
|
ComputeOverflowAreas(aMetrics.mOverflowAreas,
|
||||||
|
trialState.mBlockEndEdgeOfChildren,
|
||||||
|
aReflowInput.mStyleDisplay);
|
||||||
|
// Factor overflow container child bounds into the overflow area
|
||||||
|
aMetrics.mOverflowAreas.UnionWith(trialState.mOcBounds);
|
||||||
|
// Factor pushed float child bounds into the overflow area
|
||||||
|
aMetrics.mOverflowAreas.UnionWith(trialState.mFcBounds);
|
||||||
|
|
||||||
|
// Let the absolutely positioned container reflow any absolutely positioned
|
||||||
|
// child frames that need to be reflowed, e.g., elements with a percentage
|
||||||
|
// based width/height
|
||||||
|
// We want to do this under either of two conditions:
|
||||||
|
// 1. If we didn't do the incremental reflow above.
|
||||||
|
// 2. If our size changed.
|
||||||
|
// Even though it's the padding edge that's the containing block, we
|
||||||
|
// can use our rect (the border edge) since if the border style
|
||||||
|
// changed, the reflow would have been targeted at us so we'd satisfy
|
||||||
|
// condition 1.
|
||||||
|
// XXX checking oldSize is bogus, there are various reasons we might have
|
||||||
|
// reflowed but our size might not have been changed to what we
|
||||||
|
// asked for (e.g., we ended up being pushed to a new page)
|
||||||
|
// When WillReflowAgainForClearance is true, we will reflow again without
|
||||||
|
// resetting the size. Because of this, we must not reflow our abs-pos
|
||||||
|
// children in that situation --- what we think is our "new size" will not be
|
||||||
|
// our real new size. This also happens to be more efficient.
|
||||||
|
WritingMode parentWM = aMetrics.GetWritingMode();
|
||||||
|
if (HasAbsolutelyPositionedChildren()) {
|
||||||
|
nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
|
||||||
|
bool haveInterrupt = aPresContext->HasPendingInterrupt();
|
||||||
|
if (aReflowInput.WillReflowAgainForClearance() || haveInterrupt) {
|
||||||
|
// Make sure that when we reflow again we'll actually reflow all the abs
|
||||||
|
// pos frames that might conceivably depend on our size (or all of them,
|
||||||
|
// if we're dirty right now and interrupted; in that case we also need
|
||||||
|
// to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
|
||||||
|
// better than that, because we don't really know what our size will be,
|
||||||
|
// and it might in fact not change on the followup reflow!
|
||||||
|
if (haveInterrupt && HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
|
||||||
|
absoluteContainer->MarkAllFramesDirty();
|
||||||
|
} else {
|
||||||
|
absoluteContainer->MarkSizeDependentFramesDirty();
|
||||||
|
}
|
||||||
|
if (haveInterrupt) {
|
||||||
|
// We're not going to reflow absolute frames; make sure to account for
|
||||||
|
// their existing overflow areas, which is usually a side effect of this
|
||||||
|
// reflow.
|
||||||
|
//
|
||||||
|
// TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
|
||||||
|
// interrupt, can we just rely on it and unconditionally take the else
|
||||||
|
// branch below? That's a bit more subtle / risky, since I don't see
|
||||||
|
// what would reflow them in that case if they depended on our size.
|
||||||
|
for (nsIFrame* kid = absoluteContainer->GetChildList().FirstChild();
|
||||||
|
kid; kid = kid->GetNextSibling()) {
|
||||||
|
ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogicalSize containingBlockSize =
|
||||||
|
CalculateContainingBlockSizeForAbsolutes(parentWM, aReflowInput,
|
||||||
|
aMetrics.Size(parentWM));
|
||||||
|
|
||||||
|
// Mark frames that depend on changes we just made to this frame as dirty:
|
||||||
|
// Now we can assume that the padding edge hasn't moved.
|
||||||
|
// We need to reflow the absolutes if one of them depends on
|
||||||
|
// its placeholder position, or the containing block size in a
|
||||||
|
// direction in which the containing block size might have
|
||||||
|
// changed.
|
||||||
|
|
||||||
|
// XXX "width" and "height" in this block will become ISize and BSize
|
||||||
|
// when nsAbsoluteContainingBlock is logicalized
|
||||||
|
bool cbWidthChanged = aMetrics.Width() != oldSize.width;
|
||||||
|
bool isRoot = !GetContent()->GetParent();
|
||||||
|
// If isRoot and we have auto height, then we are the initial
|
||||||
|
// containing block and the containing block height is the
|
||||||
|
// viewport height, which can't change during incremental
|
||||||
|
// reflow.
|
||||||
|
bool cbHeightChanged =
|
||||||
|
!(isRoot && NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedHeight()) &&
|
||||||
|
aMetrics.Height() != oldSize.height;
|
||||||
|
|
||||||
|
nsRect containingBlock(nsPoint(0, 0),
|
||||||
|
containingBlockSize.GetPhysicalSize(parentWM));
|
||||||
|
AbsPosReflowFlags flags = AbsPosReflowFlags::ConstrainHeight;
|
||||||
|
if (cbWidthChanged) {
|
||||||
|
flags |= AbsPosReflowFlags::CBWidthChanged;
|
||||||
|
}
|
||||||
|
if (cbHeightChanged) {
|
||||||
|
flags |= AbsPosReflowFlags::CBHeightChanged;
|
||||||
|
}
|
||||||
|
// Setup the line cursor here to optimize line searching for
|
||||||
|
// calculating hypothetical position of absolutely-positioned
|
||||||
|
// frames.
|
||||||
|
SetupLineCursorForQuery();
|
||||||
|
absoluteContainer->Reflow(this, aPresContext, aReflowInput, reflowStatus,
|
||||||
|
containingBlock, flags,
|
||||||
|
&aMetrics.mOverflowAreas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
|
||||||
|
|
||||||
|
aStatus = reflowStatus;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Between when we drain pushed floats and when we complete reflow,
|
||||||
|
// we're allowed to have multiple continuations of the same float on
|
||||||
|
// our floats list, since a first-in-flow might get pushed to a later
|
||||||
|
// continuation of its containing block. But it's not permitted
|
||||||
|
// outside that time.
|
||||||
|
nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
|
||||||
|
|
||||||
|
if (gNoisyReflow) {
|
||||||
|
IndentBy(stdout, gNoiseIndent);
|
||||||
|
ListTag(stdout);
|
||||||
|
printf(": status=%s metrics=%d,%d carriedMargin=%d",
|
||||||
|
ToString(aStatus).c_str(), aMetrics.ISize(parentWM),
|
||||||
|
aMetrics.BSize(parentWM), aMetrics.mCarriedOutBEndMargin.get());
|
||||||
|
if (HasOverflowAreas()) {
|
||||||
|
printf(" overflow-vis={%d,%d,%d,%d}", aMetrics.InkOverflow().x,
|
||||||
|
aMetrics.InkOverflow().y, aMetrics.InkOverflow().width,
|
||||||
|
aMetrics.InkOverflow().height);
|
||||||
|
printf(" overflow-scr={%d,%d,%d,%d}", aMetrics.ScrollableOverflow().x,
|
||||||
|
aMetrics.ScrollableOverflow().y,
|
||||||
|
aMetrics.ScrollableOverflow().width,
|
||||||
|
aMetrics.ScrollableOverflow().height);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gLameReflowMetrics) {
|
||||||
|
PRTime end = PR_Now();
|
||||||
|
|
||||||
|
int32_t ectc = nsLineBox::GetCtorCount();
|
||||||
|
int32_t numLines = mLines.size();
|
||||||
|
if (!numLines) {
|
||||||
|
numLines = 1;
|
||||||
|
}
|
||||||
|
PRTime delta, perLineDelta, lines;
|
||||||
|
lines = int64_t(numLines);
|
||||||
|
delta = end - start;
|
||||||
|
perLineDelta = delta / lines;
|
||||||
|
|
||||||
|
ListTag(stdout);
|
||||||
|
char buf[400];
|
||||||
|
SprintfLiteral(buf,
|
||||||
|
": %" PRId64 " elapsed (%" PRId64
|
||||||
|
" per line) (%d lines; %d new lines)",
|
||||||
|
delta, perLineDelta, numLines, ectc - ctc);
|
||||||
|
printf("%s\n", buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext,
|
||||||
|
ReflowOutput& aMetrics,
|
||||||
|
const ReflowInput& aReflowInput,
|
||||||
|
TrialReflowState& aTrialState) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Between when we drain pushed floats and when we complete reflow,
|
// Between when we drain pushed floats and when we complete reflow,
|
||||||
// we're allowed to have multiple continuations of the same float on
|
// we're allowed to have multiple continuations of the same float on
|
||||||
|
@ -1458,21 +1768,18 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
|
IsMarginRoot(&blockStartMarginRoot, &blockEndMarginRoot);
|
||||||
|
|
||||||
BlockReflowState state(aReflowInput, aPresContext, this, blockStartMarginRoot,
|
BlockReflowState state(aReflowInput, aPresContext, this, blockStartMarginRoot,
|
||||||
blockEndMarginRoot, needFloatManager, consumedBSize,
|
blockEndMarginRoot, aTrialState.mNeedFloatManager,
|
||||||
effectiveContentBoxBSize);
|
aTrialState.mConsumedBSize,
|
||||||
|
aTrialState.mEffectiveContentBoxBSize,
|
||||||
if (HasAnyStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION) &&
|
aTrialState.mInset);
|
||||||
PresContext()->BidiEnabled()) {
|
|
||||||
static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle paginated overflow (see nsContainerFrame.h)
|
// Handle paginated overflow (see nsContainerFrame.h)
|
||||||
OverflowAreas ocBounds;
|
|
||||||
nsReflowStatus ocStatus;
|
nsReflowStatus ocStatus;
|
||||||
if (GetPrevInFlow()) {
|
if (GetPrevInFlow()) {
|
||||||
ReflowOverflowContainerChildren(
|
ReflowOverflowContainerChildren(
|
||||||
aPresContext, aReflowInput, ocBounds, ReflowChildFlags::Default,
|
aPresContext, aReflowInput, aTrialState.mOcBounds,
|
||||||
ocStatus, DefaultChildFrameMerge, Some(state.ContainerSize()));
|
ReflowChildFlags::Default, ocStatus, DefaultChildFrameMerge,
|
||||||
|
Some(state.ContainerSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that we're done cleaning up our overflow container lists, we can
|
// Now that we're done cleaning up our overflow container lists, we can
|
||||||
|
@ -1482,8 +1789,7 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
|
|
||||||
// Drain & handle pushed floats
|
// Drain & handle pushed floats
|
||||||
DrainPushedFloats();
|
DrainPushedFloats();
|
||||||
OverflowAreas fcBounds;
|
ReflowPushedFloats(state, aTrialState.mFcBounds);
|
||||||
ReflowPushedFloats(state, fcBounds);
|
|
||||||
|
|
||||||
// If we're not dirty (which means we'll mark everything dirty later)
|
// If we're not dirty (which means we'll mark everything dirty later)
|
||||||
// and our inline-size has changed, mark the lines dirty that we need to
|
// and our inline-size has changed, mark the lines dirty that we need to
|
||||||
|
@ -1500,7 +1806,13 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
mLines.front()->MarkDirty();
|
mLines.front()->MarkDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyMarkLinesDirty();
|
// For text-wrap:balance trials, we need to reflow all the lines even if
|
||||||
|
// they're not all "dirty".
|
||||||
|
if (aTrialState.mBalancing) {
|
||||||
|
MarkAllDescendantLinesDirty(this);
|
||||||
|
} else {
|
||||||
|
LazyMarkLinesDirty();
|
||||||
|
}
|
||||||
|
|
||||||
// Now reflow...
|
// Now reflow...
|
||||||
ReflowDirtyLines(state);
|
ReflowDirtyLines(state);
|
||||||
|
@ -1613,204 +1925,12 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
|
||||||
|
|
||||||
CheckFloats(state);
|
CheckFloats(state);
|
||||||
|
|
||||||
// Compute our final size
|
// Compute our final size (for this trial layout)
|
||||||
nscoord blockEndEdgeOfChildren;
|
aTrialState.mBlockEndEdgeOfChildren =
|
||||||
ComputeFinalSize(aReflowInput, state, aMetrics, &blockEndEdgeOfChildren);
|
ComputeFinalSize(aReflowInput, state, aMetrics);
|
||||||
|
aTrialState.mContainerWidth = state.ContainerSize().width;
|
||||||
|
|
||||||
// If the block direction is right-to-left, we need to update the bounds of
|
return state.mReflowStatus;
|
||||||
// lines that were placed relative to mContainerSize during reflow, as
|
|
||||||
// we typically do not know the true container size until we've reflowed all
|
|
||||||
// its children. So we use a dummy mContainerSize during reflow (see
|
|
||||||
// BlockReflowState's constructor) and then fix up the positions of the
|
|
||||||
// lines here, once the final block size is known.
|
|
||||||
//
|
|
||||||
// Note that writing-mode:vertical-rl is the only case where the block
|
|
||||||
// logical direction progresses in a negative physical direction, and
|
|
||||||
// therefore block-dir coordinate conversion depends on knowing the width
|
|
||||||
// of the coordinate space in order to translate between the logical and
|
|
||||||
// physical origins.
|
|
||||||
if (wm.IsVerticalRL()) {
|
|
||||||
nsSize containerSize = aMetrics.PhysicalSize();
|
|
||||||
nscoord deltaX = containerSize.width - state.ContainerSize().width;
|
|
||||||
if (deltaX != 0) {
|
|
||||||
// We compute our lines and markers' overflow areas later in
|
|
||||||
// ComputeOverflowAreas(), so we don't need to adjust their overflow areas
|
|
||||||
// here.
|
|
||||||
const nsPoint physicalDelta(deltaX, 0);
|
|
||||||
for (auto& line : Lines()) {
|
|
||||||
UpdateLineContainerSize(&line, containerSize);
|
|
||||||
}
|
|
||||||
fcBounds.Clear();
|
|
||||||
for (nsIFrame* f : mFloats) {
|
|
||||||
f->MovePositionBy(physicalDelta);
|
|
||||||
ConsiderChildOverflow(fcBounds, f);
|
|
||||||
}
|
|
||||||
nsFrameList* markerList = GetOutsideMarkerList();
|
|
||||||
if (markerList) {
|
|
||||||
for (nsIFrame* f : *markerList) {
|
|
||||||
f->MovePositionBy(physicalDelta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nsFrameList* overflowContainers = GetOverflowContainers()) {
|
|
||||||
ocBounds.Clear();
|
|
||||||
for (nsIFrame* f : *overflowContainers) {
|
|
||||||
f->MovePositionBy(physicalDelta);
|
|
||||||
ConsiderChildOverflow(ocBounds, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aMetrics.SetOverflowAreasToDesiredBounds();
|
|
||||||
ComputeOverflowAreas(aMetrics.mOverflowAreas, blockEndEdgeOfChildren,
|
|
||||||
aReflowInput.mStyleDisplay);
|
|
||||||
// Factor overflow container child bounds into the overflow area
|
|
||||||
aMetrics.mOverflowAreas.UnionWith(ocBounds);
|
|
||||||
// Factor pushed float child bounds into the overflow area
|
|
||||||
aMetrics.mOverflowAreas.UnionWith(fcBounds);
|
|
||||||
|
|
||||||
// Let the absolutely positioned container reflow any absolutely positioned
|
|
||||||
// child frames that need to be reflowed, e.g., elements with a percentage
|
|
||||||
// based width/height
|
|
||||||
// We want to do this under either of two conditions:
|
|
||||||
// 1. If we didn't do the incremental reflow above.
|
|
||||||
// 2. If our size changed.
|
|
||||||
// Even though it's the padding edge that's the containing block, we
|
|
||||||
// can use our rect (the border edge) since if the border style
|
|
||||||
// changed, the reflow would have been targeted at us so we'd satisfy
|
|
||||||
// condition 1.
|
|
||||||
// XXX checking oldSize is bogus, there are various reasons we might have
|
|
||||||
// reflowed but our size might not have been changed to what we
|
|
||||||
// asked for (e.g., we ended up being pushed to a new page)
|
|
||||||
// When WillReflowAgainForClearance is true, we will reflow again without
|
|
||||||
// resetting the size. Because of this, we must not reflow our abs-pos
|
|
||||||
// children in that situation --- what we think is our "new size" will not be
|
|
||||||
// our real new size. This also happens to be more efficient.
|
|
||||||
WritingMode parentWM = aMetrics.GetWritingMode();
|
|
||||||
if (HasAbsolutelyPositionedChildren()) {
|
|
||||||
nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
|
|
||||||
bool haveInterrupt = aPresContext->HasPendingInterrupt();
|
|
||||||
if (aReflowInput.WillReflowAgainForClearance() || haveInterrupt) {
|
|
||||||
// Make sure that when we reflow again we'll actually reflow all the abs
|
|
||||||
// pos frames that might conceivably depend on our size (or all of them,
|
|
||||||
// if we're dirty right now and interrupted; in that case we also need
|
|
||||||
// to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
|
|
||||||
// better than that, because we don't really know what our size will be,
|
|
||||||
// and it might in fact not change on the followup reflow!
|
|
||||||
if (haveInterrupt && HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
|
|
||||||
absoluteContainer->MarkAllFramesDirty();
|
|
||||||
} else {
|
|
||||||
absoluteContainer->MarkSizeDependentFramesDirty();
|
|
||||||
}
|
|
||||||
if (haveInterrupt) {
|
|
||||||
// We're not going to reflow absolute frames; make sure to account for
|
|
||||||
// their existing overflow areas, which is usually a side effect of this
|
|
||||||
// reflow.
|
|
||||||
//
|
|
||||||
// TODO(emilio): nsAbsoluteContainingBlock::Reflow already checks for
|
|
||||||
// interrupt, can we just rely on it and unconditionally take the else
|
|
||||||
// branch below? That's a bit more subtle / risky, since I don't see
|
|
||||||
// what would reflow them in that case if they depended on our size.
|
|
||||||
for (nsIFrame* kid = absoluteContainer->GetChildList().FirstChild();
|
|
||||||
kid; kid = kid->GetNextSibling()) {
|
|
||||||
ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogicalSize containingBlockSize =
|
|
||||||
CalculateContainingBlockSizeForAbsolutes(parentWM, aReflowInput,
|
|
||||||
aMetrics.Size(parentWM));
|
|
||||||
|
|
||||||
// Mark frames that depend on changes we just made to this frame as dirty:
|
|
||||||
// Now we can assume that the padding edge hasn't moved.
|
|
||||||
// We need to reflow the absolutes if one of them depends on
|
|
||||||
// its placeholder position, or the containing block size in a
|
|
||||||
// direction in which the containing block size might have
|
|
||||||
// changed.
|
|
||||||
|
|
||||||
// XXX "width" and "height" in this block will become ISize and BSize
|
|
||||||
// when nsAbsoluteContainingBlock is logicalized
|
|
||||||
bool cbWidthChanged = aMetrics.Width() != oldSize.width;
|
|
||||||
bool isRoot = !GetContent()->GetParent();
|
|
||||||
// If isRoot and we have auto height, then we are the initial
|
|
||||||
// containing block and the containing block height is the
|
|
||||||
// viewport height, which can't change during incremental
|
|
||||||
// reflow.
|
|
||||||
bool cbHeightChanged =
|
|
||||||
!(isRoot && NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedHeight()) &&
|
|
||||||
aMetrics.Height() != oldSize.height;
|
|
||||||
|
|
||||||
nsRect containingBlock(nsPoint(0, 0),
|
|
||||||
containingBlockSize.GetPhysicalSize(parentWM));
|
|
||||||
AbsPosReflowFlags flags = AbsPosReflowFlags::ConstrainHeight;
|
|
||||||
if (cbWidthChanged) {
|
|
||||||
flags |= AbsPosReflowFlags::CBWidthChanged;
|
|
||||||
}
|
|
||||||
if (cbHeightChanged) {
|
|
||||||
flags |= AbsPosReflowFlags::CBHeightChanged;
|
|
||||||
}
|
|
||||||
// Setup the line cursor here to optimize line searching for
|
|
||||||
// calculating hypothetical position of absolutely-positioned
|
|
||||||
// frames.
|
|
||||||
SetupLineCursorForQuery();
|
|
||||||
absoluteContainer->Reflow(this, aPresContext, aReflowInput,
|
|
||||||
state.mReflowStatus, containingBlock, flags,
|
|
||||||
&aMetrics.mOverflowAreas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
|
|
||||||
|
|
||||||
aStatus = state.mReflowStatus;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
// Between when we drain pushed floats and when we complete reflow,
|
|
||||||
// we're allowed to have multiple continuations of the same float on
|
|
||||||
// our floats list, since a first-in-flow might get pushed to a later
|
|
||||||
// continuation of its containing block. But it's not permitted
|
|
||||||
// outside that time.
|
|
||||||
nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
|
|
||||||
|
|
||||||
if (gNoisyReflow) {
|
|
||||||
IndentBy(stdout, gNoiseIndent);
|
|
||||||
ListTag(stdout);
|
|
||||||
printf(": status=%s metrics=%d,%d carriedMargin=%d",
|
|
||||||
ToString(aStatus).c_str(), aMetrics.ISize(parentWM),
|
|
||||||
aMetrics.BSize(parentWM), aMetrics.mCarriedOutBEndMargin.get());
|
|
||||||
if (HasOverflowAreas()) {
|
|
||||||
printf(" overflow-vis={%d,%d,%d,%d}", aMetrics.InkOverflow().x,
|
|
||||||
aMetrics.InkOverflow().y, aMetrics.InkOverflow().width,
|
|
||||||
aMetrics.InkOverflow().height);
|
|
||||||
printf(" overflow-scr={%d,%d,%d,%d}", aMetrics.ScrollableOverflow().x,
|
|
||||||
aMetrics.ScrollableOverflow().y,
|
|
||||||
aMetrics.ScrollableOverflow().width,
|
|
||||||
aMetrics.ScrollableOverflow().height);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gLameReflowMetrics) {
|
|
||||||
PRTime end = PR_Now();
|
|
||||||
|
|
||||||
int32_t ectc = nsLineBox::GetCtorCount();
|
|
||||||
int32_t numLines = mLines.size();
|
|
||||||
if (!numLines) {
|
|
||||||
numLines = 1;
|
|
||||||
}
|
|
||||||
PRTime delta, perLineDelta, lines;
|
|
||||||
lines = int64_t(numLines);
|
|
||||||
delta = end - start;
|
|
||||||
perLineDelta = delta / lines;
|
|
||||||
|
|
||||||
ListTag(stdout);
|
|
||||||
char buf[400];
|
|
||||||
SprintfLiteral(buf,
|
|
||||||
": %" PRId64 " elapsed (%" PRId64
|
|
||||||
" per line) (%d lines; %d new lines)",
|
|
||||||
delta, perLineDelta, numLines, ectc - ctc);
|
|
||||||
printf("%s\n", buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
|
bool nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine() {
|
||||||
|
@ -1908,10 +2028,9 @@ static nscoord ApplyLineClamp(const ReflowInput& aReflowInput,
|
||||||
return edge;
|
return edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
|
nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
|
||||||
BlockReflowState& aState,
|
BlockReflowState& aState,
|
||||||
ReflowOutput& aMetrics,
|
ReflowOutput& aMetrics) {
|
||||||
nscoord* aBEndEdgeOfChildren) {
|
|
||||||
WritingMode wm = aState.mReflowInput.GetWritingMode();
|
WritingMode wm = aState.mReflowInput.GetWritingMode();
|
||||||
const LogicalMargin& borderPadding = aState.BorderPadding();
|
const LogicalMargin& borderPadding = aState.BorderPadding();
|
||||||
#ifdef NOISY_FINAL_SIZE
|
#ifdef NOISY_FINAL_SIZE
|
||||||
|
@ -2149,7 +2268,6 @@ void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
|
||||||
|
|
||||||
// Screen out negative block sizes --- can happen due to integer overflows :-(
|
// Screen out negative block sizes --- can happen due to integer overflows :-(
|
||||||
finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
|
finalSize.BSize(wm) = std::max(0, finalSize.BSize(wm));
|
||||||
*aBEndEdgeOfChildren = blockEndEdgeOfChildren;
|
|
||||||
|
|
||||||
if (blockEndEdgeOfChildren != finalSize.BSize(wm) - borderPadding.BEnd(wm)) {
|
if (blockEndEdgeOfChildren != finalSize.BSize(wm) - borderPadding.BEnd(wm)) {
|
||||||
SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
|
SetProperty(BlockEndEdgeOfChildrenProperty(), blockEndEdgeOfChildren);
|
||||||
|
@ -2166,6 +2284,8 @@ void nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput,
|
||||||
printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
|
printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return blockEndEdgeOfChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
|
void nsBlockFrame::ConsiderBlockEndEdgeOfChildren(
|
||||||
|
@ -4552,10 +4672,10 @@ void nsBlockFrame::DoReflowInlineFrames(
|
||||||
// because it might get disabled there
|
// because it might get disabled there
|
||||||
aLine->EnableResizeReflowOptimization();
|
aLine->EnableResizeReflowOptimization();
|
||||||
|
|
||||||
aLineLayout.BeginLineReflow(iStart, aState.mBCoord, availISize, availBSize,
|
aLineLayout.BeginLineReflow(
|
||||||
aFloatAvailableSpace.HasFloats(),
|
iStart, aState.mBCoord, availISize, availBSize,
|
||||||
false, /*XXX isTopOfPage*/
|
aFloatAvailableSpace.HasFloats(), false, /*XXX isTopOfPage*/
|
||||||
lineWM, aState.mContainerSize);
|
lineWM, aState.mContainerSize, aState.mInsetForBalance);
|
||||||
|
|
||||||
aState.mFlags.mIsLineLayoutEmpty = false;
|
aState.mFlags.mIsLineLayoutEmpty = false;
|
||||||
|
|
||||||
|
|
|
@ -481,9 +481,9 @@ class nsBlockFrame : public nsContainerFrame {
|
||||||
// helper for SlideLine and UpdateLineContainerSize
|
// helper for SlideLine and UpdateLineContainerSize
|
||||||
void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord);
|
void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord);
|
||||||
|
|
||||||
void ComputeFinalSize(const ReflowInput& aReflowInput,
|
// Returns block-end edge of children.
|
||||||
BlockReflowState& aState, ReflowOutput& aMetrics,
|
nscoord ComputeFinalSize(const ReflowInput& aReflowInput,
|
||||||
nscoord* aBEndEdgeOfChildren);
|
BlockReflowState& aState, ReflowOutput& aMetrics);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for Reflow(). Computes the overflow areas created by our
|
* Helper method for Reflow(). Computes the overflow areas created by our
|
||||||
|
@ -534,6 +534,61 @@ class nsBlockFrame : public nsContainerFrame {
|
||||||
*/
|
*/
|
||||||
bool IsVisualFormControl(nsPresContext* aPresContext);
|
bool IsVisualFormControl(nsPresContext* aPresContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For text-wrap:balance, we iteratively try reflowing with adjusted inline
|
||||||
|
* size to find the "best" result (the tightest size that can be applied
|
||||||
|
* without increasing the total line count of the block).
|
||||||
|
* This record is used to manage the state of these "trial reflows", and
|
||||||
|
* return results from the final trial.
|
||||||
|
*/
|
||||||
|
struct TrialReflowState {
|
||||||
|
// Values pre-computed at start of Reflow(), constant across trials.
|
||||||
|
const nscoord mConsumedBSize;
|
||||||
|
const nscoord mEffectiveContentBoxBSize;
|
||||||
|
bool mNeedFloatManager;
|
||||||
|
// Settings for the current trial.
|
||||||
|
bool mBalancing = false;
|
||||||
|
nscoord mInset = 0;
|
||||||
|
// Results computed during the trial reflow. Values from the final trial
|
||||||
|
// will be used by the remainder of Reflow().
|
||||||
|
mozilla::OverflowAreas mOcBounds;
|
||||||
|
mozilla::OverflowAreas mFcBounds;
|
||||||
|
nscoord mBlockEndEdgeOfChildren = 0;
|
||||||
|
nscoord mContainerWidth = 0;
|
||||||
|
|
||||||
|
// Initialize for the initial trial reflow, with zero inset.
|
||||||
|
TrialReflowState(nscoord aConsumedBSize, nscoord aEffectiveContentBoxBSize,
|
||||||
|
bool aNeedFloatManager)
|
||||||
|
: mConsumedBSize(aConsumedBSize),
|
||||||
|
mEffectiveContentBoxBSize(aEffectiveContentBoxBSize),
|
||||||
|
mNeedFloatManager(aNeedFloatManager) {}
|
||||||
|
|
||||||
|
// Adjust the inset amount, and reset state for a new trial.
|
||||||
|
void ResetForBalance(nscoord aInsetDelta) {
|
||||||
|
// Tells the reflow-lines loop we must consider all lines "dirty" (as we
|
||||||
|
// are modifying the effective inline-size to be used).
|
||||||
|
mBalancing = true;
|
||||||
|
// Adjust inset to apply.
|
||||||
|
mInset += aInsetDelta;
|
||||||
|
// Re-initialize state that the reflow loop will compute.
|
||||||
|
mOcBounds.Clear();
|
||||||
|
mFcBounds.Clear();
|
||||||
|
mBlockEndEdgeOfChildren = 0;
|
||||||
|
mContainerWidth = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper for Reflow(); may be called repeatedly during a single
|
||||||
|
* Reflow() in order to implement text-wrap:balance.
|
||||||
|
* This method applies aTrialState.mInset during line-breaking to reduce
|
||||||
|
* the effective available inline-size (without affecting alignment).
|
||||||
|
*/
|
||||||
|
nsReflowStatus TrialReflow(nsPresContext* aPresContext,
|
||||||
|
ReflowOutput& aMetrics,
|
||||||
|
const ReflowInput& aReflowInput,
|
||||||
|
TrialReflowState& aTrialState);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Helper function for the frame ctor to register a ::marker frame.
|
* Helper function for the frame ctor to register a ::marker frame.
|
||||||
|
|
|
@ -140,7 +140,8 @@ void nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
|
||||||
nscoord aISize, nscoord aBSize,
|
nscoord aISize, nscoord aBSize,
|
||||||
bool aImpactedByFloats, bool aIsTopOfPage,
|
bool aImpactedByFloats, bool aIsTopOfPage,
|
||||||
WritingMode aWritingMode,
|
WritingMode aWritingMode,
|
||||||
const nsSize& aContainerSize) {
|
const nsSize& aContainerSize,
|
||||||
|
nscoord aInset) {
|
||||||
NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user");
|
NS_ASSERTION(nullptr == mRootSpan, "bad linelayout user");
|
||||||
LAYOUT_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE,
|
LAYOUT_WARN_IF_FALSE(aISize != NS_UNCONSTRAINEDSIZE,
|
||||||
"have unconstrained width; this should only result from "
|
"have unconstrained width; this should only result from "
|
||||||
|
@ -193,6 +194,9 @@ void nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
|
||||||
psd->mIStart = aICoord;
|
psd->mIStart = aICoord;
|
||||||
psd->mICoord = aICoord;
|
psd->mICoord = aICoord;
|
||||||
psd->mIEnd = aICoord + aISize;
|
psd->mIEnd = aICoord + aISize;
|
||||||
|
// Set up inset to be used for text-wrap:balance implementation, but only if
|
||||||
|
// the available size is at least 2*inset.
|
||||||
|
psd->mInset = aISize < aInset * 2 ? 0 : aInset;
|
||||||
mContainerSize = aContainerSize;
|
mContainerSize = aContainerSize;
|
||||||
|
|
||||||
mBStartEdge = aBCoord;
|
mBStartEdge = aBCoord;
|
||||||
|
@ -406,6 +410,7 @@ void nsLineLayout::BeginSpan(nsIFrame* aFrame,
|
||||||
psd->mIStart = aIStart;
|
psd->mIStart = aIStart;
|
||||||
psd->mICoord = aIStart;
|
psd->mICoord = aIStart;
|
||||||
psd->mIEnd = aIEnd;
|
psd->mIEnd = aIEnd;
|
||||||
|
psd->mInset = mCurrentSpan->mInset;
|
||||||
psd->mBaseline = aBaseline;
|
psd->mBaseline = aBaseline;
|
||||||
|
|
||||||
nsIFrame* frame = aSpanReflowInput->mFrame;
|
nsIFrame* frame = aSpanReflowInput->mFrame;
|
||||||
|
@ -801,7 +806,7 @@ void nsLineLayout::ReflowFrame(nsIFrame* aFrame, nsReflowStatus& aReflowStatus,
|
||||||
"have unconstrained width; this should only result from "
|
"have unconstrained width; this should only result from "
|
||||||
"very large sizes, not attempts at intrinsic width "
|
"very large sizes, not attempts at intrinsic width "
|
||||||
"calculation");
|
"calculation");
|
||||||
nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord;
|
nscoord availableSpaceOnLine = psd->mIEnd - psd->mICoord - psd->mInset;
|
||||||
|
|
||||||
// Setup reflow input for reflowing the frame
|
// Setup reflow input for reflowing the frame
|
||||||
Maybe<ReflowInput> reflowInputHolder;
|
Maybe<ReflowInput> reflowInputHolder;
|
||||||
|
|
|
@ -48,7 +48,10 @@ class nsLineLayout {
|
||||||
void BeginLineReflow(nscoord aICoord, nscoord aBCoord, nscoord aISize,
|
void BeginLineReflow(nscoord aICoord, nscoord aBCoord, nscoord aISize,
|
||||||
nscoord aBSize, bool aImpactedByFloats,
|
nscoord aBSize, bool aImpactedByFloats,
|
||||||
bool aIsTopOfPage, mozilla::WritingMode aWritingMode,
|
bool aIsTopOfPage, mozilla::WritingMode aWritingMode,
|
||||||
const nsSize& aContainerSize);
|
const nsSize& aContainerSize,
|
||||||
|
// aInset is used during text-wrap:balance to reduce
|
||||||
|
// the effective available space on the line.
|
||||||
|
nscoord aInset = 0);
|
||||||
|
|
||||||
void EndLineReflow();
|
void EndLineReflow();
|
||||||
|
|
||||||
|
@ -494,6 +497,7 @@ class nsLineLayout {
|
||||||
nscoord mIStart;
|
nscoord mIStart;
|
||||||
nscoord mICoord;
|
nscoord mICoord;
|
||||||
nscoord mIEnd;
|
nscoord mIEnd;
|
||||||
|
nscoord mInset;
|
||||||
|
|
||||||
nscoord mBStartLeading, mBEndLeading;
|
nscoord mBStartLeading, mBEndLeading;
|
||||||
nscoord mLogicalBSize;
|
nscoord mLogicalBSize;
|
||||||
|
|
|
@ -8777,6 +8777,12 @@
|
||||||
value: @IS_NIGHTLY_BUILD@
|
value: @IS_NIGHTLY_BUILD@
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# Maximum number of lines to try balancing.
|
||||||
|
- name: layout.css.text-wrap-balance.limit
|
||||||
|
type: int32_t
|
||||||
|
value: 10
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# Support for the css Zoom property.
|
# Support for the css Zoom property.
|
||||||
- name: layout.css.zoom.enabled
|
- name: layout.css.zoom.enabled
|
||||||
type: RelaxedAtomicBool
|
type: RelaxedAtomicBool
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[text-wrap-invalid.html]
|
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
|
@ -1,39 +1,13 @@
|
||||||
[text-wrap-valid.html]
|
[text-wrap-valid.html]
|
||||||
expected:
|
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
|
||||||
[e.style['text-wrap'\] = "wrap" should set the property value]
|
[e.style['text-wrap'\] = "wrap" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "nowrap" should set the property value]
|
[e.style['text-wrap'\] = "nowrap" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "balance" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "stable" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "pretty" should set the property value]
|
[e.style['text-wrap'\] = "pretty" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "initial" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "inherit" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "unset" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "revert" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "revert-layer" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "auto" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['text-wrap'\] = "wrap auto" should set the property value]
|
[e.style['text-wrap'\] = "wrap auto" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
[white-space-shorthand-text-wrap.html]
|
[white-space-shorthand-text-wrap.html]
|
||||||
[`text-wrap: balance` should be set]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[`text-wrap` should not be affected by previous `white-space`]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[`white-space` should overwrite previous `text-wrap`]
|
[`white-space` should overwrite previous `text-wrap`]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[`text-wrap` should not be affected by `white-space` on the parent]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[`white-space` should overwrite `text-wrap` on the parent]
|
[`white-space` should overwrite `text-wrap` on the parent]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-wrap-balance-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[text-wrap-balance-line-clamp-001.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-wrap-balance-text-indent-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[accumulation-per-property-002.html]
|
||||||
|
[text-wrap: "nowrap" onto "wrap"]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[text-wrap: "wrap" onto "nowrap"]
|
||||||
|
expected: FAIL
|
|
@ -1,3 +1,9 @@
|
||||||
[addition-per-property-002.html]
|
[addition-per-property-002.html]
|
||||||
expected:
|
expected:
|
||||||
if (os == "android") and fission: [TIMEOUT, OK]
|
if (os == "android") and fission: [TIMEOUT, OK]
|
||||||
|
|
||||||
|
[text-wrap: "nowrap" onto "wrap"]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[text-wrap: "wrap" onto "nowrap"]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
[interpolation-per-property-002.html]
|
[interpolation-per-property-002.html]
|
||||||
expected:
|
expected:
|
||||||
if (os == "android") and fission: [OK, TIMEOUT]
|
if (os == "android") and fission: [OK, TIMEOUT]
|
||||||
|
|
||||||
|
[text-wrap uses discrete animation when animating between "wrap" and "nowrap" with linear easing]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[text-wrap uses discrete animation when animating between "wrap" and "nowrap" with effect easing]
|
||||||
|
expected: FAIL
|
||||||
|
|
||||||
|
[text-wrap uses discrete animation when animating between "wrap" and "nowrap" with keyframe easing]
|
||||||
|
expected: FAIL
|
||||||
|
|
Загрузка…
Ссылка в новой задаче