Bug 1798810 - Update calculation of baseline for multicolumn containers. r=layout-reviewers,TYLin

First/Last Baseline should be from highest/lowest baseline in all columns and
spanners.

Differential Revision: https://phabricator.services.mozilla.com/D171261
This commit is contained in:
David Shin
2023-03-08 13:33:28 +00:00
parent 581d937a28
commit 771be6776d
9 changed files with 128 additions and 9 deletions

View File

@@ -5838,8 +5838,7 @@ bool nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
return false;
}
if (fType == LayoutFrameType::FieldSet ||
fType == LayoutFrameType::ColumnSet) {
if (fType == LayoutFrameType::FieldSet) {
LinePosition kidPosition;
nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
// If aFrame is fieldset, kid might be a legend frame here, but that's ok.
@@ -5851,6 +5850,29 @@ bool nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
return false;
}
if (fType == LayoutFrameType::ColumnSet) {
// Note(dshin): This is basically the same as
// `nsColumnSetFrame::GetNaturalBaselineBOffset`, but with line start and
// end, all stored in `LinePosition`. Field value apart from baseline is
// used in one other place
// (`nsBlockFrame`) - if that goes away, this becomes a duplication that
// should be removed.
LinePosition kidPosition;
for (const auto* kid : aFrame->PrincipalChildList()) {
LinePosition position;
if (!GetFirstLinePosition(aWM, kid, &position)) {
continue;
}
if (position.mBaseline < kidPosition.mBaseline) {
kidPosition = position;
}
}
if (kidPosition.mBaseline != nscoord_MAX) {
*aResult = kidPosition;
return true;
}
}
// No baseline.
return false;
}
@@ -5906,6 +5928,21 @@ bool nsLayoutUtils::GetLastLineBaseline(WritingMode aWM, const nsIFrame* aFrame,
*aResult = std::clamp(*aResult, 0, maxBaseline);
return true;
}
// No need to duplicate the baseline logic (Unlike `GetFirstLinePosition`,
// we don't need to return any other value apart from baseline), just defer
// to `GetNaturalBaselineBOffset`. Technically, we could do this at
// `ColumnSetWrapperFrame` level, but this keeps it symmetric to
// `GetFirstLinePosition`.
if (aFrame->IsColumnSetFrame()) {
const auto baseline =
aFrame->GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::Last);
if (!baseline) {
return false;
}
*aResult = aFrame->BSize(aWM) - *baseline;
return true;
}
// No baseline.
return false;
}

View File

@@ -1754,7 +1754,9 @@ class nsLayoutUtils {
* Otherwise returns false.
*/
struct LinePosition {
nscoord mBStart, mBaseline, mBEnd;
nscoord mBStart{nscoord_MAX};
nscoord mBaseline{nscoord_MAX};
nscoord mBEnd{nscoord_MAX};
LinePosition operator+(nscoord aOffset) const {
LinePosition result;

View File

@@ -232,6 +232,53 @@ nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) {
return iSize;
}
template <typename Iterator>
Maybe<nscoord> ColumnSetWrapperFrame::GetBaselineBOffset(
Iterator aStart, Iterator aEnd, WritingMode aWM,
BaselineSharingGroup aBaselineGroup) const {
// Either forward iterator + first baseline, or reverse iterator + last
// baseline
MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() &&
aBaselineGroup == BaselineSharingGroup::First) ||
(*aStart == PrincipalChildList().LastChild() &&
aBaselineGroup == BaselineSharingGroup::Last),
"Iterator direction must match baseline sharing group.");
if (StyleDisplay()->IsContainLayout()) {
return Nothing{};
}
// Start from start/end of principal child list, and use the first valid
// baseline.
for (auto itr = aStart; itr != aEnd; ++itr) {
const nsIFrame* kid = *itr;
auto kidBaseline = kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup);
if (!kidBaseline) {
continue;
}
// Baseline is offset from the kid's rectangle, so find the offset to the
// kid's rectangle.
LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
kid->GetLogicalSize(aWM)};
if (aBaselineGroup == BaselineSharingGroup::First) {
*kidBaseline += kidRect.BStart(aWM);
} else {
*kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
}
return kidBaseline;
}
return Nothing{};
}
Maybe<nscoord> ColumnSetWrapperFrame::GetNaturalBaselineBOffset(
WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
if (aBaselineGroup == BaselineSharingGroup::First) {
return GetBaselineBOffset(PrincipalChildList().cbegin(),
PrincipalChildList().cend(), aWM, aBaselineGroup);
}
return GetBaselineBOffset(PrincipalChildList().crbegin(),
PrincipalChildList().crend(), aWM, aBaselineGroup);
}
#ifdef DEBUG
/* static */

View File

@@ -59,6 +59,9 @@ class ColumnSetWrapperFrame final : public nsBlockFrame {
nscoord GetPrefISize(gfxContext* aRenderingContext) override;
Maybe<nscoord> GetNaturalBaselineBOffset(
WritingMode aWM, BaselineSharingGroup aBaselineGroup) const override;
private:
explicit ColumnSetWrapperFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext);
@@ -71,6 +74,11 @@ class ColumnSetWrapperFrame final : public nsBlockFrame {
// its descendants.
bool mFinishedBuildingColumns = false;
#endif
template <typename Iterator>
Maybe<nscoord> GetBaselineBOffset(Iterator aStart, Iterator aEnd,
WritingMode aWM,
BaselineSharingGroup aBaselineGroup) const;
};
} // namespace mozilla

View File

@@ -15,6 +15,7 @@
#include "mozilla/ToString.h"
#include "nsCSSRendering.h"
#include "nsDisplayList.h"
#include "nsIFrameInlines.h"
#include "nsLayoutUtils.h"
using namespace mozilla;
@@ -1295,6 +1296,32 @@ void nsColumnSetFrame::AppendDirectlyOwnedAnonBoxes(
aResult.AppendElement(OwnedAnonBox(column));
}
Maybe<nscoord> nsColumnSetFrame::GetNaturalBaselineBOffset(
WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
Maybe<nscoord> result;
for (const auto* kid : mFrames) {
auto kidBaseline = kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup);
if (!kidBaseline) {
continue;
}
// The kid frame may not necessarily be aligned with the columnset frame.
LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()),
kid->GetLogicalSize(aWM)};
if (aBaselineGroup == BaselineSharingGroup::First) {
*kidBaseline += kidRect.BStart(aWM);
} else {
*kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM));
}
// Take the smallest of the baselines (i.e. Closest to border-block-start
// for `BaselineSharingGroup::First`, border-block-end for
// `BaselineSharingGroup::Last`)
if (!result || *kidBaseline < *result) {
result = kidBaseline;
}
}
return result;
}
#ifdef DEBUG
void nsColumnSetFrame::SetInitialChildList(ChildListID aListID,
nsFrameList&& aChildList) {

View File

@@ -74,6 +74,10 @@ class nsColumnSetFrame final : public nsContainerFrame {
gfxContext* aCtx, const nsRect& aDirtyRect,
const nsPoint& aPt);
Maybe<nscoord> GetNaturalBaselineBOffset(
mozilla::WritingMode aWM,
BaselineSharingGroup aBaselineGroup) const override;
protected:
nscoord mLastBalanceBSize;
nsReflowStatus mLastFrameStatus;

View File

@@ -1,2 +0,0 @@
[baseline-001.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[baseline-002.html]
expected: FAIL

View File

@@ -1,2 +0,0 @@
[baseline-008.html]
expected: FAIL