diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc | 445 |
1 files changed, 287 insertions, 158 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc index 9d0e5660942..ab6b62882b8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc @@ -11,6 +11,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" @@ -37,6 +38,98 @@ namespace blink { namespace { +// Returns the logical bottom offset of the last line text, relative to +// |container| origin. This is used to decide ruby annotation box position. +// +// TODO(layout-dev): Using ScrollableOverflow() is same as legacy +// LayoutRubyRun. However its result is not good with some fonts/platforms. +LayoutUnit LastLineTextLogicalBottom(const NGPhysicalBoxFragment& container, + LayoutUnit default_value) { + const ComputedStyle& container_style = container.Style(); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (!container.Items()) + return default_value; + NGInlineCursor cursor(*container.Items()); + cursor.MoveToLastLine(); + const auto* line_item = cursor.CurrentItem(); + if (!line_item) + return default_value; + DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine); + DCHECK(line_item->LineBoxFragment()); + PhysicalRect line_rect = + line_item->LineBoxFragment()->ScrollableOverflowForLine( + container, container_style, *line_item, cursor); + return line_rect + .ConvertToLogical(container_style.GetWritingMode(), + container_style.Direction(), container.Size(), + cursor.Current().Size()) + .BlockEndOffset(); + } + + const NGPhysicalLineBoxFragment* last_line = nullptr; + PhysicalOffset last_line_offset; + for (const auto& child_link : container.PostLayoutChildren()) { + if (const auto* maybe_line = + DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) { + last_line = maybe_line; + last_line_offset = child_link.offset; + } + } + if (!last_line) + return default_value; + PhysicalRect line_rect = + last_line->ScrollableOverflow(container, container_style); + line_rect.Move(last_line_offset); + return line_rect + .ConvertToLogical(container_style.GetWritingMode(), + container_style.Direction(), container.Size(), + last_line->Size()) + .BlockEndOffset(); +} + +// Returns the logical top offset of the first line text, relative to +// |container| origin. This is used to decide ruby annotation box position. +// +// TODO(layout-dev): Using ScrollableOverflow() is same as legacy +// LayoutRubyRun. However its result is not good with some fonts/platforms. +LayoutUnit FirstLineTextLogicalTop(const NGPhysicalBoxFragment& container, + LayoutUnit default_value) { + const ComputedStyle& container_style = container.Style(); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (!container.Items()) + return default_value; + NGInlineCursor cursor(*container.Items()); + cursor.MoveToFirstLine(); + const auto* line_item = cursor.CurrentItem(); + if (!line_item) + return default_value; + DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine); + DCHECK(line_item->LineBoxFragment()); + PhysicalRect line_rect = + line_item->LineBoxFragment()->ScrollableOverflowForLine( + container, container_style, *line_item, cursor); + return line_rect + .ConvertToLogical(container_style.GetWritingMode(), + container_style.Direction(), container.Size(), + cursor.Current().Size()) + .offset.block_offset; + } + + for (const auto& child_link : container.PostLayoutChildren()) { + if (const auto* line = DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) { + PhysicalRect line_rect = + line->ScrollableOverflow(container, container_style); + line_rect.Move(child_link.offset); + return line_rect + .ConvertToLogical(container_style.GetWritingMode(), + container_style.Direction(), container.Size(), + line->Size()) + .offset.block_offset; + } + } + return default_value; +} + inline scoped_refptr<const NGLayoutResult> LayoutBlockChild( const NGConstraintSpace& space, const NGBreakToken* break_token, @@ -179,10 +272,10 @@ NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( params.fragment_geometry.padding), border_scrollbar_padding_(border_padding_ + params.fragment_geometry.scrollbar), + previous_result_(params.previous_result), is_resuming_(IsResumingLayout(params.break_token)), exclusion_space_(params.space.ExclusionSpace()), lines_until_clamp_(params.space.LinesUntilClamp()), - force_truncate_at_line_clamp_(params.space.ForceTruncateAtLineClamp()), early_break_(params.early_break) { AdjustForFragmentation(BreakToken(), &border_scrollbar_padding_); container_builder_.SetIsNewFormattingContext( @@ -198,18 +291,14 @@ void NGBlockLayoutAlgorithm::SetBoxType(NGPhysicalFragment::NGBoxType type) { container_builder_.SetBoxType(type); } -base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( +MinMaxSizesResult NGBlockLayoutAlgorithm::ComputeMinMaxSizes( const MinMaxSizesInput& input) const { - base::Optional<MinMaxSizes> sizes = - CalculateMinMaxSizesIgnoringChildren(node_, border_scrollbar_padding_); - if (sizes) - return sizes; + if (auto result = CalculateMinMaxSizesIgnoringChildren( + node_, border_scrollbar_padding_)) + return *result; - sizes.emplace(); - LayoutUnit child_percentage_resolution_block_size = - CalculateChildPercentageBlockSizeForMinMax( - ConstraintSpace(), Node(), border_padding_, - input.percentage_resolution_block_size); + MinMaxSizes sizes; + bool depends_on_percentage_block_size = false; const TextDirection direction = Style().Direction(); LayoutUnit float_left_inline_size = input.float_left_inline_size; @@ -217,6 +306,8 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( for (NGLayoutInputNode child = Node().FirstChild(); child; child = child.NextSibling()) { + // We don't check IsRubyText() here intentionally. RubyText width should + // affect this width. if (child.IsOutOfFlowPositioned() || (child.IsColumnSpanAll() && ConstraintSpace().IsInColumnBfc())) continue; @@ -236,7 +327,7 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( float_left_inline_size + float_right_inline_size; if (child_clear != EClear::kNone) - sizes->max_size = std::max(sizes->max_size, float_inline_size); + sizes.max_size = std::max(sizes.max_size, float_inline_size); if (child_clear == EClear::kBoth || child_clear == EClear::kLeft) float_left_inline_size = LayoutUnit(); @@ -245,13 +336,13 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( float_right_inline_size = LayoutUnit(); } - MinMaxSizesInput child_input(child_percentage_resolution_block_size); + MinMaxSizesInput child_input(input.percentage_resolution_block_size); if (child.IsInline() || child.IsAnonymousBlock()) { child_input.float_left_inline_size = float_left_inline_size; child_input.float_right_inline_size = float_right_inline_size; } - MinMaxSizes child_sizes; + MinMaxSizesResult child_result; if (child.IsInline()) { // From |NGBlockLayoutAlgorithm| perspective, we can handle |NGInlineNode| // almost the same as |NGBlockNode|, because an |NGInlineNode| includes @@ -259,13 +350,14 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( // an anonymous box that contains all line boxes. // |NextSibling| returns the next block sibling, or nullptr, skipping all // following inline siblings and descendants. - child_sizes = + child_result = child.ComputeMinMaxSizes(Style().GetWritingMode(), child_input); } else { - child_sizes = + child_result = ComputeMinAndMaxContentContribution(Style(), child, child_input); } - DCHECK_LE(child_sizes.min_size, child_sizes.max_size) << child.ToString(); + DCHECK_LE(child_result.sizes.min_size, child_result.sizes.max_size) + << child.ToString(); // Determine the max inline contribution of the child. NGBoxStrut margins = ComputeMinMaxMargins(Style(), child); @@ -274,7 +366,8 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( if (child.IsFloating()) { // A float adds to its inline size to the current "line". The new max // inline contribution is just the sum of all the floats on that "line". - LayoutUnit float_inline_size = child_sizes.max_size + margins.InlineSum(); + LayoutUnit float_inline_size = + child_result.sizes.max_size + margins.InlineSum(); // float_inline_size is negative when the float is completely outside of // the content area, by e.g., negative margins. Such floats do not affect @@ -309,19 +402,26 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( ? std::max(float_right_inline_size, margin_line_right) : float_right_inline_size + margin_line_right; + // The order of operations is important here. + // If child_result.sizes.max_size is saturated, adding the insets + // sequentially can result in an DCHECK. max_inline_contribution = - child_sizes.max_size + line_left_inset + line_right_inset; + child_result.sizes.max_size + (line_left_inset + line_right_inset); } else { // This is just a standard inflow child. - max_inline_contribution = child_sizes.max_size + margins.InlineSum(); + max_inline_contribution = + child_result.sizes.max_size + margins.InlineSum(); } - sizes->max_size = std::max(sizes->max_size, max_inline_contribution); + sizes.max_size = std::max(sizes.max_size, max_inline_contribution); // The min inline contribution just assumes that floats are all on their own // "line". LayoutUnit min_inline_contribution = - child_sizes.min_size + margins.InlineSum(); - sizes->min_size = std::max(sizes->min_size, min_inline_contribution); + child_result.sizes.min_size + margins.InlineSum(); + sizes.min_size = std::max(sizes.min_size, min_inline_contribution); + + depends_on_percentage_block_size |= + child_result.depends_on_percentage_block_size; // Anything that isn't a float will create a new "line" resetting the float // size trackers. @@ -331,11 +431,11 @@ base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( } } - DCHECK_GE(sizes->min_size, LayoutUnit()); - DCHECK_LE(sizes->min_size, sizes->max_size) << Node().ToString(); + DCHECK_GE(sizes.min_size, LayoutUnit()); + DCHECK_LE(sizes.min_size, sizes.max_size) << Node().ToString(); - *sizes += border_scrollbar_padding_.InlineSum(); - return sizes; + sizes += border_scrollbar_padding_.InlineSum(); + return {sizes, depends_on_percentage_block_size}; } LogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset( @@ -366,8 +466,9 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { // Inline children require an inline child layout context to be // passed between siblings. We want to stack-allocate that one, but // only on demand, as it's quite big. - if (Node().IsInlineFormattingContextRoot()) - result = LayoutWithInlineChildLayoutContext(); + NGLayoutInputNode first_child(nullptr); + if (Node().IsInlineFormattingContextRoot(&first_child)) + result = LayoutWithInlineChildLayoutContext(first_child); else result = Layout(nullptr); if (UNLIKELY(result->Status() == NGLayoutResult::kNeedsEarlierBreak)) { @@ -379,24 +480,26 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { } else if (UNLIKELY(result->Status() == NGLayoutResult:: kNeedsRelayoutWithNoForcedTruncateAtLineClamp)) { - DCHECK(force_truncate_at_line_clamp_); - return RelayoutNoForcedTruncateForLineClamp(); + DCHECK(!ignore_line_clamp_); + return RelayoutIgnoringLineClamp(); } return result; } NOINLINE scoped_refptr<const NGLayoutResult> -NGBlockLayoutAlgorithm::LayoutWithInlineChildLayoutContext() { +NGBlockLayoutAlgorithm::LayoutWithInlineChildLayoutContext( + const NGLayoutInputNode& first_child) { NGInlineChildLayoutContext context; if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) return Layout(&context); - return LayoutWithItemsBuilder(&context); + return LayoutWithItemsBuilder(To<NGInlineNode>(first_child), &context); } NOINLINE scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::LayoutWithItemsBuilder( + const NGInlineNode& first_child, NGInlineChildLayoutContext* context) { - NGFragmentItemsBuilder items_builder(&container_builder_); + NGFragmentItemsBuilder items_builder(first_child); container_builder_.SetItemsBuilder(&items_builder); context->SetItemsBuilder(&items_builder); scoped_refptr<const NGLayoutResult> result = Layout(context); @@ -425,16 +528,16 @@ NGBlockLayoutAlgorithm::RelayoutAndBreakEarlier( } NOINLINE scoped_refptr<const NGLayoutResult> -NGBlockLayoutAlgorithm::RelayoutNoForcedTruncateForLineClamp() { +NGBlockLayoutAlgorithm::RelayoutIgnoringLineClamp() { NGLayoutAlgorithmParams params(Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(), BreakToken(), nullptr); - NGBlockLayoutAlgorithm algorithm_with_forced_truncate(params); - algorithm_with_forced_truncate.force_truncate_at_line_clamp_ = false; + NGBlockLayoutAlgorithm algorithm_ignoring_line_clamp(params); + algorithm_ignoring_line_clamp.ignore_line_clamp_ = true; NGBoxFragmentBuilder& new_builder = - algorithm_with_forced_truncate.container_builder_; + algorithm_ignoring_line_clamp.container_builder_; new_builder.SetBoxType(container_builder_.BoxType()); - return algorithm_with_forced_truncate.Layout(); + return algorithm_ignoring_line_clamp.Layout(); } inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( @@ -460,16 +563,6 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( Node().IsInlineFormattingContextRoot()); container_builder_.SetIsInlineFormattingContext(inline_child_layout_context); - if (ConstraintSpace().HasBlockFragmentation()) { - container_builder_.SetHasBlockFragmentation(); - // The whereabouts of our container's so far best breakpoint is none of our - // business, but we store its appeal, so that we don't look for breakpoints - // with lower appeal than that. - container_builder_.SetBreakAppeal(ConstraintSpace().EarlyBreakAppeal()); - - if (ConstraintSpace().IsInitialColumnBalancingPass()) - container_builder_.SetIsInitialColumnBalancingPass(); - } container_builder_.SetBfcLineOffset( ConstraintSpace().BfcOffset().line_offset); @@ -487,8 +580,9 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( container_builder_.SetAdjoiningObjectTypes(adjoining_object_types); } - if (Style().IsDeprecatedWebkitBoxWithVerticalLineClamp() && - RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled()) + if (RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled() && + Style().IsDeprecatedWebkitBoxWithVerticalLineClamp() && + !ignore_line_clamp_) lines_until_clamp_ = Style().LineClamp(); LayoutUnit content_edge = border_scrollbar_padding_.block_start; @@ -577,6 +671,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( if (Node().LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren)) child_iterator = NGBlockChildIterator(NGBlockNode(nullptr), nullptr); + NGLayoutInputNode ruby_text_child(nullptr); for (auto entry = child_iterator.NextChild(); NGLayoutInputNode child = entry.node; entry = child_iterator.NextChild(previous_inline_break_token.get())) { @@ -611,6 +706,8 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( /* is_forced_break */ true); } break; + } else if (IsRubyText(child)) { + ruby_text_child = child; } else { // If this is the child we had previously determined to break before, do // so now and finish layout. @@ -671,9 +768,12 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( } } + if (ruby_text_child) + LayoutRubyText(&ruby_text_child); + if (UNLIKELY(ConstraintSpace().IsNewFormattingContext() && - force_truncate_at_line_clamp_ && - intrinsic_block_size_when_clamped_ && lines_until_clamp_ == 0)) { + !ignore_line_clamp_ && lines_until_clamp_ == 0 && + intrinsic_block_size_when_clamped_)) { // Truncation of the last line was forced, but there are no lines after the // truncated line. Rerun layout without forcing truncation. This is only // done if line-clamp was specified on the element as the element containing @@ -790,7 +890,8 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( // Recompute the block-axis size now that we know our content size. border_box_size.block_size = ComputeBlockSizeForFragment( - ConstraintSpace(), Style(), border_padding_, intrinsic_block_size_); + ConstraintSpace(), Style(), border_padding_, intrinsic_block_size_, + border_box_size.inline_size); container_builder_.SetBlockSize(border_box_size.block_size); // If our BFC block-offset is still unknown, we check: @@ -868,7 +969,7 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( #endif // Adjust the position of the final baseline if needed. - FinalizeBaseline(); + container_builder_.SetLastBaselineToBlockEndMarginEdgeIfNeeded(); // An exclusion space is confined to nodes within the same formatting context. if (!ConstraintSpace().IsNewFormattingContext()) { @@ -889,85 +990,48 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( return container_builder_.ToBoxFragment(); } -const NGInlineBreakToken* NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache( +bool NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache( NGInlineNode inline_node, NGPreviousInflowPosition* previous_inflow_position, - bool* aborted_out) { - DCHECK(RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()); - LayoutBox* layout_box = inline_node.GetLayoutBox(); - if (layout_box->SelfNeedsLayout()) - return nullptr; - - // If floats are intruding into this node, re-layout may be needed. - if (!exclusion_space_.IsEmpty()) - return nullptr; - - // Laying out from a break token is not supported yet, because this logic - // synthesize a break token. - if (BreakToken()) - return nullptr; - - const NGPaintFragment* lineboxes = - inline_node.ReusableLineBoxContainer(ConstraintSpace()); - if (!lineboxes) - return nullptr; - - // Following is a copy of logic from HandleInFlow(). They need to keep in - // sync. - if (inline_node.IsEmptyInline()) - return nullptr; - if (!ResolveBfcBlockOffset(previous_inflow_position)) { - *aborted_out = true; - return nullptr; - } + scoped_refptr<const NGInlineBreakToken>* inline_break_token_out) { + DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DCHECK(previous_result_); + DCHECK(!inline_node.IsEmptyInline()); DCHECK(container_builder_.BfcBlockOffset()); + DCHECK(previous_inflow_position->margin_strut.IsEmpty()); + DCHECK(!previous_inflow_position->self_collapsing_child_had_clearance); - WritingMode writing_mode = container_builder_.GetWritingMode(); - TextDirection direction = container_builder_.Direction(); - DCHECK_EQ(writing_mode, lineboxes->Style().GetWritingMode()); - DCHECK_EQ(direction, lineboxes->Style().Direction()); - const PhysicalSize outer_size = lineboxes->Size(); - - LayoutUnit used_block_size = previous_inflow_position->logical_block_offset; - const NGBreakToken* last_break_token = nullptr; - for (const NGPaintFragment* child : lineboxes->Children()) { - if (child->IsDirty()) - break; - - // Abort if the line propagated its descendants to outside of the line. They - // are propagated through NGLayoutResult, which we don't cache. - const NGPhysicalLineBoxFragment* line = - DynamicTo<NGPhysicalLineBoxFragment>(&child->PhysicalFragment()); - if (!line || line->HasPropagatedDescendants()) - break; - - // TODO(kojii): Running the normal layout code at least once for this child - // helps reducing the code to setup internal states after the reuse. Remove - // the last fragment if it is the end of the fragmentation to do so, but we - // should figure out how to setup the states without doing this. - const NGBreakToken* break_token = line->BreakToken(); - DCHECK(break_token); - if (break_token->IsFinished()) - break; + const auto& previous_fragment = + To<NGPhysicalBoxFragment>(previous_result_->PhysicalFragment()); + const NGFragmentItems* previous_items = previous_fragment.Items(); + DCHECK(previous_items); + previous_items->DirtyLinesFromNeedsLayout(inline_node.GetLayoutBlockFlow()); - last_break_token = break_token; - LogicalOffset logical_offset = child->Offset().ConvertToLogical( - writing_mode, direction, outer_size, line->Size()); - container_builder_.AddChild( - *line, {logical_offset.inline_offset, used_block_size}); - used_block_size += line->Size().ConvertToLogical(writing_mode).block_size; + const auto& children = container_builder_.Children(); + const wtf_size_t children_before = children.size(); + const NGConstraintSpace& space = ConstraintSpace(); + const auto result = container_builder_.ItemsBuilder()->AddPreviousItems( + *previous_items, space.GetWritingMode(), space.Direction(), + previous_fragment.Size(), &container_builder_, /* stop_at_dirty */ true); + + if (UNLIKELY(!result.succeeded)) { + DCHECK_EQ(children.size(), children_before); + DCHECK(!result.used_block_size); + DCHECK(!result.inline_break_token); + return false; } - if (!last_break_token) - return nullptr; - // Update the internal states to after the re-used fragments. - previous_inflow_position->logical_block_offset = used_block_size; + // |AddPreviousItems| may have added more than one lines. Propagate baselines + // from them. + for (const auto& child : base::make_span(children).subspan(children_before)) { + DCHECK(child.fragment->IsLineBox()); + PropagateBaselineFromChild(To<NGPhysicalContainerFragment>(*child.fragment), + child.offset.block_offset); + } - // In order to layout the rest of lines, return the break token from the last - // reused line box. - DCHECK(last_break_token); - DCHECK(!last_break_token->IsFinished()); - return To<NGInlineBreakToken>(last_break_token); + previous_inflow_position->logical_block_offset += result.used_block_size; + *inline_break_token_out = result.inline_break_token; + return true; } void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned( @@ -1497,21 +1561,26 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleInflow( DCHECK(!child.IsOutOfFlowPositioned()); DCHECK(!child.CreatesNewFormattingContext()); + bool is_non_empty_inline = false; auto* child_inline_node = DynamicTo<NGInlineNode>(child); - if (child_inline_node && !child_break_token && - RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) { - DCHECK(!*previous_inline_break_token); - bool aborted = false; - *previous_inline_break_token = TryReuseFragmentsFromCache( - *child_inline_node, previous_inflow_position, &aborted); - if (*previous_inline_break_token) - return NGLayoutResult::kSuccess; - if (aborted) - return NGLayoutResult::kBfcBlockOffsetResolved; + if (child_inline_node) { + is_non_empty_inline = !child_inline_node->IsEmptyInline(); + + // Add reusable line boxes from |previous_result_| if any. + if (is_non_empty_inline && !child_break_token && previous_result_ && + RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (!ResolveBfcBlockOffset(previous_inflow_position)) + return NGLayoutResult::kBfcBlockOffsetResolved; + DCHECK(container_builder_.BfcBlockOffset()); + + DCHECK(!*previous_inline_break_token); + if (TryReuseFragmentsFromCache(*child_inline_node, + previous_inflow_position, + previous_inline_break_token)) + return NGLayoutResult::kSuccess; + } } - bool is_non_empty_inline = - child_inline_node && !child_inline_node->IsEmptyInline(); bool has_clearance_past_adjoining_floats = !container_builder_.BfcBlockOffset() && child.IsBlock() && HasClearancePastAdjoiningFloats(container_builder_.AdjoiningObjectTypes(), @@ -1580,7 +1649,8 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow( // Only non self-collapsing children (e.g. "normal children") can be pushed // by floats in this way. - bool normal_child_had_clearance = layout_result->IsPushedByFloats(); + bool normal_child_had_clearance = + layout_result->IsPushedByFloats() && child.IsBlock(); DCHECK(!normal_child_had_clearance || !is_self_collapsing); // A child may have aborted its layout if it resolved its BFC block-offset. @@ -2123,7 +2193,8 @@ bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() { } else { block_size = ComputeBlockSizeForFragment( ConstraintSpace(), Style(), border_padding_, - consumed_block_size + intrinsic_block_size_); + consumed_block_size + intrinsic_block_size_, + container_builder_.InitialBorderBoxSize().inline_size); block_size -= consumed_block_size; DCHECK_GE(block_size, LayoutUnit()) @@ -2473,7 +2544,6 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( container_builder_.AdjoiningObjectTypes()); } builder.SetLinesUntilClamp(lines_until_clamp_); - builder.SetForceTruncateAtLineClamp(force_truncate_at_line_clamp_); } else if (child_data.is_resuming_after_break) { // If the child is being resumed after a break, margins inside the child may // be adjoining with the fragmentainer boundary, regardless of whether the @@ -2488,8 +2558,9 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( // fragmentation line. if (is_new_fc) fragmentainer_offset_delta = *child_bfc_block_offset; - SetupFragmentation(ConstraintSpace(), child, fragmentainer_offset_delta, - &builder, is_new_fc); + SetupSpaceBuilderForFragmentation(ConstraintSpace(), child, + fragmentainer_offset_delta, &builder, + is_new_fc); builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal()); } @@ -2547,21 +2618,6 @@ void NGBlockLayoutAlgorithm::PropagateBaselineFromChild( } } -void NGBlockLayoutAlgorithm::FinalizeBaseline() { - if (ConstraintSpace().BaselineAlgorithmType() != - NGBaselineAlgorithmType::kInlineBlock) - return; - - if (!Node().UseLogicalBottomMarginEdgeForInlineBlockBaseline()) - return; - - // When overflow is present (within an atomic-inline baseline context) we - // should always use the block-end margin edge as the baseline. - NGBoxStrut margins = ComputeMarginsForSelf(ConstraintSpace(), Style()); - container_builder_.SetLastBaseline(container_builder_.BlockSize() + - margins.block_end); -} - bool NGBlockLayoutAlgorithm::ResolveBfcBlockOffset( NGPreviousInflowPosition* previous_inflow_position, LayoutUnit bfc_block_offset, @@ -2747,4 +2803,77 @@ bool NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes( return true; } +bool NGBlockLayoutAlgorithm::IsRubyText(const NGLayoutInputNode& child) const { + return Node().IsRubyRun() && child.IsRubyText(); +} + +void NGBlockLayoutAlgorithm::LayoutRubyText( + NGLayoutInputNode* ruby_text_child) { + DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled()); + DCHECK(Node().IsRubyRun()); + + scoped_refptr<const NGBlockBreakToken> break_token; + if (const auto* token = BreakToken()) { + for (const auto* child_token : token->ChildBreakTokens()) { + if (child_token->InputNode() == *ruby_text_child) { + break_token = To<NGBlockBreakToken>(child_token); + break; + } + } + } + + NGConstraintSpaceBuilder builder( + ConstraintSpace(), ruby_text_child->Style().GetWritingMode(), true); + builder.SetAvailableSize(child_available_size_); + + scoped_refptr<const NGLayoutResult> result = + To<NGBlockNode>(*ruby_text_child) + .Layout(builder.ToConstraintSpace(), break_token.get()); + + LayoutUnit ruby_text_top; + const NGPhysicalBoxFragment& ruby_text_fragment = + To<NGPhysicalBoxFragment>(result->PhysicalFragment()); + if (Style().IsFlippedLinesWritingMode() == + (Style().GetRubyPosition() == RubyPosition::kAfter)) { + LayoutUnit last_line_ruby_text_bottom = LastLineTextLogicalBottom( + ruby_text_fragment, result->IntrinsicBlockSize()); + + // Find a fragment for RubyBase, and get the top of text in it. + LayoutUnit first_line_top; + for (const auto& child : container_builder_.Children()) { + if (const auto* layout_object = child.fragment->GetLayoutObject()) { + if (layout_object->IsRubyBase()) { + first_line_top = FirstLineTextLogicalTop( + To<NGPhysicalBoxFragment>(*child.fragment), LayoutUnit()); + first_line_top += child.offset.block_offset; + break; + } + } + } + ruby_text_top = first_line_top - last_line_ruby_text_bottom; + } else { + LayoutUnit first_line_ruby_text_top = + FirstLineTextLogicalTop(ruby_text_fragment, LayoutUnit()); + + // Find a fragment for RubyBase, and get the bottom of text in it. + LayoutUnit last_line_bottom; + for (const auto& child : container_builder_.Children()) { + if (const auto* layout_object = child.fragment->GetLayoutObject()) { + if (layout_object->IsRubyBase()) { + last_line_bottom = LastLineTextLogicalBottom( + To<NGPhysicalBoxFragment>(*child.fragment), + child.fragment->Size() + .ConvertToLogical(Style().GetWritingMode()) + .block_size); + last_line_bottom += child.offset.block_offset; + break; + } + } + } + ruby_text_top = last_line_bottom - first_line_ruby_text_top; + } + container_builder_.AddResult(*result, + LogicalOffset(LayoutUnit(), ruby_text_top)); +} + } // namespace blink |