Bug 1731541 - When line-clamp is in effect, make text-wrap:balance consider only the lines up to the clamp limit. r=emilio

This corresponds to how Chrome behaves, and passes the test they included in WPT.

It's unclear to me whether this behavior actually follows from the current spec
(see https://github.com/w3c/csswg-drafts/issues/9310), but it seems to be the desired
result.

(I've put it behind a (default-enabled) pref for now, so that it's possible to
experiment with the two possible interpretations, but we can remove the pref once
the spec question is clarified/confirmed.)

This patch also disables balancing for fragmented/overflowing blocks, as that will
not currently work well. We may want to address that as a followup issue (though it
won't matter to the primary balance use-cases such as titles).

Depends on D188139

Differential Revision: https://phabricator.services.mozilla.com/D188220
This commit is contained in:
Jonathan Kew 2023-09-30 15:53:13 +00:00
Родитель 526dc74d07
Коммит a3e6067540
3 изменённых файлов: 77 добавлений и 15 удалений

Просмотреть файл

@ -1449,9 +1449,25 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
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;
// Struct used to hold the "target" number of lines or clamp position to
// maintain when doing text-wrap: balance.
struct BalanceTarget {
// If line-clamp is in effect, mContent and mOffset indicate the starting
// position of the first line after the clamp limit. If line-clamp is not
// in use, mContent is null and mOffset is the total number of lines that
// the block must contain.
nsIContent* mContent = nullptr;
int32_t mOffset = -1;
bool operator==(const BalanceTarget& aOther) const {
return mContent == aOther.mContent && mOffset == aOther.mOffset;
}
bool operator!=(const BalanceTarget& aOther) const {
return !(*this == aOther);
}
};
BalanceTarget balanceTarget;
// Helpers for text-wrap: balance implementation:
@ -1467,6 +1483,24 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
return n;
};
// Return a BalanceTarget record representing the position at which line-clamp
// will take effect for the current line list. Only to be used when there are
// enough lines that the clamp will apply.
auto getClampPosition = [&](uint32_t aClampCount) -> BalanceTarget {
MOZ_ASSERT(aClampCount < mLines.size());
auto iter = mLines.begin();
for (uint32_t i = 0; i < aClampCount; i++) {
++iter;
}
nsIContent* content = iter->mFirstChild->GetContent();
int32_t offset = 0;
if (content && iter->mFirstChild->IsTextFrame()) {
auto* textFrame = static_cast<nsTextFrame*>(iter->mFirstChild);
offset = textFrame->GetContentOffset();
}
return BalanceTarget{content, offset};
};
// "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
@ -1495,30 +1529,57 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
if (!reflowStatus.IsFullyComplete()) {
break;
}
balanceTarget =
balanceTarget.mOffset =
countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
if (balanceTarget < 2) {
if (balanceTarget.mOffset < 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;
balanceStep = aReflowInput.ComputedISize() / balanceTarget.mOffset;
trialState.ResetForBalance(balanceStep);
balanceStep /= 2;
// If -webkit-line-clamp is in effect, then we need to maintain the
// content location at which clamping occurs, rather than the total
// number of lines in the block.
if (StaticPrefs::layout_css_text_wrap_balance_after_clamp_enabled() &&
IsLineClampRoot(this)) {
uint32_t lineClampCount = aReflowInput.mStyleDisplay->mWebkitLineClamp;
if (uint32_t(balanceTarget.mOffset) > lineClampCount) {
auto t = getClampPosition(lineClampCount);
if (t.mContent) {
balanceTarget = t;
}
}
}
// Restore initial floatManager state for a new trial with updated inset.
aReflowInput.mFloatManager->PopState(&floatManagerState);
continue;
}
// Helper to determine whether the current trial succeeded (i.e. was able
// to fit the content into the expected number of lines).
auto trialSucceeded = [&]() -> bool {
if (!reflowStatus.IsFullyComplete()) {
return false;
}
if (balanceTarget.mContent) {
auto t = getClampPosition(aReflowInput.mStyleDisplay->mWebkitLineClamp);
return t == balanceTarget;
}
int32_t numLines =
countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit());
return numLines == balanceTarget.mOffset;
};
// 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) {
if (trialSucceeded()) {
trialState.ResetForBalance(balanceStep);
} else {
trialState.ResetForBalance(-balanceStep);
@ -1531,10 +1592,8 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
// 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) {
if (balanceTarget.mOffset >= 0) {
if (trialSucceeded()) {
break;
}
trialState.ResetForBalance(-1);

Просмотреть файл

@ -8783,6 +8783,11 @@
value: 10
mirror: always
- name: layout.css.text-wrap-balance-after-clamp.enabled
type: bool
value: true
mirror: always
# Support for the css Zoom property.
- name: layout.css.zoom.enabled
type: RelaxedAtomicBool

Просмотреть файл

@ -1,2 +0,0 @@
[text-wrap-balance-line-clamp-001.html]
expected: FAIL