Bug 1812679 - Handle central baseline calculation in `GetNaturalBaselineBOffset`. r=layout-reviewers,emilio

Previously, for writing-mode using central baseline alignment (i.e. `vertical-(lr|rl)`,
we simply used the center of content-box in `nsLineLayout::VerticalAlignFrames`.
However, this is incorrect for e.g. a `div` with two lines of text - just like how
its alphabetical baseline is the baseline of the second line of text, the central
baseline should be the centerline of the second line.

Differential Revision: https://phabricator.services.mozilla.com/D172165
This commit is contained in:
David Shin 2023-03-23 14:35:12 +00:00
Родитель 10135dba08
Коммит 2f94be9968
25 изменённых файлов: 99 добавлений и 113 удалений

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

@ -8591,8 +8591,10 @@ void nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
// The height of our box is the sum of our font size plus the top
// and bottom border and padding. The height of children do not
// affect our height.
aMetrics.SetBlockStartAscent(aLineWM.IsLineInverted() ? fm->MaxDescent()
: fm->MaxAscent());
aMetrics.SetBlockStartAscent(
aLineWM.IsAlphabeticalBaseline()
? aLineWM.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent()
: fm->MaxHeight() / 2);
aMetrics.BSize(aLineWM) = fm->MaxHeight();
} else {
NS_WARNING("Cannot get font metrics - defaulting sizes to 0");

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

@ -103,6 +103,10 @@ Maybe<nscoord> nsCheckboxRadioFrame::GetNaturalBaselineBOffset(
return Nothing{};
}
if (aWM.IsCentralBaseline()) {
return Some(GetLogicalUsedBorderAndPadding(aWM).BStart(aWM) +
ContentSize(aWM).BSize(aWM) / 2);
}
// This is for compatibility with Chrome, Safari and Edge (Dec 2016).
// Treat radio buttons and checkboxes as having an intrinsic baseline
// at the block-end of the control (use the block-end content edge rather

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

@ -300,7 +300,9 @@ void nsHTMLButtonControlFrame::ReflowButtonContents(
// XXX is there a better strategy? should we include border-padding?
if (!aButtonReflowInput.mStyleDisplay->IsContainLayout()) {
if (aButtonDesiredSize.GetWritingMode().IsOrthogonalTo(wm)) {
aButtonDesiredSize.SetBlockStartAscent(contentsDesiredSize.ISize(wm));
aButtonDesiredSize.SetBlockStartAscent(
wm.IsAlphabeticalBaseline() ? contentsDesiredSize.ISize(wm)
: contentsDesiredSize.ISize(wm) / 2);
} else {
aButtonDesiredSize.SetBlockStartAscent(
contentsDesiredSize.BlockStartAscent() + childPos.B(wm));

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

@ -1456,16 +1456,32 @@ static void GetScrollableOverflowForPerspective(
}
}
BaselineSharingGroup nsHTMLScrollFrame::GetDefaultBaselineSharingGroup() const {
return mScrolledFrame->GetDefaultBaselineSharingGroup();
}
nscoord nsHTMLScrollFrame::SynthesizeFallbackBaseline(
mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
// Marign-end even for central baselines.
if (aWM.IsLineInverted()) {
return -GetLogicalUsedMargin(aWM).BStart(aWM);
}
return aBaselineGroup == BaselineSharingGroup::First
? BSize(aWM) + GetLogicalUsedMargin(aWM).BEnd(aWM)
: -GetLogicalUsedMargin(aWM).BEnd(aWM);
}
Maybe<nscoord> nsHTMLScrollFrame::GetNaturalBaselineBOffset(
WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
// Block containers that are scrollable always have a first & last baselines
// Block containers that are scrollable always have a last baseline
// that are synthesized from block-end margin edge.
// Note(dshin): This behaviour is really only relevant to `inline-block`
// alignment context. In the context of table/flex/grid alignment, first/last
// baselines are calculated through `GetFirstLineBaseline`, which does
// calculations of its own.
// https://drafts.csswg.org/css-align/#baseline-export
if (mScrolledFrame->IsBlockFrameOrSubclass()) {
if (aBaselineGroup == BaselineSharingGroup::Last &&
mScrolledFrame->IsBlockFrameOrSubclass()) {
return Some(SynthesizeFallbackBaseline(aWM, aBaselineGroup));
}

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

@ -129,6 +129,10 @@ class nsHTMLScrollFrame : public nsContainerFrame,
bool ComputeCustomOverflow(mozilla::OverflowAreas& aOverflowAreas) final;
BaselineSharingGroup GetDefaultBaselineSharingGroup() const override;
nscoord SynthesizeFallbackBaseline(
mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup) const override;
Maybe<nscoord> GetNaturalBaselineBOffset(
mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup) const override;

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

@ -2113,6 +2113,9 @@ nscoord nsIFrame::SynthesizeFallbackBaseline(
WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
const auto margin = GetLogicalUsedMargin(aWM);
NS_ASSERTION(!IsSubtreeDirty(), "frame must not be dirty");
if (aWM.IsCentralBaseline()) {
return (BSize(aWM) + GetLogicalUsedMargin(aWM).BEnd(aWM)) / 2;
}
// Baseline for inverted line content is the top (block-start) margin edge,
// as the frame is in effect "flipped" for alignment purposes.
if (aWM.IsLineInverted()) {

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

@ -1346,7 +1346,9 @@ void nsLineLayout::PlaceFrame(PerFrameData* pfd, ReflowOutput& aMetrics) {
// its ascent; instead, treat it as a block with baseline at the block-end
// edge (or block-begin in the case of an "inverted" line).
if (pfd->mWritingMode.GetBlockDir() != lineWM.GetBlockDir()) {
pfd->mAscent = lineWM.IsLineInverted() ? 0 : aMetrics.BSize(lineWM);
pfd->mAscent = lineWM.IsAlphabeticalBaseline()
? lineWM.IsLineInverted() ? 0 : aMetrics.BSize(lineWM)
: aMetrics.BSize(lineWM) / 2;
} else {
if (aMetrics.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
pfd->mAscent = pfd->mFrame->GetLogicalBaseline(lineWM);
@ -2014,25 +2016,7 @@ void nsLineLayout::VerticalAlignFrames(PerSpanData* psd) {
switch (keyword) {
default:
case StyleVerticalAlignKeyword::Baseline:
if (lineWM.IsVertical() && !lineWM.IsSideways()) {
// FIXME: We should really use a central baseline from the
// baseline table of the font, rather than assuming it's in
// the middle.
if (frameSpan) {
nscoord borderBoxBSize = pfd->mBounds.BSize(lineWM);
nscoord bStartBP = pfd->mBorderPadding.BStart(lineWM);
nscoord bEndBP = pfd->mBorderPadding.BEnd(lineWM);
nscoord contentBoxBSize = borderBoxBSize - bStartBP - bEndBP;
pfd->mBounds.BStart(lineWM) =
revisedBaselineBCoord - contentBoxBSize / 2 - bStartBP;
} else {
pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord -
logicalBSize / 2 +
pfd->mMargin.BStart(lineWM);
}
} else {
pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
}
pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
pfd->mBlockDirAlign = VALIGN_OTHER;
break;
@ -2152,7 +2136,7 @@ void nsLineLayout::VerticalAlignFrames(PerSpanData* psd) {
// inverted relative to block direction.
nscoord revisedBaselineBCoord =
baselineBCoord - offset * lineWM.FlowRelativeToLineRelativeFactor();
if (lineWM.IsVertical() && !lineWM.IsSideways()) {
if (lineWM.IsCentralBaseline()) {
// If we're using a dominant center baseline, we align with the center
// of the frame being placed (bug 1133945).
pfd->mBounds.BStart(lineWM) =

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

@ -9292,14 +9292,21 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
finalSize.ISize(wm) =
NSToCoordCeilClamped(std::max(gfxFloat(0.0), textMetrics.mAdvanceWidth));
nscoord fontBaseline;
// Note(dshin): Baseline should tecnhically be halfway through the em box for
// a central baseline. It is simply half of the text run block size so that it
// can be easily calculated in `GetNaturalBaselineBOffset`.
if (transformedCharsFit == 0 && !usedHyphenation) {
aMetrics.SetBlockStartAscent(0);
finalSize.BSize(wm) = 0;
fontBaseline = 0;
} else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
fontBaseline = NSToCoordCeil(textMetrics.mAscent);
const auto size = fontBaseline + NSToCoordCeil(textMetrics.mDescent);
// Use actual text metrics for floating first letter frame.
aMetrics.SetBlockStartAscent(NSToCoordCeil(textMetrics.mAscent));
finalSize.BSize(wm) =
aMetrics.BlockStartAscent() + NSToCoordCeil(textMetrics.mDescent);
aMetrics.SetBlockStartAscent(wm.IsAlphabeticalBaseline() ? fontBaseline
: size / 2);
finalSize.BSize(wm) = size;
} else {
// Otherwise, ascent should contain the overline drawable area.
// And also descent should contain the underline drawable area.
@ -9309,16 +9316,18 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
nscoord fontDescent =
wm.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
aMetrics.SetBlockStartAscent(
std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
nscoord descent =
fontBaseline = std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent);
const auto size =
fontBaseline +
std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
aMetrics.SetBlockStartAscent(wm.IsAlphabeticalBaseline() ? fontBaseline
: size / 2);
finalSize.BSize(wm) = size;
}
if (Style()->IsTextCombined()) {
nsFontMetrics* fm = provider.GetFontMetrics();
gfxFloat width = finalSize.ISize(wm);
gfxFloat em = fm->EmHeight();
nscoord width = finalSize.ISize(wm);
nscoord em = fm->EmHeight();
// Compress the characters in horizontal axis if necessary.
if (width <= em) {
RemoveProperty(TextCombineScaleFactorProperty());
@ -9328,8 +9337,9 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
}
// Make the characters be in an 1em square.
if (finalSize.BSize(wm) != em) {
aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
(em - finalSize.BSize(wm)) / 2);
fontBaseline =
aMetrics.BlockStartAscent() + (em - finalSize.BSize(wm)) / 2;
aMetrics.SetBlockStartAscent(fontBaseline);
finalSize.BSize(wm) = em;
}
}
@ -9343,7 +9353,7 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
0,
"Negative descent???");
mAscent = aMetrics.BlockStartAscent();
mAscent = fontBaseline;
// Handle text that runs outside its normal bounds.
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
@ -10021,6 +10031,10 @@ Maybe<nscoord> nsTextFrame::GetNaturalBaselineBOffset(
}
if (!aWM.IsOrthogonalTo(GetWritingMode())) {
if (aWM.IsCentralBaseline()) {
return Some(GetLogicalUsedBorderAndPadding(aWM).BStart(aWM) +
ContentSize(aWM).BSize(aWM) / 2);
}
return Some(mAscent);
}

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

@ -3548,20 +3548,25 @@ Maybe<nscoord> nsTableFrame::GetNaturalBaselineBOffset(
OrderRowGroups(orderedRowGroups);
// XXX not sure if this should be the size of the containing block instead.
nsSize containerSize = mRect.Size();
auto TableBaseline = [aWM, containerSize](nsTableRowGroupFrame* aRowGroup,
nsTableRowFrame* aRow) {
auto TableBaseline = [aWM, containerSize](
nsTableRowGroupFrame* aRowGroup,
nsTableRowFrame* aRow) -> Maybe<nscoord> {
nscoord rgBStart =
LogicalRect(aWM, aRowGroup->GetNormalRect(), containerSize).BStart(aWM);
nscoord rowBStart =
LogicalRect(aWM, aRow->GetNormalRect(), containerSize).BStart(aWM);
return rgBStart + rowBStart + aRow->GetRowBaseline(aWM);
LogicalRect(aWM, aRow->GetNormalRect(), aRowGroup->GetSize())
.BStart(aWM);
return aRow->GetRowBaseline(aWM).map(
[rgBStart, rowBStart](nscoord aBaseline) {
return rgBStart + rowBStart + aBaseline;
});
};
if (aBaselineGroup == BaselineSharingGroup::First) {
for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
nsTableRowFrame* row = rgFrame->GetFirstRow();
if (row) {
return Some(TableBaseline(rgFrame, row));
return TableBaseline(rgFrame, row);
}
}
} else {
@ -3569,7 +3574,9 @@ Maybe<nscoord> nsTableFrame::GetNaturalBaselineBOffset(
nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
nsTableRowFrame* row = rgFrame->GetLastRow();
if (row) {
return Some(BSize(aWM) - TableBaseline(rgFrame, row));
return TableBaseline(rgFrame, row).map([this, aWM](nscoord aBaseline) {
return BSize(aWM) - aBaseline;
});
}
}
}

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

@ -398,13 +398,15 @@ void nsTableRowFrame::DidResize() {
// *including* cells with rowspans
nscoord nsTableRowFrame::GetMaxCellAscent() const { return mMaxCellAscent; }
nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWM) {
Maybe<nscoord> nsTableRowFrame::GetRowBaseline(WritingMode aWM) {
if (mMaxCellAscent) {
return mMaxCellAscent;
return Some(mMaxCellAscent);
}
// If we get here, we don't have a baseline on any of the cells in this row.
if (aWM.IsCentralBaseline()) {
return Nothing{};
}
nscoord ascent = 0;
for (nsIFrame* childFrame : mFrames) {
MOZ_ASSERT(childFrame->IsTableCellFrame());
@ -412,7 +414,7 @@ nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWM) {
childFrame, aWM, BaselineSharingGroup::First);
ascent = std::max(ascent, s);
}
return ascent;
return Some(ascent);
}
nscoord nsTableRowFrame::GetInitialBSize(nscoord aPctBasis) const {

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

@ -132,7 +132,7 @@ class nsTableRowFrame : public nsContainerFrame {
/* return the row ascent
*/
nscoord GetRowBaseline(mozilla::WritingMode aWritingMode);
Maybe<nscoord> GetRowBaseline(mozilla::WritingMode aWM);
/** returns the ordinal position of this row in its table */
virtual int32_t GetRowIndex() const;

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

@ -28,9 +28,12 @@ using namespace mozilla::layout;
nscoord nsTableWrapperFrame::SynthesizeFallbackBaseline(
mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
const auto marginBlockEnd = GetLogicalUsedMargin(aWM).BEnd(aWM);
if (aWM.IsCentralBaseline()) {
return (BSize(aWM) + marginBlockEnd) / 2;
}
// Our fallback baseline is the block-end margin-edge, with respect to the
// given writing mode.
const auto marginBlockEnd = GetLogicalUsedMargin(aWM).BEnd(aWM);
if (aBaselineGroup == BaselineSharingGroup::Last) {
return -marginBlockEnd;
}

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

@ -1,2 +0,0 @@
[inline-table-inline-block-baseline-vert-rl.html]
expected: FAIL

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

@ -5,6 +5,9 @@
[.target > * 3]
expected: FAIL
[.target > * 4]
expected: FAIL
[.target > * 5]
expected: FAIL
@ -14,18 +17,12 @@
[.target > * 9]
expected: FAIL
[.target > * 11]
expected: FAIL
[.target > * 13]
[.target > * 10]
expected: FAIL
[.target > * 15]
expected: FAIL
[.target > * 17]
expected: FAIL
[.target > * 19]
expected: FAIL

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

@ -14,20 +14,8 @@
[.target > * 9]
expected: FAIL
[.target > * 11]
expected: FAIL
[.target > * 13]
expected: FAIL
[.target > * 15]
expected: FAIL
[.target > * 17]
expected: FAIL
[.target > * 19]
expected: FAIL
[.target > * 21]
expected: FAIL

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

@ -1,19 +1,17 @@
[baseline-source-last-002.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[.target > * 1]
[.target > * 3]
expected: FAIL
[.target > * 3]
[.target > * 4]
expected: FAIL
[.target > * 5]
expected: FAIL
[.target > * 7]
[.target > * 9]
expected: FAIL
[.target > * 9]
[.target > * 10]
expected: FAIL
[.target > * 11]
@ -22,9 +20,6 @@
[.target > * 13]
expected: FAIL
[.target > * 15]
expected: FAIL
[.target > * 17]
expected: FAIL

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

@ -1,16 +1,10 @@
[baseline-source-last-003.html]
[.target > * 1]
expected: FAIL
[.target > * 3]
expected: FAIL
[.target > * 5]
expected: FAIL
[.target > * 7]
expected: FAIL
[.target > * 9]
expected: FAIL
@ -20,9 +14,6 @@
[.target > * 13]
expected: FAIL
[.target > * 15]
expected: FAIL
[.target > * 17]
expected: FAIL

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

@ -1,3 +0,0 @@
[inline-block-alignment-002.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-block-alignment-003.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-block-alignment-004.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-block-alignment-005.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-table-alignment-002.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-table-alignment-003.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-table-alignment-004.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952

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

@ -1,3 +0,0 @@
[inline-table-alignment-005.xht]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1179952