Skip to content

Commit

Permalink
[last-baseline] Propagate last baslines from flexbox.
Browse files Browse the repository at this point in the history
This modifies flex's baseline accumulator to support three(!) potential
baselines for the first & last baseline.

Firstly we prefer the major/minor baseline for the first/last baseline
of the first/last line.

Then we prefer the minor/major baseline for the first/last baseline of
the first/last line.

Finally fallback baseline is the first/last item in the first/last line.

Bug: 885175
Change-Id: I8113c48e3f6861895f89585f39fd9268e5d70f65
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3914072
Commit-Queue: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: David Grogan <dgrogan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1053037}
  • Loading branch information
bfgeek authored and Chromium LUCI CQ committed Sep 29, 2022
1 parent 271ceeb commit 4293e3e
Show file tree
Hide file tree
Showing 28 changed files with 1,742 additions and 375 deletions.
11 changes: 6 additions & 5 deletions third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
Expand Up @@ -942,13 +942,13 @@ void FlexLayoutAlgorithm::FlipForWrapReverse(
line_context.cross_axis_offset_ - cross_axis_start_edge;
LayoutUnit new_offset = cross_axis_content_size - original_offset -
line_context.cross_axis_extent_;
LayoutUnit delta = new_offset - original_offset;
if (flex_line_outputs) {
line_context.cross_axis_offset_ = new_offset;
(*flex_line_outputs)[i].cross_axis_offset = new_offset;
line_context.cross_axis_offset_ += delta;
(*flex_line_outputs)[i].cross_axis_offset += delta;
}
LayoutUnit wrap_reverse_difference = new_offset - original_offset;
for (FlexItem& flex_item : line_context.line_items_)
flex_item.offset_->cross_axis_offset += wrap_reverse_difference;
flex_item.offset_->cross_axis_offset += delta;
}
}

Expand Down Expand Up @@ -1263,7 +1263,8 @@ FlexItem* FlexLayoutAlgorithm::FlexItemAtIndex(wtf_size_t line_index,
line_index = flex_lines_.size() - line_index - 1;

DCHECK_LT(item_index, flex_lines_[line_index].line_items_.size());
if (Style()->ResolvedIsColumnReverseFlexDirection())
if (Style()->ResolvedIsColumnReverseFlexDirection() ||
Style()->ResolvedIsRowReverseFlexDirection())
item_index = flex_lines_[line_index].line_items_.size() - item_index - 1;
return const_cast<FlexItem*>(
&flex_lines_[line_index].line_items_[item_index]);
Expand Down
Expand Up @@ -346,8 +346,8 @@ class FlexLine {
LayoutUnit cross_axis_offset_;
LayoutUnit cross_axis_extent_;

LayoutUnit max_major_ascent_;
LayoutUnit max_minor_ascent_;
LayoutUnit max_major_ascent_ = LayoutUnit::Min();
LayoutUnit max_minor_ascent_ = LayoutUnit::Min();
};

// This class implements the CSS Flexbox layout algorithm:
Expand Down
Expand Up @@ -87,6 +87,8 @@ const NGLayoutResult* NGCustomLayoutAlgorithm::Layout() {
ScriptForbiddenScope::AllowUserAgentScript allow_script;
CustomLayoutScope scope;

// TODO(ikilpatrick): Scale inputs/outputs by effective-zoom.
const float effective_zoom = Style().EffectiveZoom();
const AtomicString& name = Style().DisplayLayoutCustomName();
const Document& document = Node().GetDocument();
LayoutWorklet* worklet = LayoutWorklet::From(*document.domWindow());
Expand Down Expand Up @@ -171,8 +173,8 @@ const NGLayoutResult* NGCustomLayoutAlgorithm::Layout() {
// TODO(ikilpatrick): Allow setting both the first/last baseline instead of a
// general baseline.
if (fragment_result_options->hasBaseline()) {
LayoutUnit baseline =
LayoutUnit::FromDoubleRound(fragment_result_options->baseline());
LayoutUnit baseline = LayoutUnit::FromDoubleRound(
effective_zoom * fragment_result_options->baseline());
container_builder_.SetBaselines(baseline);
}

Expand Down
Expand Up @@ -45,6 +45,81 @@ namespace blink {

namespace {

class BaselineAccumulator {
STACK_ALLOCATED();

public:
explicit BaselineAccumulator(const ComputedStyle& style)
: font_baseline_(style.GetFontBaseline()) {}

void AccumulateItem(const NGBoxFragment& fragment,
const LayoutUnit block_offset,
bool is_first_line,
bool is_last_line) {
if (is_first_line) {
if (!first_fallback_baseline_) {
first_fallback_baseline_ =
block_offset + fragment.FirstBaselineOrSynthesize(font_baseline_);
}
}

if (is_last_line) {
last_fallback_baseline_ =
block_offset + fragment.LastBaselineOrSynthesize(font_baseline_);
}
}

void AccumulateLine(const NGFlexLine& line,
bool is_first_line,
bool is_last_line) {
if (is_first_line) {
if (line.major_baseline != LayoutUnit::Min()) {
first_major_baseline_ = line.cross_axis_offset + line.major_baseline;
}
if (line.minor_baseline != LayoutUnit::Min()) {
first_minor_baseline_ =
line.cross_axis_offset + line.line_cross_size - line.minor_baseline;
}
}

if (is_last_line) {
if (line.major_baseline != LayoutUnit::Min()) {
last_major_baseline_ = line.cross_axis_offset + line.major_baseline;
}
if (line.minor_baseline != LayoutUnit::Min()) {
last_minor_baseline_ =
line.cross_axis_offset + line.line_cross_size - line.minor_baseline;
}
}
}

absl::optional<LayoutUnit> FirstBaseline() const {
if (first_major_baseline_)
return *first_major_baseline_;
if (first_minor_baseline_)
return *first_minor_baseline_;
return first_fallback_baseline_;
}
absl::optional<LayoutUnit> LastBaseline() const {
if (last_minor_baseline_)
return *last_minor_baseline_;
if (last_major_baseline_)
return *last_major_baseline_;
return last_fallback_baseline_;
}

private:
FontBaseline font_baseline_;

absl::optional<LayoutUnit> first_major_baseline_;
absl::optional<LayoutUnit> first_minor_baseline_;
absl::optional<LayoutUnit> first_fallback_baseline_;

absl::optional<LayoutUnit> last_major_baseline_;
absl::optional<LayoutUnit> last_minor_baseline_;
absl::optional<LayoutUnit> last_fallback_baseline_;
};

bool ContainsNonWhitespace(const LayoutBox* box) {
const LayoutObject* next = box;
while ((next = next->NextInPreOrder(box))) {
Expand Down Expand Up @@ -1169,6 +1244,8 @@ void NGFlexLayoutAlgorithm::PlaceFlexItems(
cross_axis_offset);
flex_line_outputs->back().line_cross_size = line->cross_axis_extent_;
flex_line_outputs->back().cross_axis_offset = line->cross_axis_offset_;
flex_line_outputs->back().major_baseline = line->max_major_ascent_;
flex_line_outputs->back().minor_baseline = line->max_minor_ascent_;
}
}

Expand Down Expand Up @@ -1223,7 +1300,9 @@ void NGFlexLayoutAlgorithm::ApplyFinalAlignmentAndReversals(
if (Style().ResolvedIsColumnReverseFlexDirection()) {
algorithm_.LayoutColumnReverse(final_content_main_size,
BorderScrollbarPadding().block_start);

}
if (Style().ResolvedIsColumnReverseFlexDirection() ||
Style().ResolvedIsRowReverseFlexDirection()) {
for (auto& flex_line : *flex_line_outputs)
flex_line.line_items.Reverse();
}
Expand Down Expand Up @@ -1262,12 +1341,20 @@ NGLayoutResult::EStatus NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSize(
}
}

absl::optional<LayoutUnit> fallback_baseline;
BaselineAccumulator baseline_accumulator(Style());
NGLayoutResult::EStatus status = NGLayoutResult::kSuccess;
bool is_wrap_reverse = Style().FlexWrap() == EFlexWrap::kWrapReverse;

for (wtf_size_t flex_line_idx = 0; flex_line_idx < flex_line_outputs->size();
++flex_line_idx) {
NGFlexLine& line_output = (*flex_line_outputs)[flex_line_idx];

bool is_first_line = flex_line_idx == 0;
bool is_last_line = flex_line_idx == flex_line_outputs->size() - 1;
if (!InvolvedInBlockFragmentation(container_builder_) && !is_column_) {
baseline_accumulator.AccumulateLine(line_output, is_first_line,
is_last_line);
}

for (wtf_size_t flex_item_idx = 0;
flex_item_idx < line_output.line_items.size(); ++flex_item_idx) {
NGFlexItem& flex_item = line_output.line_items[flex_item_idx];
Expand Down Expand Up @@ -1338,14 +1425,8 @@ NGLayoutResult::EStatus NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSize(
physical_fragment);
if (!InvolvedInBlockFragmentation(container_builder_)) {
container_builder_.AddResult(*layout_result, offset);

// Only propagate baselines from children on the first flex-line.
if ((!is_wrap_reverse && flex_line_idx == 0) ||
(is_wrap_reverse &&
flex_line_idx == flex_line_outputs->size() - 1)) {
PropagateBaselineFromChild(flex_item.Style(), fragment,
offset.block_offset, &fallback_baseline);
}
baseline_accumulator.AccumulateItem(fragment, offset.block_offset,
is_first_line, is_last_line);
} else {
flex_item.total_remaining_block_size = fragment.BlockSize();
}
Expand All @@ -1358,11 +1439,10 @@ NGLayoutResult::EStatus NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSize(
}
}

// Set the baseline to the fallback, if we didn't find any children with
// baseline alignment.
if (!InvolvedInBlockFragmentation(container_builder_) &&
!container_builder_.FirstBaseline() && fallback_baseline)
container_builder_.SetFirstBaseline(*fallback_baseline);
if (auto first_baseline = baseline_accumulator.FirstBaseline())
container_builder_.SetFirstBaseline(*first_baseline);
if (auto last_baseline = baseline_accumulator.LastBaseline())
container_builder_.SetLastBaseline(*last_baseline);

// TODO(crbug.com/1131352): Avoid control-specific handling.
if (Node().IsButton()) {
Expand All @@ -1388,10 +1468,8 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
DCHECK(row_break_between_outputs);
DCHECK(broke_before_row);

absl::optional<LayoutUnit> fallback_baseline;
NGFlexItemIterator item_iterator(*flex_line_outputs, BreakToken(),
is_column_);
bool is_wrap_reverse = Style().FlexWrap() == EFlexWrap::kWrapReverse;

Vector<bool> has_inflow_child_break_inside_line(flex_line_outputs->size(),
false);
Expand All @@ -1410,6 +1488,7 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
if (BreakToken())
previously_consumed_block_size = BreakToken()->ConsumedBlockSize();

BaselineAccumulator baseline_accumulator(Style());
for (auto entry = item_iterator.NextItem(*broke_before_row);
NGFlexItem* flex_item = entry.flex_item;
entry = item_iterator.NextItem(*broke_before_row)) {
Expand All @@ -1418,6 +1497,8 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
NGFlexLine& line_output = (*flex_line_outputs)[flex_line_idx];
const auto* item_break_token = To<NGBlockBreakToken>(entry.token);
bool last_item_in_line = flex_item_idx == line_output.line_items.size() - 1;

bool is_first_line = flex_line_idx == 0;
bool is_last_line = flex_line_idx == flex_line_outputs->size() - 1;

// A child break in a parallel flow doesn't affect whether we should
Expand Down Expand Up @@ -1759,13 +1840,8 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
current_column_break_info
? &current_column_break_info->break_after
: nullptr);

// Only propagate baselines from children on the first flex-line.
if ((!is_wrap_reverse && flex_line_idx == 0) ||
(is_wrap_reverse && is_last_line)) {
PropagateBaselineFromChild(flex_item->Style(), fragment,
offset.block_offset, &fallback_baseline);
}
baseline_accumulator.AccumulateItem(fragment, offset.block_offset,
is_first_line, is_last_line);
if (last_item_in_line) {
if (!has_inflow_child_break_inside_line[flex_line_idx])
line_output.has_seen_all_children = true;
Expand Down Expand Up @@ -1802,10 +1878,10 @@ NGFlexLayoutAlgorithm::GiveItemsFinalPositionAndSizeForFragmentation(
container_builder_.SetHasSeenAllChildren();
}

// Set the baseline to the fallback, if we didn't find any children with
// baseline alignment.
if (!container_builder_.FirstBaseline() && fallback_baseline)
container_builder_.SetFirstBaseline(*fallback_baseline);
if (auto first_baseline = baseline_accumulator.FirstBaseline())
container_builder_.SetFirstBaseline(*first_baseline);
if (auto last_baseline = baseline_accumulator.LastBaseline())
container_builder_.SetLastBaseline(*last_baseline);

// Update the |total_intrinsic_block_size_| in case things expanded.
total_intrinsic_block_size_ =
Expand Down Expand Up @@ -1928,36 +2004,6 @@ void NGFlexLayoutAlgorithm::AdjustButtonBaseline(
}
}

void NGFlexLayoutAlgorithm::PropagateBaselineFromChild(
const ComputedStyle& flex_item_style,
const NGBoxFragment& fragment,
LayoutUnit block_offset,
absl::optional<LayoutUnit>* fallback_baseline) {
// Check if we've already found an appropriate baseline.
if (container_builder_.FirstBaseline())
return;

const auto baseline_type = Style().GetFontBaseline();
const LayoutUnit baseline_offset =
block_offset + fragment.FirstBaselineOrSynthesize(baseline_type);

// We prefer a baseline from a child with baseline alignment, and no
// auto-margins in the cross axis (even if we have to synthesize the
// baseline).
if (FlexLayoutAlgorithm::AlignmentForChild(Style(), flex_item_style) ==
ItemPosition::kBaseline &&
!is_column_) {
container_builder_.SetFirstBaseline(baseline_offset);
return;
}

// Set the fallback baseline if it doesn't have a value yet.
if (Style().ResolvedIsColumnReverseFlexDirection())
*fallback_baseline = baseline_offset;
else
*fallback_baseline = fallback_baseline->value_or(baseline_offset);
}

MinMaxSizesResult NGFlexLayoutAlgorithm::ComputeItemContributions(
const NGConstraintSpace& space,
const FlexItem& item) const {
Expand Down Expand Up @@ -2629,7 +2675,8 @@ void NGFlexLayoutAlgorithm::CheckFlexLines(
if (Style().FlexWrap() == EFlexWrap::kWrapReverse)
flex_line_outputs.Reverse();

if (Style().ResolvedIsColumnReverseFlexDirection()) {
if (Style().ResolvedIsColumnReverseFlexDirection() ||
Style().ResolvedIsRowReverseFlexDirection()) {
for (auto& flex_line : flex_line_outputs)
flex_line.line_items.Reverse();
}
Expand Down
Expand Up @@ -15,7 +15,6 @@ namespace blink {

class NGBlockNode;
class NGBlockBreakToken;
class NGBoxFragment;
struct DevtoolsFlexInfo;
struct NGFlexItem;

Expand Down Expand Up @@ -106,13 +105,6 @@ class CORE_EXPORT NGFlexLayoutAlgorithm

void AdjustButtonBaseline(LayoutUnit final_content_cross_size);

// Propagates the baseline from the given flex-item if needed.
void PropagateBaselineFromChild(
const ComputedStyle&,
const NGBoxFragment&,
LayoutUnit block_offset,
absl::optional<LayoutUnit>* fallback_baseline);

MinMaxSizesResult ComputeMinMaxSizeOfRowContainer();
MinMaxSizesResult ComputeMinMaxSizeOfMultilineColumnContainer();
// This implements 9.9.3. Flex Item Intrinsic Size Contributions, from
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h
Expand Up @@ -47,6 +47,8 @@ struct NGFlexLine {

LayoutUnit line_cross_size;
LayoutUnit cross_axis_offset;
LayoutUnit major_baseline;
LayoutUnit minor_baseline;
LayoutUnit item_offset_adjustment;
bool has_seen_all_children = false;
HeapVector<NGFlexItem> line_items;
Expand Down
Expand Up @@ -316,7 +316,7 @@ const NGLayoutResult* NGSimplifiedLayoutAlgorithm::Layout() {
// baseline if it is from the logical bottom margin edge.
DCHECK_EQ(previous_fragment.LastBaseline().has_value(),
container_builder_.LastBaseline().has_value());
if (container_builder_.LastBaseline())
if (Node().IsBlockFlow() && container_builder_.LastBaseline())
container_builder_.SetLastBaselineToBlockEndMarginEdgeIfNeeded();

return container_builder_.ToBoxFragment();
Expand Down
10 changes: 10 additions & 0 deletions third_party/blink/web_tests/FlagExpectations/disable-layout-ng
Expand Up @@ -449,6 +449,10 @@ crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-007.
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-fieldset-001.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-fieldset-002.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-fieldset-003.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-flex-001.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-flex-002.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-flex-003.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-flex-004.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-grid-001.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-grid-002.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/alignment/flex-align-baseline-grid-003.html [ Failure ]
Expand Down Expand Up @@ -486,6 +490,8 @@ crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-vert-rtl-003.xh
crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-vert-rtl-004.xhtml [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/flexbox-align-self-vert-rtl-005.xhtml [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/flexbox-baseline-align-self-baseline-vert-001.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/flexbox-baseline-multi-line-horiz-002.html [ Failure ]
crbug.com/591099 external/wpt/css/css-flexbox/flexbox-baseline-multi-line-vert-002.html [ Failure ]
crbug.com/553838 external/wpt/css/css-flexbox/flexbox-min-height-auto-002a.html [ Failure ]
crbug.com/553838 external/wpt/css/css-flexbox/flexbox-min-height-auto-002c.html [ Failure ]
crbug.com/1132627 external/wpt/css/css-flexbox/flexbox-min-width-auto-002a.html [ Failure ]
Expand Down Expand Up @@ -595,6 +601,10 @@ crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-004.ht
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-fieldset-001.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-fieldset-002.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-fieldset-003.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-flex-001.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-flex-002.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-flex-003.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-flex-004.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-grid-001.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-grid-002.html [ Failure ]
crbug.com/1045599 external/wpt/css/css-grid/alignment/grid-align-baseline-grid-003.html [ Failure ]
Expand Down

0 comments on commit 4293e3e

Please sign in to comment.