diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/third_party/blink/renderer/core/layout | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz |
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e
Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout')
434 files changed, 17225 insertions, 10894 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/BUILD.gn b/chromium/third_party/blink/renderer/core/layout/BUILD.gn index c7a4d88018b..cb2f84ebd46 100644 --- a/chromium/third_party/blink/renderer/core/layout/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/layout/BUILD.gn @@ -139,16 +139,14 @@ blink_core_sources("layout") { "layout_image_resource_style_image.h", "layout_inline.cc", "layout_inline.h", - "layout_list_box.cc", - "layout_list_box.h", + "layout_inside_list_marker.cc", + "layout_inside_list_marker.h", "layout_list_item.cc", "layout_list_item.h", "layout_list_marker.cc", "layout_list_marker.h", "layout_media.cc", "layout_media.h", - "layout_menu_list.cc", - "layout_menu_list.h", "layout_multi_column_flow_thread.cc", "layout_multi_column_flow_thread.h", "layout_multi_column_set.cc", @@ -162,6 +160,8 @@ blink_core_sources("layout") { "layout_object_factory.cc", "layout_object_factory.h", "layout_object_inlines.h", + "layout_outside_list_marker.cc", + "layout_outside_list_marker.h", "layout_progress.cc", "layout_progress.h", "layout_quote.cc", @@ -176,8 +176,6 @@ blink_core_sources("layout") { "layout_ruby_run.h", "layout_ruby_text.cc", "layout_ruby_text.h", - "layout_search_field.cc", - "layout_search_field.h", "layout_shift_region.cc", "layout_shift_region.h", "layout_shift_tracker.cc", @@ -267,14 +265,16 @@ blink_core_sources("layout") { "list_marker_text.cc", "list_marker_text.h", "map_coordinates_flags.h", - "min_max_size.cc", - "min_max_size.h", + "min_max_sizes.cc", + "min_max_sizes.h", "multi_column_fragmentainer_group.cc", "multi_column_fragmentainer_group.h", "ng/custom/css_layout_definition.cc", "ng/custom/css_layout_definition.h", "ng/custom/css_layout_worklet.cc", "ng/custom/css_layout_worklet.h", + "ng/custom/custom_intrinsic_sizes.cc", + "ng/custom/custom_intrinsic_sizes.h", "ng/custom/custom_layout_child.cc", "ng/custom/custom_layout_child.h", "ng/custom/custom_layout_constraints.cc", @@ -323,8 +323,6 @@ blink_core_sources("layout") { "ng/inline/layout_ng_text_fragment.h", "ng/inline/ng_abstract_inline_text_box.cc", "ng/inline/ng_abstract_inline_text_box.h", - "ng/inline/ng_baseline.cc", - "ng/inline/ng_baseline.h", "ng/inline/ng_bidi_paragraph.cc", "ng/inline/ng_bidi_paragraph.h", "ng/inline/ng_caret_position.cc", @@ -411,12 +409,24 @@ blink_core_sources("layout") { "ng/list/layout_ng_inside_list_marker.h", "ng/list/layout_ng_list_item.cc", "ng/list/layout_ng_list_item.h", - "ng/list/layout_ng_list_marker.cc", - "ng/list/layout_ng_list_marker.h", "ng/list/layout_ng_list_marker_image.cc", "ng/list/layout_ng_list_marker_image.h", + "ng/list/layout_ng_outside_list_marker.cc", + "ng/list/layout_ng_outside_list_marker.h", + "ng/list/list_marker.cc", + "ng/list/list_marker.h", "ng/list/ng_unpositioned_list_marker.cc", "ng/list/ng_unpositioned_list_marker.h", + "ng/mathml/layout_ng_mathml_block.cc", + "ng/mathml/layout_ng_mathml_block.h", + "ng/mathml/ng_math_fraction_layout_algorithm.cc", + "ng/mathml/ng_math_fraction_layout_algorithm.h", + "ng/mathml/ng_math_layout_utils.cc", + "ng/mathml/ng_math_layout_utils.h", + "ng/mathml/ng_math_row_layout_algorithm.cc", + "ng/mathml/ng_math_row_layout_algorithm.h", + "ng/mathml/ng_math_space_layout_algorithm.cc", + "ng/mathml/ng_math_space_layout_algorithm.h", "ng/ng_absolute_utils.cc", "ng/ng_absolute_utils.h", "ng/ng_block_break_token.cc", @@ -447,12 +457,16 @@ blink_core_sources("layout") { "ng/ng_early_break.h", "ng/ng_fieldset_layout_algorithm.cc", "ng/ng_fieldset_layout_algorithm.h", + "ng/ng_flex_child_iterator.cc", + "ng/ng_flex_child_iterator.h", "ng/ng_flex_layout_algorithm.cc", "ng/ng_flex_layout_algorithm.h", "ng/ng_floats_utils.cc", "ng/ng_floats_utils.h", "ng/ng_fragment.h", "ng/ng_fragment_builder.h", + "ng/ng_fragment_child_iterator.cc", + "ng/ng_fragment_child_iterator.h", "ng/ng_fragmentation_utils.cc", "ng/ng_fragmentation_utils.h", "ng/ng_ink_overflow.cc", diff --git a/chromium/third_party/blink/renderer/core/layout/DEPS b/chromium/third_party/blink/renderer/core/layout/DEPS index 3b34fd02b70..98473842d29 100644 --- a/chromium/third_party/blink/renderer/core/layout/DEPS +++ b/chromium/third_party/blink/renderer/core/layout/DEPS @@ -1,5 +1,5 @@ specific_include_rules = { - "layout_theme\.cc": [ + "layout_theme\.cc|layout_theme_mac\.mm": [ "+ui/native_theme/native_theme.h", ], } diff --git a/chromium/third_party/blink/renderer/core/layout/api/line_layout_list_marker.h b/chromium/third_party/blink/renderer/core/layout/api/line_layout_list_marker.h index f78350e836f..f5cc4ed4fcd 100644 --- a/chromium/third_party/blink/renderer/core/layout/api/line_layout_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/api/line_layout_list_marker.h @@ -24,16 +24,7 @@ class LineLayoutListMarker : public LineLayoutBox { LineLayoutListMarker() = default; - bool IsInside() const { return ToListMarker()->IsInside(); } - - private: - LayoutListMarker* ToListMarker() { - return ToLayoutListMarker(GetLayoutObject()); - } - - const LayoutListMarker* ToListMarker() const { - return ToLayoutListMarker(GetLayoutObject()); - } + bool IsInside() const { return GetLayoutObject()->IsInsideListMarker(); } }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.cc b/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.cc index 456ffcf24d8..5136d0d40a3 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.cc +++ b/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.cc @@ -36,14 +36,6 @@ namespace blink { -Scrollbar* CustomScrollbar::CreateCustomScrollbar( - ScrollableArea* scrollable_area, - ScrollbarOrientation orientation, - Element* style_source) { - return MakeGarbageCollected<CustomScrollbar>(scrollable_area, orientation, - style_source); -} - CustomScrollbar::CustomScrollbar(ScrollableArea* scrollable_area, ScrollbarOrientation orientation, Element* style_source) @@ -104,7 +96,7 @@ int CustomScrollbar::HypotheticalScrollbarThickness( enclosing_box.ClientWidth().ToInt(), part_style.get()); } -void CustomScrollbar::Trace(blink::Visitor* visitor) { +void CustomScrollbar::Trace(Visitor* visitor) { Scrollbar::Trace(visitor); } @@ -150,14 +142,18 @@ void CustomScrollbar::SetPressedPart(ScrollbarPart part, UpdateScrollbarPart(kTrackBGPart); } -scoped_refptr<ComputedStyle> CustomScrollbar::GetScrollbarPseudoElementStyle( - ScrollbarPart part_type, - PseudoId pseudo_id) { +scoped_refptr<const ComputedStyle> +CustomScrollbar::GetScrollbarPseudoElementStyle(ScrollbarPart part_type, + PseudoId pseudo_id) { if (!StyleSource()->GetLayoutObject()) return nullptr; - return StyleSource()->StyleForPseudoElement( - PseudoElementStyleRequest(pseudo_id, this, part_type), - StyleSource()->GetLayoutObject()->Style()); + const ComputedStyle* source_style = StyleSource()->GetLayoutObject()->Style(); + scoped_refptr<const ComputedStyle> part_style = + StyleSource()->StyleForPseudoElement( + PseudoElementStyleRequest(pseudo_id, this, part_type), source_style); + if (!part_style) + return nullptr; + return source_style->AddCachedPseudoElementStyle(std::move(part_style)); } void CustomScrollbar::UpdateScrollbarParts(bool destroy) { @@ -236,41 +232,28 @@ void CustomScrollbar::UpdateScrollbarPart(ScrollbarPart part_type, if (part_type == kNoPart) return; - scoped_refptr<ComputedStyle> part_style = + scoped_refptr<const ComputedStyle> part_style = !destroy ? GetScrollbarPseudoElementStyle( part_type, PseudoForScrollbarPart(part_type)) - : scoped_refptr<ComputedStyle>(nullptr); + : scoped_refptr<const ComputedStyle>(nullptr); bool need_layout_object = !destroy && part_style && part_style->Display() != EDisplay::kNone; - if (need_layout_object && part_style->Display() != EDisplay::kBlock) { - // See if we are a button that should not be visible according to OS - // settings. - WebScrollbarButtonsPlacement buttons_placement = - GetTheme().ButtonsPlacement(); + if (need_layout_object && + // display:block overrides OS settings. + part_style->Display() != EDisplay::kBlock) { + // If not display:block, visibility of buttons depends on OS settings. switch (part_type) { case kBackButtonStartPart: - need_layout_object = - (buttons_placement == kWebScrollbarButtonsPlacementSingle || - buttons_placement == kWebScrollbarButtonsPlacementDoubleStart || - buttons_placement == kWebScrollbarButtonsPlacementDoubleBoth); - break; - case kForwardButtonStartPart: - need_layout_object = - (buttons_placement == kWebScrollbarButtonsPlacementDoubleStart || - buttons_placement == kWebScrollbarButtonsPlacementDoubleBoth); + case kForwardButtonEndPart: + // Create buttons only if the OS theme has scrollbar buttons. + need_layout_object = GetTheme().NativeThemeHasButtons(); break; case kBackButtonEndPart: - need_layout_object = - (buttons_placement == kWebScrollbarButtonsPlacementDoubleEnd || - buttons_placement == kWebScrollbarButtonsPlacementDoubleBoth); - break; - case kForwardButtonEndPart: - need_layout_object = - (buttons_placement == kWebScrollbarButtonsPlacementSingle || - buttons_placement == kWebScrollbarButtonsPlacementDoubleEnd || - buttons_placement == kWebScrollbarButtonsPlacementDoubleBoth); + case kForwardButtonStartPart: + // These buttons are not supported by any OS. + need_layout_object = false; break; default: break; diff --git a/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.h b/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.h index 6f097019e0f..734cb2d7931 100644 --- a/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.h +++ b/chromium/third_party/blink/renderer/core/layout/custom_scrollbar.h @@ -45,10 +45,6 @@ class LayoutObject; // LayoutCustomScrollbarPart. class CustomScrollbar final : public Scrollbar { public: - static Scrollbar* CreateCustomScrollbar(ScrollableArea*, - ScrollbarOrientation, - Element*); - CustomScrollbar(ScrollableArea*, ScrollbarOrientation, Element*); ~CustomScrollbar() override; @@ -77,7 +73,7 @@ class CustomScrollbar final : public Scrollbar { void SetVisualRect(const IntRect&) final; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: friend class Scrollbar; @@ -94,8 +90,9 @@ class CustomScrollbar final : public Scrollbar { void UpdateScrollbarParts(bool destroy = false); - scoped_refptr<ComputedStyle> GetScrollbarPseudoElementStyle(ScrollbarPart, - PseudoId); + scoped_refptr<const ComputedStyle> GetScrollbarPseudoElementStyle( + ScrollbarPart, + PseudoId); void UpdateScrollbarPart(ScrollbarPart, bool destroy = false); HashMap<unsigned, LayoutCustomScrollbarPart*> parts_; diff --git a/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc index e5556b46d71..ebc3d542dff 100644 --- a/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc @@ -32,7 +32,8 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" namespace blink { namespace { @@ -72,77 +73,97 @@ ContentDistributionType BoxPackToContentDistribution(EBoxPack box_pack) { } // namespace -FlexItem::FlexItem(LayoutBox* box, +FlexItem::FlexItem(const FlexLayoutAlgorithm* algorithm, + LayoutBox* box, + const ComputedStyle& style, LayoutUnit flex_base_content_size, - MinMaxSize min_max_sizes, - base::Optional<MinMaxSize> min_max_cross_axis_sizes, + MinMaxSizes min_max_main_sizes, + base::Optional<MinMaxSizes> min_max_cross_sizes, LayoutUnit main_axis_border_padding, - LayoutUnit main_axis_margin) - : algorithm(nullptr), + LayoutUnit cross_axis_border_padding, + NGPhysicalBoxStrut physical_margins) + : algorithm(algorithm), line_number(0), box(box), + style(style), flex_base_content_size(flex_base_content_size), - min_max_sizes(min_max_sizes), - min_max_cross_sizes(min_max_cross_axis_sizes), + min_max_main_sizes(min_max_main_sizes), + min_max_cross_sizes(min_max_cross_sizes), hypothetical_main_content_size( - min_max_sizes.ClampSizeToMinAndMax(flex_base_content_size)), + min_max_main_sizes.ClampSizeToMinAndMax(flex_base_content_size)), main_axis_border_padding(main_axis_border_padding), - main_axis_margin(main_axis_margin), + cross_axis_border_padding(cross_axis_border_padding), + physical_margins(physical_margins), frozen(false), needs_relayout_for_stretch(false), ng_input_node(/* LayoutBox* */ nullptr) { - DCHECK(!box->IsOutOfFlowPositioned()); - DCHECK_GE(min_max_sizes.max_size, LayoutUnit()) + DCHECK_GE(min_max_main_sizes.max_size, LayoutUnit()) << "Use LayoutUnit::Max() for no max size"; } bool FlexItem::MainAxisIsInlineAxis() const { - return algorithm->IsHorizontalFlow() == box->IsHorizontalWritingMode(); + return algorithm->IsHorizontalFlow() == style.IsHorizontalWritingMode(); } LayoutUnit FlexItem::FlowAwareMarginStart() const { if (algorithm->IsHorizontalFlow()) { - return algorithm->IsLeftToRightFlow() ? box->MarginLeft() - : box->MarginRight(); + return algorithm->IsLeftToRightFlow() ? physical_margins.left + : physical_margins.right; } - return algorithm->IsLeftToRightFlow() ? box->MarginTop() - : box->MarginBottom(); + return algorithm->IsLeftToRightFlow() ? physical_margins.top + : physical_margins.bottom; } LayoutUnit FlexItem::FlowAwareMarginEnd() const { if (algorithm->IsHorizontalFlow()) { - return algorithm->IsLeftToRightFlow() ? box->MarginRight() - : box->MarginLeft(); + return algorithm->IsLeftToRightFlow() ? physical_margins.right + : physical_margins.left; } - return algorithm->IsLeftToRightFlow() ? box->MarginBottom() - : box->MarginTop(); + return algorithm->IsLeftToRightFlow() ? physical_margins.bottom + : physical_margins.top; } LayoutUnit FlexItem::FlowAwareMarginBefore() const { switch (algorithm->GetTransformedWritingMode()) { case TransformedWritingMode::kTopToBottomWritingMode: - return box->MarginTop(); + return physical_margins.top; case TransformedWritingMode::kBottomToTopWritingMode: - return box->MarginBottom(); + return physical_margins.bottom; case TransformedWritingMode::kLeftToRightWritingMode: - return box->MarginLeft(); + return physical_margins.left; case TransformedWritingMode::kRightToLeftWritingMode: - return box->MarginRight(); + return physical_margins.right; } NOTREACHED(); - return box->MarginTop(); + return LayoutUnit(); +} + +LayoutUnit FlexItem::MainAxisMarginExtent() const { + return algorithm->IsHorizontalFlow() ? physical_margins.HorizontalSum() + : physical_margins.VerticalSum(); } LayoutUnit FlexItem::CrossAxisMarginExtent() const { - return algorithm->IsHorizontalFlow() ? box->MarginHeight() - : box->MarginWidth(); + return algorithm->IsHorizontalFlow() ? physical_margins.VerticalSum() + : physical_margins.HorizontalSum(); } LayoutUnit FlexItem::MarginBoxAscent() const { - LayoutUnit ascent(box->FirstLineBoxBaseline()); - if (ascent == -1) - ascent = cross_axis_size; - return ascent + FlowAwareMarginBefore(); + if (box) { + LayoutUnit ascent(box->FirstLineBoxBaseline()); + if (ascent == -1) + ascent = cross_axis_size; + return ascent + FlowAwareMarginBefore(); + } + + DCHECK(layout_result); + base::Optional<LayoutUnit> baseline = + NGBoxFragment( + algorithm->StyleRef().GetWritingMode(), + algorithm->StyleRef().Direction(), + To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment())) + .Baseline(); + return baseline.value_or(cross_axis_size) + FlowAwareMarginBefore(); } LayoutUnit FlexItem::AvailableAlignmentSpace() const { @@ -152,64 +173,59 @@ LayoutUnit FlexItem::AvailableAlignmentSpace() const { bool FlexItem::HasAutoMarginsInCrossAxis() const { if (algorithm->IsHorizontalFlow()) { - return box->StyleRef().MarginTop().IsAuto() || - box->StyleRef().MarginBottom().IsAuto(); + return style.MarginTop().IsAuto() || style.MarginBottom().IsAuto(); } - return box->StyleRef().MarginLeft().IsAuto() || - box->StyleRef().MarginRight().IsAuto(); + return style.MarginLeft().IsAuto() || style.MarginRight().IsAuto(); } ItemPosition FlexItem::Alignment() const { - return FlexLayoutAlgorithm::AlignmentForChild(*algorithm->Style(), - box->StyleRef()); + return FlexLayoutAlgorithm::AlignmentForChild(*algorithm->Style(), style); } void FlexItem::UpdateAutoMarginsInMainAxis(LayoutUnit auto_margin_offset) { DCHECK_GE(auto_margin_offset, LayoutUnit()); if (algorithm->IsHorizontalFlow()) { - if (box->StyleRef().MarginLeft().IsAuto()) - box->SetMarginLeft(auto_margin_offset); - if (box->StyleRef().MarginRight().IsAuto()) - box->SetMarginRight(auto_margin_offset); + if (style.MarginLeft().IsAuto()) + physical_margins.left = auto_margin_offset; + if (style.MarginRight().IsAuto()) + physical_margins.right = auto_margin_offset; } else { - if (box->StyleRef().MarginTop().IsAuto()) - box->SetMarginTop(auto_margin_offset); - if (box->StyleRef().MarginBottom().IsAuto()) - box->SetMarginBottom(auto_margin_offset); + if (style.MarginTop().IsAuto()) + physical_margins.top = auto_margin_offset; + if (style.MarginBottom().IsAuto()) + physical_margins.bottom = auto_margin_offset; } } bool FlexItem::UpdateAutoMarginsInCrossAxis( LayoutUnit available_alignment_space) { - DCHECK(!box->IsOutOfFlowPositioned()); DCHECK_GE(available_alignment_space, LayoutUnit()); bool is_horizontal = algorithm->IsHorizontalFlow(); - const Length& top_or_left = is_horizontal ? box->StyleRef().MarginTop() - : box->StyleRef().MarginLeft(); - const Length& bottom_or_right = is_horizontal ? box->StyleRef().MarginBottom() - : box->StyleRef().MarginRight(); + const Length& top_or_left = + is_horizontal ? style.MarginTop() : style.MarginLeft(); + const Length& bottom_or_right = + is_horizontal ? style.MarginBottom() : style.MarginRight(); if (top_or_left.IsAuto() && bottom_or_right.IsAuto()) { desired_location.Move(LayoutUnit(), available_alignment_space / 2); if (is_horizontal) { - box->SetMarginTop(available_alignment_space / 2); - box->SetMarginBottom(available_alignment_space / 2); + physical_margins.top = available_alignment_space / 2; + physical_margins.bottom = available_alignment_space / 2; } else { - box->SetMarginLeft(available_alignment_space / 2); - box->SetMarginRight(available_alignment_space / 2); + physical_margins.left = available_alignment_space / 2; + physical_margins.right = available_alignment_space / 2; } return true; } bool should_adjust_top_or_left = true; - if (algorithm->IsColumnFlow() && !box->StyleRef().IsLeftToRightDirection()) { + if (algorithm->IsColumnFlow() && !style.IsLeftToRightDirection()) { // For column flows, only make this adjustment if topOrLeft corresponds to // the "before" margin, so that flipForRightToLeftColumn will do the right // thing. should_adjust_top_or_left = false; } - if (!algorithm->IsColumnFlow() && - box->StyleRef().IsFlippedBlocksWritingMode()) { + if (!algorithm->IsColumnFlow() && style.IsFlippedBlocksWritingMode()) { // If we are a flipped writing mode, we need to adjust the opposite side. // This is only needed for row flows because this only affects the // block-direction axis. @@ -221,9 +237,9 @@ bool FlexItem::UpdateAutoMarginsInCrossAxis( desired_location.Move(LayoutUnit(), available_alignment_space); if (is_horizontal) - box->SetMarginTop(available_alignment_space); + physical_margins.top = available_alignment_space; else - box->SetMarginLeft(available_alignment_space); + physical_margins.left = available_alignment_space; return true; } if (bottom_or_right.IsAuto()) { @@ -231,9 +247,9 @@ bool FlexItem::UpdateAutoMarginsInCrossAxis( desired_location.Move(LayoutUnit(), available_alignment_space); if (is_horizontal) - box->SetMarginBottom(available_alignment_space); + physical_margins.bottom = available_alignment_space; else - box->SetMarginRight(available_alignment_space); + physical_margins.right = available_alignment_space; return true; } return false; @@ -241,25 +257,24 @@ bool FlexItem::UpdateAutoMarginsInCrossAxis( void FlexItem::ComputeStretchedSize() { DCHECK_EQ(Alignment(), ItemPosition::kStretch); - if (MainAxisIsInlineAxis() && box->StyleRef().LogicalHeight().IsAuto()) { - LayoutUnit stretched_logical_height = - std::max(box->BorderAndPaddingLogicalHeight(), - Line()->cross_axis_extent - CrossAxisMarginExtent()); - cross_axis_size = box->ConstrainLogicalHeightByMinMax( - stretched_logical_height, box->IntrinsicContentLogicalHeight()); - } else if (!MainAxisIsInlineAxis() && - box->StyleRef().LogicalWidth().IsAuto()) { - LayoutUnit child_width = - (Line()->cross_axis_extent - CrossAxisMarginExtent()) - .ClampNegativeToZero(); - if (LayoutFlexibleBox* flexbox = ToLayoutFlexibleBoxOrNull(box->Parent())) { + LayoutUnit stretched_size = + std::max(cross_axis_border_padding, + Line()->cross_axis_extent - CrossAxisMarginExtent()); + if (box) { + if (MainAxisIsInlineAxis() && style.LogicalHeight().IsAuto()) { + cross_axis_size = box->ConstrainLogicalHeightByMinMax( + stretched_size, box->IntrinsicContentLogicalHeight()); + } else if (!MainAxisIsInlineAxis() && style.LogicalWidth().IsAuto()) { + const LayoutFlexibleBox* flexbox = ToLayoutFlexibleBox(box->Parent()); cross_axis_size = box->ConstrainLogicalWidthByMinMax( - child_width, flexbox->CrossAxisContentExtent(), flexbox); - } else { - DCHECK(box->Parent()->IsLayoutNGFlexibleBox()); - cross_axis_size = min_max_cross_sizes->ClampSizeToMinAndMax(child_width); + stretched_size, flexbox->CrossAxisContentExtent(), flexbox); } + return; } + + if ((MainAxisIsInlineAxis() && style.LogicalHeight().IsAuto()) || + (!MainAxisIsInlineAxis() && style.LogicalWidth().IsAuto())) + cross_axis_size = min_max_cross_sizes->ClampSizeToMinAndMax(stretched_size); } // static @@ -314,12 +329,11 @@ void FlexLine::FreezeViolations(ViolationsVector& violations) { const ComputedStyle& flex_box_style = algorithm->StyleRef(); for (size_t i = 0; i < violations.size(); ++i) { DCHECK(!violations[i]->frozen) << i; - LayoutBox* child = violations[i]->box; + const ComputedStyle& child_style = violations[i]->style; LayoutUnit child_size = violations[i]->flexed_content_size; remaining_free_space -= child_size - violations[i]->flex_base_content_size; - total_flex_grow -= child->StyleRef().ResolvedFlexGrow(flex_box_style); - const float flex_shrink = - child->StyleRef().ResolvedFlexShrink(flex_box_style); + total_flex_grow -= child_style.ResolvedFlexGrow(flex_box_style); + const float flex_shrink = child_style.ResolvedFlexShrink(flex_box_style); total_flex_shrink -= flex_shrink; total_weighted_flex_shrink -= flex_shrink * violations[i]->flex_base_content_size; @@ -344,13 +358,11 @@ void FlexLine::FreezeInflexibleItems() { const ComputedStyle& flex_box_style = algorithm->StyleRef(); for (size_t i = 0; i < line_items.size(); ++i) { FlexItem& flex_item = line_items[i]; - LayoutBox* child = flex_item.box; - DCHECK(!flex_item.box->IsOutOfFlowPositioned()); DCHECK(!flex_item.frozen) << i; float flex_factor = (flex_sign == kPositiveFlexibility) - ? child->StyleRef().ResolvedFlexGrow(flex_box_style) - : child->StyleRef().ResolvedFlexShrink(flex_box_style); + ? flex_item.style.ResolvedFlexGrow(flex_box_style) + : flex_item.style.ResolvedFlexShrink(flex_box_style); if (flex_factor == 0 || (flex_sign == kPositiveFlexibility && flex_item.flex_base_content_size > @@ -384,7 +396,6 @@ bool FlexLine::ResolveFlexibleLengths() { const ComputedStyle& flex_box_style = algorithm->StyleRef(); for (size_t i = 0; i < line_items.size(); ++i) { FlexItem& flex_item = line_items[i]; - LayoutBox* child = flex_item.box; // This check also covers out-of-flow children. if (flex_item.frozen) @@ -395,14 +406,14 @@ bool FlexLine::ResolveFlexibleLengths() { if (remaining_free_space > 0 && total_flex_grow > 0 && flex_sign == kPositiveFlexibility && std::isfinite(total_flex_grow)) { extra_space = remaining_free_space * - child->StyleRef().ResolvedFlexGrow(flex_box_style) / + flex_item.style.ResolvedFlexGrow(flex_box_style) / total_flex_grow; } else if (remaining_free_space < 0 && total_weighted_flex_shrink > 0 && flex_sign == kNegativeFlexibility && std::isfinite(total_weighted_flex_shrink) && - child->StyleRef().ResolvedFlexShrink(flex_box_style)) { + flex_item.style.ResolvedFlexShrink(flex_box_style)) { extra_space = remaining_free_space * - child->StyleRef().ResolvedFlexShrink(flex_box_style) * + flex_item.style.ResolvedFlexShrink(flex_box_style) * flex_item.flex_base_content_size / total_weighted_flex_shrink; } @@ -438,17 +449,16 @@ LayoutUnit FlexLine::ApplyMainAxisAutoMarginAdjustment() { int number_of_auto_margins = 0; bool is_horizontal = algorithm->IsHorizontalFlow(); for (size_t i = 0; i < line_items.size(); ++i) { - LayoutBox* child = line_items[i].box; - DCHECK(!child->IsOutOfFlowPositioned()); + const ComputedStyle& style = line_items[i].style; if (is_horizontal) { - if (child->StyleRef().MarginLeft().IsAuto()) + if (style.MarginLeft().IsAuto()) ++number_of_auto_margins; - if (child->StyleRef().MarginRight().IsAuto()) + if (style.MarginRight().IsAuto()) ++number_of_auto_margins; } else { - if (child->StyleRef().MarginTop().IsAuto()) + if (style.MarginTop().IsAuto()) ++number_of_auto_margins; - if (child->StyleRef().MarginBottom().IsAuto()) + if (style.MarginBottom().IsAuto()) ++number_of_auto_margins; } } @@ -468,11 +478,8 @@ void FlexLine::ComputeLineItemsPosition(LayoutUnit main_axis_start_offset, // Recalculate the remaining free space. The adjustment for flex factors // between 0..1 means we can't just use remainingFreeSpace here. LayoutUnit total_item_size; - for (size_t i = 0; i < line_items.size(); ++i) { - FlexItem& flex_item = line_items[i]; - DCHECK(!flex_item.box->IsOutOfFlowPositioned()); - total_item_size += flex_item.FlexedMarginBoxSize(); - } + for (size_t i = 0; i < line_items.size(); ++i) + total_item_size += line_items[i].FlexedMarginBoxSize(); remaining_free_space = container_main_inner_size - total_item_size; const StyleContentAlignmentData justify_content = @@ -516,8 +523,6 @@ void FlexLine::ComputeLineItemsPosition(LayoutUnit main_axis_start_offset, for (size_t i = 0; i < line_items.size(); ++i) { FlexItem& flex_item = line_items[i]; - DCHECK(!flex_item.box->IsOutOfFlowPositioned()); - flex_item.UpdateAutoMarginsInMainAxis(auto_margin_offset); LayoutUnit child_cross_axis_margin_box_extent; @@ -590,7 +595,6 @@ FlexLine* FlexLayoutAlgorithm::ComputeNextFlexLine( for (; next_item_index_ < all_items_.size(); ++next_item_index_) { FlexItem& flex_item = all_items_[next_item_index_]; - DCHECK(!flex_item.box->IsOutOfFlowPositioned()); if (IsMultiline() && sum_hypothetical_main_size + flex_item.HypotheticalMainAxisMarginBoxSize() > @@ -600,9 +604,8 @@ FlexLine* FlexLayoutAlgorithm::ComputeNextFlexLine( } line_has_in_flow_item = true; sum_flex_base_size += flex_item.FlexBaseMarginBoxSize(); - total_flex_grow += flex_item.box->StyleRef().ResolvedFlexGrow(StyleRef()); - const float flex_shrink = - flex_item.box->StyleRef().ResolvedFlexShrink(StyleRef()); + total_flex_grow += flex_item.style.ResolvedFlexGrow(StyleRef()); + const float flex_shrink = flex_item.style.ResolvedFlexShrink(StyleRef()); total_flex_shrink += flex_shrink; total_weighted_flex_shrink += flex_shrink * flex_item.flex_base_content_size; @@ -663,6 +666,8 @@ bool FlexLayoutAlgorithm::ShouldApplyMinSizeAutoForChild( // css-flexbox section 4.5 const Length& min = IsHorizontalFlow() ? child.StyleRef().MinWidth() : child.StyleRef().MinHeight(); + // TODO(dgrogan): min.IsIntrinsic should also get past this check when in the + // item's block direction. if (!min.IsAuto()) return false; @@ -670,6 +675,8 @@ bool FlexLayoutAlgorithm::ShouldApplyMinSizeAutoForChild( if (StyleRef().IsDeprecatedWebkitBox()) return false; + // TODO(dgrogan): MainAxisOverflowForChild == kClip also qualifies, not just + // kVisible. return !child.ShouldApplySizeContainment() && MainAxisOverflowForChild(child) == EOverflow::kVisible; } @@ -737,12 +744,9 @@ void FlexLayoutAlgorithm::AlignChildren() { LayoutUnit max_ascent = line_context.max_ascent; for (FlexItem& flex_item : line_context.line_items) { - DCHECK(!flex_item.box->IsOutOfFlowPositioned()); - if (flex_item.UpdateAutoMarginsInCrossAxis( - std::max(LayoutUnit(), flex_item.AvailableAlignmentSpace()))) { + flex_item.AvailableAlignmentSpace().ClampNegativeToZero())) continue; - } ItemPosition position = flex_item.Alignment(); if (position == ItemPosition::kStretch) { @@ -980,14 +984,17 @@ void FlexLayoutAlgorithm::LayoutColumnReverse( child_number < line_context.line_items.size(); ++child_number) { FlexItem& flex_item = line_context.line_items[child_number]; LayoutUnit item_main_size = flex_item.FlexedBorderBoxSize(); + + NGBoxStrut margins = flex_item.physical_margins.ConvertToLogical( + Style()->GetWritingMode(), Style()->Direction()); + // We passed 0 as the initial main_axis offset to ComputeLineItemsPosition // for ColumnReverse containers so here we have to add the // border_scrollbar_padding of the container. flex_item.desired_location.SetX( main_axis_content_size + border_scrollbar_padding_before - - flex_item.desired_location.X() - item_main_size - - flex_item.box->MarginAfter(Style()) + - flex_item.box->MarginBefore(Style())); + flex_item.desired_location.X() - item_main_size - margins.block_end + + margins.block_start); } } } diff --git a/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.h index 8fc05cdab10..19ccd4575f2 100644 --- a/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/flexible_box_algorithm.h @@ -33,7 +33,7 @@ #include "base/macros.h" #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/order_iterator.h" @@ -49,7 +49,7 @@ class FlexItem; class FlexLine; class FlexLayoutAlgorithm; class LayoutBox; -struct MinMaxSize; +struct MinMaxSizes; enum FlexSign { kPositiveFlexibility, @@ -113,24 +113,28 @@ class FlexItem { public: // Parameters: // - |flex_base_content_size| includes scrollbar size but not border/padding. - // - |min_max_sizes| is the resolved min and max size properties in the + // - |min_max_main_sizes| is the resolved min and max size properties in the // main axis direction (not intrinsic widths). It does not include - // border/scrollbar/padding. - FlexItem(LayoutBox*, + // border/padding. + FlexItem(const FlexLayoutAlgorithm*, + LayoutBox*, + const ComputedStyle& style, LayoutUnit flex_base_content_size, - MinMaxSize min_max_main_axis_sizes, + MinMaxSizes min_max_main_sizes, // Ignored for legacy, required for NG: - base::Optional<MinMaxSize> min_max_cross_axis_sizes, + base::Optional<MinMaxSizes> min_max_cross_sizes, LayoutUnit main_axis_border_padding, - LayoutUnit main_axis_margin); + LayoutUnit cross_axis_border_padding, + NGPhysicalBoxStrut physical_margins); LayoutUnit HypotheticalMainAxisMarginBoxSize() const { return hypothetical_main_content_size + main_axis_border_padding + - main_axis_margin; + MainAxisMarginExtent(); } LayoutUnit FlexBaseMarginBoxSize() const { - return flex_base_content_size + main_axis_border_padding + main_axis_margin; + return flex_base_content_size + main_axis_border_padding + + MainAxisMarginExtent(); } LayoutUnit FlexedBorderBoxSize() const { @@ -138,11 +142,12 @@ class FlexItem { } LayoutUnit FlexedMarginBoxSize() const { - return flexed_content_size + main_axis_border_padding + main_axis_margin; + return flexed_content_size + main_axis_border_padding + + MainAxisMarginExtent(); } LayoutUnit ClampSizeToMinAndMax(LayoutUnit size) const { - return min_max_sizes.ClampSizeToMinAndMax(size); + return min_max_main_sizes.ClampSizeToMinAndMax(size); } ItemPosition Alignment() const; @@ -152,6 +157,8 @@ class FlexItem { LayoutUnit FlowAwareMarginStart() const; LayoutUnit FlowAwareMarginEnd() const; LayoutUnit FlowAwareMarginBefore() const; + + LayoutUnit MainAxisMarginExtent() const; LayoutUnit CrossAxisMarginExtent() const; LayoutUnit MarginBoxAscent() const; @@ -178,15 +185,18 @@ class FlexItem { bool is_wrap_reverse, bool is_deprecated_webkit_box); - FlexLayoutAlgorithm* algorithm; + const FlexLayoutAlgorithm* algorithm; wtf_size_t line_number; LayoutBox* box; + const ComputedStyle& style; const LayoutUnit flex_base_content_size; - const MinMaxSize min_max_sizes; - const base::Optional<MinMaxSize> min_max_cross_sizes; + const MinMaxSizes min_max_main_sizes; + const base::Optional<MinMaxSizes> min_max_cross_sizes; const LayoutUnit hypothetical_main_content_size; const LayoutUnit main_axis_border_padding; - const LayoutUnit main_axis_margin; + const LayoutUnit cross_axis_border_padding; + NGPhysicalBoxStrut physical_margins; + LayoutUnit flexed_content_size; // When set by the caller, this should be the size pre-stretching. @@ -327,7 +337,7 @@ class FlexLine { // https://drafts.csswg.org/css-flexbox/ // // Expected usage is as follows: -// FlexLayoutAlgorithm algorithm(Style(), MainAxisLength(), flex_items); +// FlexLayoutAlgorithm algorithm(Style(), MainAxisLength()); // for (each child) { // algorithm.emplace_back(...caller must compute these values...) // } @@ -353,14 +363,13 @@ class FlexLayoutAlgorithm { template <typename... Args> FlexItem& emplace_back(Args&&... args) { - FlexItem& item = all_items_.emplace_back(std::forward<Args>(args)...); - item.algorithm = this; - return item; + return all_items_.emplace_back(this, std::forward<Args>(args)...); } const ComputedStyle* Style() const { return style_; } const ComputedStyle& StyleRef() const { return *style_; } + const Vector<FlexLine>& FlexLines() const { return flex_lines_; } Vector<FlexLine>& FlexLines() { return flex_lines_; } // Computes the next flex line, stores it in FlexLines(), and returns a diff --git a/chromium/third_party/blink/renderer/core/layout/floating_objects.cc b/chromium/third_party/blink/renderer/core/layout/floating_objects.cc index 1fe5923973b..d3bc48f940f 100644 --- a/chromium/third_party/blink/renderer/core/layout/floating_objects.cc +++ b/chromium/third_party/blink/renderer/core/layout/floating_objects.cc @@ -47,7 +47,7 @@ struct SameSizeAsFloatingObject { static_assert(sizeof(FloatingObject) == sizeof(SameSizeAsFloatingObject), "FloatingObject should stay small"); -FloatingObject::FloatingObject(LayoutBox* layout_object, Type type) +FloatingObject::FloatingObject(PassKey key, LayoutBox* layout_object, Type type) : layout_object_(layout_object), originating_line_(nullptr), type_(type), @@ -63,7 +63,8 @@ FloatingObject::FloatingObject(LayoutBox* layout_object, Type type) { } -FloatingObject::FloatingObject(LayoutBox* layout_object, +FloatingObject::FloatingObject(PassKey key, + LayoutBox* layout_object, Type type, const LayoutRect& frame_rect, bool should_paint, @@ -89,7 +90,7 @@ FloatingObject::FloatingObject(LayoutBox* layout_object, std::unique_ptr<FloatingObject> FloatingObject::Create(LayoutBox* layout_object, Type type) { std::unique_ptr<FloatingObject> new_obj = - base::WrapUnique(new FloatingObject(layout_object, type)); + base::WrapUnique(new FloatingObject(PassKey(), layout_object, type)); // If a layer exists, the float will paint itself. Otherwise someone else // will. @@ -114,14 +115,14 @@ std::unique_ptr<FloatingObject> FloatingObject::CopyToNewContainer( bool should_paint, bool is_descendant) const { return base::WrapUnique(new FloatingObject( - GetLayoutObject(), GetType(), + PassKey(), GetLayoutObject(), GetType(), LayoutRect(FrameRect().Location() - offset, FrameRect().Size()), should_paint, is_descendant, IsLowestNonOverhangingFloatInChild())); } std::unique_ptr<FloatingObject> FloatingObject::UnsafeClone() const { std::unique_ptr<FloatingObject> clone_object = base::WrapUnique( - new FloatingObject(GetLayoutObject(), GetType(), frame_rect_, + new FloatingObject(PassKey(), GetLayoutObject(), GetType(), frame_rect_, should_paint_, is_descendant_, false)); clone_object->is_placed_ = is_placed_; #if DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/core/layout/floating_objects.h b/chromium/third_party/blink/renderer/core/layout/floating_objects.h index f11a30e3aad..11edc377a8d 100644 --- a/chromium/third_party/blink/renderer/core/layout/floating_objects.h +++ b/chromium/third_party/blink/renderer/core/layout/floating_objects.h @@ -27,6 +27,7 @@ #include <memory> #include "base/macros.h" +#include "base/util/type_safety/pass_key.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/list_hash_set.h" @@ -147,15 +148,17 @@ class FloatingObject { RootInlineBox* OriginatingLine() const { return originating_line_; } void SetOriginatingLine(RootInlineBox* line) { originating_line_ = line; } - private: - FloatingObject(LayoutBox*, Type); - FloatingObject(LayoutBox*, + using PassKey = util::PassKey<FloatingObject>; + FloatingObject(PassKey, LayoutBox*, Type); + FloatingObject(PassKey, + LayoutBox*, Type, const LayoutRect&, bool should_paint, bool is_descendant, bool is_lowest_non_overhanging_float_in_child); + private: LayoutBox* layout_object_; RootInlineBox* originating_line_; LayoutRect frame_rect_; diff --git a/chromium/third_party/blink/renderer/core/layout/generated_children.h b/chromium/third_party/blink/renderer/core/layout/generated_children.h index 52db3958b73..6ed0c9020de 100644 --- a/chromium/third_party/blink/renderer/core/layout/generated_children.h +++ b/chromium/third_party/blink/renderer/core/layout/generated_children.h @@ -18,7 +18,7 @@ static bool CanHaveGeneratedChildren(const LayoutObject& layout_object) { // FIXME: LayoutMedia::layout makes assumptions about what children are // allowed so we can't support generated content. if (layout_object.IsMedia() || layout_object.IsTextControl() || - layout_object.IsMenuList()) + IsMenuList(&layout_object)) return false; // Input elements can't have generated children, but button elements can. diff --git a/chromium/third_party/blink/renderer/core/layout/geometry/physical_offset.h b/chromium/third_party/blink/renderer/core/layout/geometry/physical_offset.h index f435b7ec3bd..20ddb662d16 100644 --- a/chromium/third_party/blink/renderer/core/layout/geometry/physical_offset.h +++ b/chromium/third_party/blink/renderer/core/layout/geometry/physical_offset.h @@ -96,6 +96,8 @@ struct CORE_EXPORT PhysicalOffset { : left(point.X()), top(point.Y()) {} explicit PhysicalOffset(const IntSize& size) : left(size.Width()), top(size.Height()) {} + explicit PhysicalOffset(const gfx::Point& point) + : left(point.x()), top(point.y()) {} static PhysicalOffset FromFloatPointFloor(const FloatPoint& point) { return {LayoutUnit::FromFloatFloor(point.X()), diff --git a/chromium/third_party/blink/renderer/core/layout/geometry/physical_rect.h b/chromium/third_party/blink/renderer/core/layout/geometry/physical_rect.h index 35436292847..8c8a236c58c 100644 --- a/chromium/third_party/blink/renderer/core/layout/geometry/physical_rect.h +++ b/chromium/third_party/blink/renderer/core/layout/geometry/physical_rect.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GEOMETRY_PHYSICAL_RECT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GEOMETRY_PHYSICAL_RECT_H_ +#include "base/compiler_specific.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h" #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h" @@ -97,8 +98,8 @@ struct CORE_EXPORT PhysicalRect { return Contains(point.left, point.top); } - bool Intersects(const PhysicalRect&) const; - bool IntersectsInclusively(const PhysicalRect&) const; + WARN_UNUSED_RESULT bool Intersects(const PhysicalRect&) const; + WARN_UNUSED_RESULT bool IntersectsInclusively(const PhysicalRect&) const; // Whether all edges of the rect are at full-pixel boundaries. // i.e.: EnclosingIntRect(this)) == this @@ -107,10 +108,6 @@ struct CORE_EXPORT PhysicalRect { !size.width.HasFraction() && !size.height.HasFraction(); } - PhysicalRect operator+(const PhysicalOffset&) const { - return {this->offset + offset, size}; - } - void Unite(const PhysicalRect&); void UniteIfNonZero(const PhysicalRect&); void UniteEvenIfEmpty(const PhysicalRect&); diff --git a/chromium/third_party/blink/renderer/core/layout/grid.cc b/chromium/third_party/blink/renderer/core/layout/grid.cc index f13eca09ca2..d86bcfc238c 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid.cc @@ -8,6 +8,7 @@ #include <memory> #include <utility> +#include "base/memory/ptr_util.h" #include "third_party/blink/renderer/core/layout/layout_grid.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/grid_test.cc b/chromium/third_party/blink/renderer/core/layout/grid_test.cc index 65bdebf1006..e9779901a9d 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid_test.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/grid.h" #include "third_party/blink/renderer/core/layout/layout_grid.h" +#include "base/memory/ptr_util.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" namespace blink { diff --git a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc index d803cb4052f..9ac29ea0ea3 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc @@ -148,29 +148,47 @@ class DefiniteSizeStrategy final : public GridTrackSizingAlgorithmStrategy { GridTrackSizingAlgorithmStrategy::~GridTrackSizingAlgorithmStrategy() = default; -bool GridTrackSizingAlgorithmStrategy:: - ShouldClearOverrideContainingBlockContentSizeForChild( - const LayoutGrid& grid, - const LayoutBox& child, - GridTrackSizingDirection direction) { +bool GridTrackSizingAlgorithmStrategy::HasRelativeMarginOrPaddingForChild( + const LayoutGrid& grid, + const LayoutBox& child, + GridTrackSizingDirection direction) { GridTrackSizingDirection child_inline_direction = GridLayoutUtils::FlowAwareDirectionForChild(grid, child, kForColumns); if (direction == child_inline_direction) { - return child.HasRelativeLogicalWidth() || - child.StyleRef().LogicalWidth().IsIntrinsicOrAuto() || - child.StyleRef().MarginStart().IsPercentOrCalc() || + return child.StyleRef().MarginStart().IsPercentOrCalc() || child.StyleRef().MarginEnd().IsPercentOrCalc() || child.StyleRef().PaddingStart().IsPercentOrCalc() || child.StyleRef().PaddingEnd().IsPercentOrCalc(); } - return child.HasRelativeLogicalHeight() || - child.StyleRef().LogicalHeight().IsIntrinsicOrAuto() || - child.StyleRef().MarginBefore().IsPercentOrCalc() || + return child.StyleRef().MarginBefore().IsPercentOrCalc() || child.StyleRef().MarginAfter().IsPercentOrCalc() || child.StyleRef().PaddingBefore().IsPercentOrCalc() || child.StyleRef().PaddingAfter().IsPercentOrCalc(); } +bool GridTrackSizingAlgorithmStrategy::HasRelativeOrIntrinsicSizeForChild( + const LayoutGrid& grid, + const LayoutBox& child, + GridTrackSizingDirection direction) { + GridTrackSizingDirection child_inline_direction = + GridLayoutUtils::FlowAwareDirectionForChild(grid, child, kForColumns); + if (direction == child_inline_direction) { + return child.HasRelativeLogicalWidth() || + child.StyleRef().LogicalWidth().IsIntrinsicOrAuto(); + } + return child.HasRelativeLogicalHeight() || + child.StyleRef().LogicalHeight().IsIntrinsicOrAuto(); +} + +bool GridTrackSizingAlgorithmStrategy:: + ShouldClearOverrideContainingBlockContentSizeForChild( + const LayoutGrid& grid, + const LayoutBox& child, + GridTrackSizingDirection direction) { + return HasRelativeOrIntrinsicSizeForChild(grid, child, direction) || + HasRelativeMarginOrPaddingForChild(grid, child, direction); +} + void GridTrackSizingAlgorithmStrategy:: SetOverrideContainingBlockContentSizeForChild( LayoutBox& child, @@ -224,7 +242,8 @@ LayoutUnit GridTrackSizingAlgorithm::EstimatedGridAreaBreadthForChild( kForColumns); if (grid_area_is_indefinite) { return direction == child_inline_direction - ? std::max(child.MaxPreferredLogicalWidth(), grid_area_size) + ? std::max(child.PreferredLogicalWidths().max_size, + grid_area_size) : LayoutUnit(-1); } return grid_area_size; @@ -338,7 +357,7 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::MinContentForChild( // FIXME: It's unclear if we should return the intrinsic width or the // preferred width. // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html - return child.MinPreferredLogicalWidth() + + return child.PreferredLogicalWidths().min_size + GridLayoutUtils::MarginLogicalWidthForChild(*GetLayoutGrid(), child) + algorithm_.BaselineOffsetForChild(child, @@ -362,7 +381,7 @@ LayoutUnit GridTrackSizingAlgorithmStrategy::MaxContentForChild( // FIXME: It's unclear if we should return the intrinsic width or the // preferred width. // See http://lists.w3.org/Archives/Public/www-style/2013Jan/0245.html - return child.MaxPreferredLogicalWidth() + + return child.PreferredLogicalWidths().max_size + GridLayoutUtils::MarginLogicalWidthForChild(*GetLayoutGrid(), child) + algorithm_.BaselineOffsetForChild(child, @@ -570,8 +589,11 @@ LayoutUnit DefiniteSizeStrategy::MinLogicalSizeForChild( kForColumns); LayoutUnit indefinite_size = Direction() == child_inline_direction ? LayoutUnit() : LayoutUnit(-1); - if (ShouldClearOverrideContainingBlockContentSizeForChild( - *GetLayoutGrid(), child, Direction())) { + if (HasRelativeMarginOrPaddingForChild(*GetLayoutGrid(), child, + Direction()) || + (Direction() != child_inline_direction && + HasRelativeOrIntrinsicSizeForChild(*GetLayoutGrid(), child, + Direction()))) { SetOverrideContainingBlockContentSizeForChild(child, Direction(), indefinite_size); } diff --git a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h index c2f67decf4f..5472026fedd 100644 --- a/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.h @@ -326,6 +326,12 @@ class GridTrackSizingAlgorithmStrategy { } // Helper functions + static bool HasRelativeMarginOrPaddingForChild(const LayoutGrid&, + const LayoutBox& child, + GridTrackSizingDirection); + static bool HasRelativeOrIntrinsicSizeForChild(const LayoutGrid&, + const LayoutBox& child, + GridTrackSizingDirection); static bool ShouldClearOverrideContainingBlockContentSizeForChild( const LayoutGrid&, const LayoutBox& child, diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_cache.cc b/chromium/third_party/blink/renderer/core/layout/hit_test_cache.cc index 2b536141ab6..5a46d3b68cb 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_cache.cc +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_cache.cc @@ -40,7 +40,7 @@ bool HitTestCache::LookupCachedResult(const HitTestLocation& location, return result; } -void HitTestCacheEntry::Trace(blink::Visitor* visitor) { +void HitTestCacheEntry::Trace(Visitor* visitor) { visitor->Trace(result); } @@ -85,7 +85,7 @@ void HitTestCache::Clear() { items_.clear(); } -void HitTestCache::Trace(blink::Visitor* visitor) { +void HitTestCache::Trace(Visitor* visitor) { visitor->Trace(items_); } diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_cache.h b/chromium/third_party/blink/renderer/core/layout/hit_test_cache.h index 43f3e253c9d..985e58c1840 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_cache.h +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_cache.h @@ -37,7 +37,7 @@ namespace blink { struct HitTestCacheEntry { DISALLOW_NEW(); - void Trace(blink::Visitor*); + void Trace(Visitor*); HitTestLocation location; HitTestResult result; @@ -61,7 +61,7 @@ class CORE_EXPORT HitTestCache final : public GarbageCollected<HitTestCache> { const HitTestResult&, uint64_t dom_tree_version); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: // The below UMA values reference a validity region. This code has not diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.cc b/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.cc index 8c1f87c84d1..79e9b485d94 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.cc @@ -17,7 +17,7 @@ Element* HitTestCanvasResult::GetControl() const { return control_.Get(); } -void HitTestCanvasResult::Trace(blink::Visitor* visitor) { +void HitTestCanvasResult::Trace(Visitor* visitor) { visitor->Trace(control_); } diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.h b/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.h index 341faf0840b..4dcc8d19cdb 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.h +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_canvas_result.h @@ -17,7 +17,7 @@ class CORE_EXPORT HitTestCanvasResult final String GetId() const; Element* GetControl() const; - void Trace(blink::Visitor*); + void Trace(Visitor*); private: String id_; diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_location.cc b/chromium/third_party/blink/renderer/core/layout/hit_test_location.cc index e68cb3bd7dd..bae6bafa322 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_location.cc +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_location.cc @@ -88,8 +88,6 @@ HitTestLocation::HitTestLocation(const HitTestLocation& other, HitTestLocation::HitTestLocation(const HitTestLocation& other) = default; -HitTestLocation::~HitTestLocation() = default; - HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other) = default; diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_location.h b/chromium/third_party/blink/renderer/core/layout/hit_test_location.h index 08b89056581..016b930ff21 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_location.h +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_location.h @@ -62,7 +62,6 @@ class CORE_EXPORT HitTestLocation { HitTestLocation(const HitTestLocation&, const PhysicalOffset& offset); HitTestLocation(const HitTestLocation&); - ~HitTestLocation(); HitTestLocation& operator=(const HitTestLocation&); const PhysicalOffset& Point() const { return point_; } diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc b/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc index 2138f41923d..68ac6dfdaa8 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_result.cc @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/core/editing/visible_units.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h" #include "third_party/blink/renderer/core/html/html_area_element.h" @@ -38,7 +39,10 @@ #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/input_type_names.h" +#include "third_party/blink/renderer/core/layout/layout_block.h" #include "third_party/blink/renderer/core/layout/layout_image.h" +#include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h" #include "third_party/blink/renderer/core/scroll/scrollbar.h" #include "third_party/blink/renderer/core/svg/svg_element.h" #include "third_party/blink/renderer/platform/geometry/region.h" @@ -124,7 +128,7 @@ void HitTestResult::PopulateFromCachedResult(const HitTestResult& other) { : nullptr; } -void HitTestResult::Trace(blink::Visitor* visitor) { +void HitTestResult::Trace(Visitor* visitor) { visitor->Trace(inner_node_); visitor->Trace(inert_node_); visitor->Trace(inner_element_); @@ -174,6 +178,31 @@ void HitTestResult::SetToShadowHostIfInRestrictedShadowRoot() { SetInnerNode(shadow_host); } +CompositorElementId HitTestResult::GetScrollableContainer() const { + DCHECK(InnerNode()); + LayoutBox* cur_box = InnerNode()->GetLayoutObject()->EnclosingBox(); + + // Scrolling propagates along the containing block chain and ends at the + // RootScroller node. The RootScroller node will have a custom applyScroll + // callback that performs scrolling as well as associated "root" actions like + // browser control movement and overscroll glow. + while (cur_box) { + if (cur_box->IsGlobalRootScroller() || + cur_box->NeedsScrollNode(CompositingReason::kNone)) { + return CompositorElementIdFromUniqueObjectId( + cur_box->UniqueId(), CompositorElementIdNamespace::kScroll); + } + + cur_box = cur_box->ContainingBlock(); + } + + return InnerNode() + ->GetDocument() + .GetPage() + ->GetVisualViewport() + .GetScrollElementId(); +} + HTMLAreaElement* HitTestResult::ImageAreaForImage() const { DCHECK(inner_node_); auto* image_element = DynamicTo<HTMLImageElement>(inner_node_.Get()); @@ -374,9 +403,7 @@ HTMLMediaElement* HitTestResult::MediaElement() const { inner_node_->GetLayoutObject()->IsMedia())) return nullptr; - if (IsHTMLMediaElement(*inner_node_)) - return ToHTMLMediaElement(inner_node_); - return nullptr; + return DynamicTo<HTMLMediaElement>(*inner_node_); } KURL HitTestResult::AbsoluteLinkURL() const { diff --git a/chromium/third_party/blink/renderer/core/layout/hit_test_result.h b/chromium/third_party/blink/renderer/core/layout/hit_test_result.h index c6ea07e53a6..d40ba852d00 100644 --- a/chromium/third_party/blink/renderer/core/layout/hit_test_result.h +++ b/chromium/third_party/blink/renderer/core/layout/hit_test_result.h @@ -27,6 +27,7 @@ #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h" #include "third_party/blink/renderer/core/layout/hit_test_location.h" #include "third_party/blink/renderer/core/layout/hit_test_request.h" +#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/platform/geometry/float_quad.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -66,7 +67,7 @@ class CORE_EXPORT HitTestResult { HitTestResult(const HitTestResult&); ~HitTestResult(); HitTestResult& operator=(const HitTestResult&); - void Trace(blink::Visitor*); + void Trace(Visitor*); bool EqualForCacheability(const HitTestResult&) const; void CacheValues(const HitTestResult& other); @@ -86,6 +87,7 @@ class CORE_EXPORT HitTestResult { Node* InnerPossiblyPseudoNode() const { return inner_possibly_pseudo_node_.Get(); } + CompositorElementId GetScrollableContainer() const; Element* InnerElement() const { return inner_element_.Get(); } // If innerNode is an image map or image map area, return the associated image diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block.cc b/chromium/third_party/blink/renderer/core/layout/layout_block.cc index 228e4a3494e..c17f2501e74 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block.cc @@ -32,7 +32,6 @@ #include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element.h" -#include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/editing/drag_caret.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" @@ -103,7 +102,6 @@ LayoutBlock::LayoutBlock(ContainerNode* node) : LayoutBox(node), has_margin_before_quirk_(false), has_margin_after_quirk_(false), - being_destroyed_(false), has_markup_truncation_(false), width_available_to_children_changed_(false), height_available_to_children_changed_(false), @@ -461,6 +459,7 @@ void LayoutBlock::ComputeVisualOverflow(bool) { AddVisualOverflowFromTheme(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } @@ -548,7 +547,7 @@ void LayoutBlock::AddLayoutOverflowFromPositionedObjects() { // Fixed positioned elements whose containing block is the LayoutView // don't contribute to layout overflow, since they don't scroll with the // content. - if (!IsLayoutView() || + if (!IsA<LayoutView>(this) || positioned_object->StyleRef().GetPosition() != EPosition::kFixed) { AddLayoutOverflowFromChild(*positioned_object, ToLayoutSize(positioned_object->Location())); @@ -593,7 +592,8 @@ void LayoutBlock::UpdateBlockChildDirtyBitsBeforeLayout(bool relayout_children, child.HasRelativeLogicalHeight() || (child.IsAnonymous() && HasRelativeLogicalHeight()) || child.StretchesToViewport(); - if (relayout_children || (has_relative_logical_height && !IsLayoutView()) || + if (relayout_children || + (has_relative_logical_height && !IsA<LayoutView>(this)) || (height_available_to_children_changed_ && ChangeInAvailableLogicalHeightAffectsChild(this, child)) || (child.IsListMarker() && IsListItem() && @@ -719,11 +719,11 @@ void LayoutBlock::MarkFixedPositionObjectForLayoutIfNeeded( return; LayoutObject* o = child->Parent(); - while (!o->IsLayoutView() && - o->StyleRef().GetPosition() != EPosition::kAbsolute) + bool is_layout_view = IsA<LayoutView>(o); + while (!is_layout_view && o->StyleRef().GetPosition() != EPosition::kAbsolute) o = o->Parent(); // The LayoutView is absolute-positioned, but does not move. - if (o->IsLayoutView()) + if (is_layout_view) return; // We must compute child's width and height, but not update them now. @@ -1176,7 +1176,7 @@ bool LayoutBlock::IsPointInOverflowControl( bool LayoutBlock::HitTestOverflowControl( HitTestResult& result, const HitTestLocation& hit_test_location, - const PhysicalOffset& adjusted_location) { + const PhysicalOffset& adjusted_location) const { if (VisibleToHitTestRequest(result.GetHitTestRequest()) && IsPointInOverflowControl(result, hit_test_location.Point(), adjusted_location)) { @@ -1262,7 +1262,7 @@ static inline bool IsEditingBoundary(const LayoutObject* ancestor, DCHECK(child); DCHECK(child.NonPseudoNode()); return !ancestor || !ancestor->Parent() || - (ancestor->HasLayer() && ancestor->Parent()->IsLayoutView()) || + (ancestor->HasLayer() && IsA<LayoutView>(ancestor->Parent())) || HasEditableStyle(*ancestor->NonPseudoNode()) == HasEditableStyle(*child.NonPseudoNode()); } @@ -1399,10 +1399,9 @@ void LayoutBlock::ScrollbarsChanged(bool horizontal_scrollbar_changed, height_available_to_children_changed_ |= horizontal_scrollbar_changed; } -void LayoutBlock::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - int scrollbar_width = ScrollbarLogicalWidth(); +MinMaxSizes LayoutBlock::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth() + ScrollbarLogicalWidth(); // See if we can early out sooner if the logical width is overridden or we're // size contained. Note that for multicol containers we need the column gaps. @@ -1410,50 +1409,56 @@ void LayoutBlock::ComputeIntrinsicLogicalWidths( const auto* block_flow = DynamicTo<LayoutBlockFlow>(this); if (!block_flow || !block_flow->MultiColumnFlowThread()) { if (HasOverrideIntrinsicContentLogicalWidth()) { - max_logical_width = min_logical_width = - OverrideIntrinsicContentLogicalWidth() + LayoutUnit(scrollbar_width); - return; + sizes += OverrideIntrinsicContentLogicalWidth(); + return sizes; } - if (ShouldApplySizeContainment()) { - max_logical_width = min_logical_width = LayoutUnit(scrollbar_width); - return; + LayoutUnit default_inline_size = DefaultIntrinsicContentInlineSize(); + if (default_inline_size != kIndefiniteSize) { + sizes.max_size += default_inline_size; + if (!StyleRef().LogicalWidth().IsPercentOrCalc()) + sizes.min_size = sizes.max_size; + return sizes; } + if (ShouldApplySizeContainment()) + return sizes; } + MinMaxSizes child_sizes; if (ChildrenInline()) { // FIXME: Remove this const_cast. To<LayoutBlockFlow>(const_cast<LayoutBlock*>(this)) - ->ComputeInlinePreferredLogicalWidths(min_logical_width, - max_logical_width); + ->ComputeInlinePreferredLogicalWidths(child_sizes.min_size, + child_sizes.max_size); } else { - ComputeBlockPreferredLogicalWidths(min_logical_width, max_logical_width); + ComputeBlockPreferredLogicalWidths(child_sizes.min_size, + child_sizes.max_size); } - max_logical_width = std::max(min_logical_width, max_logical_width); + child_sizes.max_size = std::max(child_sizes.min_size, child_sizes.max_size); auto* html_marquee_element = DynamicTo<HTMLMarqueeElement>(GetNode()); if (html_marquee_element && html_marquee_element->IsHorizontal()) - min_logical_width = LayoutUnit(); + child_sizes.min_size = LayoutUnit(); + if (UNLIKELY(IsListBox(this) && StyleRef().LogicalWidth().IsPercentOrCalc())) + child_sizes.min_size = LayoutUnit(); if (IsTableCell()) { Length table_cell_width = ToInterface<LayoutNGTableCellInterface>(this)->StyleOrColLogicalWidth(); - if (table_cell_width.IsFixed() && table_cell_width.Value() > 0) - max_logical_width = std::max(min_logical_width, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(table_cell_width.Value()))); + if (table_cell_width.IsFixed() && table_cell_width.Value() > 0) { + child_sizes.max_size = std::max( + child_sizes.min_size, AdjustContentBoxLogicalWidthForBoxSizing( + LayoutUnit(table_cell_width.Value()))); + } } - max_logical_width += scrollbar_width; - min_logical_width += scrollbar_width; + sizes += child_sizes; + return sizes; } DISABLE_CFI_PERF -void LayoutBlock::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); - - min_preferred_logical_width_ = LayoutUnit(); - max_preferred_logical_width_ = LayoutUnit(); +MinMaxSizes LayoutBlock::PreferredLogicalWidths() const { + MinMaxSizes sizes; // FIXME: The isFixed() calls here should probably be checking for isSpecified // since you should be able to use percentage, calc or viewport relative @@ -1462,52 +1467,32 @@ void LayoutBlock::ComputePreferredLogicalWidths() { if (!IsTableCell() && style_to_use.LogicalWidth().IsFixed() && style_to_use.LogicalWidth().Value() >= 0 && !(IsFlexItemCommon() && Parent()->StyleRef().IsDeprecatedWebkitBox() && - !style_to_use.LogicalWidth().IntValue())) - min_preferred_logical_width_ = max_preferred_logical_width_ = - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.LogicalWidth().Value())); - else - ComputeIntrinsicLogicalWidths(min_preferred_logical_width_, - max_preferred_logical_width_); + !style_to_use.LogicalWidth().IntValue())) { + sizes = AdjustBorderBoxLogicalWidthForBoxSizing( + LayoutUnit(style_to_use.LogicalWidth().Value())); + } else { + sizes = IntrinsicLogicalWidths(); + } if (style_to_use.LogicalMaxWidth().IsFixed()) { - max_preferred_logical_width_ = - std::min(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.LogicalMaxWidth().Value()))); - min_preferred_logical_width_ = - std::min(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.LogicalMaxWidth().Value()))); + sizes.Constrain(AdjustBorderBoxLogicalWidthForBoxSizing( + LayoutUnit(style_to_use.LogicalMaxWidth().Value()))); } if (style_to_use.LogicalMinWidth().IsFixed() && style_to_use.LogicalMinWidth().Value() > 0) { - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.LogicalMinWidth().Value()))); - min_preferred_logical_width_ = - std::max(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.LogicalMinWidth().Value()))); + sizes.Encompass(AdjustBorderBoxLogicalWidthForBoxSizing( + LayoutUnit(style_to_use.LogicalMinWidth().Value()))); } - LayoutUnit border_and_padding = BorderAndPaddingLogicalWidth(); - DCHECK_GE(border_and_padding, LayoutUnit()); - min_preferred_logical_width_ += border_and_padding; - max_preferred_logical_width_ += border_and_padding; - // Table layout uses integers, ceil the preferred widths to ensure that they // can contain the contents. if (IsTableCell()) { - min_preferred_logical_width_ = - LayoutUnit(min_preferred_logical_width_.Ceil()); - max_preferred_logical_width_ = - LayoutUnit(max_preferred_logical_width_.Ceil()); + sizes.min_size = LayoutUnit(sizes.min_size.Ceil()); + sizes.max_size = LayoutUnit(sizes.max_size.Ceil()); } - ClearPreferredLogicalWidthsDirty(); + return sizes; } void LayoutBlock::ComputeBlockPreferredLogicalWidths( @@ -1532,7 +1517,10 @@ void LayoutBlock::ComputeBlockPreferredLogicalWidths( // We don't really know whether the containing block of this child did // change or is going to change size. However, this is our only // opportunity to make sure that it gets its min/max widths calculated. - child->SetPreferredLogicalWidthsDirty(); + // This is also an important hook for flow threads; if the container of a + // flow thread needs its preferred logical widths recalculated, so does + // the flow thread, potentially. + child->SetIntrinsicLogicalWidthsDirty(); } scoped_refptr<const ComputedStyle> child_style = child->Style(); @@ -1648,8 +1636,10 @@ void LayoutBlock::ComputeChildPreferredLogicalWidths( ToLayoutBox(child).ComputeLogicalHeightWithoutLayout(); return; } - min_preferred_logical_width = child.MinPreferredLogicalWidth(); - max_preferred_logical_width = child.MaxPreferredLogicalWidth(); + + MinMaxSizes child_preferred_logical_widths = child.PreferredLogicalWidths(); + min_preferred_logical_width = child_preferred_logical_widths.min_size; + max_preferred_logical_width = child_preferred_logical_widths.max_size; // For non-replaced blocks if the inline size is min|max-content or a definite // size the min|max-content contribution is that size plus border, padding and @@ -1664,18 +1654,29 @@ void LayoutBlock::ComputeChildPreferredLogicalWidths( } bool LayoutBlock::HasLineIfEmpty() const { - if (!GetNode()) - return false; - - if (IsRootEditableElement(*GetNode())) - return true; - - if (auto* shadow_root = DynamicTo<ShadowRoot>(GetNode())) { - if (IsA<HTMLInputElement>(shadow_root->host())) + if (GetNode()) { + if (IsRootEditableElement(*GetNode())) return true; } + return FirstLineStyleRef().HasLineIfEmpty(); +} - return false; +LayoutUnit LayoutBlock::EmptyLineBaseline( + LineDirectionMode line_direction) const { + if (!HasLineIfEmpty()) + return LayoutUnit(-1); + const SimpleFontData* font_data = FirstLineStyle()->GetFont().PrimaryFont(); + if (!font_data) + return LayoutUnit(-1); + const auto& font_metrics = font_data->GetFontMetrics(); + const LayoutUnit line_height = + LineHeight(true, line_direction, kPositionOfInteriorLineBoxes); + const LayoutUnit border_padding = line_direction == kHorizontalLine + ? BorderTop() + PaddingTop() + : BorderRight() + PaddingRight(); + return LayoutUnit((font_metrics.Ascent() + + (line_height - font_metrics.Height()) / 2 + border_padding) + .ToInt()); } LayoutUnit LayoutBlock::LineHeight(bool first_line, @@ -1816,7 +1817,7 @@ bool LayoutBlock::UseLogicalBottomMarginEdgeForInlineBlockBaseline() const { // where the block's contents shouldn't be considered when laying out its // ancestors or siblings. return (!StyleRef().IsOverflowVisible() && - !ShouldIgnoreOverflowPropertyForInlineBlockBaseline()) || + !StyleRef().ShouldIgnoreOverflowPropertyForInlineBlockBaseline()) || ShouldApplyLayoutContainment(); } @@ -1845,18 +1846,8 @@ LayoutUnit LayoutBlock::InlineBlockBaseline( } } } - const SimpleFontData* font_data = FirstLineStyle()->GetFont().PrimaryFont(); - if (font_data && !have_normal_flow_child && HasLineIfEmpty()) { - const FontMetrics& font_metrics = font_data->GetFontMetrics(); - return LayoutUnit( - (font_metrics.Ascent() + - (LineHeight(true, line_direction, kPositionOfInteriorLineBoxes) - - font_metrics.Height()) / - 2 + - (line_direction == kHorizontalLine ? BorderTop() + PaddingTop() - : BorderRight() + PaddingRight())) - .ToInt()); - } + if (!have_normal_flow_child) + return EmptyLineBaseline(line_direction); return LayoutUnit(-1); } @@ -2320,7 +2311,7 @@ LayoutUnit LayoutBlock::AvailableLogicalHeightForPercentageComputation() const { available_height = computed_values.extent_ - BorderAndPaddingLogicalHeight() - ScrollbarLogicalHeight(); - } else if (IsLayoutView()) { + } else if (IsA<LayoutView>(this)) { available_height = View()->ViewLogicalHeightForPercentages(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block.h b/chromium/third_party/blink/renderer/core/layout/layout_block.h index 64c3ba8c89a..68f16211d48 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_block.h @@ -142,6 +142,10 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { const char* GetName() const override; + virtual const NGPhysicalBoxFragment* CurrentFragment() const { + return nullptr; + } + protected: // Insert a child correctly into the tree when |beforeDescendant| isn't a // direct child of |this|. This happens e.g. when there's an anonymous block @@ -395,6 +399,7 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { virtual void PaintChildren(const PaintInfo&, const PhysicalOffset& paint_offset) const; void UpdateAfterLayout() override; + MinMaxSizes PreferredLogicalWidths() const override; protected: virtual void AdjustInlineDirectionLineBounds( @@ -402,10 +407,7 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { LayoutUnit& /* logicalLeft */, LayoutUnit& /* logicalWidth */) const {} - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; - void ComputePreferredLogicalWidths() override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; void ComputeChildPreferredLogicalWidths( LayoutObject& child, LayoutUnit& min_preferred_logical_width, @@ -414,17 +416,10 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { LayoutUnit FirstLineBoxBaseline() const override; LayoutUnit InlineBlockBaseline(LineDirectionMode) const override; - // This function disables the 'overflow' check in inlineBlockBaseline. - // For 'inline-block', CSS says that the baseline is the bottom margin edge - // if 'overflow' is not visible. But some descendant classes want to ignore - // this condition. - virtual bool ShouldIgnoreOverflowPropertyForInlineBlockBaseline() const { - return false; - } - - bool HitTestOverflowControl(HitTestResult&, - const HitTestLocation&, - const PhysicalOffset& adjusted_location) override; + bool HitTestOverflowControl( + HitTestResult&, + const HitTestLocation&, + const PhysicalOffset& adjusted_location) const override; bool HitTestChildren(HitTestResult&, const HitTestLocation&, const PhysicalOffset& accumulated_offset, @@ -482,6 +477,8 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { hit_test_action == kHitTestChildBlockBackground; } + LayoutUnit EmptyLineBaseline(LineDirectionMode line_direction) const; + private: LayoutObjectChildList* VirtualChildren() final { return Children(); } const LayoutObjectChildList* VirtualChildren() const final { @@ -558,7 +555,6 @@ class CORE_EXPORT LayoutBlock : public LayoutBox { // in LayoutBlockRareData since they are // set too frequently. unsigned has_margin_after_quirk_ : 1; - unsigned being_destroyed_ : 1; unsigned has_markup_truncation_ : 1; unsigned width_available_to_children_changed_ : 1; unsigned height_available_to_children_changed_ : 1; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc index 596ea315e8a..b0d5df85710 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.cc @@ -77,7 +77,8 @@ bool LayoutBlockFlow::can_propagate_float_into_sibling_ = false; struct SameSizeAsLayoutBlockFlow : public LayoutBlock { LineBoxList line_boxes; - void* pointers[2]; + void* pointers[1]; + Persistent<void*> persistent[1]; }; static_assert(sizeof(LayoutBlockFlow) == sizeof(SameSizeAsLayoutBlockFlow), @@ -131,7 +132,6 @@ class MarginInfo { bool has_margin_after_quirk_ : 1; bool determined_margin_before_quirk_ : 1; - bool discard_margin_ : 1; bool last_child_is_self_collapsing_block_with_clearance_ : 1; // These flags track the previous maximal positive and negative margins. @@ -155,26 +155,21 @@ class MarginInfo { determined_margin_before_quirk_ = b; } void SetPositiveMargin(LayoutUnit p) { - DCHECK(!discard_margin_); positive_margin_ = p; } void SetNegativeMargin(LayoutUnit n) { - DCHECK(!discard_margin_); negative_margin_ = n; } void SetPositiveMarginIfLarger(LayoutUnit p) { - DCHECK(!discard_margin_); if (p > positive_margin_) positive_margin_ = p; } void SetNegativeMarginIfLarger(LayoutUnit n) { - DCHECK(!discard_margin_); if (n > negative_margin_) negative_margin_ = n; } void SetMargin(LayoutUnit p, LayoutUnit n) { - DCHECK(!discard_margin_); positive_margin_ = p; negative_margin_ = n; } @@ -184,7 +179,6 @@ class MarginInfo { void SetCanCollapseMarginAfterWithLastChild(bool collapse) { can_collapse_margin_after_with_last_child_ = collapse; } - void SetDiscardMargin(bool value) { discard_margin_ = value; } bool AtBeforeSideOfBlock() const { return at_before_side_of_block_; } bool CanCollapseWithMarginBefore() const { @@ -211,7 +205,6 @@ class MarginInfo { bool HasMarginAfterQuirk() const { return has_margin_after_quirk_; } LayoutUnit PositiveMargin() const { return positive_margin_; } LayoutUnit NegativeMargin() const { return negative_margin_; } - bool DiscardMargin() const { return discard_margin_; } LayoutUnit Margin() const { return positive_margin_ - negative_margin_; } void SetLastChildIsSelfCollapsingBlockWithClearance(bool value) { last_child_is_self_collapsing_block_with_clearance_ = value; @@ -261,9 +254,16 @@ class BlockChildrenLayoutInfo { bool IsAtFirstInFlowChild() const { return is_at_first_in_flow_child_; } void ClearIsAtFirstInFlowChild() { is_at_first_in_flow_child_ = false; } + // The page name of the previous sibling. Consecutive siblings with the same + // name are allowed on the same page, but if they differ, we need a page + // break. + const AtomicString& ChildPageName() const { return child_page_name_; } + void SetChildPageName(const AtomicString& name) { child_page_name_ = name; } + private: MultiColumnLayoutState multi_column_layout_state_; MarginInfo margin_info_; + AtomicString child_page_name_; LayoutUnit previous_float_logical_bottom_; EBreakBetween previous_break_after_value_; bool is_at_first_in_flow_child_; @@ -387,9 +387,7 @@ bool LayoutBlockFlow::CheckIfIsSelfCollapsingBlock() const { (element && element->ShadowPseudoId() == "-webkit-input-placeholder")); if (LogicalHeight() > LayoutUnit() || - StyleRef().LogicalMinHeight().IsPositive() || - StyleRef().MarginBeforeCollapse() == EMarginCollapse::kSeparate || - StyleRef().MarginAfterCollapse() == EMarginCollapse::kSeparate) + StyleRef().LogicalMinHeight().IsPositive()) return false; const Length& logical_height_length = StyleRef().LogicalHeight(); @@ -398,7 +396,7 @@ bool LayoutBlockFlow::CheckIfIsSelfCollapsingBlock() const { !GetDocument().InQuirksMode()) { has_auto_height = true; if (LayoutBlock* cb = ContainingBlock()) { - if (!cb->IsLayoutView() && + if (!IsA<LayoutView>(cb) && (cb->StyleRef().LogicalHeight().IsFixed() || cb->IsTableCell())) has_auto_height = false; } @@ -467,7 +465,7 @@ void LayoutBlockFlow::UpdateBlockLayout(bool relayout_children) { TextAutosizer::LayoutScope text_autosizer_layout_scope(this, &layout_scope); bool pagination_state_changed = pagination_state_changed_; - bool preferred_logical_widths_were_dirty = PreferredLogicalWidthsDirty(); + bool intrinsic_logical_widths_were_dirty = IntrinsicLogicalWidthsDirty(); // Multiple passes might be required for column based layout. // The number of passes could be as high as the number of columns. @@ -484,7 +482,7 @@ void LayoutBlockFlow::UpdateBlockLayout(bool relayout_children) { LayoutChildren(relayout_children, layout_scope); - if (!preferred_logical_widths_were_dirty && PreferredLogicalWidthsDirty()) { + if (!intrinsic_logical_widths_were_dirty && IntrinsicLogicalWidthsDirty()) { // The only thing that should dirty preferred widths at this point is the // addition of overflow:auto scrollbars in a descendant. To avoid a // potential infinite loop, run layout again with auto scrollbars frozen @@ -854,6 +852,24 @@ void LayoutBlockFlow::InsertForcedBreakBeforeChildIfNeeded( // those preceding the break. EBreakBetween class_a_break_point_value = child.ClassABreakPointValue(layout_info.PreviousBreakAfterValue()); + + bool is_named_page_break; + if (layout_info.ChildPageName()) { + // Adjacent siblings with the same page name may be put on the same + // page. Otherwise, we need a break. + is_named_page_break = + layout_info.ChildPageName() != child.StyleRef().Page(); + } else { + // If the previous sibling (if any) didn't specify a page name, see if one + // is specified on an ancestor. If the child specifies a page name, and it + // doesn't match what's specified further up (if anything), we need a break. + is_named_page_break = + child.StyleRef().Page() && + child.StyleRef().Page() != View()->GetLayoutState()->PageName(); + } + if (is_named_page_break) + class_a_break_point_value = EBreakBetween::kPage; + if (IsForcedFragmentainerBreakValue(class_a_break_point_value)) { layout_info.GetMarginInfo().ClearMargin(); LayoutUnit old_logical_top = LogicalHeight(); @@ -862,6 +878,10 @@ void LayoutBlockFlow::InsertForcedBreakBeforeChildIfNeeded( SetLogicalHeight(new_logical_top); LayoutUnit pagination_strut = new_logical_top - old_logical_top; child.SetPaginationStrut(pagination_strut); + if (is_named_page_break) { + // This was a forced break because of named pages. + layout_info.SetChildPageName(child.StyleRef().Page()); + } } } @@ -898,8 +918,6 @@ void LayoutBlockFlow::LayoutBlockChild(LayoutBox& child, // Cache if we are at the top of the block right now. bool at_before_side_of_block = margin_info.AtBeforeSideOfBlock(); bool child_is_self_collapsing = child.IsSelfCollapsingBlock(); - bool child_discard_margin_before = MustDiscardMarginBeforeForChild(child); - bool child_discard_margin_after = MustDiscardMarginAfterForChild(child); bool paginated = View()->GetLayoutState()->IsPaginated(); // If there should be a forced break before the child, we need to insert it @@ -917,15 +935,12 @@ void LayoutBlockFlow::LayoutBlockChild(LayoutBox& child, // Now determine the correct ypos based off examination of collapsing margin // values. LayoutUnit logical_top_before_clear = - CollapseMargins(child, layout_info, child_is_self_collapsing, - child_discard_margin_before, child_discard_margin_after); + CollapseMargins(child, layout_info, child_is_self_collapsing); // Now check for clear. - bool child_discard_margin = - child_discard_margin_before || child_discard_margin_after; LayoutUnit new_logical_top = ClearFloatsIfNeeded( child, margin_info, old_pos_margin_before, old_neg_margin_before, - logical_top_before_clear, child_is_self_collapsing, child_discard_margin); + logical_top_before_clear, child_is_self_collapsing); // If there's a forced break in front of this child, its final position has // already been determined. Otherwise, see if there are other reasons for @@ -982,10 +997,7 @@ void LayoutBlockFlow::LayoutBlockChild(LayoutBox& child, // Update our height now that the child has been placed in the correct // position. SetLogicalHeight(LogicalHeight() + LogicalHeightForChild(child)); - if (MustSeparateMarginAfterForChild(child)) { - SetLogicalHeight(LogicalHeight() + MarginAfterForChild(child)); - margin_info.ClearMargin(); - } + // If the child has overhanging floats that intrude into following siblings // (or possibly out of this block), then the parent gets notified of the // floats now. @@ -1376,8 +1388,9 @@ void LayoutBlockFlow::RebuildFloatsFromIntruding() { // Inline blocks are covered by the isAtomicInlineLevel() check in the // avoidFloats method. - if (CreatesNewFormattingContext() || IsDocumentElement() || IsLayoutView() || - IsFloatingOrOutOfFlowPositioned() || IsTableCell()) { + if (CreatesNewFormattingContext() || IsDocumentElement() || + IsA<LayoutView>(this) || IsFloatingOrOutOfFlowPositioned() || + IsTableCell()) { if (floating_objects_) { floating_objects_->Clear(); } @@ -1620,17 +1633,15 @@ MarginInfo::MarginInfo(LayoutBlockFlow* block_flow, has_margin_before_quirk_(false), has_margin_after_quirk_(false), determined_margin_before_quirk_(false), - discard_margin_(false), last_child_is_self_collapsing_block_with_clearance_(false) { const ComputedStyle& block_style = block_flow->StyleRef(); - DCHECK(block_flow->IsLayoutView() || block_flow->Parent()); + DCHECK(IsA<LayoutView>(block_flow) || block_flow->Parent()); can_collapse_with_children_ = !block_flow->CreatesNewFormattingContext() && !block_flow->IsLayoutFlowThread() && - !block_flow->IsLayoutView(); + !IsA<LayoutView>(block_flow); can_collapse_margin_before_with_children_ = - can_collapse_with_children_ && !before_border_padding && - block_style.MarginBeforeCollapse() != EMarginCollapse::kSeparate; + can_collapse_with_children_ && !before_border_padding; // If any height other than auto is specified in CSS, then we don't collapse // our bottom margins with our children's margins. To do otherwise would be to @@ -1640,20 +1651,14 @@ MarginInfo::MarginInfo(LayoutBlockFlow* block_flow, can_collapse_margin_after_with_children_ = can_collapse_with_children_ && !after_border_padding && (block_style.LogicalHeight().IsAuto() && - !block_style.LogicalHeight().Value()) && - block_style.MarginAfterCollapse() != EMarginCollapse::kSeparate; + !block_style.LogicalHeight().Value()); quirk_container_ = block_flow->IsTableCell() || block_flow->IsBody(); - discard_margin_ = can_collapse_margin_before_with_children_ && - block_flow->MustDiscardMarginBefore(); - - positive_margin_ = (can_collapse_margin_before_with_children_ && - !block_flow->MustDiscardMarginBefore()) + positive_margin_ = can_collapse_margin_before_with_children_ ? block_flow->MaxPositiveMarginBefore() : LayoutUnit(); - negative_margin_ = (can_collapse_margin_before_with_children_ && - !block_flow->MustDiscardMarginBefore()) + negative_margin_ = can_collapse_margin_before_with_children_ ? block_flow->MaxNegativeMarginBefore() : LayoutUnit(); } @@ -1766,17 +1771,9 @@ static LayoutBlockFlow* PreviousBlockFlowInFormattingContext( LayoutUnit LayoutBlockFlow::CollapseMargins( LayoutBox& child, BlockChildrenLayoutInfo& layout_info, - bool child_is_self_collapsing, - bool child_discard_margin_before, - bool child_discard_margin_after) { + bool child_is_self_collapsing) { MarginInfo& margin_info = layout_info.GetMarginInfo(); - // The child discards the before margin when the the after margin has discard - // in the case of a self collapsing block. - child_discard_margin_before = - child_discard_margin_before || - (child_discard_margin_after && child_is_self_collapsing); - // Get the four margin values for the child and cache them. const LayoutBlockFlow::MarginValues child_margins = MarginValuesForChild(child); @@ -1797,46 +1794,32 @@ LayoutUnit LayoutBlockFlow::CollapseMargins( bool top_quirk = HasMarginBeforeQuirk(&child); if (margin_info.CanCollapseWithMarginBefore()) { - if (!child_discard_margin_before && !margin_info.DiscardMargin()) { - // This child is collapsing with the top of the - // block. If it has larger margin values, then we need to update - // our own maximal values. - if (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() || - !top_quirk) - SetMaxMarginBeforeValues(std::max(pos_top, MaxPositiveMarginBefore()), - std::max(neg_top, MaxNegativeMarginBefore())); - - // The minute any of the margins involved isn't a quirk, don't - // collapse it away, even if the margin is smaller (www.webreference.com - // has an example of this, a <dt> with 0.8em author-specified inside - // a <dl> inside a <td>. - if (!margin_info.DeterminedMarginBeforeQuirk() && !top_quirk && - (pos_top - neg_top)) { - SetHasMarginBeforeQuirk(false); - margin_info.SetDeterminedMarginBeforeQuirk(true); - } + // This child is collapsing with the top of the block. If it has larger + // margin values, then we need to update our own maximal values. + if (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() || + !top_quirk) { + SetMaxMarginBeforeValues(std::max(pos_top, MaxPositiveMarginBefore()), + std::max(neg_top, MaxNegativeMarginBefore())); + } - if (!margin_info.DeterminedMarginBeforeQuirk() && top_quirk && - !MarginBefore()) { - // We have no top margin and our top child has a quirky margin. - // We will pick up this quirky margin and pass it through. - // This deals with the <td><div><p> case. - // Don't do this for a block that split two inlines though. You do - // still apply margins in this case. - SetHasMarginBeforeQuirk(true); - } - } else { - // The before margin of the container will also discard all the margins it - // is collapsing with. - SetMustDiscardMarginBefore(); + // The minute any of the margins involved isn't a quirk, don't collapse it + // away, even if the margin is smaller (www.webreference.com has an example + // of this, a <dt> with 0.8em author-specified inside a <dl> inside a <td>). + if (!margin_info.DeterminedMarginBeforeQuirk() && !top_quirk && + (pos_top - neg_top)) { + SetHasMarginBeforeQuirk(false); + margin_info.SetDeterminedMarginBeforeQuirk(true); } - } - // Once we find a child with discardMarginBefore all the margins collapsing - // with us must also discard. - if (child_discard_margin_before) { - margin_info.SetDiscardMargin(true); - margin_info.ClearMargin(); + if (!margin_info.DeterminedMarginBeforeQuirk() && top_quirk && + !MarginBefore()) { + // We have no top margin and our top child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the <td><div><p> case. + // Don't do this for a block that split two inlines though. You do still + // apply margins in this case. + SetHasMarginBeforeQuirk(true); + } } if (margin_info.QuirkContainer() && margin_info.AtBeforeSideOfBlock() && @@ -1866,71 +1849,46 @@ LayoutUnit LayoutBlockFlow::CollapseMargins( MarginValuesForChild(*previous_block_flow).PositiveMarginBefore()); if (child_is_self_collapsing) { - // For a self collapsing block both the before and after margins get - // discarded. The block doesn't contribute anything to the height of the - // block. Also, the child's top position equals the logical height of the - // container. - if (!child_discard_margin_before && !margin_info.DiscardMargin()) { - // This child has no height. We need to compute our - // position before we collapse the child's margins together, - // so that we can get an accurate position for the zero-height block. - LayoutUnit collapsed_before_pos = std::max( - margin_info.PositiveMargin(), child_margins.PositiveMarginBefore()); - LayoutUnit collapsed_before_neg = std::max( - margin_info.NegativeMargin(), child_margins.NegativeMarginBefore()); - margin_info.SetMargin(collapsed_before_pos, collapsed_before_neg); - - // Now collapse the child's margins together, which means examining our - // bottom margin values as well. - margin_info.SetPositiveMarginIfLarger( - child_margins.PositiveMarginAfter()); - margin_info.SetNegativeMarginIfLarger( - child_margins.NegativeMarginAfter()); - - if (!margin_info.CanCollapseWithMarginBefore()) { - // We need to make sure that the position of the self-collapsing block - // is correct, since it could have overflowing content - // that needs to be positioned correctly (e.g., a block that - // had a specified height of 0 but that actually had subcontent). - logical_top = - LogicalHeight() + collapsed_before_pos - collapsed_before_neg; - } + // The block doesn't contribute anything to the height of the block. Also, + // the child's top position equals the logical height of the container. + + // This child has no height. We need to compute our position before we + // collapse the child's margins together, so that we can get an accurate + // position for the zero-height block. + LayoutUnit collapsed_before_pos = std::max( + margin_info.PositiveMargin(), child_margins.PositiveMarginBefore()); + LayoutUnit collapsed_before_neg = std::max( + margin_info.NegativeMargin(), child_margins.NegativeMarginBefore()); + margin_info.SetMargin(collapsed_before_pos, collapsed_before_neg); + + // Now collapse the child's margins together, which means examining our + // bottom margin values as well. + margin_info.SetPositiveMarginIfLarger(child_margins.PositiveMarginAfter()); + margin_info.SetNegativeMarginIfLarger(child_margins.NegativeMarginAfter()); + + if (!margin_info.CanCollapseWithMarginBefore()) { + // We need to make sure that the position of the self-collapsing block is + // correct, since it could have overflowing content that needs to be + // positioned correctly (e.g., a block that had a specified height of 0 + // but that actually had subcontent). + logical_top = + LogicalHeight() + collapsed_before_pos - collapsed_before_neg; } } else { - if (MustSeparateMarginBeforeForChild(child)) { - DCHECK(!margin_info.DiscardMargin() || - (margin_info.DiscardMargin() && !margin_info.Margin())); - // If we are at the before side of the block and we collapse, ignore the - // computed margin and just add the child margin to the container height. - // This will correctly position the child inside the container. - LayoutUnit separate_margin = !margin_info.CanCollapseWithMarginBefore() - ? margin_info.Margin() - : LayoutUnit(); - SetLogicalHeight(LogicalHeight() + separate_margin + - MarginBeforeForChild(child)); - logical_top = LogicalHeight(); - } else if (!margin_info.DiscardMargin() && - (!margin_info.AtBeforeSideOfBlock() || - (!margin_info.CanCollapseMarginBeforeWithChildren() && - (!GetDocument().InQuirksMode() || - !margin_info.QuirkContainer() || - !margin_info.HasMarginBeforeQuirk())))) { - // We're collapsing with a previous sibling's margins and not - // with the top of the block. + if (!margin_info.AtBeforeSideOfBlock() || + (!margin_info.CanCollapseMarginBeforeWithChildren() && + (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() || + !margin_info.HasMarginBeforeQuirk()))) { + // We're collapsing with a previous sibling's margins and not with the + // top of the block. SetLogicalHeight(LogicalHeight() + std::max(margin_info.PositiveMargin(), pos_top) - std::max(margin_info.NegativeMargin(), neg_top)); logical_top = LogicalHeight(); } - margin_info.SetDiscardMargin(child_discard_margin_after); - - if (!margin_info.DiscardMargin()) { - margin_info.SetPositiveMargin(child_margins.PositiveMarginAfter()); - margin_info.SetNegativeMargin(child_margins.NegativeMarginAfter()); - } else { - margin_info.ClearMargin(); - } + margin_info.SetPositiveMargin(child_margins.PositiveMarginAfter()); + margin_info.SetNegativeMargin(child_margins.NegativeMarginAfter()); if (margin_info.Margin()) margin_info.SetHasMarginAfterQuirk(HasMarginAfterQuirk(&child)); @@ -2016,8 +1974,7 @@ LayoutUnit LayoutBlockFlow::ClearFloatsIfNeeded(LayoutBox& child, LayoutUnit old_top_pos_margin, LayoutUnit old_top_neg_margin, LayoutUnit y_pos, - bool child_is_self_collapsing, - bool child_discard_margin) { + bool child_is_self_collapsing) { LayoutUnit height_increase = GetClearDelta(&child, y_pos); margin_info.SetLastChildIsSelfCollapsingBlockWithClearance(false); @@ -2026,24 +1983,17 @@ LayoutUnit LayoutBlockFlow::ClearFloatsIfNeeded(LayoutBox& child, if (child_is_self_collapsing) { margin_info.SetLastChildIsSelfCollapsingBlockWithClearance(true); - margin_info.SetDiscardMargin(child_discard_margin); // For self-collapsing blocks that clear, they can still collapse their // margins with following siblings. Reset the current margins to represent // the self-collapsing block's margins only. - // If DISCARD is specified for -webkit-margin-collapse, reset the margin - // values. LayoutBlockFlow::MarginValues child_margins = MarginValuesForChild(child); - if (!child_discard_margin) { - margin_info.SetPositiveMargin( - std::max(child_margins.PositiveMarginBefore(), - child_margins.PositiveMarginAfter())); - margin_info.SetNegativeMargin( - std::max(child_margins.NegativeMarginBefore(), - child_margins.NegativeMarginAfter())); - } else { - margin_info.ClearMargin(); - } + margin_info.SetPositiveMargin( + std::max(child_margins.PositiveMarginBefore(), + child_margins.PositiveMarginAfter())); + margin_info.SetNegativeMargin( + std::max(child_margins.NegativeMarginBefore(), + child_margins.NegativeMarginAfter())); // CSS2.1 states: // "If the top and bottom margins of an element with clearance are @@ -2080,11 +2030,6 @@ LayoutUnit LayoutBlockFlow::ClearFloatsIfNeeded(LayoutBox& child, // occurred. The empty blocks collapse into the cleared block. SetMaxMarginBeforeValues(old_top_pos_margin, old_top_neg_margin); margin_info.SetAtBeforeSideOfBlock(false); - - // In case the child discarded the before margin of the block we need to - // reset the mustDiscardMarginBefore flag to the initial value. - SetMustDiscardMarginBefore(StyleRef().MarginBeforeCollapse() == - EMarginCollapse::kDiscard); } return y_pos + height_increase; @@ -2093,14 +2038,6 @@ LayoutUnit LayoutBlockFlow::ClearFloatsIfNeeded(LayoutBox& child, void LayoutBlockFlow::SetCollapsedBottomMargin(const MarginInfo& margin_info) { if (margin_info.CanCollapseWithMarginAfter() && !margin_info.CanCollapseWithMarginBefore()) { - // Update the after side margin of the container to discard if the after - // margin of the last child also discards and we collapse with it. - // Don't update the max margin values because we won't need them anyway. - if (margin_info.DiscardMargin()) { - SetMustDiscardMarginAfter(); - return; - } - // Update our max pos/neg bottom margins, since we collapsed our bottom // margins with our children. SetMaxMarginAfterValues( @@ -2123,28 +2060,14 @@ DISABLE_CFI_PERF void LayoutBlockFlow::MarginBeforeEstimateForChild( LayoutBox& child, LayoutUnit& positive_margin_before, - LayoutUnit& negative_margin_before, - bool& discard_margin_before) const { + LayoutUnit& negative_margin_before) const { // Give up if in quirks mode and we're a body/table cell and the top margin of // the child box is quirky. - // Give up if the child specified -webkit-margin-collapse: separate that - // prevents collapsing. // FIXME: Use writing mode independent accessor for marginBeforeCollapse. - if ((GetDocument().InQuirksMode() && HasMarginBeforeQuirk(&child) && - (IsTableCell() || IsBody())) || - child.StyleRef().MarginBeforeCollapse() == EMarginCollapse::kSeparate) + if (GetDocument().InQuirksMode() && HasMarginBeforeQuirk(&child) && + (IsTableCell() || IsBody())) return; - // The margins are discarded by a child that specified - // -webkit-margin-collapse: discard. - // FIXME: Use writing mode independent accessor for marginBeforeCollapse. - if (child.StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard) { - positive_margin_before = LayoutUnit(); - negative_margin_before = LayoutUnit(); - discard_margin_before = true; - return; - } - LayoutUnit before_child_margin = MarginBeforeForChild(child); positive_margin_before = std::max(positive_margin_before, before_child_margin); @@ -2200,8 +2123,7 @@ void LayoutBlockFlow::MarginBeforeEstimateForChild( // Collapse the margin of the grandchild box with our own to produce an // estimate. child_block_flow->MarginBeforeEstimateForChild( - *grandchild_box, positive_margin_before, negative_margin_before, - discard_margin_before); + *grandchild_box, positive_margin_before, negative_margin_before); } LayoutUnit LayoutBlockFlow::EstimateLogicalTopPosition( @@ -2215,13 +2137,11 @@ LayoutUnit LayoutBlockFlow::EstimateLogicalTopPosition( LayoutUnit logical_top_estimate = LogicalHeight(); LayoutUnit positive_margin_before; LayoutUnit negative_margin_before; - bool discard_margin_before = false; if (!margin_info.CanCollapseWithMarginBefore()) { if (child.SelfNeedsLayout()) { // Try to do a basic estimation of how the collapse is going to go. MarginBeforeEstimateForChild(child, positive_margin_before, - negative_margin_before, - discard_margin_before); + negative_margin_before); } else { // Use the cached collapsed margin values from a previous layout. Most of // the time they will be right. @@ -2230,14 +2150,12 @@ LayoutUnit LayoutBlockFlow::EstimateLogicalTopPosition( margin_values.PositiveMarginBefore()); negative_margin_before = std::max(negative_margin_before, margin_values.NegativeMarginBefore()); - discard_margin_before = MustDiscardMarginBeforeForChild(child); } // Collapse the result with our current margins. - if (!discard_margin_before) - logical_top_estimate += - std::max(margin_info.PositiveMargin(), positive_margin_before) - - std::max(margin_info.NegativeMargin(), negative_margin_before); + logical_top_estimate += + std::max(margin_info.PositiveMargin(), positive_margin_before) - + std::max(margin_info.NegativeMargin(), negative_margin_before); } LayoutState* layout_state = View()->GetLayoutState(); @@ -2265,9 +2183,7 @@ LayoutUnit LayoutBlockFlow::EstimateLogicalTopPosition( // Disregard previous margins, since they will collapse with the // fragmentainer boundary, due to the forced break. Only apply margins // that have been specified on the child or its descendants. - if (!discard_margin_before) - logical_top_estimate += - positive_margin_before - negative_margin_before; + logical_top_estimate += positive_margin_before - negative_margin_before; // Clearance may already have taken us past the beginning of the next // fragmentainer. @@ -2325,11 +2241,10 @@ void LayoutBlockFlow::HandleAfterSideOfBlock(LayoutBox* last_child, // If we can't collapse with children then go ahead and add in the bottom // margin. - if (!margin_info.DiscardMargin() && - (!margin_info.CanCollapseWithMarginAfter() && - !margin_info.CanCollapseWithMarginBefore() && - (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() || - !margin_info.HasMarginAfterQuirk()))) + if (!margin_info.CanCollapseWithMarginAfter() && + !margin_info.CanCollapseWithMarginBefore() && + (!GetDocument().InQuirksMode() || !margin_info.QuirkContainer() || + !margin_info.HasMarginAfterQuirk())) SetLogicalHeight(LogicalHeight() + margin_info.Margin()); // Now add in our bottom border/padding. @@ -2352,97 +2267,12 @@ void LayoutBlockFlow::HandleAfterSideOfBlock(LayoutBox* last_child, JoinFragmentainerBreakValues(BreakAfter(), last_child->BreakAfter())); } -void LayoutBlockFlow::SetMustDiscardMarginBefore(bool value) { - if (StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard) { - DCHECK(value); - return; - } - - if (!rare_data_ && !value) - return; - - if (!rare_data_) - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); - - rare_data_->discard_margin_before_ = value; -} - -void LayoutBlockFlow::SetMustDiscardMarginAfter(bool value) { - if (StyleRef().MarginAfterCollapse() == EMarginCollapse::kDiscard) { - DCHECK(value); - return; - } - - if (!rare_data_ && !value) - return; - - if (!rare_data_) - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); - - rare_data_->discard_margin_after_ = value; -} - -bool LayoutBlockFlow::MustDiscardMarginBefore() const { - return StyleRef().MarginBeforeCollapse() == EMarginCollapse::kDiscard || - (rare_data_ && rare_data_->discard_margin_before_); -} - -bool LayoutBlockFlow::MustDiscardMarginAfter() const { - return StyleRef().MarginAfterCollapse() == EMarginCollapse::kDiscard || - (rare_data_ && rare_data_->discard_margin_after_); -} - -bool LayoutBlockFlow::MustDiscardMarginBeforeForChild( - const LayoutBox& child) const { - DCHECK(!child.SelfNeedsLayout() || - child.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kSelf)); - if (!child.IsWritingModeRoot()) { - auto* child_layout_block = DynamicTo<LayoutBlockFlow>(&child); - return child_layout_block ? child_layout_block->MustDiscardMarginBefore() - : (child.StyleRef().MarginBeforeCollapse() == - EMarginCollapse::kDiscard); - } - if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) { - auto* child_layout_block = DynamicTo<LayoutBlockFlow>(&child); - return child_layout_block ? child_layout_block->MustDiscardMarginAfter() - : (child.StyleRef().MarginAfterCollapse() == - EMarginCollapse::kDiscard); - } - - // FIXME: We return false here because the implementation is not geometrically - // complete. We have values only for before/after, not start/end. - // In case the boxes are perpendicular we assume the property is not - // specified. - return false; -} - -bool LayoutBlockFlow::MustDiscardMarginAfterForChild( - const LayoutBox& child) const { - DCHECK(!child.SelfNeedsLayout() || - child.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kSelf)); - if (!child.IsWritingModeRoot()) { - auto* child_layout_block = DynamicTo<LayoutBlockFlow>(&child); - return child_layout_block ? child_layout_block->MustDiscardMarginAfter() - : (child.StyleRef().MarginAfterCollapse() == - EMarginCollapse::kDiscard); - } - if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) { - auto* child_layout_block = DynamicTo<LayoutBlockFlow>(&child); - return child_layout_block ? child_layout_block->MustDiscardMarginBefore() - : (child.StyleRef().MarginBeforeCollapse() == - EMarginCollapse::kDiscard); - } - - // FIXME: See |mustDiscardMarginBeforeForChild| above. - return false; -} - void LayoutBlockFlow::SetMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg) { if (!rare_data_) { if (pos == LayoutBlockFlowRareData::PositiveMarginBeforeDefault(this) && neg == LayoutBlockFlowRareData::NegativeMarginBeforeDefault(this)) return; - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); + rare_data_ = MakeGarbageCollected<LayoutBlockFlowRareData>(this); } rare_data_->margins_.SetPositiveMarginBefore(pos); rare_data_->margins_.SetNegativeMarginBefore(neg); @@ -2453,40 +2283,12 @@ void LayoutBlockFlow::SetMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg) { if (pos == LayoutBlockFlowRareData::PositiveMarginAfterDefault(this) && neg == LayoutBlockFlowRareData::NegativeMarginAfterDefault(this)) return; - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); + rare_data_ = MakeGarbageCollected<LayoutBlockFlowRareData>(this); } rare_data_->margins_.SetPositiveMarginAfter(pos); rare_data_->margins_.SetNegativeMarginAfter(neg); } -bool LayoutBlockFlow::MustSeparateMarginBeforeForChild( - const LayoutBox& child) const { - DCHECK(!child.SelfNeedsLayout() || - child.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kSelf)); - const ComputedStyle& child_style = child.StyleRef(); - if (!child.IsWritingModeRoot()) - return child_style.MarginBeforeCollapse() == EMarginCollapse::kSeparate; - if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) - return child_style.MarginAfterCollapse() == EMarginCollapse::kSeparate; - - // FIXME: See |mustDiscardMarginBeforeForChild| above. - return false; -} - -bool LayoutBlockFlow::MustSeparateMarginAfterForChild( - const LayoutBox& child) const { - DCHECK(!child.SelfNeedsLayout() || - child.LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kSelf)); - const ComputedStyle& child_style = child.StyleRef(); - if (!child.IsWritingModeRoot()) - return child_style.MarginAfterCollapse() == EMarginCollapse::kSeparate; - if (child.IsHorizontalWritingMode() == IsHorizontalWritingMode()) - return child_style.MarginBeforeCollapse() == EMarginCollapse::kSeparate; - - // FIXME: See |mustDiscardMarginBeforeForChild| above. - return false; -} - LayoutUnit LayoutBlockFlow::ApplyForcedBreak(LayoutUnit logical_offset, EBreakBetween break_value) { if (!IsForcedFragmentainerBreakValue(break_value)) @@ -2559,6 +2361,27 @@ void LayoutBlockFlow::AddVisualOverflowFromFloats() { } } +void LayoutBlockFlow::AddVisualOverflowFromFloats( + const NGPhysicalContainerFragment& fragment) { + DCHECK(fragment.HasFloatingDescendantsForPaint()); + for (const NGLink& child : fragment.Children()) { + if (child->HasSelfPaintingLayer()) + continue; + + if (child->IsFloating()) { + AddVisualOverflowFromChild(ToLayoutBox(*child->GetLayoutObject())); + continue; + } + + if (const NGPhysicalContainerFragment* child_container = + DynamicTo<NGPhysicalContainerFragment>(child.get())) { + if (child_container->HasFloatingDescendantsForPaint() && + !child_container->IsFormattingContextRoot()) + AddVisualOverflowFromFloats(*child_container); + } + } +} + void LayoutBlockFlow::AddLayoutOverflowFromFloats() { if (!floating_objects_) return; @@ -2597,6 +2420,7 @@ void LayoutBlockFlow::ComputeVisualOverflow( HasSelfPaintingLayer())) AddVisualOverflowFromFloats(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } @@ -2712,15 +2536,13 @@ LayoutUnit LayoutBlockFlow::FirstLineBoxBaseline() const { NGBoxFragment box_fragment( StyleRef().GetWritingMode(), StyleRef().Direction(), To<NGPhysicalBoxFragment>(paint_fragment->PhysicalFragment())); - NGLineHeightMetrics metrics = - box_fragment.BaselineMetricsWithoutSynthesize( - {NGBaselineAlgorithmType::kFirstLine, - StyleRef().GetFontBaseline()}); - if (!metrics.IsEmpty()) - return metrics.ascent; + base::Optional<LayoutUnit> baseline = box_fragment.Baseline(); + if (baseline) + return *baseline; } } - return LayoutUnit(-1); + return EmptyLineBaseline(IsHorizontalWritingMode() ? kHorizontalLine + : kVerticalLine); } LayoutUnit LayoutBlockFlow::InlineBlockBaseline( @@ -2750,23 +2572,7 @@ LayoutUnit LayoutBlockFlow::InlineBlockBaseline( return LastLineBox()->LogicalTop() + font_data->GetFontMetrics().Ascent(LastRootBox()->BaselineType()); } - if (!HasLineIfEmpty()) - return LayoutUnit(-1); - - const SimpleFontData* font_data = FirstLineStyle()->GetFont().PrimaryFont(); - DCHECK(font_data); - if (!font_data) - return LayoutUnit(-1); - - const FontMetrics& font_metrics = font_data->GetFontMetrics(); - return LayoutUnit( - (font_metrics.Ascent() + - (LineHeight(true, line_direction, kPositionOfInteriorLineBoxes) - - font_metrics.Height()) / - 2 + - (line_direction == kHorizontalLine ? BorderTop() + PaddingTop() - : BorderRight() + PaddingRight())) - .ToInt()); + return EmptyLineBaseline(line_direction); } void LayoutBlockFlow::RemoveFloatingObjectsFromDescendants() { @@ -2939,9 +2745,6 @@ void LayoutBlockFlow::CreateFloatingObjects() { } void LayoutBlockFlow::WillBeDestroyed() { - // Mark as being destroyed to avoid trouble with merges in removeChild(). - being_destroyed_ = true; - // Make sure to destroy anonymous children first while they are still // connected to the rest of the tree, so that they will properly dirty line // boxes that they are removed from. Effects that do :before/:after only on @@ -3030,7 +2833,7 @@ void LayoutBlockFlow::StyleDidChange(StyleDifference diff, const FloatingObjectSet& floating_object_set = floating_objects_->Set(); FloatingObjectSetIterator end = floating_object_set.end(); - for (LayoutObject* curr = Parent(); curr && !curr->IsLayoutView(); + for (LayoutObject* curr = Parent(); !IsA<LayoutView>(curr); curr = curr->Parent()) { auto* curr_block = DynamicTo<LayoutBlockFlow>(curr); if (curr_block) { @@ -3149,9 +2952,9 @@ void LayoutBlockFlow::AddChild(LayoutObject* new_child, return; } - // LayoutNGListMarker is out-of-flow for the tree building purpose, and that - // is not inline level, but IsInline(). - if (new_child->IsInline() && !new_child->IsLayoutNGListMarker()) { + // LayoutNGOutsideListMarker is out-of-flow for the tree building purpose, + // and that is not inline level, but IsInline(). + if (new_child->IsInline() && !new_child->IsLayoutNGOutsideListMarker()) { // No suitable existing anonymous box - create a new one. auto* new_block = To<LayoutBlockFlow>(CreateAnonymousBlock()); LayoutBox::AddChild(new_block, before_child); @@ -3364,7 +3167,7 @@ void LayoutBlockFlow::CollapseAnonymousBlockChild(LayoutBlockFlow* child) { // design, so we don't remove them. if (child->IsRubyRun() || child->IsRubyBase()) return; - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kChildAnonymousBlockChanged); child->MoveAllChildrenTo(this, child->NextSibling(), child->HasLayer()); @@ -3390,7 +3193,7 @@ bool LayoutBlockFlow::MergeSiblingContiguousAnonymousBlock( !IsMergeableAnonymousBlock(sibling_that_may_be_deleted)) return false; - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); // If the inlineness of children of the two block don't match, we'd need @@ -3510,9 +3313,9 @@ static void GetInlineRun(LayoutObject* start, // Start by skipping as many non-inlines as we can. LayoutObject* curr = start; - // LayoutNGListMarker is out-of-flow for the tree building purpose. Skip here - // because it's the first child. - if (curr && curr->IsLayoutNGListMarker()) + // LayoutNGOutsideListMarker is out-of-flow for the tree building purpose. + // Skip here because it's the first child. + if (curr && curr->IsLayoutNGOutsideListMarker()) curr = curr->NextSibling(); bool saw_inline; @@ -3577,7 +3380,7 @@ void LayoutBlockFlow::MakeChildrenNonInline(LayoutObject* insertion_point) { #if DCHECK_IS_ON() for (LayoutObject* c = FirstChild(); c; c = c->NextSibling()) - DCHECK(!c->IsInline() || c->IsLayoutNGListMarker()); + DCHECK(!c->IsInline() || c->IsLayoutNGOutsideListMarker()); #endif SetShouldDoFullPaintInvalidation(); @@ -4422,7 +4225,7 @@ void LayoutBlockFlow::SetPaginationStrutPropagatedFromChild(LayoutUnit strut) { if (!rare_data_) { if (!strut) return; - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); + rare_data_ = MakeGarbageCollected<LayoutBlockFlowRareData>(this); } rare_data_->pagination_strut_propagated_from_child_ = strut; } @@ -4431,7 +4234,7 @@ void LayoutBlockFlow::SetFirstForcedBreakOffset(LayoutUnit block_offset) { if (!rare_data_) { if (!block_offset) return; - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); + rare_data_ = MakeGarbageCollected<LayoutBlockFlowRareData>(this); } rare_data_->first_forced_break_offset_ = block_offset; } @@ -4450,7 +4253,7 @@ bool LayoutBlockFlow::CreatesNewFormattingContext() const { if (IsInline() || IsFloatingOrOutOfFlowPositioned() || HasOverflowClip() || IsFlexItemIncludingDeprecatedAndNG() || IsCustomItem() || IsDocumentElement() || IsGridItem() || IsWritingModeRoot() || - StyleRef().Display() == EDisplay::kFlowRoot || + IsMathItem() || StyleRef().Display() == EDisplay::kFlowRoot || ShouldApplyPaintContainment() || ShouldApplyLayoutContainment() || StyleRef().SpecifiesColumns() || StyleRef().GetColumnSpan() == EColumnSpan::kAll) { @@ -4518,7 +4321,7 @@ void LayoutBlockFlow::CreateOrDestroyMultiColumnFlowThreadIfNeeded( // Form controls are replaced content, and are therefore not supposed to // support multicol. - if (IsFileUploadControl() || IsTextControl() || IsListBox()) + if (IsFileUploadControl() || IsTextControl() || IsListBox(this)) return; // We don't allow custom layout and multicol on the same object. This is @@ -4546,7 +4349,7 @@ LayoutBlockFlow::LayoutBlockFlowRareData& LayoutBlockFlow::EnsureRareData() { if (rare_data_) return *rare_data_; - rare_data_ = std::make_unique<LayoutBlockFlowRareData>(this); + rare_data_ = MakeGarbageCollected<LayoutBlockFlowRareData>(this); return *rare_data_; } @@ -4631,10 +4434,9 @@ void LayoutBlockFlow::RecalcInlineChildrenVisualOverflow() { if (const NGFragmentItems* items = fragment->Items()) { NGInlineCursor cursor(*items); NGFragmentItem::RecalcInkOverflowForCursor(&cursor); - } - - if (fragment->HasFloatingDescendantsForPaint()) + } else if (fragment->HasFloatingDescendantsForPaint()) { RecalcFloatingDescendantsVisualOverflow(*fragment); + } return; } } @@ -4946,9 +4748,7 @@ LayoutBlockFlow::LayoutBlockFlowRareData::LayoutBlockFlowRareData( break_before_(static_cast<unsigned>(EBreakBetween::kAuto)), break_after_(static_cast<unsigned>(EBreakBetween::kAuto)), line_break_to_avoid_widow_(-1), - did_break_at_line_to_avoid_widow_(false), - discard_margin_before_(false), - discard_margin_after_(false) {} + did_break_at_line_to_avoid_widow_(false) {} LayoutBlockFlow::LayoutBlockFlowRareData::~LayoutBlockFlowRareData() = default; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h index bbe43e33163..e7f1a334a53 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow.h @@ -105,7 +105,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { static LayoutBlockFlow* CreateAnonymous(Document*, scoped_refptr<ComputedStyle>, LegacyLayout); - bool BeingDestroyed() const { return being_destroyed_; } bool IsLayoutBlockFlow() const final { return true; } @@ -454,6 +453,7 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { // These functions are only public so we can call it from NGBlockNode while // we're still working on LayoutNG. void AddVisualOverflowFromFloats(); + void AddVisualOverflowFromFloats(const NGPhysicalContainerFragment& fragment); void AddLayoutOverflowFromFloats(); virtual NGInlineNodeData* TakeNGInlineNodeData() { return nullptr; } @@ -464,9 +464,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { virtual void WillCollectInlines() {} virtual void SetPaintFragment(const NGBlockBreakToken*, scoped_refptr<const NGPhysicalFragment>); - virtual const NGPhysicalBoxFragment* CurrentFragment() const { - return nullptr; - } const NGFragmentItems* FragmentItems() const; #if DCHECK_IS_ON() @@ -530,7 +527,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { const PhysicalOffset& additional_offset, NGOutlineType) const override; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; void InvalidateDisplayItemClients(PaintInvalidationReason) const override; Node* NodeForHitTest() const final; @@ -736,9 +732,8 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { MarginValues MarginValuesForChild(LayoutBox& child) const; // Allocated only when some of these fields have non-default values - struct LayoutBlockFlowRareData { - USING_FAST_MALLOC(LayoutBlockFlowRareData); - + struct LayoutBlockFlowRareData final + : public GarbageCollected<LayoutBlockFlowRareData> { public: explicit LayoutBlockFlowRareData(const LayoutBlockFlow* block); ~LayoutBlockFlowRareData(); @@ -758,6 +753,8 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { return (-block->MarginAfter()).ClampNegativeToZero(); } + void Trace(Visitor*) {} + MarginValues margins_; LayoutUnit pagination_strut_propagated_from_child_; @@ -775,8 +772,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { unsigned break_after_ : 4; int line_break_to_avoid_widow_; bool did_break_at_line_to_avoid_widow_ : 1; - bool discard_margin_before_ : 1; - bool discard_margin_after_ : 1; DISALLOW_COPY_AND_ASSIGN(LayoutBlockFlowRareData); }; @@ -823,18 +818,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { void SetMaxMarginBeforeValues(LayoutUnit pos, LayoutUnit neg); void SetMaxMarginAfterValues(LayoutUnit pos, LayoutUnit neg); - void SetMustDiscardMarginBefore(bool = true); - void SetMustDiscardMarginAfter(bool = true); - - bool MustDiscardMarginBefore() const; - bool MustDiscardMarginAfter() const; - - bool MustDiscardMarginBeforeForChild(const LayoutBox&) const; - bool MustDiscardMarginAfterForChild(const LayoutBox&) const; - - bool MustSeparateMarginBeforeForChild(const LayoutBox&) const; - bool MustSeparateMarginAfterForChild(const LayoutBox&) const; - void InitMaxMarginValues() { if (rare_data_) { rare_data_->margins_ = MarginValues( @@ -842,9 +825,6 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { LayoutBlockFlowRareData::NegativeMarginBeforeDefault(this), LayoutBlockFlowRareData::PositiveMarginAfterDefault(this), LayoutBlockFlowRareData::NegativeMarginAfterDefault(this)); - - rare_data_->discard_margin_before_ = false; - rare_data_->discard_margin_after_ = false; } } @@ -866,24 +846,18 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { LayoutUnit CollapseMargins(LayoutBox& child, BlockChildrenLayoutInfo&, - bool child_is_self_collapsing, - bool child_discard_margin_before, - bool child_discard_margin_after); + bool child_is_self_collapsing); LayoutUnit ClearFloatsIfNeeded(LayoutBox& child, MarginInfo&, LayoutUnit old_top_pos_margin, LayoutUnit old_top_neg_margin, LayoutUnit y_pos, - bool child_is_self_collapsing, - bool child_discard_margin); + bool child_is_self_collapsing); LayoutUnit EstimateLogicalTopPosition( LayoutBox& child, const BlockChildrenLayoutInfo&, LayoutUnit& estimate_without_pagination); - void MarginBeforeEstimateForChild(LayoutBox&, - LayoutUnit&, - LayoutUnit&, - bool&) const; + void MarginBeforeEstimateForChild(LayoutBox&, LayoutUnit&, LayoutUnit&) const; void HandleAfterSideOfBlock(LayoutBox* last_child, LayoutUnit top, LayoutUnit bottom, @@ -937,7 +911,7 @@ class CORE_EXPORT LayoutBlockFlow : public LayoutBlock { bool CheckIfIsSelfCollapsingBlock() const; protected: - std::unique_ptr<LayoutBlockFlowRareData> rare_data_; + Persistent<LayoutBlockFlowRareData> rare_data_; std::unique_ptr<FloatingObjects> floating_objects_; friend class MarginInfo; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc index ca02eebdb92..96f21e1d24c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_flow_line.cc @@ -38,6 +38,7 @@ #include "third_party/blink/renderer/core/layout/line/word_measurement.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h" #include "third_party/blink/renderer/core/layout/vertical_position_cache.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" @@ -956,7 +957,7 @@ RootInlineBox* LayoutBlockFlow::CreateLineBoxesFromBidiRuns( // text selection in RTL boxes would not work as expected. if (is_svg_root_inline_box) { DCHECK(IsSVGText()); - ToSVGRootInlineBox(line_box)->ComputePerCharacterLayoutInformation(); + To<SVGRootInlineBox>(line_box)->ComputePerCharacterLayoutInformation(); } // Compute our overflow now. @@ -1179,7 +1180,13 @@ void LayoutBlockFlow::LayoutRunsAndFloatsInRange( resolver, bidi_runs, end_of_line, override, layout_state.GetLineInfo().PreviousLineBrokeCleanly(), is_new_uba_paragraph); - DCHECK(resolver.GetPosition() == end_of_line); + // |resolver| to be at |end_of_line| is critical, because + // |SetLineBreakInfo| below copies |end_of_line.current_| to + // |RootInlineBox::line_break_obj_|. When the object is destroyed, + // |RootInlineBox::ChildRemoved()| clears |line_break_obj_| to avoid + // use-after-free, but we cannot find the correct |RootInlineBox| if + // |end_of_line| is actually not in this |RootInlineBox|. + CHECK(resolver.GetPosition() == end_of_line); BidiRun* trailing_space_run = resolver.TrailingSpaceRun(); @@ -1690,7 +1697,7 @@ void LayoutBlockFlow::ComputeInlinePreferredLogicalWidths( // did change or is going to change size. However, this is our only // opportunity to make sure that it gets its min/max widths // calculated. - child->SetPreferredLogicalWidthsDirty(); + child->SetIntrinsicLogicalWidthsDirty(); } // Case (1) and (2). Inline replaced and inline flow elements. @@ -1699,13 +1706,14 @@ void LayoutBlockFlow::ComputeInlinePreferredLogicalWidths( child_min, child_max); inline_min += child_min; inline_max += child_max; - child->ClearPreferredLogicalWidthsDirty(); + child->ClearIntrinsicLogicalWidthsDirty(); } else { AdjustMarginForInlineReplaced(child, child_min, child_max); } } - if (!child->IsLayoutInline() && !child->IsText()) { + if (!child->IsLayoutInline() && !child->IsText() && + !child->IsOutsideListMarker()) { // Case (2). Inline replaced elements and floats. // Go ahead and terminate the current line as far as // minwidth is concerned. @@ -1893,7 +1901,7 @@ void LayoutBlockFlow::ComputeInlinePreferredLogicalWidths( } // Ignore spaces after a list marker. - if (child->IsListMarkerIncludingNG()) + if (child->IsListMarkerIncludingNGOutside()) strip_front_spaces = true; } else { min_logical_width = std::max(min_logical_width, inline_min); @@ -2394,17 +2402,21 @@ void LayoutBlockFlow::AddVisualOverflowFromInlineChildren() { AddContentsVisualOverflow(child_rect); } } - } else if (const NGFragmentItems* items = FragmentItems()) { - for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNextSibling()) { - const NGFragmentItem* child = cursor.CurrentItem(); - DCHECK(child); - if (child->HasSelfPaintingLayer()) - continue; - PhysicalRect child_rect = child->InkOverflow(); - if (!child_rect.IsEmpty()) { - child_rect.offset += child->Offset(); - AddContentsVisualOverflow(child_rect); + } else if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { + if (const NGFragmentItems* items = fragment->Items()) { + for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNextSibling()) { + const NGFragmentItem* child = cursor.CurrentItem(); + DCHECK(child); + if (child->HasSelfPaintingLayer()) + continue; + PhysicalRect child_rect = child->InkOverflow(); + if (!child_rect.IsEmpty()) { + child_rect.offset += child->OffsetInContainerBlock(); + AddContentsVisualOverflow(child_rect); + } } + } else if (fragment->HasFloatingDescendantsForPaint()) { + AddVisualOverflowFromFloats(*fragment); } } else { for (RootInlineBox* curr = FirstRootBox(); curr; @@ -2744,18 +2756,36 @@ LayoutUnit LayoutBlockFlow::StartAlignedOffsetForLine( void LayoutBlockFlow::SetShouldDoFullPaintInvalidationForFirstLine() { DCHECK(ChildrenInline()); - if (RootInlineBox* first_root_box = FirstRootBox()) - first_root_box->SetShouldDoFullPaintInvalidationForFirstLine(); - else if (const NGPaintFragment* paint_fragment = PaintFragment()) + + if (const NGPaintFragment* paint_fragment = PaintFragment()) { paint_fragment->SetShouldDoFullPaintInvalidationForFirstLine(); -} + return; + } -bool LayoutBlockFlow::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - // LayoutBlockFlow is in charge of paint invalidation of the first line. - if (FirstLineBox()) - return false; + if (const NGFragmentItems* fragment_items = FragmentItems()) { + NGInlineCursor first_line(*fragment_items); + if (first_line) { + DCHECK(!FirstRootBox()); + first_line.MoveToFirstLine(); + if (first_line && first_line.Current().UsesFirstLineStyle()) { + // Mark all descendants of the first line if first-line style. + for (NGInlineCursor descendants = first_line.CursorForDescendants(); + descendants; descendants.MoveToNext()) { + LayoutObject* layout_object = + descendants.Current()->GetMutableLayoutObject(); + DCHECK(layout_object); + layout_object->StyleRef().ClearCachedPseudoElementStyles(); + layout_object->SetShouldDoFullPaintInvalidation(); + } + StyleRef().ClearCachedPseudoElementStyles(); + SetShouldDoFullPaintInvalidation(); + } + } + return; + } - return LayoutBlock::PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); + if (RootInlineBox* first_root_box = FirstRootBox()) + first_root_box->SetShouldDoFullPaintInvalidationForFirstLine(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_block_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_block_test.cc index 98e24416cfd..a08a3b30dc6 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_block_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_block_test.cc @@ -54,7 +54,8 @@ TEST_F(LayoutBlockTest, WidthAvailableToChildrenChanged) { 150 - list_box->VerticalScrollbarWidth()); DummyExceptionStateForTesting exception_state; - list_element->style()->setCSSText(&GetDocument(), "width:150px;height:100px;", + list_element->style()->setCSSText(GetDocument().GetExecutionContext(), + "width:150px;height:100px;", exception_state); ASSERT_FALSE(exception_state.HadException()); UpdateAllLifecyclePhasesForTest(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_box.cc index e9e3cfdf053..d10c1623fc7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box.cc @@ -30,9 +30,11 @@ #include <algorithm> #include "cc/input/scroll_snap_data.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h" #include "third_party/blink/public/platform/web_rect.h" -#include "third_party/blink/public/platform/web_scroll_into_view_params.h" +#include "third_party/blink/public/strings/grit/blink_strings.h" #include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -40,10 +42,15 @@ #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h" +#include "third_party/blink/renderer/core/html/forms/html_input_element.h" +#include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h" +#include "third_party/blink/renderer/core/html/forms/html_select_element.h" +#include "third_party/blink/renderer/core/html/html_div_element.h" #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_frame_element_base.h" #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/input/event_handler.h" +#include "third_party/blink/renderer/core/input_type_names.h" #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h" #include "third_party/blink/renderer/core/layout/api/line_layout_box.h" #include "third_party/blink/renderer/core/layout/box_layout_extra_input.h" @@ -52,6 +59,7 @@ #include "third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_fieldset.h" +#include "third_party/blink/renderer/core/layout/layout_file_upload_control.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" #include "third_party/blink/renderer/core/layout/layout_grid.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" @@ -91,6 +99,7 @@ #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/text/platform_locale.h" namespace blink { @@ -107,13 +116,117 @@ struct SameSizeAsLayoutBox : public LayoutBoxModelObject { LayoutSize previous_size; LayoutUnit intrinsic_content_logical_height; LayoutRectOutsets margin_box_outsets; - LayoutUnit preferred_logical_width[2]; - void* pointers[5]; + MinMaxSizes intrinsic_logical_widths; + LayoutUnit intrinsic_logical_widths_percentage_resolution_block_size; + void* pointers[4]; + Persistent<void*> rare_data; + Vector<scoped_refptr<const NGLayoutResult>, 1> layout_results; }; static_assert(sizeof(LayoutBox) == sizeof(SameSizeAsLayoutBox), "LayoutBox should stay small"); +namespace { + +LayoutUnit FileUploadControlIntrinsicInlineSize(const HTMLInputElement& input, + const LayoutBox& box) { + // Figure out how big the filename space needs to be for a given number of + // characters (using "0" as the nominal character). + constexpr int kDefaultWidthNumChars = 34; + constexpr UChar kCharacter = '0'; + const String character_as_string = String(&kCharacter, 1); + const Font& font = box.StyleRef().GetFont(); + const float min_default_label_width = + kDefaultWidthNumChars * + font.Width(ConstructTextRun(font, character_as_string, box.StyleRef(), + TextRun::kAllowTrailingExpansion)); + + const String label = + input.GetLocale().QueryString(IDS_FORM_FILE_NO_FILE_LABEL); + float default_label_width = font.Width(ConstructTextRun( + font, label, box.StyleRef(), TextRun::kAllowTrailingExpansion)); + if (HTMLInputElement* button = input.UploadButton()) { + if (LayoutObject* button_layout_object = button->GetLayoutObject()) { + default_label_width += + button_layout_object->PreferredLogicalWidths().max_size + + LayoutFileUploadControl::kAfterButtonSpacing; + } + } + return LayoutUnit( + ceilf(std::max(min_default_label_width, default_label_width))); +} + +LayoutUnit ListBoxDefaultItemHeight(const LayoutBox& box) { + constexpr int kDefaultPaddingBottom = 1; + + const SimpleFontData* font_data = box.StyleRef().GetFont().PrimaryFont(); + if (!font_data) + return LayoutUnit(); + return LayoutUnit(font_data->GetFontMetrics().Height() + + kDefaultPaddingBottom); +} + +// TODO(crbug.com/1040826): This function is written in LayoutObject API +// so that this works in both of the legacy layout and LayoutNG. We +// should have LayoutNG-specific code. +LayoutUnit ListBoxItemHeight(const HTMLSelectElement& select, + const LayoutBox& box) { + const auto& items = select.GetListItems(); + if (items.IsEmpty() || box.ShouldApplySizeContainment()) + return ListBoxDefaultItemHeight(box); + + LayoutUnit max_height; + for (Element* element : items) { + if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(element)) + element = &optgroup->OptGroupLabelElement(); + LayoutUnit item_height; + if (auto* layout_box = element->GetLayoutBox()) + item_height = layout_box->Size().Height(); + else + item_height = ListBoxDefaultItemHeight(box); + max_height = std::max(max_height, item_height); + } + return max_height; +} + +LayoutUnit MenuListIntrinsicInlineSize(const HTMLSelectElement& select, + const LayoutBox& box) { + const ComputedStyle& style = box.StyleRef(); + float max_option_width = 0; + if (!box.ShouldApplySizeContainment()) { + for (auto* const option : select.GetOptionList()) { + String text = option->TextIndentedToRespectGroupLabel(); + const ComputedStyle* item_style = + option->GetComputedStyle() ? option->GetComputedStyle() : &style; + item_style->ApplyTextTransform(&text); + // We apply SELECT's style, not OPTION's style because max_option_width is + // used to determine intrinsic width of the menulist box. + TextRun text_run = ConstructTextRun(style.GetFont(), text, style); + max_option_width = + std::max(max_option_width, style.GetFont().Width(text_run)); + } + } + + LayoutTheme& theme = LayoutTheme::GetTheme(); + int paddings = theme.PopupInternalPaddingStart(style) + + theme.PopupInternalPaddingEnd(box.GetFrame(), style); + return std::max(static_cast<int>(ceilf(max_option_width)), + LayoutTheme::GetTheme().MinimumMenuListSize(style)) + + LayoutUnit(paddings); +} + +LayoutUnit MenuListIntrinsicBlockSize(const HTMLSelectElement& select, + const LayoutBox& box) { + if (!box.StyleRef().HasEffectiveAppearance()) + return kIndefiniteSize; + const SimpleFontData* font_data = box.StyleRef().GetFont().PrimaryFont(); + DCHECK(font_data); + return (font_data ? font_data->GetFontMetrics().Height() : 0) + + select.InnerElement().GetLayoutBox()->BorderAndPaddingLogicalHeight(); +} + +} // anonymous namespace + BoxLayoutExtraInput::BoxLayoutExtraInput(LayoutBox& box) : box(box) { box.SetBoxLayoutExtraInput(this); } @@ -135,11 +248,15 @@ LayoutBoxRareData::LayoutBoxRareData() snap_container_(nullptr), snap_areas_(nullptr) {} +void LayoutBoxRareData::Trace(Visitor* visitor) { + visitor->Trace(layout_child_); +} + LayoutBox::LayoutBox(ContainerNode* node) : LayoutBoxModelObject(node), intrinsic_content_logical_height_(-1), - min_preferred_logical_width_(-1), - max_preferred_logical_width_(-1), + intrinsic_logical_widths_percentage_resolution_block_size_( + LayoutUnit::Min()), inline_box_wrapper_(nullptr) { SetIsBox(); if (blink::IsA<HTMLLegendElement>(node)) @@ -181,6 +298,8 @@ void LayoutBox::WillBeDestroyed() { if (!DocumentBeingDestroyed()) { if (NGPaintFragment* first_inline_fragment = FirstInlineFragment()) first_inline_fragment->LayoutObjectWillBeDestroyed(); + for (auto result : layout_results_) + result->PhysicalFragment().LayoutObjectWillBeDestroyed(); } SetSnapContainer(nullptr); @@ -242,8 +361,7 @@ void LayoutBox::StyleWillChange(StyleDifference diff, // The background of the root element or the body element could propagate up // to the canvas. Just dirty the entire canvas when our style changes // substantially. - if ((diff.NeedsFullPaintInvalidation() || diff.NeedsLayout()) && - GetNode() && + if ((diff.NeedsPaintInvalidation() || diff.NeedsLayout()) && GetNode() && (IsDocumentElement() || IsA<HTMLBodyElement>(*GetNode()))) { View()->SetShouldDoFullPaintInvalidation(); } @@ -258,7 +376,7 @@ void LayoutBox::StyleWillChange(StyleDifference diff, // We're about to go out of flow. Before that takes place, we need to // mark the current containing block chain for preferred widths // recalculation. - SetNeedsLayoutAndPrefWidthsRecalc( + SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kStyleChange); } else { MarkContainerChainForLayout(); @@ -468,7 +586,6 @@ void LayoutBox::UpdateScrollSnapMappingAfterStyleChange( const ComputedStyle& old_style) { DCHECK(Style()); SnapCoordinator& snap_coordinator = GetDocument().GetSnapCoordinator(); - // scroll-snap-type and scroll-padding invalidate the snap container. if (old_style.GetScrollSnapType() != StyleRef().GetScrollSnapType() || old_style.ScrollPaddingBottom() != StyleRef().ScrollPaddingBottom() || @@ -487,6 +604,10 @@ void LayoutBox::UpdateScrollSnapMappingAfterStyleChange( old_style.ScrollMarginTop() != StyleRef().ScrollMarginTop() || old_style.ScrollMarginRight() != StyleRef().ScrollMarginRight()) snap_coordinator.SnapAreaDidChange(*this, StyleRef().GetScrollSnapAlign()); + + // Transform invalidates the snap area. + if (old_style.Transform() != StyleRef().Transform()) + snap_coordinator.SnapAreaDidChange(*this, StyleRef().GetScrollSnapAlign()); } void LayoutBox::AddScrollSnapMapping() { @@ -647,9 +768,9 @@ int LayoutBox::PixelSnappedScrollHeight() const { PhysicalRect LayoutBox::ScrollRectToVisibleRecursive( const PhysicalRect& absolute_rect, - const WebScrollIntoViewParams& params) { - DCHECK(params.GetScrollType() == kProgrammaticScroll || - params.GetScrollType() == kUserScroll); + mojom::blink::ScrollIntoViewParamsPtr params) { + DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic || + params->type == mojom::blink::ScrollType::kUser); if (!GetFrameView()) return absolute_rect; @@ -659,12 +780,9 @@ PhysicalRect LayoutBox::ScrollRectToVisibleRecursive( // if the stop_at_main_frame_layout_viewport option is set. We do this so // that we can allow a smooth "scroll and zoom" animation to do the final // scroll in cases like scrolling a focused editable box into view. - if (params.stop_at_main_frame_layout_viewport && IsGlobalRootScroller()) + if (params->stop_at_main_frame_layout_viewport && IsGlobalRootScroller()) return absolute_rect; - // Presumably the same issue as in setScrollTop. See crbug.com/343132. - DisableCompositingQueryAsserts disabler; - PhysicalRect absolute_rect_to_scroll = absolute_rect; if (absolute_rect_to_scroll.Width() <= 0) absolute_rect_to_scroll.SetWidth(LayoutUnit(1)); @@ -677,11 +795,11 @@ PhysicalRect LayoutBox::ScrollRectToVisibleRecursive( parent_box = ContainingBlock(); PhysicalRect absolute_rect_for_parent; - if (!IsLayoutView() && HasOverflowClip()) { + if (!IsA<LayoutView>(this) && HasOverflowClip()) { absolute_rect_for_parent = GetScrollableArea()->ScrollIntoView(absolute_rect_to_scroll, params); } else if (!parent_box && CanBeProgramaticallyScrolled()) { - ScrollableArea* area_to_scroll = params.make_visible_in_visual_viewport + ScrollableArea* area_to_scroll = params->make_visible_in_visual_viewport ? GetFrameView()->GetScrollableArea() : GetFrameView()->LayoutViewport(); absolute_rect_for_parent = @@ -706,7 +824,7 @@ PhysicalRect LayoutBox::ScrollRectToVisibleRecursive( // have any effect, so we avoid using the RootFrameViewport and explicitly // scroll the visual viewport if we can. If not, we're done. if (StyleRef().GetPosition() == EPosition::kFixed && Container() == View() && - params.make_visible_in_visual_viewport) { + params->make_visible_in_visual_viewport) { if (GetFrame()->IsMainFrame()) { // TODO(donnd): We should continue the recursion if we're in a subframe. return GetFrame()->GetPage()->GetVisualViewport().ScrollIntoView( @@ -718,11 +836,11 @@ PhysicalRect LayoutBox::ScrollRectToVisibleRecursive( if (parent_box) { return parent_box->ScrollRectToVisibleRecursive(absolute_rect_for_parent, - params); + std::move(params)); } else if (GetFrame()->IsLocalRoot() && !GetFrame()->IsMainFrame()) { if (AllowedToPropagateRecursiveScrollToParentFrame(params)) { GetFrameView()->ScrollRectToVisibleInRemoteParent( - absolute_rect_for_parent, params); + absolute_rect_for_parent, std::move(params)); } } @@ -767,67 +885,74 @@ void LayoutBox::UpdateAfterLayout() { // e.g. an OOF-positioned object is laid out by an NG containing block, then // Legacy, then NG again, NG won't use a stale layout result. if (IsOutOfFlowPositioned() && !IsLayoutNGObject()) - cached_layout_result_.reset(); + ClearLayoutResults(); } bool LayoutBox::HasOverrideIntrinsicContentWidth() const { - const auto& style = StyleRef(); - const IntrinsicLength& intrinsic_length = style.IntrinsicWidth(); - if (intrinsic_length.IsLegacy()) + if (!ShouldApplySizeContainment()) return false; - if (intrinsic_length.IsAuto()) { - // If we have an overflow that is not 'visible' in this direction, then we - // override the intrinsic length to be 0. Otherwise, it's the same as - // legacy, meaning we don't override it. - // https://drafts.csswg.org/css-sizing-4/#valdef-intrinsic-block-size-auto - return style.OverflowX() != EOverflow::kVisible; - } - return true; + const Length& intrinsic_length = StyleRef().ContainIntrinsicSize().Width(); + return !intrinsic_length.IsAuto(); } bool LayoutBox::HasOverrideIntrinsicContentHeight() const { - const auto& style = StyleRef(); - const IntrinsicLength& intrinsic_length = style.IntrinsicHeight(); - if (intrinsic_length.IsLegacy()) + if (!ShouldApplySizeContainment()) return false; - if (intrinsic_length.IsAuto()) { - // If we have an overflow that is not 'visible' in this direction, then we - // override the intrinsic length to be 0. Otherwise, it's the same as - // legacy, meaning we don't override it. - // https://drafts.csswg.org/css-sizing-4/#valdef-intrinsic-block-size-auto - return style.OverflowY() != EOverflow::kVisible; - } - return true; + const Length& intrinsic_length = StyleRef().ContainIntrinsicSize().Height(); + return !intrinsic_length.IsAuto(); } LayoutUnit LayoutBox::OverrideIntrinsicContentWidth() const { DCHECK(HasOverrideIntrinsicContentWidth()); const auto& style = StyleRef(); - const IntrinsicLength& intrinsic_length = style.IntrinsicWidth(); - DCHECK(!intrinsic_length.IsLegacy()); - if (intrinsic_length.IsAuto()) { - DCHECK(style.OverflowX() != EOverflow::kVisible); - return LayoutUnit(); - } - DCHECK(intrinsic_length.GetLength().IsFixed()); - DCHECK_GE(intrinsic_length.GetLength().Value(), 0.f); - return LayoutUnit(intrinsic_length.GetLength().Value()); + const Length& intrinsic_length = style.ContainIntrinsicSize().Width(); + DCHECK(!intrinsic_length.IsAuto()); + DCHECK(intrinsic_length.IsFixed()); + DCHECK_GE(intrinsic_length.Value(), 0.f); + return LayoutUnit(intrinsic_length.Value()); } LayoutUnit LayoutBox::OverrideIntrinsicContentHeight() const { DCHECK(HasOverrideIntrinsicContentHeight()); const auto& style = StyleRef(); - const IntrinsicLength& intrinsic_length = style.IntrinsicHeight(); - DCHECK(!intrinsic_length.IsLegacy()); - if (intrinsic_length.IsAuto()) { - DCHECK(style.OverflowY() != EOverflow::kVisible); - return LayoutUnit(); + const Length& intrinsic_length = style.ContainIntrinsicSize().Height(); + DCHECK(!intrinsic_length.IsAuto()); + DCHECK(intrinsic_length.IsFixed()); + DCHECK_GE(intrinsic_length.Value(), 0.f); + return LayoutUnit(intrinsic_length.Value()); +} + +LayoutUnit LayoutBox::DefaultIntrinsicContentInlineSize() const { + // If the intrinsic-inline-size is specified, then we shouldn't ever need to + // get here. + DCHECK(!HasOverrideIntrinsicContentLogicalWidth()); + + auto* select = DynamicTo<HTMLSelectElement>(GetNode()); + if (UNLIKELY(select && select->UsesMenuList())) { + return MenuListIntrinsicInlineSize(*select, *this); + } + auto* input = DynamicTo<HTMLInputElement>(GetNode()); + if (UNLIKELY(input && input->type() == input_type_names::kFile)) + return FileUploadControlIntrinsicInlineSize(*input, *this); + return kIndefiniteSize; +} + +LayoutUnit LayoutBox::DefaultIntrinsicContentBlockSize() const { + // If the intrinsic-block-size is specified, then we shouldn't ever need to + // get here. + DCHECK(!HasOverrideIntrinsicContentLogicalHeight()); + + if (const auto* select = DynamicTo<HTMLSelectElement>(GetNode())) { + if (select->UsesMenuList()) { + return MenuListIntrinsicBlockSize(*select, *this); + } else { + return ListBoxItemHeight(*select, *this) * select->ListBoxSize() - + ScrollbarLogicalHeight(); + } } - DCHECK(intrinsic_length.GetLength().IsFixed()); - DCHECK_GE(intrinsic_length.GetLength().Value(), 0.f); - return LayoutUnit(intrinsic_length.GetLength().Value()); + return kIndefiniteSize; } LayoutUnit LayoutBox::LogicalHeightWithVisibleOverflow() const { @@ -844,7 +969,7 @@ LayoutUnit LayoutBox::ConstrainLogicalWidthByMinMax( LayoutUnit available_width, const LayoutBlock* cb) const { const ComputedStyle& style_to_use = StyleRef(); - if (!style_to_use.LogicalMaxWidth().IsMaxSizeNone()) + if (!style_to_use.LogicalMaxWidth().IsNone()) logical_width = std::min( logical_width, ComputeLogicalWidthUsing(kMaxSize, style_to_use.LogicalMaxWidth(), @@ -860,8 +985,7 @@ LayoutUnit LayoutBox::ConstrainLogicalHeightByMinMax( // Note that the values 'min-content', 'max-content' and 'fit-content' should // behave as the initial value if specified in the block direction. const Length& logical_max_height = StyleRef().LogicalMaxHeight(); - if (!logical_max_height.IsMaxSizeNone() && - !logical_max_height.IsMinContent() && + if (!logical_max_height.IsNone() && !logical_max_height.IsMinContent() && !logical_max_height.IsMaxContent() && !logical_max_height.IsFitContent()) { LayoutUnit max_h = ComputeLogicalHeightUsing(kMaxSize, logical_max_height, @@ -885,7 +1009,7 @@ LayoutUnit LayoutBox::ConstrainContentBoxLogicalHeightByMinMax( // advantage of already knowing the current resolved percentage height // to avoid recursing up through our containing blocks again to determine it. const ComputedStyle& style_to_use = StyleRef(); - if (!style_to_use.LogicalMaxHeight().IsMaxSizeNone()) { + if (!style_to_use.LogicalMaxHeight().IsNone()) { if (style_to_use.LogicalMaxHeight().IsPercent() && style_to_use.LogicalHeight().IsPercent()) { LayoutUnit available_logical_height( @@ -1083,9 +1207,9 @@ void LayoutBox::Autoscroll(const PhysicalOffset& position_in_root_frame) { ScrollRectToVisibleRecursive( PhysicalRect(absolute_position, PhysicalSize(LayoutUnit(1), LayoutUnit(1))), - WebScrollIntoViewParams(ScrollAlignment::kAlignToEdgeIfNeeded, - ScrollAlignment::kAlignToEdgeIfNeeded, - kUserScroll)); + ScrollAlignment::CreateScrollIntoViewParams( + ScrollAlignment::ToEdgeIfNeeded(), ScrollAlignment::ToEdgeIfNeeded(), + mojom::blink::ScrollType::kUser)); } bool LayoutBox::CanAutoscroll() const { @@ -1155,11 +1279,6 @@ LayoutBox* LayoutBox::FindAutoscrollable(LayoutObject* layout_object, : nullptr; } -void LayoutBox::MayUpdateHoverWhenContentUnderMouseChanged( - EventHandler& event_handler) { - event_handler.MayUpdateHoverAfterScroll(AbsoluteBoundingBoxFloatRect()); -} - void LayoutBox::ScrollByRecursively(const ScrollOffset& delta) { if (delta.IsZero() || !HasOverflowClip()) return; @@ -1168,7 +1287,8 @@ void LayoutBox::ScrollByRecursively(const ScrollOffset& delta) { DCHECK(scrollable_area); ScrollOffset new_scroll_offset = scrollable_area->GetScrollOffset() + delta; - scrollable_area->SetScrollOffset(new_scroll_offset, kProgrammaticScroll); + scrollable_area->SetScrollOffset(new_scroll_offset, + mojom::blink::ScrollType::kProgrammatic); // If this layer can't do the scroll we ask the next layer up that can // scroll to try. @@ -1336,7 +1456,7 @@ bool LayoutBox::MapContentsRectToBoxSpace( } bool LayoutBox::ContainedContentsScroll(const LayoutObject& contents) const { - if (IsLayoutView() && + if (IsA<LayoutView>(this) && contents.StyleRef().GetPosition() == EPosition::kFixed) { return false; } @@ -1366,40 +1486,23 @@ bool LayoutBox::ApplyBoxClips( return does_intersect; } -void LayoutBox::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - min_logical_width = - MinPreferredLogicalWidth() - BorderAndPaddingLogicalWidth(); - max_logical_width = - MaxPreferredLogicalWidth() - BorderAndPaddingLogicalWidth(); +MinMaxSizes LayoutBox::PreferredLogicalWidths() const { + NOTREACHED(); + return MinMaxSizes(); } -LayoutUnit LayoutBox::MinPreferredLogicalWidth() const { - if (PreferredLogicalWidthsDirty()) { -#if DCHECK_IS_ON() - SetLayoutNeededForbiddenScope layout_forbidden_scope( - const_cast<LayoutBox&>(*this)); -#endif - const_cast<LayoutBox*>(this)->ComputePreferredLogicalWidths(); - DCHECK(!PreferredLogicalWidthsDirty()); - } - - return min_preferred_logical_width_; -} +void LayoutBox::UpdateCachedIntrinsicLogicalWidthsIfNeeded() { + if (!IntrinsicLogicalWidthsDirty()) + return; -DISABLE_CFI_PERF -LayoutUnit LayoutBox::MaxPreferredLogicalWidth() const { - if (PreferredLogicalWidthsDirty()) { #if DCHECK_IS_ON() - SetLayoutNeededForbiddenScope layout_forbidden_scope( - const_cast<LayoutBox&>(*this)); + SetLayoutNeededForbiddenScope layout_forbidden_scope(*this); #endif - const_cast<LayoutBox*>(this)->ComputePreferredLogicalWidths(); - DCHECK(!PreferredLogicalWidthsDirty()); - } - return max_preferred_logical_width_; + intrinsic_logical_widths_ = ComputeIntrinsicLogicalWidths(); + intrinsic_logical_widths_percentage_resolution_block_size_ = + LayoutUnit::Min(); + ClearIntrinsicLogicalWidthsDirty(); } LayoutUnit LayoutBox::OverrideLogicalWidth() const { @@ -1771,7 +1874,7 @@ bool LayoutBox::GetBackgroundPaintedExtent(PhysicalRect& painted_extent) const { // LayoutView is special in the sense that it expands to the whole canvas, // thus can't be handled by this function. - DCHECK(!IsLayoutView()); + DCHECK(!IsA<LayoutView>(this)); PhysicalRect background_rect(PhysicalBorderBoxRect()); @@ -1901,7 +2004,7 @@ bool LayoutBox::ComputeBackgroundIsKnownToBeObscured() const { if (!StyleRef().HasBackground()) return false; // Root background painting is special. - if (IsLayoutView()) + if (IsA<LayoutView>(this)) return false; // FIXME: box-shadow is painted while background painting. if (StyleRef().BoxShadow()) @@ -2020,10 +2123,6 @@ void LayoutBox::SizeChanged() { // object for paint invalidation. if (!NeedsLayout()) SetShouldCheckForPaintInvalidation(); - - if (auto* element = DynamicTo<Element>(GetNode())) { - element->SetNeedsResizeObserverUpdate(); - } } bool LayoutBox::IntersectsVisibleViewport() const { @@ -2089,12 +2188,33 @@ PhysicalRect LayoutBox::OverflowClipRect( if (HasOverflowClip()) ExcludeScrollbars(clip_rect, overlay_scrollbar_clip_behavior); - if (HasControlClip()) - clip_rect.Intersect(ControlClipRect(location)); + auto* input = DynamicTo<HTMLInputElement>(GetNode()); + if (UNLIKELY(input)) { + // As for LayoutButton, ControlClip is to for not BUTTONs but INPUT + // buttons for IE/Firefox compatibility. + if (IsTextField() || IsLayoutButton()) { + DCHECK(HasControlClip()); + PhysicalRect control_clip = PhysicalPaddingBoxRect(); + control_clip.Move(location); + clip_rect.Intersect(control_clip); + } + } else if (UNLIKELY(IsMenuList(this))) { + DCHECK(HasControlClip()); + PhysicalRect control_clip = PhysicalContentBoxRect(); + control_clip.Move(location); + clip_rect.Intersect(control_clip); + } else { + DCHECK(!HasControlClip()); + } return clip_rect; } +bool LayoutBox::HasControlClip() const { + return UNLIKELY(IsTextField() || IsFileUploadControl() || IsMenuList(this) || + (IsLayoutButton() && IsA<HTMLInputElement>(GetNode()))); +} + void LayoutBox::ExcludeScrollbars( PhysicalRect& rect, OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const { @@ -2343,6 +2463,17 @@ void LayoutBox::DirtyLineBoxes(bool full_layout) { } } +bool LayoutBox::HasInlineFragments() const { + if (!IsInLayoutNGInlineFormattingContext()) + return inline_box_wrapper_; + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return first_paint_fragment_; + // TODO(yosin): We should use |first_fragment_item_index_|. + NGInlineCursor cursor; + cursor.MoveTo(*this); + return cursor; +} + void LayoutBox::SetFirstInlineFragment(NGPaintFragment* fragment) { CHECK(IsInLayoutNGInlineFormattingContext()) << *this; // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of @@ -2361,7 +2492,9 @@ void LayoutBox::SetFirstInlineFragmentItemIndex(wtf_size_t index) { CHECK(IsInLayoutNGInlineFormattingContext()) << *this; DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); DCHECK_NE(index, 0u); - first_fragment_item_index_ = index; + // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|, + // we should enable below. + // first_fragment_item_index_ = index; } void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) { @@ -2372,33 +2505,76 @@ void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) { DCHECK(new_value ? !first_paint_fragment_ : !inline_box_wrapper_); } -void LayoutBox::SetCachedLayoutResult(const NGLayoutResult& layout_result, - const NGBreakToken* break_token) { - DCHECK_EQ(layout_result.Status(), NGLayoutResult::kSuccess); +void LayoutBox::SetCachedLayoutResult( + scoped_refptr<const NGLayoutResult> result) { + DCHECK(!result->PhysicalFragment().BreakToken()); + DCHECK(!result->IsSingleUse()); - if (break_token) - return; - if (layout_result.PhysicalFragment().BreakToken()) - return; + if (result->GetConstraintSpaceForCaching().CacheSlot() == + NGCacheSlot::kMeasure) { + if (measure_result_) + InvalidateItems(*measure_result_); + measure_result_ = result; + // When setting the "measure" result we also set the "layout" result. + } - ClearCachedLayoutResult(); - cached_layout_result_ = &layout_result; + AddLayoutResult(std::move(result), 0); } -void LayoutBox::ClearCachedLayoutResult() { - if (!cached_layout_result_) - return; +void LayoutBox::AddLayoutResult(scoped_refptr<const NGLayoutResult> result, + wtf_size_t index) { + DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess); + if (index != WTF::kNotFound) + ShrinkLayoutResults(index); + layout_results_.push_back(std::move(result)); +} +void LayoutBox::ClearLayoutResults() { + if (measure_result_) + InvalidateItems(*measure_result_); + measure_result_ = nullptr; + + ShrinkLayoutResults(0); +} + +void LayoutBox::ShrinkLayoutResults(wtf_size_t results_to_keep) { + DCHECK_GE(layout_results_.size(), results_to_keep); // Invalidate if inline |DisplayItemClient|s will be destroyed. - if (const auto* box_fragment = DynamicTo<NGPhysicalBoxFragment>( - &cached_layout_result_->PhysicalFragment())) { - if (box_fragment->HasItems()) { - DCHECK_EQ(this, box_fragment->GetLayoutObject()); - ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint(); - } - } + for (wtf_size_t i = results_to_keep; i < layout_results_.size(); i++) + InvalidateItems(*layout_results_[i]); + layout_results_.Shrink(results_to_keep); +} + +void LayoutBox::InvalidateItems(const NGLayoutResult& result) { + // Invalidate if inline |DisplayItemClient|s will be destroyed. + const auto& box_fragment = + To<NGPhysicalBoxFragment>(result.PhysicalFragment()); + if (!box_fragment.HasItems()) + return; + + DCHECK_EQ(this, box_fragment.GetLayoutObject()); + ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint(); +} + +const NGLayoutResult* LayoutBox::GetCachedLayoutResult() const { + if (layout_results_.IsEmpty()) + return nullptr; + // Only return re-usable results. + const NGLayoutResult* result = layout_results_[0].get(); + if (result->IsSingleUse()) + return nullptr; + DCHECK_EQ(layout_results_.size(), 1u); + return result; +} + +const NGLayoutResult* LayoutBox::GetCachedMeasureResult() const { + if (!measure_result_) + return nullptr; + + if (measure_result_->IsSingleUse()) + return nullptr; - cached_layout_result_ = nullptr; + return measure_result_.get(); } scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( @@ -2409,10 +2585,12 @@ scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( NGLayoutCacheStatus* out_cache_status) { *out_cache_status = NGLayoutCacheStatus::kNeedsLayout; - if (!RuntimeEnabledFeatures::LayoutNGFragmentCachingEnabled()) - return nullptr; + const NGLayoutResult* cached_layout_result = + new_space.CacheSlot() == NGCacheSlot::kLayout && + !layout_results_.IsEmpty() + ? GetCachedLayoutResult() + : GetCachedMeasureResult(); - const NGLayoutResult* cached_layout_result = GetCachedLayoutResult(); if (!cached_layout_result) return nullptr; @@ -2509,8 +2687,7 @@ scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( bool is_exclusion_space_equal = new_space.ExclusionSpace() == old_space.ExclusionSpace(); - bool is_new_formatting_context = - physical_fragment.IsBlockFormattingContextRoot(); + bool is_new_formatting_context = physical_fragment.IsFormattingContextRoot(); // If a node *doesn't* establish a new formatting context it may be affected // by floats, or clearance. @@ -2545,7 +2722,7 @@ scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( // "simplified" layout then abort now. *out_cache_status = cache_status; if (*out_cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout) - return nullptr; + return cached_layout_result; physical_fragment.CheckType(); @@ -2587,11 +2764,29 @@ scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult( bfc_block_offset, block_offset_delta)); if (needs_cached_result_update) - SetCachedLayoutResult(*new_result, break_token); + SetCachedLayoutResult(new_result); return new_result; } +const NGPhysicalBoxFragment* LayoutBox::GetPhysicalFragment( + wtf_size_t index) const { + return &To<NGPhysicalBoxFragment>(layout_results_[index]->PhysicalFragment()); +} + +const FragmentData* LayoutBox::FragmentDataFromPhysicalFragment( + const NGPhysicalBoxFragment& physical_fragment) const { + const FragmentData* fragment_data = &FirstFragment(); + for (const auto& result : layout_results_) { + if (&result->PhysicalFragment() == &physical_fragment) + return fragment_data; + DCHECK(fragment_data->NextFragment()); + fragment_data = fragment_data->NextFragment(); + } + NOTREACHED(); + return fragment_data; +} + void LayoutBox::PositionLineBox(InlineBox* box) { if (IsOutOfFlowPositioned()) { // Cache the x position only if we were an INLINE type originally. @@ -2772,27 +2967,6 @@ bool LayoutBox::NeedsForcedBreakBefore( return IsForcedFragmentainerBreakValue(break_value); } -bool LayoutBox::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - if (HasNonCompositedScrollbars() || IsSelected() || - HasBoxDecorationBackground() || StyleRef().HasBoxDecorations() || - StyleRef().HasVisualOverflowingEffect()) - return false; - - // Both mask and clip-path generates drawing display items that depends on - // the size of the box. - if (HasMask() || HasClipPath()) - return false; - - // If the box paints into its own backing, we can assume that it's painting - // may have some effect. For example, honoring the border-radius clip on - // a composited child paints into a mask for an otherwise non-painting - // element, because children of that element will require the mask. - if (HasLayer() && Layer()->GetCompositingState() == kPaintsIntoOwnBacking) - return false; - - return true; -} - PhysicalRect LayoutBox::LocalVisualRectIgnoringVisibility() const { return PhysicalSelfVisualOverflowRect(); } @@ -2900,10 +3074,10 @@ bool LayoutBox::MapToVisualRectInAncestorSpaceInternal( return true; } - if (container->IsLayoutView()) { + if (auto* layout_view = DynamicTo<LayoutView>(container)) { bool use_fixed_position_adjustment = position == EPosition::kFixed && container == ancestor; - return ToLayoutView(container)->MapToVisualRectInAncestorSpaceInternal( + return layout_view->MapToVisualRectInAncestorSpaceInternal( ancestor, transform_state, use_fixed_position_adjustment ? kIsFixed : 0, visual_rect_flags); } else { @@ -2926,7 +3100,7 @@ void LayoutBox::InflateVisualRectForFilter( static bool ShouldRecalculateMinMaxWidthsAffectedByAncestor( const LayoutBox* box) { - if (box->PreferredLogicalWidthsDirty()) { + if (box->IntrinsicLogicalWidthsDirty()) { // If the preferred widths are already dirty at this point (during layout), // it actually means that we never need to calculate them, since that should // have been carried out by an ancestor that's sized based on preferred @@ -2937,7 +3111,7 @@ static bool ShouldRecalculateMinMaxWidthsAffectedByAncestor( } if (const LayoutBox* containing_block = box->ContainingBlock()) { if (containing_block->NeedsPreferredWidthsRecalculation() && - !containing_block->PreferredLogicalWidthsDirty()) { + !containing_block->IntrinsicLogicalWidthsDirty()) { // If our containing block also has min/max widths that are affected by // the ancestry, we have already dealt with this object as well. Avoid // unnecessary work and O(n^2) time complexity. @@ -2956,14 +3130,14 @@ void LayoutBox::UpdateLogicalWidth() { // bottom-up, but that's not always the case), so since the containing // block size may have changed, we need to recalculate the min/max widths // of this object, and every child that has the same issue, recursively. - SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); // Since all this takes place during actual layout, instead of being part // of min/max the width calculation machinery, we need to enter said - // machinery here, to make sure that what was dirtied is actualy + // machinery here, to make sure that what was dirtied is actually // recalculated. Leaving things dirty would mean that any subsequent // dirtying of descendants would fail. - ComputePreferredLogicalWidths(); + UpdateCachedIntrinsicLogicalWidthsIfNeeded(); } } @@ -3058,7 +3232,7 @@ void LayoutBox::ComputeLogicalWidth( if (treat_as_replaced) { computed_values.extent_ = std::max( ComputeReplacedLogicalWidth() + BorderAndPaddingLogicalWidth(), - MinPreferredLogicalWidth()); + PreferredLogicalWidths().min_size); } return; } @@ -3163,33 +3337,27 @@ LayoutUnit LayoutBox::FillAvailableMeasure(LayoutUnit available_logical_width, DISABLE_CFI_PERF LayoutUnit LayoutBox::ComputeIntrinsicLogicalWidthUsing( const Length& logical_width_length, - LayoutUnit available_logical_width, - LayoutUnit border_and_padding) const { + LayoutUnit available_logical_width) const { if (logical_width_length.IsFillAvailable()) { if (!IsA<HTMLMarqueeElement>(GetNode())) { UseCounter::Count(GetDocument(), WebFeature::kCSSFillAvailableLogicalWidth); } - return std::max(border_and_padding, + return std::max(BorderAndPaddingLogicalWidth(), FillAvailableMeasure(available_logical_width)); } - LayoutUnit min_logical_width; - LayoutUnit max_logical_width; - ComputeIntrinsicLogicalWidths(min_logical_width, max_logical_width); + MinMaxSizes sizes = IntrinsicLogicalWidths(); if (logical_width_length.IsMinContent()) - return min_logical_width + border_and_padding; + return sizes.min_size; if (logical_width_length.IsMaxContent()) - return max_logical_width + border_and_padding; + return sizes.max_size; if (logical_width_length.IsFitContent()) { - min_logical_width += border_and_padding; - max_logical_width += border_and_padding; - return std::max(min_logical_width, - std::min(max_logical_width, - FillAvailableMeasure(available_logical_width))); + return sizes.ClampSizeToMinAndMax( + FillAvailableMeasure(available_logical_width)); } NOTREACHED(); @@ -3214,9 +3382,10 @@ LayoutUnit LayoutBox::ComputeLogicalWidthUsing( ValueForLength(logical_width, available_logical_width)); } - if (logical_width.IsIntrinsic()) - return ComputeIntrinsicLogicalWidthUsing( - logical_width, available_logical_width, BorderAndPaddingLogicalWidth()); + if (logical_width.IsIntrinsic()) { + return ComputeIntrinsicLogicalWidthUsing(logical_width, + available_logical_width); + } LayoutUnit margin_start; LayoutUnit margin_end; @@ -3238,9 +3407,9 @@ LayoutUnit LayoutBox::ComputeLogicalWidthUsing( // TODO(crbug.com/710026): Remove const_cast LayoutUnit w = LogicalWidth(); const_cast<LayoutBox*>(this)->SetLogicalWidth(LayoutUnit()); + MinMaxSizes preferred_logical_widths = PreferredLogicalWidths(); LayoutUnit result = - std::max(MinPreferredLogicalWidth(), - std::min(MaxPreferredLogicalWidth(), logical_width_result)); + preferred_logical_widths.ClampSizeToMinAndMax(logical_width_result); const_cast<LayoutBox*>(this)->SetLogicalWidth(w); return result; } @@ -3513,10 +3682,16 @@ void LayoutBox::ComputeLogicalHeight( if (HasOverrideIntrinsicContentLogicalHeight()) { height = OverrideIntrinsicContentLogicalHeight() + BorderAndPaddingLogicalHeight() + ScrollbarLogicalHeight(); - } else if (ShouldApplySizeContainment() && !IsLayoutGrid()) { - height = BorderAndPaddingLogicalHeight() + ScrollbarLogicalHeight(); } else { - height = LogicalHeight(); + LayoutUnit default_height = DefaultIntrinsicContentBlockSize(); + if (default_height != kIndefiniteSize) { + height = default_height + BorderAndPaddingLogicalHeight() + + ScrollbarLogicalHeight(); + } else if (ShouldApplySizeContainment() && !IsLayoutGrid()) { + height = BorderAndPaddingLogicalHeight() + ScrollbarLogicalHeight(); + } else { + height = LogicalHeight(); + } } ComputeLogicalHeight(height, LogicalTop(), computed_values); } @@ -3564,22 +3739,13 @@ void LayoutBox::ComputeLogicalHeight( return; } - // FIXME: Account for writing-mode in flexible boxes. - // https://bugs.webkit.org/show_bug.cgi?id=46418 - bool in_horizontal_box = - Parent()->IsDeprecatedFlexibleBox() && - Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal; - bool stretching = - Parent()->StyleRef().BoxAlign() == EBoxAlignment::kStretch; - bool treat_as_replaced = - ShouldComputeSizeAsReplaced() && (!in_horizontal_box || !stretching); bool check_min_max_height = false; // The parent box is flexing us, so it has increased or decreased our // height. We have to grab our cached flexible height. if (HasOverrideLogicalHeight()) { h = Length::Fixed(OverrideLogicalHeight()); - } else if (treat_as_replaced) { + } else if (ShouldComputeSizeAsReplaced()) { h = Length::Fixed(ComputeReplacedLogicalHeight() + BorderAndPaddingLogicalHeight()); } else { @@ -3587,16 +3753,6 @@ void LayoutBox::ComputeLogicalHeight( check_min_max_height = true; } - // Block children of horizontal flexible boxes fill the height of the box. - // FIXME: Account for writing-mode in flexible boxes. - // https://bugs.webkit.org/show_bug.cgi?id=46418 - if (h.IsAuto() && in_horizontal_box && - ToLayoutDeprecatedFlexibleBox(Parent())->IsStretchingChildren()) { - h = Length::Fixed(ParentBox()->ContentLogicalHeight() - MarginBefore() - - MarginAfter()); - check_min_max_height = false; - } - LayoutUnit height_result; if (check_min_max_height) { height_result = ComputeLogicalHeightUsing( @@ -3793,7 +3949,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPercentageResolution( const LayoutBox* containing_block_child = this; bool skipped_auto_height_containing_block = false; LayoutUnit root_margin_border_padding_height; - while (!cb->IsLayoutView() && + while (!IsA<LayoutView>(cb) && (IsHorizontalWritingMode() == cb->IsHorizontalWritingMode() && SkipContainingBlockForPercentHeightCalculation(cb))) { if ((cb->IsBody() || cb->IsDocumentElement()) && @@ -3931,7 +4087,7 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalWidthRespectingMinMaxWidth( LayoutUnit max_logical_width = (should_compute_preferred == kComputePreferred && StyleRef().LogicalMaxWidth().IsPercentOrCalc()) || - StyleRef().LogicalMaxWidth().IsMaxSizeNone() + StyleRef().LogicalMaxWidth().IsNone() ? logical_width : ComputeReplacedLogicalWidthUsing(kMaxSize, StyleRef().LogicalMaxWidth()); @@ -3955,8 +4111,7 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalWidthUsing( // MinContent/MaxContent don't need the availableLogicalWidth argument. LayoutUnit available_logical_width; return ComputeIntrinsicLogicalWidthUsing(logical_width, - available_logical_width, - BorderAndPaddingLogicalWidth()) - + available_logical_width) - BorderAndPaddingLogicalWidth(); } case Length::kFitContent: @@ -3978,8 +4133,7 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalWidthUsing( // FIXME: Handle cases when containing block width is calculated or // viewport percent. https://bugs.webkit.org/show_bug.cgi?id=91071 if (logical_width.IsIntrinsic()) - return ComputeIntrinsicLogicalWidthUsing( - logical_width, cw, BorderAndPaddingLogicalWidth()) - + return ComputeIntrinsicLogicalWidthUsing(logical_width, cw) - BorderAndPaddingLogicalWidth(); if (cw > 0 || (!cw && (container_logical_width.IsFixed() || container_logical_width.IsPercentOrCalc()))) @@ -3988,7 +4142,7 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalWidthUsing( return LayoutUnit(); } case Length::kAuto: - case Length::kMaxSizeNone: + case Length::kNone: return IntrinsicLogicalWidth(); case Length::kExtendToZoom: case Length::kDeviceWidth: @@ -4025,6 +4179,11 @@ bool LayoutBox::LogicalHeightComputesAsNone(SizeType size_type) const { if (logical_height == initial_logical_height) return true; + if (logical_height.IsPercentOrCalc() && + HasOverrideContainingBlockContentLogicalHeight() && + OverrideContainingBlockContentLogicalHeight() == kIndefiniteSize) + return true; + // CustomLayout items can resolve their percentages against an available or // percentage size override. if (IsCustomItem() && (HasOverrideContainingBlockContentLogicalHeight() || @@ -4114,7 +4273,7 @@ LayoutUnit LayoutBox::ComputeReplacedLogicalHeightUsing( // FIXME: This needs to be made writing-mode-aware. If the cell and // image are perpendicular writing-modes, this isn't right. // https://bugs.webkit.org/show_bug.cgi?id=46997 - while (cb && !cb->IsLayoutView() && + while (!IsA<LayoutView>(cb) && (cb->StyleRef().LogicalHeight().IsAuto() || cb->StyleRef().LogicalHeight().IsPercentOrCalc())) { if (cb->IsTableCell()) { @@ -4173,10 +4332,10 @@ LayoutUnit LayoutBox::AvailableLogicalHeight( LayoutUnit LayoutBox::AvailableLogicalHeightUsing( const Length& h, AvailableLogicalHeightType height_type) const { - if (IsLayoutView()) { + if (auto* layout_view = DynamicTo<LayoutView>(this)) { return LayoutUnit(IsHorizontalWritingMode() - ? ToLayoutView(this)->GetFrameView()->Size().Height() - : ToLayoutView(this)->GetFrameView()->Size().Width()); + ? layout_view->GetFrameView()->Size().Height() + : layout_view->GetFrameView()->Size().Width()); } // We need to stop here, since we don't want to increase the height of the @@ -4192,10 +4351,17 @@ LayoutUnit LayoutBox::AvailableLogicalHeightUsing( return LogicalHeight() - BorderAndPaddingLogicalHeight(); } - if (IsFlexItem()) { - const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); - if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this)) - return OverrideContentLogicalHeight(); + if (IsFlexItemIncludingNG()) { + if (IsFlexItem()) { + const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); + if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this)) + return OverrideContentLogicalHeight(); + } else if (GetCachedLayoutResult()) { + const NGConstraintSpace& space = + GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + if (space.IsFixedBlockSize() && !space.IsFixedBlockSizeIndefinite()) + return space.AvailableSize().block_size; + } } if (h.IsPercentOrCalc() && IsOutOfFlowPositioned()) { @@ -4271,9 +4437,9 @@ LayoutUnit LayoutBox::ContainingBlockLogicalWidthForPositioned( return ContainingBlockLogicalHeightForPositioned(containing_block, false); // Use viewport as container for top-level fixed-position elements. - if (StyleRef().GetPosition() == EPosition::kFixed && - containing_block->IsLayoutView() && !GetDocument().Printing()) { - const LayoutView* view = ToLayoutView(containing_block); + const auto* view = DynamicTo<LayoutView>(containing_block); + if (StyleRef().GetPosition() == EPosition::kFixed && view && + !GetDocument().Printing()) { if (LocalFrameView* frame_view = view->GetFrameView()) { // Don't use visibleContentRect since the PaintLayer's size has not been // set yet. @@ -4342,9 +4508,9 @@ LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPositioned( return ContainingBlockLogicalWidthForPositioned(containing_block, false); // Use viewport as container for top-level fixed-position elements. - if (StyleRef().GetPosition() == EPosition::kFixed && - containing_block->IsLayoutView() && !GetDocument().Printing()) { - const LayoutView* view = ToLayoutView(containing_block); + const auto* view = DynamicTo<LayoutView>(containing_block); + if (StyleRef().GetPosition() == EPosition::kFixed && view && + !GetDocument().Printing()) { if (LocalFrameView* frame_view = view->GetFrameView()) { // Don't use visibleContentRect since the PaintLayer's size has not been // set yet. @@ -4368,7 +4534,7 @@ LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPositioned( const LayoutInline* flow = ToLayoutInline(containing_block); // If the containing block is empty, return a height of 0. - if (flow->IsEmpty()) + if (!flow->HasInlineFragments()) return LayoutUnit(); LayoutUnit height_result; @@ -4583,7 +4749,7 @@ void LayoutBox::ComputePositionedLogicalWidth( margin_logical_right, computed_values); // Calculate constraint equation values for 'max-width' case. - if (!StyleRef().LogicalMaxWidth().IsMaxSizeNone()) { + if (!StyleRef().LogicalMaxWidth().IsNone()) { LogicalExtentComputedValues max_values; ComputePositionedLogicalWidthUsing( @@ -4653,13 +4819,9 @@ void LayoutBox::ComputeLogicalLeftPositionedOffset( LayoutUnit LayoutBox::ShrinkToFitLogicalWidth( LayoutUnit available_logical_width, LayoutUnit borders_plus_padding) const { - LayoutUnit preferred_logical_width = - MaxPreferredLogicalWidth() - borders_plus_padding; - LayoutUnit preferred_min_logical_width = - MinPreferredLogicalWidth() - borders_plus_padding; - return std::min( - std::max(preferred_min_logical_width, available_logical_width), - preferred_logical_width); + MinMaxSizes sizes = PreferredLogicalWidths(); + sizes -= borders_plus_padding; + return sizes.ShrinkToFit(available_logical_width); } void LayoutBox::ComputePositionedLogicalWidthUsing( @@ -4678,16 +4840,16 @@ void LayoutBox::ComputePositionedLogicalWidthUsing( DCHECK(width_size_type == kMinSize || width_size_type == kMainOrPreferredSize || !logical_width.IsAuto()); - if (width_size_type == kMinSize && logical_width.IsAuto()) + if (width_size_type == kMinSize && logical_width.IsAuto()) { logical_width_value = LayoutUnit(); - else if (logical_width.IsIntrinsic()) - logical_width_value = - ComputeIntrinsicLogicalWidthUsing( - logical_width, container_logical_width, borders_plus_padding) - - borders_plus_padding; - else + } else if (logical_width.IsIntrinsic()) { + logical_width_value = ComputeIntrinsicLogicalWidthUsing( + logical_width, container_logical_width) - + borders_plus_padding; + } else { logical_width_value = AdjustContentBoxLogicalWidthForBoxSizing( ValueForLength(logical_width, container_logical_width)); + } // 'left' and 'right' cannot both be 'auto' because one would of been // converted to the static position already @@ -4996,8 +5158,7 @@ void LayoutBox::ComputePositionedLogicalHeight( // Calculate constraint equation values for 'max-height' case. const Length& logical_max_height = style_to_use.LogicalMaxHeight(); - if (!logical_max_height.IsMaxSizeNone() && - !logical_max_height.IsMinContent() && + if (!logical_max_height.IsNone() && !logical_max_height.IsMinContent() && !logical_max_height.IsMaxContent() && !logical_max_height.IsFitContent()) { LogicalExtentComputedValues max_values; @@ -5695,7 +5856,7 @@ void LayoutBox::AddLayoutOverflow(const LayoutRect& rect) { // For overflow clip objects, we don't want to propagate overflow into // unreachable areas. LayoutRect overflow_rect(rect); - if (HasOverflowClip() || IsLayoutView()) { + if (HasOverflowClip() || IsA<LayoutView>(this)) { // Overflow is in the block's coordinate space and thus is flipped for // vertical-rl writing // mode. At this stage that is actually a simplification, since we can @@ -5813,7 +5974,7 @@ bool LayoutBox::HasUnsplittableScrollingOverflow() const { // world and is what we used to do in the old model anyway. return !StyleRef().LogicalHeight().IsIntrinsicOrAuto() || (!StyleRef().LogicalMaxHeight().IsIntrinsicOrAuto() && - !StyleRef().LogicalMaxHeight().IsMaxSizeNone() && + !StyleRef().LogicalMaxHeight().IsNone() && (!StyleRef().LogicalMaxHeight().IsPercentOrCalc() || PercentageLogicalHeightIsResolvable())) || (!StyleRef().LogicalMinHeight().IsIntrinsicOrAuto() && @@ -6029,7 +6190,7 @@ static void MarkBoxForRelayoutAfterSplit(LayoutBox* box) { ToInterface<LayoutNGTableSectionInterface>(box)->SetNeedsCellRecalc(); } - box->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + box->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); } @@ -6336,11 +6497,11 @@ void LayoutBox::ReassignSnapAreas(LayoutBox& new_container) { } bool LayoutBox::AllowedToPropagateRecursiveScrollToParentFrame( - const WebScrollIntoViewParams& params) { + const mojom::blink::ScrollIntoViewParamsPtr& params) { if (!GetFrameView()->SafeToPropagateScrollToParent()) return false; - if (params.GetScrollType() != kProgrammaticScroll) + if (params->type != mojom::blink::ScrollType::kProgrammatic) return true; return !GetDocument().IsVerticalScrollEnforced(); @@ -6420,7 +6581,7 @@ TextDirection LayoutBox::ResolvedDirection() const { NGInlineCursor cursor; cursor.MoveTo(*this); if (cursor) - return cursor.CurrentResolvedDirection(); + return cursor.Current().ResolvedDirection(); } if (InlineBoxWrapper()) return InlineBoxWrapper()->Direction(); @@ -6428,4 +6589,15 @@ TextDirection LayoutBox::ResolvedDirection() const { return StyleRef().Direction(); } +bool LayoutBox::NeedsScrollNode( + CompositingReasons direct_compositing_reasons) const { + if (!HasOverflowClip()) + return false; + + if (direct_compositing_reasons & CompositingReason::kRootScroller) + return true; + + return GetScrollableArea()->ScrollsOverflow(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box.h b/chromium/third_party/blink/renderer/core/layout/layout_box.h index d7c8e1e3355..a9484751df2 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_box.h @@ -26,16 +26,16 @@ #include <memory> #include "base/macros.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/overflow_model.h" #include "third_party/blink/renderer/platform/graphics/scroll_types.h" namespace blink { class CustomLayoutChild; -class EventHandler; class LayoutBlockFlow; class LayoutMultiColumnSpannerPlaceholder; class NGBoxFragmentBuilder; @@ -49,7 +49,6 @@ enum class NGLayoutCacheStatus; class NGLayoutResult; struct NGPhysicalBoxStrut; struct PaintInfo; -struct WebScrollIntoViewParams; enum SizeType { kMainOrPreferredSize, kMinSize, kMaxSize }; enum AvailableLogicalHeightType { @@ -66,12 +65,12 @@ enum ShouldComputePreferred { kComputeActual, kComputePreferred }; using SnapAreaSet = HashSet<LayoutBox*>; -struct LayoutBoxRareData { - USING_FAST_MALLOC(LayoutBoxRareData); - +struct LayoutBoxRareData final : public GarbageCollected<LayoutBoxRareData> { public: LayoutBoxRareData(); + void Trace(Visitor* visitor); + // For spanners, the spanner placeholder that lays us out within the multicol // container. LayoutMultiColumnSpannerPlaceholder* spanner_placeholder_; @@ -115,7 +114,7 @@ struct LayoutBoxRareData { // Used by CSSLayoutDefinition::Instance::Layout. Represents the script // object for this box that web developers can query style, and perform // layout upon. Only created if IsCustomItem() is true. - Persistent<CustomLayoutChild> layout_child_; + Member<CustomLayoutChild> layout_child_; DISALLOW_COPY_AND_ASSIGN(LayoutBoxRareData); }; @@ -224,6 +223,11 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { virtual bool BackgroundShouldAlwaysBeClipped() const { return false; } + // Returns whether this object needs a scroll paint property tree node. These + // are a requirement for composited scrolling but are also created for + // non-composited scrollers. + bool NeedsScrollNode(CompositingReasons direct_compositing_reasons) const; + // Use this with caution! No type checking is done! LayoutBox* FirstChildBox() const; LayoutBox* FirstInFlowChildBox() const; @@ -408,16 +412,17 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { ClientHeight()); } - // TODO(crbu.com/962299): This method is only correct when |offset| is the - // correct paint offset. It's also incrrect in flipped blocks writing mode. - IntRect PixelSnappedBorderBoxRect(const PhysicalOffset& offset) const { - return PixelSnappedBorderBoxRect(offset.ToLayoutSize()); - } - IntRect PixelSnappedBorderBoxRect( - const LayoutSize& offset = LayoutSize()) const { + // TODO(crbug.com/962299): This method snaps to pixels incorrectly because + // Location() is not the correct paint offset. It's also incorrect in flipped + // blocks writing mode. + IntRect PixelSnappedBorderBoxRect() const { return IntRect(IntPoint(), - PixelSnappedIntSize(frame_rect_.Size(), - frame_rect_.Location() + offset)); + PixelSnappedBorderBoxSize(PhysicalOffset(Location()))); + } + // TODO(crbug.com/962299): This method is only correct when |offset| is the + // correct paint offset. + IntSize PixelSnappedBorderBoxSize(const PhysicalOffset& offset) const { + return PixelSnappedIntSize(Size(), offset.ToLayoutPoint()); } IntRect BorderBoundingBox() const final { return PixelSnappedBorderBoxRect(); @@ -635,6 +640,11 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { : OverrideIntrinsicContentWidth(); } + // Returns element-native intrinsic size. Returns kIndefiniteSize if no such + // size. + LayoutUnit DefaultIntrinsicContentInlineSize() const; + LayoutUnit DefaultIntrinsicContentBlockSize() const; + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines // (LayoutFlow) to return the remaining width on a given line (and the height // of a single line). @@ -718,8 +728,9 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void ScrollByRecursively(const ScrollOffset& delta); // If makeVisibleInVisualViewport is set, the visual viewport will be scrolled // if required to make the rect visible. - PhysicalRect ScrollRectToVisibleRecursive(const PhysicalRect&, - const WebScrollIntoViewParams&); + PhysicalRect ScrollRectToVisibleRecursive( + const PhysicalRect&, + mojom::blink::ScrollIntoViewParamsPtr); LayoutRectOutsets MarginBoxOutsets() const { return margin_box_outsets_; } LayoutUnit MarginTop() const override { return margin_box_outsets_.Top(); } @@ -795,8 +806,11 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { const PhysicalOffset& accumulated_offset, HitTestAction) override; - LayoutUnit MinPreferredLogicalWidth() const override; - LayoutUnit MaxPreferredLogicalWidth() const override; + // This function calculates the preferred widths for an object. + // + // See INTRINSIC SIZES / PREFERRED LOGICAL WIDTHS in layout_object.h for more + // details about those widths. + MinMaxSizes PreferredLogicalWidths() const override; LayoutUnit OverrideLogicalHeight() const; LayoutUnit OverrideLogicalWidth() const; @@ -941,17 +955,27 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void SetInlineBoxWrapper(InlineBox*); void DeleteLineBoxWrapper(); + bool HasInlineFragments() const final; NGPaintFragment* FirstInlineFragment() const final; void SetFirstInlineFragment(NGPaintFragment*) final; wtf_size_t FirstInlineFragmentItemIndex() const final; void ClearFirstInlineFragmentItemIndex() final; void SetFirstInlineFragmentItemIndex(wtf_size_t) final; - void SetCachedLayoutResult(const NGLayoutResult&, const NGBreakToken*); - void ClearCachedLayoutResult(); - const NGLayoutResult* GetCachedLayoutResult() const { - return cached_layout_result_.get(); - } + void InvalidateItems(const NGLayoutResult&); + + void SetCachedLayoutResult(scoped_refptr<const NGLayoutResult>); + + // Store one layout result (with its physical fragment) at the specified + // index, and delete all entries following it. + void AddLayoutResult(scoped_refptr<const NGLayoutResult>, wtf_size_t index); + + void ShrinkLayoutResults(wtf_size_t results_to_keep); + void ClearLayoutResults(); + + const NGLayoutResult* GetCachedLayoutResult() const; + const NGLayoutResult* GetCachedMeasureResult() const; + // Returns the last layout result for this block flow with the given // constraint space and break token, or null if it is not up-to-date or // otherwise unavailable. @@ -969,6 +993,11 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { base::Optional<NGFragmentGeometry>* initial_fragment_geometry, NGLayoutCacheStatus* out_cache_status); + const NGPhysicalBoxFragment* GetPhysicalFragment(wtf_size_t i) const; + const FragmentData* FragmentDataFromPhysicalFragment( + const NGPhysicalBoxFragment&) const; + wtf_size_t PhysicalFragmentCount() const { return layout_results_.size(); } + void SetSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder&); void ClearSpannerPlaceholder(); LayoutMultiColumnSpannerPlaceholder* SpannerPlaceholder() const final { @@ -1018,7 +1047,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { // value of the previous in-flow sibling. bool NeedsForcedBreakBefore(EBreakBetween previous_break_after_value) const; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; bool MapToVisualRectInAncestorSpaceInternal( const LayoutBoxModelObject* ancestor, TransformState&, @@ -1076,6 +1104,7 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutUnit ShrinkLogicalWidthToAvoidFloats(LayoutUnit child_margin_start, LayoutUnit child_margin_end, const LayoutBlockFlow* cb) const; + bool AutoWidthShouldFitContent() const; LayoutUnit ComputeLogicalWidthUsing( SizeType, @@ -1166,8 +1195,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { const FloatPoint& point_in_root_frame) const; static LayoutBox* FindAutoscrollable(LayoutObject*, bool is_middle_click_autoscroll); - virtual void StopAutoscroll() {} - virtual void MayUpdateHoverWhenContentUnderMouseChanged(EventHandler&); DISABLE_CFI_PERF bool HasAutoVerticalScrollbar() const { return HasOverflowClip() && StyleRef().HasAutoVerticalScroll(); @@ -1315,6 +1342,8 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { bool IsGridItem() const { return Parent() && Parent()->IsLayoutGrid(); } + bool IsMathItem() const { return Parent() && Parent()->IsMathML(); } + LayoutUnit LineHeight( bool first_line, LineDirectionMode, @@ -1501,6 +1530,12 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { const HitTestLocation&, const PhysicalOffset& border_box_location) const; + virtual bool HitTestOverflowControl(HitTestResult&, + const HitTestLocation&, + const PhysicalOffset&) const { + return false; + } + // Returns true if the box intersects the viewport visible to the user. bool IntersectsVisibleViewport() const; @@ -1508,7 +1543,7 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void EnsureIsReadyForPaintInvalidation() override; - virtual bool HasControlClip() const { return false; } + bool HasControlClip() const; class MutableForPainting : public LayoutObject::MutableForPainting { public: @@ -1553,46 +1588,53 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { : PhysicalRect(PhysicalOffset(), PreviousSize()); } - // This function calculates the preferred widths for an object. + // Calculates the intrinsic logical widths for this layout box. + // https://drafts.csswg.org/css-sizing-3/#intrinsic // - // This function is only expected to be called if - // the boolean preferredLogicalWidthsDirty is true. It also MUST clear the - // boolean before returning. + // intrinsicWidth is defined as: + // intrinsic size of content (with our border and padding) + + // scrollbarWidth. // - // See INTRINSIC SIZES / PREFERRED LOGICAL WIDTHS in layout_object.h for more - // details about those widths. + // preferredWidth is defined as: + // fixedWidth OR (intrinsicWidth plus border and padding). + // Note: fixedWidth includes border and padding and scrollbarWidth. // - // This function is public only for use by LayoutNG. Other callers should go - // through MinPreferredLogicalWidth/MaxPreferredLogicalWidth. - virtual void ComputePreferredLogicalWidths() { - ClearPreferredLogicalWidthsDirty(); + // This is public only for use by LayoutNG. Do not call this elsewhere. + virtual MinMaxSizes ComputeIntrinsicLogicalWidths() const = 0; + + // Returns the (maybe cached) intrinsic logical widths for this layout box. + MinMaxSizes IntrinsicLogicalWidths() const { + const_cast<LayoutBox*>(this)->UpdateCachedIntrinsicLogicalWidthsIfNeeded(); + return intrinsic_logical_widths_; } - // LayoutNG can use this function to update our cache of preferred logical + // If |IntrinsicLogicalWidthsDirty()| is true, recalculates the intrinsic + // logical widths. + void UpdateCachedIntrinsicLogicalWidthsIfNeeded(); + + // LayoutNG can use this function to update our cache of intrinsic logical // widths when the layout object is managed by NG. Should not be called by // regular code. - // Also clears the "dirty" flag for preferred widths. - void SetPreferredLogicalWidthsFromNG(MinMaxSize sizes) { - min_preferred_logical_width_ = sizes.min_size; - max_preferred_logical_width_ = sizes.max_size; - ClearPreferredLogicalWidthsDirty(); + // + // Also clears the "dirty" flag for the intrinsic logical widths. + void SetIntrinsicLogicalWidthsFromNG( + const MinMaxSizes& sizes, + LayoutUnit intrinsic_logical_widths_percentage_resolution_block_size) { + intrinsic_logical_widths_ = sizes; + intrinsic_logical_widths_percentage_resolution_block_size_ = + intrinsic_logical_widths_percentage_resolution_block_size; + + ClearIntrinsicLogicalWidthsDirty(); } - // Calculates the intrinsic(https://drafts.csswg.org/css-sizing-3/#intrinsic) - // logical widths for this layout box. - // - // intrinsicWidth is defined as: - // intrinsic size of content (without our border and padding) + - // scrollbarWidth. - // - // preferredWidth is defined as: - // fixedWidth OR (intrinsicWidth plus border and padding). - // Note: fixedWidth includes border and padding and scrollbarWidth. + // Returns what %-resolution-block-size was used in the intrinsic logical + // widths phase. // - // This is public only for use by LayoutNG. Do not call this elsewhere. - virtual void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const; + // For non-LayoutNG code this is always LayoutUnit::Min(), and should not be + // used for caching purposes. + LayoutUnit IntrinsicLogicalWidthsPercentageResolutionBlockSize() const { + return intrinsic_logical_widths_percentage_resolution_block_size_; + } // Make it public. using LayoutObject::BackgroundIsKnownToBeObscured; @@ -1601,9 +1643,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { ~LayoutBox() override; virtual bool ComputeShouldClipOverflow() const; - virtual PhysicalRect ControlClipRect(const PhysicalOffset&) const { - return PhysicalRect(); - } void WillBeDestroyed() override; @@ -1636,8 +1675,7 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutUnit ComputeIntrinsicLogicalWidthUsing( const Length& logical_width_length, - LayoutUnit available_logical_width, - LayoutUnit border_and_padding) const; + LayoutUnit available_logical_width) const; LayoutUnit ComputeIntrinsicLogicalContentHeightUsing( const Length& logical_height_length, LayoutUnit intrinsic_content_height, @@ -1645,11 +1683,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutObject* SplitAnonymousBoxesAroundChild(LayoutObject* before_child); - virtual bool HitTestOverflowControl(HitTestResult&, - const HitTestLocation&, - const PhysicalOffset&) { - return false; - } virtual bool HitTestChildren(HitTestResult&, const HitTestLocation&, const PhysicalOffset& accumulated_offset, @@ -1726,7 +1759,6 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { void ClearScrollSnapMapping(); void AddScrollSnapMapping(); - bool AutoWidthShouldFitContent() const; LayoutUnit ShrinkToFitLogicalWidth(LayoutUnit available_logical_width, LayoutUnit borders_plus_padding) const; @@ -1766,8 +1798,8 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { LayoutBoxRareData& EnsureRareData() { if (!rare_data_) - rare_data_ = std::make_unique<LayoutBoxRareData>(); - return *rare_data_.get(); + rare_data_ = MakeGarbageCollected<LayoutBoxRareData>(); + return *rare_data_.Get(); } bool LogicalHeightComputesAsNone(SizeType) const; @@ -1794,7 +1826,7 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { // Returns true when the current recursive scroll into visible could propagate // to parent frame. bool AllowedToPropagateRecursiveScrollToParentFrame( - const WebScrollIntoViewParams&); + const mojom::blink::ScrollIntoViewParamsPtr&); PhysicalRect DebugRect() const override; @@ -1845,16 +1877,8 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { mutable LayoutUnit intrinsic_content_logical_height_; protected: - // The logical width of the element if it were to break its lines at every - // possible opportunity. - // - // See LayoutObject::minPreferredLogicalWidth() for more details. - LayoutUnit min_preferred_logical_width_; - - // The logical width of the element if it never breaks any lines at all. - // - // See LayoutObject::maxPreferredLogicalWidth() for more details. - LayoutUnit max_preferred_logical_width_; + MinMaxSizes intrinsic_logical_widths_; + LayoutUnit intrinsic_logical_widths_percentage_resolution_block_size_; // LayoutBoxUtils is used for the LayoutNG code querying protected methods on // this class, e.g. determining the static-position of OOF elements. @@ -1892,8 +1916,9 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject { wtf_size_t first_fragment_item_index_; }; - std::unique_ptr<LayoutBoxRareData> rare_data_; - scoped_refptr<const NGLayoutResult> cached_layout_result_; + Persistent<LayoutBoxRareData> rare_data_; + scoped_refptr<const NGLayoutResult> measure_result_; + Vector<scoped_refptr<const NGLayoutResult>, 1> layout_results_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutBox, IsBox()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc index 21782639cd2..e3b20800d32 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.cc @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/html/html_body_element.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" #include "third_party/blink/renderer/core/layout/layout_block.h" @@ -36,6 +37,8 @@ #include "third_party/blink/renderer/core/layout/layout_geometry_map.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h" @@ -85,10 +88,6 @@ void LayoutBoxModelObject::ContentChanged(ContentChangeType change_type) { Layer()->ContentChanged(change_type); } -bool LayoutBoxModelObject::HasAcceleratedCompositing() const { - return View()->Compositor()->HasAcceleratedCompositing(); -} - LayoutBoxModelObject::LayoutBoxModelObject(ContainerNode* node) : LayoutObject(node) {} @@ -97,9 +96,10 @@ bool LayoutBoxModelObject::UsesCompositedScrolling() const { Layer()->GetScrollableArea()->UsesCompositedScrolling(); } -BackgroundPaintLocation LayoutBoxModelObject::GetBackgroundPaintLocation( +BackgroundPaintLocation +LayoutBoxModelObject::ComputeBackgroundPaintLocationIfComposited( uint32_t* main_thread_scrolling_reasons) const { - bool may_have_scrolling_layers_without_scrolling = IsLayoutView(); + bool may_have_scrolling_layers_without_scrolling = IsA<LayoutView>(this); const auto* scrollable_area = GetScrollableArea(); bool scrolls_overflow = scrollable_area && scrollable_area->ScrollsOverflow(); if (!scrolls_overflow && !may_have_scrolling_layers_without_scrolling) @@ -108,9 +108,8 @@ BackgroundPaintLocation LayoutBoxModelObject::GetBackgroundPaintLocation( // If we care about LCD text, paint root backgrounds into scrolling contents // layer even if style suggests otherwise. (For non-root scrollers, we just // avoid compositing - see PLSA::ComputeNeedsCompositedScrolling.) - if (IsLayoutView()) { - DCHECK(Layer()->Compositor()); - if (!Layer()->Compositor()->PreferCompositingToLCDTextEnabled()) + if (IsA<LayoutView>(this)) { + if (!GetDocument().GetSettings()->GetPreferCompositingToLCDTextEnabled()) return kBackgroundPaintInScrollingContents; } @@ -283,13 +282,9 @@ void LayoutBoxModelObject::StyleDidChange(StyleDifference diff, // block/inline position. // Position changes and other types of display changes are handled elsewhere. if (old_style && IsOutOfFlowPositioned() && Parent() && - (Parent() != ContainingBlock()) && (StyleRef().GetPosition() == old_style->GetPosition()) && - (StyleRef().OriginalDisplay() != old_style->OriginalDisplay()) && - ((StyleRef().OriginalDisplay() == EDisplay::kBlock) || - (StyleRef().OriginalDisplay() == EDisplay::kInlineBlock)) && - ((old_style->OriginalDisplay() == EDisplay::kBlock) || - (old_style->OriginalDisplay() == EDisplay::kInlineBlock))) + (StyleRef().IsOriginalDisplayInlineType() != + old_style->IsOriginalDisplayInlineType())) Parent()->SetNeedsLayout(layout_invalidation_reason::kChildChanged, kMarkContainerChain); @@ -320,7 +315,7 @@ void LayoutBoxModelObject::StyleDidChange(StyleDifference diff, if (EverHadLayout()) SetChildNeedsLayout(); if (had_transform_related_property) { - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kStyleChange); } if (!NeedsLayout()) { @@ -444,7 +439,6 @@ void LayoutBoxModelObject::StyleDidChange(StyleDifference diff, // Remove sticky constraints for this layer. if (Layer()) { - DisableCompositingQueryAsserts disabler; if (const PaintLayer* ancestor_overflow_layer = Layer()->AncestorOverflowLayer()) { if (PaintLayerScrollableArea* scrollable_area = @@ -519,7 +513,6 @@ void LayoutBoxModelObject::InvalidateStickyConstraints() { // This intentionally uses the stale ancestor overflow layer compositing input // as if we have saved constraints for this layer they were saved in the // previous frame. - DisableCompositingQueryAsserts disabler; if (const PaintLayer* ancestor_overflow_layer = enclosing->AncestorOverflowLayer()) { if (PaintLayerScrollableArea* ancestor_scrollable_area = @@ -537,6 +530,8 @@ void LayoutBoxModelObject::CreateLayerAfterStyleChange() { // Creating a layer may affect existence of the LocalBorderBoxProperties, so // we need to ensure that we update paint properties. SetNeedsPaintPropertyUpdate(); + if (GetScrollableArea()) + GetScrollableArea()->InvalidateScrollTimeline(); } void LayoutBoxModelObject::DestroyLayer() { @@ -546,6 +541,7 @@ void LayoutBoxModelObject::DestroyLayer() { // Removing a layer may affect existence of the LocalBorderBoxProperties, so // we need to ensure that we update paint properties. SetNeedsPaintPropertyUpdate(); + SetBackgroundPaintLocation(kBackgroundPaintInGraphicsLayer); } bool LayoutBoxModelObject::HasSelfPaintingLayer() const { @@ -679,7 +675,7 @@ LayoutBlock* LayoutBoxModelObject::ContainingBlockForAutoHeightDetection( // Match LayoutBox::availableLogicalHeightUsing by special casing the layout // view. The available height is taken from the frame. - if (cb->IsLayoutView()) + if (IsA<LayoutView>(cb)) return nullptr; if (IsOutOfFlowPositionedWithImplicitHeight(cb)) @@ -700,10 +696,17 @@ bool LayoutBoxModelObject::HasAutoHeightOrContainingBlockWithAutoHeight( logical_height_length.IsPercentOrCalc() && cb && IsBox()) { cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(ToLayoutBox(this))); } - if (this_box && this_box->IsFlexItem()) { - const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); - if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box)) - return false; + if (this_box && this_box->IsFlexItemIncludingNG()) { + if (this_box->IsFlexItem()) { + const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent()); + if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box)) + return false; + } else if (this_box->GetCachedLayoutResult()) { + const NGConstraintSpace& space = + this_box->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + if (space.IsFixedBlockSize() && !space.IsFixedBlockSizeIndefinite()) + return false; + } } if (this_box && this_box->IsGridItem() && this_box->HasOverrideContainingBlockContentLogicalHeight()) @@ -722,8 +725,14 @@ bool LayoutBoxModelObject::HasAutoHeightOrContainingBlockWithAutoHeight( // resolve the block-size of the descendant, except when in quirks mode. // Flexboxes follow strict behavior even in quirks mode, though. if (!GetDocument().InQuirksMode() || - cb->IsFlexibleBoxIncludingDeprecatedAndNG()) + cb->IsFlexibleBoxIncludingDeprecatedAndNG()) { + if (this_box && + this_box->HasOverrideContainingBlockContentLogicalHeight()) { + return this_box->OverrideContainingBlockContentLogicalHeight() == + LayoutUnit(-1); + } return !cb->HasDefiniteLogicalHeight(); + } } return false; @@ -881,7 +890,7 @@ void LayoutBoxModelObject::UpdateStickyPositionConstraints() const { ToLayoutBox(Layer()->AncestorOverflowLayer()->GetLayoutObject()); LayoutUnit max_container_width = - containing_block->IsLayoutView() + IsA<LayoutView>(containing_block) ? containing_block->LogicalWidth() : containing_block->ContainingBlockLogicalWidthForContent(); // Sticky positioned element ignore any override logical width on the diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.h b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.h index 40b2a33964e..dd19932660c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object.h @@ -185,13 +185,13 @@ class CORE_EXPORT LayoutBoxModelObject : public LayoutObject { bool UsesCompositedScrolling() const; - // Returns which layers backgrounds should be painted into for overflow - // scrolling boxes. + // Returns which layers backgrounds should be painted into for a overflow + // scrolling box if it uses composited scrolling. // TODO(yigu): PaintLayerScrollableArea::ComputeNeedsCompositedScrolling // calls this method to obtain main thread scrolling reasons due to // background paint location. Once the cases get handled on compositor the // parameter "reasons" could be removed. - BackgroundPaintLocation GetBackgroundPaintLocation( + BackgroundPaintLocation ComputeBackgroundPaintLocationIfComposited( uint32_t* main_thread_scrolling_reasons = nullptr) const; // These return the CSS computed padding values. @@ -415,7 +415,6 @@ class CORE_EXPORT LayoutBoxModelObject : public LayoutObject { LayoutGeometryMap&) const override; void ContentChanged(ContentChangeType); - bool HasAcceleratedCompositing() const; // Returns true if the background is painted opaque in the given rect. // The query rect is given in local coordinate system. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc index b4c95094980..990362b2148 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc @@ -423,7 +423,8 @@ TEST_F(LayoutBoxModelObjectTest, StickyPositionConstraintInvalidation) { .at(sticky->Layer()) .scroll_container_relative_sticky_box_rect.X()); To<HTMLElement>(target->GetNode())->classList().Add("hide"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); // Layout should invalidate the sticky constraints of the sticky element and // mark it as needing a compositing inputs update. EXPECT_FALSE( @@ -1075,7 +1076,8 @@ TEST_F(LayoutBoxModelObjectTest, InvalidatePaintLayerOnStackedChange) { EXPECT_NE(parent, original_compositing_container->GetLayoutObject()); target_element->setAttribute(html_names::kClassAttr, "non-stacked"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_FALSE(target->StyleRef().IsStacked()); EXPECT_TRUE(target->Layer()->SelfNeedsRepaint()); @@ -1085,7 +1087,8 @@ TEST_F(LayoutBoxModelObjectTest, InvalidatePaintLayerOnStackedChange) { UpdateAllLifecyclePhasesForTest(); target_element->setAttribute(html_names::kClassAttr, "stacked"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(target->StyleRef().IsStacked()); EXPECT_TRUE(target->Layer()->SelfNeedsRepaint()); @@ -1156,13 +1159,15 @@ TEST_F(LayoutBoxModelObjectTest, BackfaceVisibilityChange) { target->setAttribute(html_names::kStyleAttr, base_style + "; backface-visibility: hidden"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(target_layer->SelfNeedsRepaint()); UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(target_layer->SelfNeedsRepaint()); target->setAttribute(html_names::kStyleAttr, base_style); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(target_layer->SelfNeedsRepaint()); UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(target_layer->SelfNeedsRepaint()); @@ -1216,7 +1221,8 @@ TEST_F(LayoutBoxModelObjectTest, EXPECT_EQ(target->Layer(), target->Layer()->NearestContainedLayoutLayer()); To<HTMLElement>(target->GetNode())->classList().Remove("contained"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(target->Layer()->NeedsCompositingInputsUpdate()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc index e298eb904a7..99bcb9ca795 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_box_test.cc @@ -392,12 +392,7 @@ TEST_P(LayoutBoxTest, ControlClip) { EXPECT_TRUE(target->HasControlClip()); EXPECT_TRUE(target->HasClipRelatedProperty()); EXPECT_TRUE(target->ShouldClipOverflow()); -#if defined(OS_MACOSX) - EXPECT_EQ(PhysicalRect(0, 0, 100, 18), - target->ClippingRect(PhysicalOffset())); -#else EXPECT_EQ(PhysicalRect(2, 2, 96, 46), target->ClippingRect(PhysicalOffset())); -#endif } TEST_P(LayoutBoxTest, LocalVisualRectWithMask) { @@ -1436,7 +1431,8 @@ TEST_P(LayoutBoxTest, HasNonCollapsedBorderDecoration) { To<Element>(div->GetNode()) ->setAttribute(html_names::kStyleAttr, "border: 1px solid black"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason ::kTest); EXPECT_TRUE(div->HasNonCollapsedBorderDecoration()); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_button.cc b/chromium/third_party/blink/renderer/core/layout/layout_button.cc index c743f0cd09a..28d3a96c0a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_button.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_button.cc @@ -73,14 +73,6 @@ void LayoutButton::UpdateAnonymousChildStyle(const LayoutObject* child, child_style.SetAlignContent(StyleRef().AlignContent()); } -PhysicalRect LayoutButton::ControlClipRect( - const PhysicalOffset& additional_offset) const { - // Clip to the padding box to at least give content the extra padding space. - PhysicalRect rect(additional_offset, Size()); - rect.Expand(BorderInsets()); - return rect; -} - LayoutUnit LayoutButton::BaselinePosition( FontBaseline baseline, bool first_line, @@ -106,8 +98,4 @@ LayoutUnit LayoutButton::BaselinePosition( line_position_mode); } -// For compatibility with IE/FF we only clip overflow on input elements. -bool LayoutButton::HasControlClip() const { - return !IsA<HTMLButtonElement>(GetNode()); -} } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_button.h b/chromium/third_party/blink/renderer/core/layout/layout_button.h index b9aaedc709e..60ce95bc9a4 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_button.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_button.h @@ -48,9 +48,6 @@ class LayoutButton final : public LayoutFlexibleBox { void RemoveLeftoverAnonymousBlock(LayoutBlock*) override {} bool CreatesAnonymousWrapper() const override { return true; } - bool HasControlClip() const override; - PhysicalRect ControlClipRect(const PhysicalOffset&) const override; - LayoutUnit BaselinePosition(FontBaseline, bool first_line, LineDirectionMode, @@ -60,10 +57,6 @@ class LayoutButton final : public LayoutFlexibleBox { void UpdateAnonymousChildStyle(const LayoutObject* child, ComputedStyle& child_style) const override; - bool HasLineIfEmpty() const override { - return IsA<HTMLInputElement>(GetNode()); - } - LayoutBlock* inner_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_counter.cc b/chromium/third_party/blink/renderer/core/layout/layout_counter.cc index d7c8f9d0587..d3a3e1359e0 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_counter.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_counter.cc @@ -474,23 +474,21 @@ void LayoutCounter::WillBeDestroyed() { scoped_refptr<StringImpl> LayoutCounter::OriginalText() const { if (!counter_node_) { - LayoutObject* before_after_container = Parent(); + LayoutObject* container = Parent(); while (true) { - if (!before_after_container) + if (!container) return nullptr; - if (!before_after_container->IsAnonymous() && - !before_after_container->IsPseudoElement()) - return nullptr; // LayoutCounters are restricted to before and after - // pseudo elements - PseudoId container_style = before_after_container->StyleRef().StyleType(); + if (!container->IsAnonymous() && !container->IsPseudoElement()) + return nullptr; // LayoutCounters are restricted to before, after and + // marker pseudo elements + PseudoId container_style = container->StyleRef().StyleType(); if ((container_style == kPseudoIdBefore) || (container_style == kPseudoIdAfter) || (container_style == kPseudoIdMarker)) break; - before_after_container = before_after_container->Parent(); + container = container->Parent(); } - MakeCounterNodeIfNeeded(*before_after_container, counter_.Identifier(), - true) + MakeCounterNodeIfNeeded(*container, counter_.Identifier(), true) ->AddLayoutObject(const_cast<LayoutCounter*>(this)); DCHECK(counter_node_); } @@ -522,7 +520,7 @@ void LayoutCounter::Invalidate() { DCHECK(!counter_node_); if (DocumentBeingDestroyed()) return; - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kCountersChanged); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc b/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc index 92de8406cde..e44c69567e7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.cc @@ -141,7 +141,7 @@ int LayoutCustomScrollbarPart::ComputeScrollbarWidth( int min_width = CalcScrollbarThicknessUsing(kMinSize, style->MinWidth(), visible_size, theme); int max_width = w; - if (!style->MaxWidth().IsMaxSizeNone()) { + if (!style->MaxWidth().IsNone()) { max_width = CalcScrollbarThicknessUsing(kMaxSize, style->MaxWidth(), visible_size, theme); } @@ -158,7 +158,7 @@ int LayoutCustomScrollbarPart::ComputeScrollbarHeight( int min_height = CalcScrollbarThicknessUsing(kMinSize, style->MinHeight(), visible_size, theme); int max_height = h; - if (!style->MaxHeight().IsMaxSizeNone()) { + if (!style->MaxHeight().IsNone()) { max_height = CalcScrollbarThicknessUsing(kMaxSize, style->MaxHeight(), visible_size, theme); } @@ -211,13 +211,8 @@ void LayoutCustomScrollbarPart::UpdateScrollbarHeight() { .Round())); } -void LayoutCustomScrollbarPart::ComputePreferredLogicalWidths() { - if (!PreferredLogicalWidthsDirty()) - return; - - min_preferred_logical_width_ = max_preferred_logical_width_ = LayoutUnit(); - - ClearPreferredLogicalWidthsDirty(); +MinMaxSizes LayoutCustomScrollbarPart::PreferredLogicalWidths() const { + return MinMaxSizes(); } void LayoutCustomScrollbarPart::StyleWillChange( @@ -235,7 +230,7 @@ void LayoutCustomScrollbarPart::StyleDidChange(StyleDifference diff, SetInline(false); ClearPositionedState(); SetFloating(false); - if (old_style && (diff.NeedsFullPaintInvalidation() || diff.NeedsLayout())) + if (old_style && (diff.NeedsPaintInvalidation() || diff.NeedsLayout())) SetNeedsPaintInvalidation(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h b/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h index c6848666b51..dd1f0caf2cd 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h @@ -83,7 +83,7 @@ class LayoutCustomScrollbarPart final : public LayoutBlock { private: LayoutCustomScrollbarPart(ScrollableArea*, CustomScrollbar*, ScrollbarPart); - void ComputePreferredLogicalWidths() override; + MinMaxSizes PreferredLogicalWidths() const override; // Have all padding getters return 0. The important point here is to avoid // resolving percents against the containing block, since scroll bar corners diff --git a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc index 3dbbe77bdda..8a44c7f8c14 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc @@ -43,11 +43,7 @@ class FlexBoxIterator { public: FlexBoxIterator(LayoutDeprecatedFlexibleBox* parent) : box_(parent), largest_ordinal_(1) { - if (box_->StyleRef().BoxOrient() == EBoxOrient::kHorizontal && - !box_->StyleRef().IsLeftToRightDirection()) - forward_ = box_->StyleRef().BoxDirection() != EBoxDirection::kNormal; - else - forward_ = box_->StyleRef().BoxDirection() == EBoxDirection::kNormal; + forward_ = box_->StyleRef().BoxDirection() == EBoxDirection::kNormal; if (!forward_) { // No choice, since we're going backwards, we have to find out the highest // ordinal up front. @@ -271,10 +267,9 @@ static void ClearTruncation(LayoutBlockFlow* block_flow) { } } -LayoutDeprecatedFlexibleBox::LayoutDeprecatedFlexibleBox(Element& element) - : LayoutBlock(&element) { +LayoutDeprecatedFlexibleBox::LayoutDeprecatedFlexibleBox(Element* element) + : LayoutBlock(element) { DCHECK(!ChildrenInline()); - stretching_children_ = false; if (!IsAnonymous()) { const KURL& url = GetDocument().Url(); if (url.ProtocolIs("chrome")) { @@ -305,18 +300,6 @@ static LayoutUnit MarginWidthForChild(LayoutBox* child) { return margin; } -static LayoutUnit WidthForChild(LayoutBox* child) { - if (child->HasOverrideLogicalWidth()) - return child->OverrideLogicalWidth(); - return child->LogicalWidth(); -} - -static LayoutUnit ContentWidthForChild(LayoutBox* child) { - // TODO(rego): Shouldn't we subtract the scrollbar width too? - return (WidthForChild(child) - child->BorderAndPaddingLogicalWidth()) - .ClampNegativeToZero(); -} - static LayoutUnit HeightForChild(LayoutBox* child) { if (child->HasOverrideLogicalHeight()) return child->OverrideLogicalHeight(); @@ -339,43 +322,27 @@ void LayoutDeprecatedFlexibleBox::StyleWillChange( LayoutBlock::StyleWillChange(diff, new_style); } -void LayoutDeprecatedFlexibleBox::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - if (IsVertical()) { - for (LayoutBox* child = FirstChildBox(); child; - child = child->NextSiblingBox()) { - if (child->IsOutOfFlowPositioned()) - continue; - - LayoutUnit margin = MarginWidthForChild(child); - LayoutUnit width = child->MinPreferredLogicalWidth() + margin; - min_logical_width = std::max(width, min_logical_width); +MinMaxSizes LayoutDeprecatedFlexibleBox::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + for (LayoutBox* child = FirstChildBox(); child; + child = child->NextSiblingBox()) { + if (child->IsOutOfFlowPositioned()) + continue; - width = child->MaxPreferredLogicalWidth() + margin; - max_logical_width = std::max(width, max_logical_width); - } - } else { - for (LayoutBox* child = FirstChildBox(); child; - child = child->NextSiblingBox()) { - if (child->IsOutOfFlowPositioned()) - continue; + MinMaxSizes child_sizes = child->PreferredLogicalWidths(); + child_sizes += MarginWidthForChild(child); - LayoutUnit margin = MarginWidthForChild(child); - min_logical_width += child->MinPreferredLogicalWidth() + margin; - max_logical_width += child->MaxPreferredLogicalWidth() + margin; - } + sizes.Encompass(child_sizes); } - max_logical_width = std::max(min_logical_width, max_logical_width); - - LayoutUnit scrollbar_width(ScrollbarLogicalWidth()); - max_logical_width += scrollbar_width; - min_logical_width += scrollbar_width; + sizes.max_size = std::max(sizes.min_size, sizes.max_size); + sizes += BorderAndPaddingLogicalWidth() + ScrollbarLogicalWidth(); + return sizes; } void LayoutDeprecatedFlexibleBox::UpdateBlockLayout(bool relayout_children) { DCHECK(NeedsLayout()); + DCHECK_EQ(StyleRef().BoxOrient(), EBoxOrient::kVertical); UseCounter::Count(GetDocument(), WebFeature::kWebkitBoxLayout); @@ -417,23 +384,13 @@ void LayoutDeprecatedFlexibleBox::UpdateBlockLayout(bool relayout_children) { TextAutosizer::LayoutScope text_autosizer_layout_scope(this); - if (previous_size != Size() || - (Parent()->StyleRef().IsDeprecatedWebkitBox() && - Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal && - Parent()->StyleRef().BoxAlign() == EBoxAlignment::kStretch)) + if (previous_size != Size()) relayout_children = true; SetHeight(LayoutUnit()); - stretching_children_ = false; - - if (IsHorizontal()) { - UseCounter::Count(GetDocument(), WebFeature::kWebkitBoxLayoutHorizontal); - LayoutHorizontalBox(relayout_children); - } else { - UseCounter::Count(GetDocument(), WebFeature::kWebkitBoxLayoutVertical); - LayoutVerticalBox(relayout_children); - } + UseCounter::Count(GetDocument(), WebFeature::kWebkitBoxLayoutVertical); + LayoutVerticalBox(relayout_children); LayoutUnit old_client_after_edge = ClientLogicalBottom(); UpdateLogicalHeight(); @@ -480,322 +437,6 @@ static void GatherFlexChildrenInfo(FlexBoxIterator& iterator, } } -void LayoutDeprecatedFlexibleBox::LayoutHorizontalBox(bool relayout_children) { - LayoutUnit to_add = - BorderBottom() + PaddingBottom() + HorizontalScrollbarHeight(); - LayoutUnit y_pos = BorderTop() + PaddingTop(); - LayoutUnit x_pos = BorderLeft() + PaddingLeft(); - bool height_specified = false; - bool paginated = View()->GetLayoutState()->IsPaginated(); - LayoutUnit old_height; - - LayoutUnit remaining_space; - - FlexBoxIterator iterator(this); - bool have_flex = false, flexing_children = false; - GatherFlexChildrenInfo(iterator, GetDocument(), relayout_children, have_flex); - - PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope; - - // We do 2 passes. The first pass is simply to lay everyone out at - // their preferred widths. The second pass handles flexing the children. - do { - // Reset our height. - SetHeight(y_pos); - - x_pos = BorderLeft() + PaddingLeft(); - - // Our first pass is done without flexing. We simply lay the children - // out within the box. We have to do a layout first in order to determine - // our box's intrinsic height. - LayoutUnit max_ascent; - LayoutUnit max_descent; - for (LayoutBox* child = iterator.First(); child; child = iterator.Next()) { - if (child->IsOutOfFlowPositioned()) - continue; - - SubtreeLayoutScope layout_scope(*child); - // TODO(jchaffraix): It seems incorrect to check isAtomicInlineLevel in - // this file. - // We probably want to check if the element is replaced. - if (relayout_children || (child->IsAtomicInlineLevel() && - (child->StyleRef().Width().IsPercentOrCalc() || - child->StyleRef().Height().IsPercentOrCalc()))) - layout_scope.SetChildNeedsLayout(child); - - // Compute the child's vertical margins. - child->ComputeAndSetBlockDirectionMargins(this); - - if (!child->NeedsLayout()) - MarkChildForPaginationRelayoutIfNeeded(*child, layout_scope); - - // Now do the layout. - child->LayoutIfNeeded(); - - // Update our height and overflow height. - if (StyleRef().BoxAlign() == EBoxAlignment::kBaseline) { - LayoutUnit ascent(child->FirstLineBoxBaseline()); - if (ascent == -1) - ascent = child->Size().Height() + child->MarginBottom(); - ascent += child->MarginTop(); - LayoutUnit descent = - (child->Size().Height() + child->MarginHeight()) - ascent; - - // Update our maximum ascent. - max_ascent = std::max(max_ascent, ascent); - - // Update our maximum descent. - max_descent = std::max(max_descent, descent); - - // Now update our height. - SetHeight(std::max(y_pos + max_ascent + max_descent, Size().Height())); - } else { - SetHeight(std::max(Size().Height(), y_pos + child->Size().Height() + - child->MarginHeight())); - } - - if (paginated) - UpdateFragmentationInfoForChild(*child); - } - - if (!iterator.First() && HasLineIfEmpty()) { - SetHeight(Size().Height() + - LineHeight(true, - StyleRef().IsHorizontalWritingMode() - ? kHorizontalLine - : kVerticalLine, - kPositionOfInteriorLineBoxes)); - } - - SetHeight(Size().Height() + to_add); - - old_height = Size().Height(); - UpdateLogicalHeight(); - - relayout_children = false; - if (old_height != Size().Height()) - height_specified = true; - - // Now that our height is actually known, we can place our boxes. - stretching_children_ = (StyleRef().BoxAlign() == EBoxAlignment::kStretch); - for (LayoutBox* child = iterator.First(); child; child = iterator.Next()) { - if (child->IsOutOfFlowPositioned()) { - child->ContainingBlock()->InsertPositionedObject(child); - PaintLayer* child_layer = child->Layer(); - child_layer->SetStaticInlinePosition(x_pos); - if (child_layer->StaticBlockPosition() != y_pos) { - child_layer->SetStaticBlockPosition(y_pos); - if (child->StyleRef().HasStaticBlockPosition( - StyleRef().IsHorizontalWritingMode())) - child->SetChildNeedsLayout(kMarkOnlyThis); - } - continue; - } - - SubtreeLayoutScope layout_scope(*child); - - // We need to see if this child's height will change, since we make block - // elements fill the height of a containing box by default. We cannot - // actually *set* the new height here, though. Need to do that from - // within layout, or we won't be able to detect the change and duly - // notify any positioned descendants that are affected by it. - LayoutUnit old_child_height = child->LogicalHeight(); - LogicalExtentComputedValues computed_values; - child->ComputeLogicalHeight(child->LogicalHeight(), child->LogicalTop(), - computed_values); - LayoutUnit new_child_height = computed_values.extent_; - if (old_child_height != new_child_height) - layout_scope.SetChildNeedsLayout(child); - - if (!child->NeedsLayout()) - MarkChildForPaginationRelayoutIfNeeded(*child, layout_scope); - - child->LayoutIfNeeded(); - - // We can place the child now, using our value of box-align. - x_pos += child->MarginLeft(); - LayoutUnit child_y = y_pos; - switch (StyleRef().BoxAlign()) { - case EBoxAlignment::kCenter: - child_y += child->MarginTop() + - ((ContentHeight() - - (child->Size().Height() + child->MarginHeight())) / - 2) - .ClampNegativeToZero(); - break; - case EBoxAlignment::kBaseline: { - LayoutUnit ascent(child->FirstLineBoxBaseline()); - if (ascent == -1) - ascent = child->Size().Height() + child->MarginBottom(); - ascent += child->MarginTop(); - child_y += child->MarginTop() + (max_ascent - ascent); - break; - } - case EBoxAlignment::kEnd: - child_y += - ContentHeight() - child->MarginBottom() - child->Size().Height(); - break; - default: // BSTART - child_y += child->MarginTop(); - break; - } - - PlaceChild(child, LayoutPoint(x_pos, child_y)); - - x_pos += child->Size().Width() + child->MarginRight(); - } - - remaining_space = Size().Width() - BorderRight() - PaddingRight() - - VerticalScrollbarWidth() - x_pos; - - stretching_children_ = false; - if (flexing_children) { - have_flex = false; // We're done. - } else if (have_flex) { - // We have some flexible objects. See if we need to grow/shrink them at - // all. - if (!remaining_space) - break; - - // Allocate the remaining space among the flexible objects. - bool expanding = remaining_space > 0; - do { - // Flexing consists of multiple passes, since we have to change - // ratios every time an object hits its max/min-width For a given - // pass, we always start off by computing the totalFlex of all - // objects that can grow/shrink at all, and computing the allowed - // growth before an object hits its min/max width (and thus forces a - // totalFlex recomputation). - LayoutUnit remaining_space_at_beginning = remaining_space; - float total_flex = 0.0f; - for (LayoutBox* child = iterator.First(); child; - child = iterator.Next()) { - if (AllowedChildFlex(child, expanding)) - total_flex += child->StyleRef().BoxFlex(); - } - LayoutUnit space_available_this_pass = remaining_space; - for (LayoutBox* child = iterator.First(); child; - child = iterator.Next()) { - LayoutUnit allowed_flex = AllowedChildFlex(child, expanding); - if (allowed_flex) { - LayoutUnit projected_flex = - (allowed_flex == LayoutUnit::Max()) - ? allowed_flex - : LayoutUnit(allowed_flex * - (total_flex / child->StyleRef().BoxFlex())); - space_available_this_pass = - expanding ? std::min(space_available_this_pass, projected_flex) - : std::max(space_available_this_pass, projected_flex); - } - } - - // If we can't grow/shrink anymore, break. - if (!space_available_this_pass || total_flex == 0.0f) - break; - - // Now distribute the space to objects. - for (LayoutBox* child = iterator.First(); - child && space_available_this_pass && total_flex; - child = iterator.Next()) { - if (AllowedChildFlex(child, expanding)) { - LayoutUnit space_add = - LayoutUnit(space_available_this_pass * - (child->StyleRef().BoxFlex() / total_flex)); - if (space_add) { - child->SetOverrideLogicalWidth(WidthForChild(child) + space_add); - flexing_children = true; - relayout_children = true; - } - - space_available_this_pass -= space_add; - remaining_space -= space_add; - - total_flex -= child->StyleRef().BoxFlex(); - } - } - if (remaining_space == remaining_space_at_beginning) { - // This is not advancing, avoid getting stuck by distributing the - // remaining pixels. - LayoutUnit space_add = LayoutUnit(remaining_space > 0 ? 1 : -1); - for (LayoutBox* child = iterator.First(); child && remaining_space; - child = iterator.Next()) { - if (AllowedChildFlex(child, expanding)) { - child->SetOverrideLogicalWidth(WidthForChild(child) + space_add); - flexing_children = true; - relayout_children = true; - remaining_space -= space_add; - } - } - } - } while (AbsoluteValue(remaining_space) >= 1); - - // We didn't find any children that could grow. - if (have_flex && !flexing_children) - have_flex = false; - } - } while (have_flex); - - if (remaining_space > 0 && ((StyleRef().IsLeftToRightDirection() && - StyleRef().BoxPack() != EBoxPack::kStart) || - (!StyleRef().IsLeftToRightDirection() && - StyleRef().BoxPack() != EBoxPack::kEnd))) { - // Children must be repositioned. - LayoutUnit offset; - if (StyleRef().BoxPack() == EBoxPack::kJustify) { - // Determine the total number of children. - int total_children = 0; - for (LayoutBox* child = iterator.First(); child; - child = iterator.Next()) { - if (child->IsOutOfFlowPositioned()) - continue; - ++total_children; - } - - // Iterate over the children and space them out according to the - // justification level. - if (total_children > 1) { - --total_children; - bool first_child = true; - for (LayoutBox* child = iterator.First(); child; - child = iterator.Next()) { - if (child->IsOutOfFlowPositioned()) - continue; - - if (first_child) { - first_child = false; - continue; - } - - offset += remaining_space / total_children; - remaining_space -= (remaining_space / total_children); - --total_children; - - PlaceChild(child, - child->Location() + LayoutSize(offset, LayoutUnit())); - } - } - } else { - if (StyleRef().BoxPack() == EBoxPack::kCenter) - offset += remaining_space / 2; - else // END for LTR, START for RTL - offset += remaining_space; - for (LayoutBox* child = iterator.First(); child; - child = iterator.Next()) { - if (child->IsOutOfFlowPositioned()) - continue; - - PlaceChild(child, child->Location() + LayoutSize(offset, LayoutUnit())); - } - } - } - - // So that the computeLogicalHeight in layoutBlock() knows to relayout - // positioned objects because of a height change, we revert our height back - // to the intrinsic height before returning. - if (height_specified) - SetHeight(old_height); -} - void LayoutDeprecatedFlexibleBox::LayoutVerticalBox(bool relayout_children) { LayoutUnit y_pos = BorderTop() + PaddingTop(); LayoutUnit to_add = @@ -1021,6 +662,8 @@ void LayoutDeprecatedFlexibleBox::LayoutVerticalBox(bool relayout_children) { // Children must be repositioned. LayoutUnit offset; if (StyleRef().BoxPack() == EBoxPack::kJustify) { + UseCounter::Count(GetDocument(), + WebFeature::kWebkitBoxPackJustifyDoesSomething); // Determine the total number of children. int total_children = 0; for (LayoutBox* child = iterator.First(); child; @@ -1054,10 +697,15 @@ void LayoutDeprecatedFlexibleBox::LayoutVerticalBox(bool relayout_children) { } } } else { - if (StyleRef().BoxPack() == EBoxPack::kCenter) + if (StyleRef().BoxPack() == EBoxPack::kCenter) { + UseCounter::Count(GetDocument(), + WebFeature::kWebkitBoxPackCenterDoesSomething); offset += remaining_space / 2; - else // END + } else { // END + UseCounter::Count(GetDocument(), + WebFeature::kWebkitBoxPackEndDoesSomething); offset += remaining_space; + } for (LayoutBox* child = iterator.First(); child; child = iterator.Next()) { if (child->IsOutOfFlowPositioned()) @@ -1248,16 +896,6 @@ LayoutUnit LayoutDeprecatedFlexibleBox::AllowedChildFlex(LayoutBox* child, return LayoutUnit(); if (expanding) { - if (IsHorizontal()) { - // FIXME: For now just handle fixed values. - LayoutUnit max_width = LayoutUnit::Max(); - LayoutUnit width = ContentWidthForChild(child); - if (child->StyleRef().MaxWidth().IsFixed()) - max_width = LayoutUnit(child->StyleRef().MaxWidth().Value()); - if (max_width == LayoutUnit::Max()) - return max_width; - return (max_width - width).ClampNegativeToZero(); - } // FIXME: For now just handle fixed values. LayoutUnit max_height = LayoutUnit::Max(); LayoutUnit height = ContentHeightForChild(child); @@ -1269,18 +907,6 @@ LayoutUnit LayoutDeprecatedFlexibleBox::AllowedChildFlex(LayoutBox* child, } // FIXME: For now just handle fixed values. - if (IsHorizontal()) { - LayoutUnit min_width = child->MinPreferredLogicalWidth(); - LayoutUnit width = ContentWidthForChild(child); - const Length& min_width_length = child->StyleRef().MinWidth(); - if (min_width_length.IsFixed()) - min_width = LayoutUnit(min_width_length.Value()); - else if (min_width_length.IsAuto()) - min_width = LayoutUnit(); - - LayoutUnit allowed_shrinkage = (min_width - width).ClampPositiveToZero(); - return allowed_shrinkage; - } const Length& min_height_length = child->StyleRef().MinHeight(); if (min_height_length.IsFixed() || min_height_length.IsAuto()) { LayoutUnit min_height(min_height_length.Value()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h index d05ba26dc16..4ee630f91b7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h @@ -33,7 +33,7 @@ class FlexBoxIterator; // eventually be replaced by LayoutFlexibleBox. class LayoutDeprecatedFlexibleBox final : public LayoutBlock { public: - LayoutDeprecatedFlexibleBox(Element&); + LayoutDeprecatedFlexibleBox(Element* element); ~LayoutDeprecatedFlexibleBox() override; const char* GetName() const override { return "LayoutDeprecatedFlexibleBox"; } @@ -42,38 +42,22 @@ class LayoutDeprecatedFlexibleBox final : public LayoutBlock { const ComputedStyle& new_style) override; void UpdateBlockLayout(bool relayout_children) override; - void LayoutHorizontalBox(bool relayout_children); void LayoutVerticalBox(bool relayout_children); bool IsDeprecatedFlexibleBox() const override { return true; } bool IsFlexibleBoxIncludingDeprecatedAndNG() const override { return true; } - bool IsStretchingChildren() const { return stretching_children_; } void PlaceChild(LayoutBox* child, const LayoutPoint& location); private: - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; LayoutUnit AllowedChildFlex(LayoutBox* child, bool expanding); - bool IsVertical() const { - return StyleRef().BoxOrient() == EBoxOrient::kVertical; - } - bool IsHorizontal() const { - return StyleRef().BoxOrient() == EBoxOrient::kHorizontal; - } - void ApplyLineClamp(FlexBoxIterator&, bool relayout_children); void ClearLineClamp(); - - bool stretching_children_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutDeprecatedFlexibleBox, - IsDeprecatedFlexibleBox()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_DEPRECATED_FLEXIBLE_BOX_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_details_marker.h b/chromium/third_party/blink/renderer/core/layout/layout_details_marker.h index 087b367b199..a2efcbffdfa 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_details_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_details_marker.h @@ -41,9 +41,6 @@ class LayoutDetailsMarker final : public LayoutBlockFlow { LayoutBlockFlow::IsOfType(type); } void Paint(const PaintInfo&) const override; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } bool IsOpen() const; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc index f768337c124..41b92eaa2eb 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.cc @@ -24,6 +24,7 @@ #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" #include "third_party/blink/renderer/core/frame/embedded_content_view.h" @@ -68,8 +69,7 @@ void LayoutEmbeddedContent::WillBeDestroyed() { LayoutReplaced::WillBeDestroyed(); } -void LayoutEmbeddedContent::Destroy() { - WillBeDestroyed(); +void LayoutEmbeddedContent::DeleteThis() { // We call clearNode here because LayoutEmbeddedContent is ref counted. This // call to destroy may not actually destroy the layout object. We can keep it // around because of references from the LocalFrameView class. (The actual @@ -95,7 +95,7 @@ FrameView* LayoutEmbeddedContent::ChildFrameView() const { WebPluginContainerImpl* LayoutEmbeddedContent::Plugin() const { EmbeddedContentView* embedded_content_view = GetEmbeddedContentView(); if (embedded_content_view && embedded_content_view->IsPluginView()) - return ToWebPluginContainerImpl(embedded_content_view); + return To<WebPluginContainerImpl>(embedded_content_view); return nullptr; } @@ -128,8 +128,15 @@ bool LayoutEmbeddedContent::RequiresAcceleratedCompositing() const { if (!element) return false; - if (element->ContentFrame() && element->ContentFrame()->IsRemoteFrame()) - return true; + if (Frame* content_frame = element->ContentFrame()) { + if (content_frame->IsRemoteFrame()) + return true; + if (base::FeatureList::IsEnabled( + blink::features::kCompositeCrossOriginIframes) && + content_frame->IsCrossOriginToParentFrame()) { + return true; + } + } if (Document* content_document = element->contentDocument()) { auto* layout_view = content_document->GetLayoutView(); @@ -302,7 +309,7 @@ void LayoutEmbeddedContent::InvalidatePaint( } CursorDirective LayoutEmbeddedContent::GetCursor(const PhysicalOffset& point, - Cursor& cursor) const { + ui::Cursor& cursor) const { if (Plugin()) { // A plugin is responsible for setting the cursor when the pointer is over // it. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.h b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.h index cde0a2f09aa..ce5d1ef6a97 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_embedded_content.h @@ -27,6 +27,10 @@ #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/layout/layout_replaced.h" +namespace ui { +class Cursor; +} + namespace blink { class EmbeddedContentView; @@ -75,15 +79,16 @@ class CORE_EXPORT LayoutEmbeddedContent : public LayoutReplaced { void PaintReplaced(const PaintInfo&, const PhysicalOffset& paint_offset) const override; void InvalidatePaint(const PaintInvalidatorContext&) const final; - CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const final; + CursorDirective GetCursor(const PhysicalOffset&, ui::Cursor&) const final; bool CanBeSelectionLeafInternal() const final { return true; } private: + bool CanHaveAdditionalCompositingReasons() const override { return true; } CompositingReasons AdditionalCompositingReasons() const override; void WillBeDestroyed() final; - void Destroy() final; + void DeleteThis() final; bool NodeAtPointOverEmbeddedContentView( HitTestResult&, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_fieldset.cc b/chromium/third_party/blink/renderer/core/layout/layout_fieldset.cc index eaf12e75073..7fba1ae5141 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_fieldset.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_fieldset.cc @@ -32,14 +32,14 @@ namespace blink { LayoutFieldset::LayoutFieldset(Element* element) : LayoutBlockFlow(element) {} -void LayoutFieldset::ComputePreferredLogicalWidths() { - LayoutBlockFlow::ComputePreferredLogicalWidths(); +MinMaxSizes LayoutFieldset::PreferredLogicalWidths() const { + MinMaxSizes sizes = LayoutBlockFlow::PreferredLogicalWidths(); // Size-contained elements don't consider their contents for preferred sizing. if (ShouldApplySizeContainment()) - return; + return sizes; if (LayoutBox* legend = FindInFlowLegend()) { - int legend_min_width = legend->MinPreferredLogicalWidth().ToInt(); + int legend_min_width = legend->PreferredLogicalWidths().min_size.ToInt(); const Length& legend_margin_left = legend->StyleRef().MarginLeft(); const Length& legend_margin_right = legend->StyleRef().MarginRight(); @@ -50,10 +50,11 @@ void LayoutFieldset::ComputePreferredLogicalWidths() { if (legend_margin_right.IsFixed()) legend_min_width += legend_margin_right.Value(); - min_preferred_logical_width_ = - max(min_preferred_logical_width_, - legend_min_width + BorderAndPaddingWidth()); + sizes.min_size = + max(sizes.min_size, legend_min_width + BorderAndPaddingWidth()); } + + return sizes; } LayoutObject* LayoutFieldset::LayoutSpecialExcludedChild(bool relayout_children, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_fieldset.h b/chromium/third_party/blink/renderer/core/layout/layout_fieldset.h index 16db91bde89..e5edff116ae 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_fieldset.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_fieldset.h @@ -49,7 +49,7 @@ class LayoutFieldset final : public LayoutBlockFlow { LayoutObject* LayoutSpecialExcludedChild(bool relayout_children, SubtreeLayoutScope&) override; - void ComputePreferredLogicalWidths() override; + MinMaxSizes PreferredLogicalWidths() const override; void PaintBoxDecorationBackground( const PaintInfo&, @@ -58,8 +58,6 @@ class LayoutFieldset final : public LayoutBlockFlow { const PhysicalOffset& paint_offset) const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutFieldset, IsFieldset()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_FIELDSET_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.cc b/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.cc index 737a0bca9c0..589ba9e2cba 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.cc @@ -21,47 +21,35 @@ #include "third_party/blink/renderer/core/layout/layout_file_upload_control.h" #include <math.h> -#include "third_party/blink/public/strings/grit/blink_strings.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" -#include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/core/fileapi/file_list.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/input_type_names.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/paint/file_upload_control_painter.h" #include "third_party/blink/renderer/platform/fonts/font.h" -#include "third_party/blink/renderer/platform/text/platform_locale.h" +#include "third_party/blink/renderer/platform/fonts/string_truncator.h" #include "third_party/blink/renderer/platform/text/text_run.h" namespace blink { -const int kDefaultWidthNumChars = 34; const int kButtonShadowHeight = 2; -LayoutFileUploadControl::LayoutFileUploadControl(HTMLInputElement* input) - : LayoutBlockFlow(input), - can_receive_dropped_files_(input->CanReceiveDroppedFiles()) {} +LayoutFileUploadControl::LayoutFileUploadControl(Element* input) + : LayoutBlockFlow(input) { + DCHECK_EQ(To<HTMLInputElement>(input)->type(), input_type_names::kFile); +} LayoutFileUploadControl::~LayoutFileUploadControl() = default; -void LayoutFileUploadControl::UpdateFromElement() { - auto* input = To<HTMLInputElement>(GetNode()); - DCHECK_EQ(input->type(), input_type_names::kFile); - - if (HTMLInputElement* button = UploadButton()) { - bool new_can_receive_dropped_files_state = input->CanReceiveDroppedFiles(); - if (can_receive_dropped_files_ != new_can_receive_dropped_files_state) { - can_receive_dropped_files_ = new_can_receive_dropped_files_state; - button->SetActive(new_can_receive_dropped_files_state); - } - } - - // This only supports clearing out the files, but that's OK because for - // security reasons that's the only change the DOM is allowed to make. - FileList* files = input->files(); - DCHECK(files); - if (files && files->IsEmpty()) - SetShouldDoFullPaintInvalidation(); +bool LayoutFileUploadControl::IsChildAllowed(LayoutObject* child, + const ComputedStyle& style) const { + const Node* child_node = child->GetNode(); + // Reject shadow nodes other than UploadButton. + if (child_node && child_node->OwnerShadowHost() == GetNode() && + child_node != UploadButton()) + return false; + return LayoutBlockFlow::IsChildAllowed(child, style); } int LayoutFileUploadControl::MaxFilenameWidth() const { @@ -79,110 +67,27 @@ void LayoutFileUploadControl::PaintObject( FileUploadControlPainter(*this).PaintObject(paint_info, paint_offset); } -void LayoutFileUploadControl::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - // Figure out how big the filename space needs to be for a given number of - // characters (using "0" as the nominal character). - const UChar kCharacter = '0'; - const String character_as_string = String(&kCharacter, 1); - const Font& font = StyleRef().GetFont(); - float min_default_label_width = - kDefaultWidthNumChars * - font.Width(ConstructTextRun(font, character_as_string, StyleRef(), - TextRun::kAllowTrailingExpansion)); - - const String label = To<HTMLInputElement>(GetNode())->GetLocale().QueryString( - IDS_FORM_FILE_NO_FILE_LABEL); - float default_label_width = font.Width(ConstructTextRun( - font, label, StyleRef(), TextRun::kAllowTrailingExpansion)); - if (HTMLInputElement* button = UploadButton()) { - if (LayoutObject* button_layout_object = button->GetLayoutObject()) - default_label_width += button_layout_object->MaxPreferredLogicalWidth() + - kAfterButtonSpacing; - } - max_logical_width = - LayoutUnit(ceilf(std::max(min_default_label_width, default_label_width))); - - if (!StyleRef().Width().IsPercentOrCalc()) - min_logical_width = max_logical_width; -} - -void LayoutFileUploadControl::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); - - min_preferred_logical_width_ = LayoutUnit(); - max_preferred_logical_width_ = LayoutUnit(); - const ComputedStyle& style_to_use = StyleRef(); - - if (style_to_use.Width().IsFixed() && style_to_use.Width().Value() > 0) - min_preferred_logical_width_ = max_preferred_logical_width_ = - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.Width().Value())); - else - ComputeIntrinsicLogicalWidths(min_preferred_logical_width_, - max_preferred_logical_width_); - - if (style_to_use.MinWidth().IsFixed() && - style_to_use.MinWidth().Value() > 0) { - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.MinWidth().Value()))); - min_preferred_logical_width_ = - std::max(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.MinWidth().Value()))); - } - - if (style_to_use.MaxWidth().IsFixed()) { - max_preferred_logical_width_ = - std::min(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.MaxWidth().Value()))); - min_preferred_logical_width_ = - std::min(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - LayoutUnit(style_to_use.MaxWidth().Value()))); - } - - int to_add = BorderAndPaddingWidth().ToInt(); - min_preferred_logical_width_ += to_add; - max_preferred_logical_width_ += to_add; - - ClearPreferredLogicalWidthsDirty(); -} - -PositionWithAffinity LayoutFileUploadControl::PositionForPoint( - const PhysicalOffset&) const { - return PositionWithAffinity(); -} - HTMLInputElement* LayoutFileUploadControl::UploadButton() const { - // FIXME: This should be on HTMLInputElement as an API like - // innerButtonElement(). - auto* input = To<HTMLInputElement>(GetNode()); - return DynamicTo<HTMLInputElement>( - input->UserAgentShadowRoot()->firstChild()); -} - -String LayoutFileUploadControl::ButtonValue() { - if (HTMLInputElement* button = UploadButton()) - return button->value(); - - return String(); + return To<HTMLInputElement>(GetNode())->UploadButton(); } String LayoutFileUploadControl::FileTextValue() const { + int width = MaxFilenameWidth(); + if (width <= 0) + return String(); auto* input = To<HTMLInputElement>(GetNode()); DCHECK(input->files()); - return LayoutTheme::GetTheme().FileListNameForWidth( - input->GetLocale(), input->files(), StyleRef().GetFont(), - MaxFilenameWidth()); + String text = input->FileStatusText(); + if (input->files()->length() >= 2) + return StringTruncator::RightTruncate(text, width, StyleRef().GetFont()); + return StringTruncator::CenterTruncate(text, width, StyleRef().GetFont()); } -PhysicalRect LayoutFileUploadControl::ControlClipRect( - const PhysicalOffset& additional_offset) const { +// Override to allow effective clip rect to be bigger than the padding box +// because of kButtonShadowHeight. +PhysicalRect LayoutFileUploadControl::OverflowClipRect( + const PhysicalOffset& additional_offset, + OverlayScrollbarClipBehavior) const { PhysicalRect rect(additional_offset, Size()); rect.Expand(BorderInsets()); rect.offset.top -= LayoutUnit(kButtonShadowHeight); @@ -190,12 +95,4 @@ PhysicalRect LayoutFileUploadControl::ControlClipRect( return rect; } -// Override to allow effective ControlClipRect to be bigger than the padding -// box because of kButtonShadowHeight. -PhysicalRect LayoutFileUploadControl::OverflowClipRect( - const PhysicalOffset& additional_offset, - OverlayScrollbarClipBehavior) const { - return ControlClipRect(additional_offset); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.h b/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.h index 83aceca59f4..372714a41ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_file_upload_control.h @@ -27,8 +27,6 @@ namespace blink { -class HTMLInputElement; - // Each LayoutFileUploadControl contains a LayoutButton (for opening the file // chooser), and sufficient space to draw a file icon and filename. The // LayoutButton has a shadow node associated with it to receive click/hover @@ -36,7 +34,7 @@ class HTMLInputElement; class CORE_EXPORT LayoutFileUploadControl final : public LayoutBlockFlow { public: - LayoutFileUploadControl(HTMLInputElement*); + explicit LayoutFileUploadControl(Element*); ~LayoutFileUploadControl() override; bool IsOfType(LayoutObjectType type) const override { @@ -44,39 +42,24 @@ class CORE_EXPORT LayoutFileUploadControl final : public LayoutBlockFlow { LayoutBlockFlow::IsOfType(type); } - String ButtonValue(); String FileTextValue() const; HTMLInputElement* UploadButton() const; - int UploadButtonWidth(); - bool HasControlClip() const override { return true; } - PhysicalRect ControlClipRect(const PhysicalOffset&) const override; PhysicalRect OverflowClipRect(const PhysicalOffset&, OverlayScrollbarClipBehavior) const override; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } - static const int kAfterButtonSpacing = 4; const char* GetName() const override { return "LayoutFileUploadControl"; } private: - void UpdateFromElement() override; - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; - void ComputePreferredLogicalWidths() override; + bool IsChildAllowed(LayoutObject* child, + const ComputedStyle& style) const override; void PaintObject(const PaintInfo&, const PhysicalOffset& paint_offset) const override; int MaxFilenameWidth() const; - - PositionWithAffinity PositionForPoint(const PhysicalOffset&) const override; - - bool can_receive_dropped_files_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutFileUploadControl, IsFileUploadControl()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc index 97ebd2901a5..0e51f4ec744 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.cc @@ -33,10 +33,12 @@ #include <limits> #include "base/auto_reset.h" #include "third_party/blink/renderer/core/frame/web_feature.h" +#include "third_party/blink/renderer/core/html/forms/html_select_element.h" #include "third_party/blink/renderer/core/layout/flexible_box_algorithm.h" #include "third_party/blink/renderer/core/layout/layout_state.h" +#include "third_party/blink/renderer/core/layout/layout_video.h" #include "third_party/blink/renderer/core/layout/layout_view.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/text_autosizer.h" @@ -52,7 +54,7 @@ namespace blink { static bool HasAspectRatio(const LayoutBox& child) { - return child.IsImage() || child.IsCanvas() || child.IsVideo(); + return child.IsImage() || child.IsCanvas() || IsA<LayoutVideo>(child); } LayoutFlexibleBox::LayoutFlexibleBox(Element* element) @@ -66,19 +68,37 @@ LayoutFlexibleBox::LayoutFlexibleBox(Element* element) LayoutFlexibleBox::~LayoutFlexibleBox() = default; -void LayoutFlexibleBox::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - LayoutUnit scrollbar_width(ScrollbarLogicalWidth()); +bool LayoutFlexibleBox::IsChildAllowed(LayoutObject* object, + const ComputedStyle& style) const { + const auto* select = DynamicTo<HTMLSelectElement>(GetNode()); + if (UNLIKELY(select && select->UsesMenuList())) { + // For a size=1 <select>, we only render the active option label through the + // InnerElement. We do not allow adding layout objects for options and + // optgroups. + return object->GetNode() == &select->InnerElement(); + } + return LayoutBlock::IsChildAllowed(object, style); +} + +MinMaxSizes LayoutFlexibleBox::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth() + ScrollbarLogicalWidth(); + if (HasOverrideIntrinsicContentLogicalWidth()) { - max_logical_width = min_logical_width = - OverrideIntrinsicContentLogicalWidth() + scrollbar_width; - return; + sizes += OverrideIntrinsicContentLogicalWidth(); + return sizes; } - if (ShouldApplySizeContainment()) { - max_logical_width = min_logical_width = scrollbar_width; - return; + LayoutUnit default_inline_size = DefaultIntrinsicContentInlineSize(); + if (default_inline_size != kIndefiniteSize) { + sizes.max_size += default_inline_size; + if (!StyleRef().LogicalWidth().IsPercentOrCalc()) + sizes.min_size = sizes.max_size; + return sizes; } + if (ShouldApplySizeContainment()) + return sizes; + + MinMaxSizes child_sizes; // FIXME: We're ignoring flex-basis here and we shouldn't. We can't start // honoring it though until the flex shorthand stops setting it to 0. See @@ -95,7 +115,7 @@ void LayoutFlexibleBox::ComputeIntrinsicLogicalWidths( LayoutUnit min_preferred_logical_width; LayoutUnit max_preferred_logical_width; if (child->NeedsPreferredWidthsRecalculation()) - child->SetPreferredLogicalWidthsDirty(); + child->SetIntrinsicLogicalWidthsDirty(); ComputeChildPreferredLogicalWidths(*child, min_preferred_logical_width, max_preferred_logical_width); DCHECK_GE(min_preferred_logical_width, LayoutUnit()); @@ -103,35 +123,35 @@ void LayoutFlexibleBox::ComputeIntrinsicLogicalWidths( min_preferred_logical_width += margin; max_preferred_logical_width += margin; if (!IsColumnFlow()) { - max_logical_width += max_preferred_logical_width; + child_sizes.max_size += max_preferred_logical_width; if (IsMultiline()) { // For multiline, the min preferred width is if you put a break between // each item. - min_logical_width = - std::max(min_logical_width, min_preferred_logical_width); + child_sizes.min_size = + std::max(child_sizes.min_size, min_preferred_logical_width); } else { - min_logical_width += min_preferred_logical_width; + child_sizes.min_size += min_preferred_logical_width; } } else { - min_logical_width = - std::max(min_preferred_logical_width, min_logical_width); - max_logical_width = - std::max(max_preferred_logical_width, max_logical_width); + child_sizes.min_size = + std::max(min_preferred_logical_width, child_sizes.min_size); + child_sizes.max_size = + std::max(max_preferred_logical_width, child_sizes.max_size); } previous_max_content_flex_fraction = CountIntrinsicSizeForAlgorithmChange( max_preferred_logical_width, child, previous_max_content_flex_fraction); } - max_logical_width = std::max(min_logical_width, max_logical_width); + child_sizes.max_size = std::max(child_sizes.min_size, child_sizes.max_size); // Due to negative margins, it is possible that we calculated a negative // intrinsic width. Make sure that we never return a negative width. - min_logical_width = std::max(LayoutUnit(), min_logical_width); - max_logical_width = std::max(LayoutUnit(), max_logical_width); + child_sizes.min_size = std::max(LayoutUnit(), child_sizes.min_size); + child_sizes.max_size = std::max(LayoutUnit(), child_sizes.max_size); - max_logical_width += scrollbar_width; - min_logical_width += scrollbar_width; + sizes += child_sizes; + return sizes; } float LayoutFlexibleBox::CountIntrinsicSizeForAlgorithmChange( @@ -622,9 +642,9 @@ LayoutUnit LayoutFlexibleBox::ComputeMainAxisExtentForChild( // that here. (Compare code in LayoutBlock::computePreferredLogicalWidths) if (child.StyleRef().LogicalWidth().IsAuto() && !HasAspectRatio(child)) { if (size.IsMinContent()) - return child.MinPreferredLogicalWidth() - border_and_padding; + return child.PreferredLogicalWidths().min_size - border_and_padding; if (size.IsMaxContent()) - return child.MaxPreferredLogicalWidth() - border_and_padding; + return child.PreferredLogicalWidths().max_size - border_and_padding; } return child.ComputeLogicalWidthUsing(size_type, size, ContentLogicalWidth(), this) - @@ -808,7 +828,7 @@ void LayoutFlexibleBox::CacheChildMainSize(const LayoutBox& child) { DisplayLockLifecycleTarget::kChildren)); LayoutUnit main_size; if (MainAxisIsInlineAxis(child)) { - main_size = child.MaxPreferredLogicalWidth(); + main_size = child.PreferredLogicalWidths().max_size; } else { if (FlexBasisForChild(child).IsPercentOrCalc() && !MainAxisLengthIsDefinite(child, FlexBasisForChild(child))) { @@ -861,7 +881,7 @@ LayoutUnit LayoutFlexibleBox::ComputeInnerFlexBaseSizeForChild( LayoutBox& child, LayoutUnit main_axis_border_and_padding, ChildLayoutType child_layout_type) { - if (child.IsImage() || child.IsVideo() || child.IsCanvas()) + if (child.IsImage() || IsA<LayoutVideo>(child) || child.IsCanvas()) UseCounter::Count(GetDocument(), WebFeature::kAspectRatioFlexItem); Length flex_basis = FlexBasisForChild(child); @@ -895,7 +915,7 @@ LayoutUnit LayoutFlexibleBox::ComputeInnerFlexBaseSizeForChild( if (MainAxisIsInlineAxis(child)) { // We don't need to add ScrollbarLogicalWidth here because the preferred // width includes the scrollbar, even for overflow: auto. - main_axis_extent = child.MaxPreferredLogicalWidth(); + main_axis_extent = child.PreferredLogicalWidths().max_size; } else { // The needed value here is the logical height. This value does not include // the border/scrollbar/padding size, so we have to add the scrollbar. @@ -1019,28 +1039,20 @@ void LayoutFlexibleBox::PrepareOrderIteratorAndMargins() { continue; // Before running the flex algorithm, 'auto' has a margin of 0. - // Also, if we're not auto sizing, we don't do a layout that computes the - // start/end margins. - if (IsHorizontalFlow()) { - child->SetMarginLeft( - ComputeChildMarginValue(child->StyleRef().MarginLeft())); - child->SetMarginRight( - ComputeChildMarginValue(child->StyleRef().MarginRight())); - } else { - child->SetMarginTop( - ComputeChildMarginValue(child->StyleRef().MarginTop())); - child->SetMarginBottom( - ComputeChildMarginValue(child->StyleRef().MarginBottom())); - } + const ComputedStyle& style = child->StyleRef(); + child->SetMarginTop(ComputeChildMarginValue(style.MarginTop())); + child->SetMarginRight(ComputeChildMarginValue(style.MarginRight())); + child->SetMarginBottom(ComputeChildMarginValue(style.MarginBottom())); + child->SetMarginLeft(ComputeChildMarginValue(style.MarginLeft())); } } DISABLE_CFI_PERF -MinMaxSize LayoutFlexibleBox::ComputeMinAndMaxSizesForChild( +MinMaxSizes LayoutFlexibleBox::ComputeMinAndMaxSizesForChild( const FlexLayoutAlgorithm& algorithm, const LayoutBox& child, LayoutUnit border_and_padding) const { - MinMaxSize sizes{LayoutUnit(), LayoutUnit::Max()}; + MinMaxSizes sizes{LayoutUnit(), LayoutUnit::Max()}; const Length& max = IsHorizontalFlow() ? child.StyleRef().MaxWidth() : child.StyleRef().MaxHeight(); @@ -1204,21 +1216,25 @@ void LayoutFlexibleBox::ConstructAndAppendFlexItem( } } - LayoutUnit border_and_padding = IsHorizontalFlow() - ? child.BorderAndPaddingWidth() - : child.BorderAndPaddingHeight(); + LayoutUnit main_axis_border_padding = IsHorizontalFlow() + ? child.BorderAndPaddingWidth() + : child.BorderAndPaddingHeight(); + LayoutUnit cross_axis_border_padding = IsHorizontalFlow() + ? child.BorderAndPaddingHeight() + : child.BorderAndPaddingWidth(); - LayoutUnit child_inner_flex_base_size = - ComputeInnerFlexBaseSizeForChild(child, border_and_padding, layout_type); + LayoutUnit child_inner_flex_base_size = ComputeInnerFlexBaseSizeForChild( + child, main_axis_border_padding, layout_type); - MinMaxSize sizes = - ComputeMinAndMaxSizesForChild(*algorithm, child, border_and_padding); + MinMaxSizes sizes = ComputeMinAndMaxSizesForChild(*algorithm, child, + main_axis_border_padding); - LayoutUnit margin = - IsHorizontalFlow() ? child.MarginWidth() : child.MarginHeight(); - algorithm->emplace_back(&child, child_inner_flex_base_size, sizes, - /* cross axis min max sizes */ base::nullopt, - border_and_padding, margin); + NGPhysicalBoxStrut physical_margins(child.MarginTop(), child.MarginRight(), + child.MarginBottom(), child.MarginLeft()); + algorithm->emplace_back( + &child, child.StyleRef(), child_inner_flex_base_size, sizes, + /* min_max_cross_sizes */ base::nullopt, main_axis_border_padding, + cross_axis_border_padding, physical_margins); } void LayoutFlexibleBox::SetOverrideMainAxisContentSizeForChild(FlexItem& item) { @@ -1455,6 +1471,7 @@ void LayoutFlexibleBox::ApplyLineItemsPosition(FlexLine* current_line) { const FlexItem& flex_item = current_line->line_items[i]; LayoutBox* child = flex_item.box; SetFlowAwareLocationForChild(*child, flex_item.desired_location); + child->SetMargin(flex_item.physical_margins); if (is_paginated) UpdateFragmentationInfoForChild(*child); @@ -1558,6 +1575,7 @@ void LayoutFlexibleBox::AlignChildren(FlexLayoutAlgorithm& algorithm) { flex_item.needs_relayout_for_stretch = false; } ResetAlignmentForChild(*flex_item.box, flex_item.desired_location.Y()); + flex_item.box->SetMargin(flex_item.physical_margins); } } } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h index 903f746a233..3b3d34ea1ca 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box.h @@ -41,7 +41,7 @@ class FlexItem; class FlexItemVectorView; class FlexLayoutAlgorithm; class FlexLine; -struct MinMaxSize; +struct MinMaxSizes; class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { public: @@ -55,6 +55,8 @@ class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { bool IsFlexibleBoxIncludingDeprecatedAndNG() const final { return true; } void UpdateBlockLayout(bool relayout_children) final; + bool IsChildAllowed(LayoutObject* object, + const ComputedStyle& style) const override; LayoutUnit BaselinePosition( FontBaseline, bool first_line, @@ -99,9 +101,7 @@ class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { LayoutUnit CrossAxisContentExtent() const; protected: - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; bool HitTestChildren(HitTestResult&, const HitTestLocation&, @@ -177,9 +177,10 @@ class CORE_EXPORT LayoutFlexibleBox : public LayoutBlock { LayoutUnit ComputeChildMarginValue(const Length& margin); void PrepareOrderIteratorAndMargins(); - MinMaxSize ComputeMinAndMaxSizesForChild(const FlexLayoutAlgorithm& algorithm, - const LayoutBox& child, - LayoutUnit border_and_padding) const; + MinMaxSizes ComputeMinAndMaxSizesForChild( + const FlexLayoutAlgorithm& algorithm, + const LayoutBox& child, + LayoutUnit border_and_padding) const; LayoutUnit AdjustChildSizeForAspectRatioCrossAxisMinAndMax( const LayoutBox& child, LayoutUnit child_size) const; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc index 781b9c7e0fb..07853d0ae57 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc @@ -496,11 +496,12 @@ TEST_P(LayoutFlexibleBoxTest, ResizedFlexChildRequiresVisualOverflowRecalc) { auto* child1_element = GetElementById("child1"); auto* child2_element = GetElementById("child2"); child2_element->setAttribute(html_names::kStyleAttr, "height: 100px;"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); auto* child1_box = ToLayoutBox(child1_element->GetLayoutObject()); ASSERT_TRUE(child1_box->HasSelfPaintingLayer()); - EXPECT_TRUE(child1_box->Layer()->NeedsVisualOverflowRecalcForTesting()); + EXPECT_TRUE(child1_box->Layer()->NeedsVisualOverflowRecalc()); UpdateAllLifecyclePhasesForTest(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_flow_thread.h b/chromium/third_party/blink/renderer/core/layout/layout_flow_thread.h index 51130666c56..d43f6eb612d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_flow_thread.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_flow_thread.h @@ -105,6 +105,8 @@ class CORE_EXPORT LayoutFlowThread : public LayoutBlockFlow { // can easily avoid drawing the children directly. PaintLayerType LayerTypeRequired() const final { return kNormalPaintLayer; } + bool NeedsPreferredWidthsRecalculation() const final { return true; } + virtual void FlowThreadDescendantWasInserted(LayoutObject*) {} virtual void FlowThreadDescendantWillBeRemoved(LayoutObject*) {} virtual void FlowThreadDescendantStyleWillChange( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_frame_set.cc b/chromium/third_party/blink/renderer/core/layout/layout_frame_set.cc index 8fe99fd5403..fc00cf0cf40 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_frame_set.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_frame_set.cc @@ -32,7 +32,7 @@ #include "third_party/blink/renderer/core/layout/layout_frame.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/frame_set_painter.h" -#include "third_party/blink/renderer/platform/cursor.h" +#include "third_party/blink/renderer/platform/cursors.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" namespace blink { @@ -65,12 +65,6 @@ void LayoutFrameSet::Paint(const PaintInfo& paint_info) const { FrameSetPainter(*this).Paint(paint_info); } -void LayoutFrameSet::ComputePreferredLogicalWidths() { - min_preferred_logical_width_ = LayoutUnit(); - max_preferred_logical_width_ = LayoutUnit(); - ClearPreferredLogicalWidthsDirty(); -} - void LayoutFrameSet::GridAxis::Resize(int size) { sizes_.resize(size); deltas_.resize(size); @@ -577,7 +571,7 @@ bool LayoutFrameSet::IsChildAllowed(LayoutObject* child, } CursorDirective LayoutFrameSet::GetCursor(const PhysicalOffset& point, - Cursor& cursor) const { + ui::Cursor& cursor) const { IntPoint rounded_point = RoundedIntPoint(point); if (CanResizeRow(rounded_point)) { cursor = RowResizeCursor(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_frame_set.h b/chromium/third_party/blink/renderer/core/layout/layout_frame_set.h index 3d193caad5c..6c9aae4e7c9 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_frame_set.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_frame_set.h @@ -132,9 +132,15 @@ class LayoutFrameSet final : public LayoutBox { void UpdateLayout() override; void Paint(const PaintInfo&) const override; - void ComputePreferredLogicalWidths() override; + + MinMaxSizes PreferredLogicalWidths() const override { return MinMaxSizes(); } + MinMaxSizes ComputeIntrinsicLogicalWidths() const final { + NOTREACHED(); + return MinMaxSizes(); + } + bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override; - CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const override; + CursorDirective GetCursor(const PhysicalOffset&, ui::Cursor&) const override; void SetIsResizing(bool); @@ -149,10 +155,6 @@ class LayoutFrameSet final : public LayoutBox { void StartResizing(GridAxis&, int position); void ContinueResizing(GridAxis&, int position); - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } - LayoutObjectChildList children_; GridAxis rows_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc index 8a933f61e59..5333c86d923 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map.cc @@ -28,6 +28,7 @@ #include "base/auto_reset.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #define LAYOUT_GEOMETRY_MAP_LOGGING 0 @@ -105,7 +106,7 @@ void LayoutGeometryMap::MapToAncestor( } } - if (in_fixed && current_step.layout_object_->IsLayoutView()) { + if (in_fixed && IsA<LayoutView>(current_step.layout_object_)) { transform_state.Move(current_step.offset_for_fixed_position_); in_fixed = false; } @@ -119,7 +120,7 @@ void LayoutGeometryMap::MapToAncestor( const LayoutGeometryMapStep& current_step = mapping_[i]; if (current_step.flags_ & (kContainsFixedPosition | kIsFixedPosition)) break; - if (current_step.layout_object_->IsLayoutView()) { + if (IsA<LayoutView>(current_step.layout_object_)) { transform_state.Move(current_step.offset_for_fixed_position_); break; } @@ -282,7 +283,7 @@ void LayoutGeometryMap::PushMappingsToAncestor( // The LayoutView must be pushed first. if (!mapping_.size()) { - DCHECK(ancestor_layer->GetLayoutObject().IsLayoutView()); + DCHECK(IsA<LayoutView>(ancestor_layer->GetLayoutObject())); PushMappingsToAncestor(&ancestor_layer->GetLayoutObject(), nullptr); } @@ -311,9 +312,9 @@ void LayoutGeometryMap::Push(const LayoutObject* layout_object, #endif DCHECK_NE(insertion_position_, kNotFound); - DCHECK(!layout_object->IsLayoutView() || !insertion_position_ || + DCHECK(!IsA<LayoutView>(layout_object) || !insertion_position_ || map_coordinates_flags_ & kTraverseDocumentBoundaries); - DCHECK(offset_for_fixed_position.IsZero() || layout_object->IsLayoutView()); + DCHECK(offset_for_fixed_position.IsZero() || IsA<LayoutView>(layout_object)); mapping_.insert(insertion_position_, LayoutGeometryMapStep(layout_object, flags)); @@ -330,9 +331,9 @@ void LayoutGeometryMap::Push(const LayoutObject* layout_object, GeometryInfoFlags flags, PhysicalOffset offset_for_fixed_position) { DCHECK_NE(insertion_position_, kNotFound); - DCHECK(!layout_object->IsLayoutView() || !insertion_position_ || + DCHECK(!IsA<LayoutView>(layout_object) || !insertion_position_ || map_coordinates_flags_ & kTraverseDocumentBoundaries); - DCHECK(offset_for_fixed_position.IsZero() || layout_object->IsLayoutView()); + DCHECK(offset_for_fixed_position.IsZero() || IsA<LayoutView>(layout_object)); mapping_.insert(insertion_position_, LayoutGeometryMapStep(layout_object, flags)); @@ -411,7 +412,7 @@ void LayoutGeometryMap::StepRemoved(const LayoutGeometryMapStep& step) { #if DCHECK_IS_ON() bool LayoutGeometryMap::IsTopmostLayoutView( const LayoutObject* layout_object) const { - if (!layout_object->IsLayoutView()) + if (!IsA<LayoutView>(layout_object)) return false; // If we're not working with multiple LayoutViews, then any view is considered diff --git a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc index e04efaedda7..381b7eb16ad 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_geometry_map_test.cc @@ -144,7 +144,7 @@ class LayoutGeometryMapTest : public testing::Test { void UpdateAllLifecyclePhases(WebView* web_view) { web_view->MainFrameWidget()->UpdateAllLifecyclePhases( - WebWidget::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); } const std::string base_url_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_grid.cc b/chromium/third_party/blink/renderer/core/layout/layout_grid.cc index 0d9e5a5dc20..1b03b70feb3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_grid.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_grid.cc @@ -194,7 +194,12 @@ bool LayoutGrid::ExplicitGridDidResize(const ComputedStyle& old_style) const { bool LayoutGrid::NamedGridLinesDefinitionDidChange( const ComputedStyle& old_style) const { return old_style.NamedGridRowLines() != StyleRef().NamedGridRowLines() || - old_style.NamedGridColumnLines() != StyleRef().NamedGridColumnLines(); + old_style.NamedGridColumnLines() != + StyleRef().NamedGridColumnLines() || + old_style.ImplicitNamedGridRowLines() != + StyleRef().ImplicitNamedGridRowLines() || + old_style.ImplicitNamedGridColumnLines() != + StyleRef().ImplicitNamedGridColumnLines(); } void LayoutGrid::ComputeTrackSizesForDefiniteSize( @@ -246,11 +251,8 @@ void LayoutGrid::RepeatTracksSizingIfNeeded( void LayoutGrid::UpdateBlockLayout(bool relayout_children) { DCHECK(NeedsLayout()); - // We cannot perform a simplifiedLayout() on a dirty grid that - // has positioned items to be laid out. - if (!relayout_children && - (!grid_->NeedsItemsPlacement() || !PosChildNeedsLayout()) && - SimplifiedLayout()) + // We cannot perform a |SimplifiedLayout()| with a dirty grid. + if (!relayout_children && !grid_->NeedsItemsPlacement() && SimplifiedLayout()) return; SubtreeLayoutScope layout_scope(*this); @@ -502,17 +504,13 @@ LayoutUnit LayoutGrid::GuttersSize( return gap_accumulator; } -void LayoutGrid::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - LayoutUnit scrollbar_width = LayoutUnit(ScrollbarLogicalWidth()); - min_logical_width = scrollbar_width; - max_logical_width = scrollbar_width; +MinMaxSizes LayoutGrid::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth() + ScrollbarLogicalWidth(); if (HasOverrideIntrinsicContentLogicalWidth()) { - min_logical_width += OverrideIntrinsicContentLogicalWidth(); - max_logical_width = min_logical_width; - return; + sizes += OverrideIntrinsicContentLogicalWidth(); + return sizes; } std::unique_ptr<Grid> grid = Grid::Create(this); @@ -538,8 +536,9 @@ void LayoutGrid::ComputeIntrinsicLogicalWidths( LayoutUnit total_gutters_size = GuttersSize( algorithm.GetGrid(), kForColumns, 0, number_of_tracks, base::nullopt); - min_logical_width += algorithm.MinContentSize() + total_gutters_size; - max_logical_width += algorithm.MaxContentSize() + total_gutters_size; + sizes.min_size += algorithm.MinContentSize() + total_gutters_size; + sizes.max_size += algorithm.MaxContentSize() + total_gutters_size; + return sizes; } void LayoutGrid::ComputeTrackSizesForIndefiniteSize( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_grid.h b/chromium/third_party/blink/renderer/core/layout/layout_grid.h index abfcb38ae67..9b9bb6b31d7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_grid.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_grid.h @@ -128,9 +128,7 @@ class LayoutGrid final : public LayoutBlock { bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectLayoutGrid || LayoutBlock::IsOfType(type); } - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; void AddChild(LayoutObject* new_child, LayoutObject* before_child = nullptr) override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc index 8f8c5d015d4..1023bca17ae 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.cc @@ -61,23 +61,8 @@ void LayoutHTMLCanvas::CanvasSizeChanged() { if (!Parent()) return; - if (!PreferredLogicalWidthsDirty()) - SetPreferredLogicalWidthsDirty(); - - LayoutSize old_size = Size(); - UpdateLogicalWidth(); - UpdateLogicalHeight(); - if (old_size == Size() && !HasOverrideLogicalWidth() && - !HasOverrideLogicalHeight()) { - // If we have an override size, then we're probably a flex item, and the - // check above is insufficient because updateLogical{Width,Height} just - // used the override size. We actually have to mark ourselves as needing - // layout so the flex algorithm can run and compute our size correctly. - return; - } - - if (!SelfNeedsLayout()) - SetNeedsLayout(layout_invalidation_reason::kSizeChanged); + SetIntrinsicLogicalWidthsDirty(); + SetNeedsLayout(layout_invalidation_reason::kSizeChanged); } void LayoutHTMLCanvas::InvalidatePaint( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.h b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.h index 55c979f7e9d..d1b8612e2c7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_html_canvas.h @@ -56,6 +56,7 @@ class CORE_EXPORT LayoutHTMLCanvas final : public LayoutReplaced { const PhysicalOffset& paint_offset) const override; void IntrinsicSizeChanged() override { CanvasSizeChanged(); } + bool CanHaveAdditionalCompositingReasons() const override { return true; } CompositingReasons AdditionalCompositingReasons() const override; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_iframe.h b/chromium/third_party/blink/renderer/core/layout/layout_iframe.h index eab361c0b9a..5de2e13470c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_iframe.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_iframe.h @@ -50,8 +50,6 @@ class LayoutIFrame final : public LayoutEmbeddedContent { PaintLayerType LayerTypeRequired() const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutIFrame, IsLayoutIFrame()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_IFRAME_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image.cc b/chromium/third_party/blink/renderer/core/layout/layout_image.cc index 571e7c8926a..6f2a3ae49ac 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_image.cc @@ -35,10 +35,12 @@ #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/html/html_area_element.h" #include "third_party/blink/renderer/core/html/html_image_element.h" +#include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html/media/media_element_parser_helpers.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h" +#include "third_party/blink/renderer/core/layout/layout_video.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h" #include "third_party/blink/renderer/core/paint/image_element_timing.h" @@ -156,6 +158,10 @@ bool LayoutImage::NeedsLayoutOnIntrinsicSizeChange() const { kDontRegisterPercentageDescendant); if (!image_size_is_constrained) return true; + // Flex layout algorithm uses the intrinsic image width/height even if + // width/height are specified. + if (IsFlexItemIncludingNG()) + return true; // FIXME: We only need to recompute the containing block's preferred size if // the containing block's size depends on the image's size (i.e., the // container uses shrink-to-fit sizing). There's no easy way to detect that @@ -179,7 +185,7 @@ void LayoutImage::InvalidatePaintAndMarkForLayoutIfNeeded( return; if (old_intrinsic_size != new_intrinsic_size) { - SetPreferredLogicalWidthsDirty(); + SetIntrinsicLogicalWidthsDirty(); if (NeedsLayoutOnIntrinsicSizeChange()) { SetNeedsLayoutAndFullPaintInvalidation( @@ -306,20 +312,19 @@ bool LayoutImage::NodeAtPoint(HitTestResult& result, return inside; } -IntSize LayoutImage::GetOverriddenIntrinsicSize() const { - if (auto* image_element = DynamicTo<HTMLImageElement>(GetNode())) { - if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) - return image_element->GetOverriddenIntrinsicSize(); - } - return IntSize(); +bool LayoutImage::HasOverriddenIntrinsicSize() const { + if (!RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) + return false; + auto* image_element = DynamicTo<HTMLImageElement>(GetNode()); + return image_element && image_element->IsDefaultIntrinsicSize(); } FloatSize LayoutImage::ImageSizeOverriddenByIntrinsicSize( float multiplier) const { - FloatSize overridden_intrinsic_size = FloatSize(GetOverriddenIntrinsicSize()); - if (overridden_intrinsic_size.IsEmpty()) + if (!HasOverriddenIntrinsicSize()) return image_resource_->ImageSize(multiplier); + FloatSize overridden_intrinsic_size(kDefaultWidth, kDefaultHeight); if (multiplier != 1) { overridden_intrinsic_size.Scale(multiplier); if (overridden_intrinsic_size.Width() < 1.0f) @@ -333,11 +338,11 @@ FloatSize LayoutImage::ImageSizeOverriddenByIntrinsicSize( bool LayoutImage::OverrideIntrinsicSizingInfo( IntrinsicSizingInfo& intrinsic_sizing_info) const { - IntSize overridden_intrinsic_size = GetOverriddenIntrinsicSize(); - if (overridden_intrinsic_size.IsEmpty()) + if (!HasOverriddenIntrinsicSize()) return false; - intrinsic_sizing_info.size = FloatSize(overridden_intrinsic_size); + FloatSize overridden_intrinsic_size(kDefaultWidth, kDefaultHeight); + intrinsic_sizing_info.size = overridden_intrinsic_size; intrinsic_sizing_info.aspect_ratio = intrinsic_sizing_info.size; if (!IsHorizontalWritingMode()) intrinsic_sizing_info.Transpose(); @@ -393,7 +398,8 @@ void LayoutImage::ComputeIntrinsicSizingInfo( // we're painting alt text and/or a broken image. // Video is excluded from this behavior because video elements have a default // aspect ratio that a failed poster image load should not override. - if (image_resource_ && image_resource_->ErrorOccurred() && !IsVideo()) { + if (image_resource_ && image_resource_->ErrorOccurred() && + !IsA<LayoutVideo>(this)) { intrinsic_sizing_info.aspect_ratio = FloatSize(1, 1); return; } @@ -414,15 +420,18 @@ SVGImage* LayoutImage::EmbeddedSVGImage() const { // https://crbug.com/761026 if (!cached_image || cached_image->IsCacheValidator()) return nullptr; - return ToSVGImageOrNull(cached_image->GetImage()); + return DynamicTo<SVGImage>(cached_image->GetImage()); } void LayoutImage::UpdateAfterLayout() { LayoutBox::UpdateAfterLayout(); Node* node = GetNode(); if (auto* image_element = DynamicTo<HTMLImageElement>(node)) { - media_element_parser_helpers::ReportUnsizedMediaViolation( + media_element_parser_helpers::CheckUnsizedMediaViolation( this, image_element->IsDefaultIntrinsicSize()); + } else if (auto* video_element = DynamicTo<HTMLVideoElement>(node)) { + media_element_parser_helpers::CheckUnsizedMediaViolation( + this, video_element->IsDefaultIntrinsicSize()); } } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image.h b/chromium/third_party/blink/renderer/core/layout/layout_image.h index 43364aaf8d3..3ed38c13197 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_image.h @@ -135,8 +135,8 @@ class CORE_EXPORT LayoutImage : public LayoutReplaced { // Override intrinsic sizing info to default if "unsized-media" // is disabled and the element has no sizing info. bool OverrideIntrinsicSizingInfo(IntrinsicSizingInfo&) const; + bool HasOverriddenIntrinsicSize() const; FloatSize ImageSizeOverriddenByIntrinsicSize(float multiplier) const; - IntSize GetOverriddenIntrinsicSize() const; // This member wraps the associated decoded image. // diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image_resource.cc b/chromium/third_party/blink/renderer/core/layout/layout_image_resource.cc index 1f260426bcd..57bc7eb48e2 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image_resource.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_image_resource.cc @@ -168,7 +168,8 @@ scoped_refptr<Image> LayoutImageResource::GetImage( layout_object_->StyleRef().EffectiveZoom()); } - if (!image->IsSVGImage()) + auto* svg_image = DynamicTo<SVGImage>(image); + if (!svg_image) return image; KURL url; @@ -177,8 +178,8 @@ scoped_refptr<Image> LayoutImageResource::GetImage( url = element->GetDocument().CompleteURL(url_string); } return SVGImageForContainer::Create( - ToSVGImage(image), container_size, - layout_object_->StyleRef().EffectiveZoom(), url); + svg_image, container_size, layout_object_->StyleRef().EffectiveZoom(), + url); } bool LayoutImageResource::MaybeAnimated() const { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image_resource.h b/chromium/third_party/blink/renderer/core/layout/layout_image_resource.h index af9230cd5f0..808a0958044 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image_resource.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_image_resource.h @@ -70,7 +70,7 @@ class CORE_EXPORT LayoutImageResource const LayoutSize&) const; virtual WrappedImagePtr ImagePtr() const { return cached_image_.Get(); } - virtual void Trace(blink::Visitor* visitor) { visitor->Trace(cached_image_); } + virtual void Trace(Visitor* visitor) { visitor->Trace(cached_image_); } protected: // Device scale factor for the associated LayoutObject. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.cc b/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.cc index 77873f52cef..ed8c3dfe576 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.cc @@ -78,10 +78,11 @@ FloatSize LayoutImageResourceStyleImage::ImageSize(float multiplier) const { FloatSize LayoutImageResourceStyleImage::ImageSizeWithDefaultSize( float multiplier, const LayoutSize& default_size) const { - return style_image_->ImageSize(layout_object_->GetDocument(), multiplier, - default_size); + return style_image_->ImageSize( + layout_object_->GetDocument(), multiplier, default_size, + LayoutObject::ShouldRespectImageOrientation(layout_object_)); } -void LayoutImageResourceStyleImage::Trace(blink::Visitor* visitor) { +void LayoutImageResourceStyleImage::Trace(Visitor* visitor) { visitor->Trace(style_image_); LayoutImageResource::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.h b/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.h index 1b2bd849409..108d42d137f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_image_resource_style_image.h @@ -55,7 +55,7 @@ class LayoutImageResourceStyleImage final : public LayoutImageResource { const LayoutSize&) const override; WrappedImagePtr ImagePtr() const override { return style_image_->Data(); } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: Member<StyleImage> style_image_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline.cc b/chromium/third_party/blink/renderer/core/layout/layout_inline.cc index a8a84440bd0..a7dd0e57408 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline.cc @@ -171,7 +171,20 @@ void LayoutInline::SetFirstInlineFragmentItemIndex(wtf_size_t index) { CHECK(IsInLayoutNGInlineFormattingContext()) << *this; DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); DCHECK_NE(index, 0u); - first_fragment_item_index_ = index; + // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|, + // we should enable below. + // first_fragment_item_index_ = index; +} + +bool LayoutInline::HasInlineFragments() const { + if (!IsInLayoutNGInlineFormattingContext()) + return FirstLineBox(); + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return first_paint_fragment_; + // TODO(yosin): We should use |first_fragment_item_index_|. + NGInlineCursor cursor; + cursor.MoveTo(*this); + return cursor; } void LayoutInline::InLayoutNGInlineFormattingContextWillChange(bool new_value) { @@ -518,7 +531,7 @@ void LayoutInline::AddChildIgnoringContinuation(LayoutObject* new_child, LayoutBoxModelObject::AddChild(new_child, before_child); - new_child->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + new_child->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kChildChanged); } @@ -673,7 +686,7 @@ void LayoutInline::SplitFlow(LayoutObject* before_child, o = no->NextSibling(); pre->Children()->AppendChildNode( pre, block->Children()->RemoveChildNode(block, no)); - no->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + no->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); } } @@ -691,11 +704,11 @@ void LayoutInline::SplitFlow(LayoutObject* before_child, // wrappers for images) get deleted properly. Because objects moves from the // pre block into the post block, we want to make new line boxes instead of // leaving the old line boxes around. - pre->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + pre->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); - block->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + block->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); - post->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + post->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAnonymousBlockChange); } @@ -790,9 +803,16 @@ void LayoutInline::CollectLineBoxRects( const auto* box_fragment = ContainingBlockFlowFragmentOf(*this); if (!box_fragment) return; - for (const auto& fragment : - NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this)) - yield(fragment.RectInContainerBox()); + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + for (const auto& fragment : + NGInlineFragmentTraversal::SelfFragmentsOf(*box_fragment, this)) + yield(fragment.RectInContainerBox()); + return; + } + NGInlineCursor cursor; + cursor.MoveTo(*this); + for (; cursor; cursor.MoveToNextForSameLayoutObject()) + yield(cursor.Current().RectInContainerBlock()); return; } if (!AlwaysCreateLineBoxes()) { @@ -940,7 +960,7 @@ base::Optional<PhysicalOffset> LayoutInline::FirstLineBoxTopLeftInternal() cursor.MoveTo(*this); if (!cursor) return base::nullopt; - return cursor.CurrentOffset(); + return cursor.Current().OffsetInContainerBlock(); } if (const InlineBox* first_box = FirstLineBoxIncludingCulling()) { LayoutPoint location = first_box->Location(); @@ -1027,21 +1047,41 @@ bool LayoutInline::NodeAtPoint(HitTestResult& result, HitTestAction hit_test_action) { if (ContainingNGBlockFlow()) { // TODO(crbug.com/965976): We should fix the root cause of the missed - // layout, and then turn this into a DCHECK. - CHECK(!NeedsLayout()) << this; + // layout. + if (UNLIKELY(NeedsLayout())) { + NOTREACHED(); + return false; + } // In LayoutNG, we reach here only when called from // PaintLayer::HitTestContents() without going through any ancestor, in // which case the element must have self painting layer. DCHECK(HasSelfPaintingLayer()); - for (const NGPaintFragment* fragment : - NGPaintFragment::InlineFragmentsFor(this)) { + NGInlineCursor cursor; + for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { + if (const NGPaintFragment* paint_fragment = + cursor.Current().PaintFragment()) { + // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is + // accumulated up to the fragment itself. Compute this offset. + const PhysicalOffset child_offset = + accumulated_offset + paint_fragment->OffsetInContainerBlock(); + if (NGBoxFragmentPainter(*paint_fragment) + .NodeAtPoint(result, hit_test_location, child_offset, + hit_test_action)) + return true; + continue; + } + DCHECK(cursor.Current().Item()); + const NGFragmentItem& item = *cursor.Current().Item(); + const NGPhysicalBoxFragment* box_fragment = item.BoxFragment(); + DCHECK(box_fragment); // NGBoxFragmentPainter::NodeAtPoint() takes an offset that is accumulated // up to the fragment itself. Compute this offset. - PhysicalOffset adjusted_location = - accumulated_offset + fragment->InlineOffsetToContainerBox(); - if (NGBoxFragmentPainter(*fragment).NodeAtPoint( - result, hit_test_location, adjusted_location, hit_test_action)) + const PhysicalOffset child_offset = + accumulated_offset + item.OffsetInContainerBlock(); + if (NGBoxFragmentPainter(cursor, item, *box_fragment) + .NodeAtPoint(result, hit_test_location, child_offset, + accumulated_offset, hit_test_action)) return true; } return false; @@ -1083,7 +1123,7 @@ bool LayoutInline::HitTestCulledInline( container_fragment->PhysicalFragment().IsLineBox()); NGInlineCursor cursor(*container_fragment); for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) - yield(cursor.CurrentRect()); + yield(cursor.Current().RectInContainerBlock()); } else { DCHECK(!ContainingNGBlockFlow()); CollectCulledLineBoxRects(yield); @@ -1135,7 +1175,7 @@ PhysicalRect LayoutInline::PhysicalLinesBoundingBox() const { cursor.MoveTo(*this); PhysicalRect bounding_box; for (; cursor; cursor.MoveToNextForSameLayoutObject()) - bounding_box.UniteIfNonZero(cursor.CurrentRect()); + bounding_box.UniteIfNonZero(cursor.Current().RectInContainerBlock()); return bounding_box; } @@ -1283,8 +1323,8 @@ PhysicalRect LayoutInline::LinesVisualOverflowBoundingBox() const { NGInlineCursor cursor; cursor.MoveTo(*this); for (; cursor; cursor.MoveToNextForSameLayoutObject()) { - PhysicalRect child_rect = cursor.CurrentInkOverflow(); - child_rect.offset += cursor.CurrentOffset(); + PhysicalRect child_rect = cursor.Current().InkOverflow(); + child_rect.offset += cursor.Current().OffsetInContainerBlock(); result.Unite(child_rect); } return result; @@ -1394,7 +1434,7 @@ PhysicalRect LayoutInline::ReferenceBoxForClipPath() const { NGInlineCursor cursor; cursor.MoveTo(*this); if (cursor) - return cursor.CurrentRect(); + return cursor.Current().RectInContainerBlock(); } if (const InlineFlowBox* flow_box = FirstLineBox()) return FlipForWritingMode(flow_box->FrameRect()); @@ -1736,7 +1776,8 @@ void LayoutInline::InvalidateDisplayItemClients( PaintInvalidationReason invalidation_reason) const { ObjectPaintInvalidator paint_invalidator(*this); - if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled()) { + if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled() && + !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { auto fragments = NGPaintFragment::InlineFragmentsFor(this); if (fragments.IsInLayoutNGInlineFormattingContext()) { for (NGPaintFragment* fragment : fragments) { @@ -1748,13 +1789,14 @@ void LayoutInline::InvalidateDisplayItemClients( } if (IsInLayoutNGInlineFormattingContext()) { - if (!ShouldCreateBoxFragment()) + if (!ShouldCreateBoxFragment() && + !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) return; NGInlineCursor cursor; for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { - DCHECK_EQ(cursor.CurrentLayoutObject(), this); + DCHECK_EQ(cursor.Current().GetLayoutObject(), this); paint_invalidator.InvalidateDisplayItemClient( - *cursor.CurrentDisplayItemClient(), invalidation_reason); + *cursor.Current().GetDisplayItemClient(), invalidation_reason); } return; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline.h b/chromium/third_party/blink/renderer/core/layout/layout_inline.h index a6a4fe2a5fa..448bf29cadd 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline.h @@ -172,16 +172,13 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject { return AlwaysCreateLineBoxes() ? LastLineBox() : CulledInlineLastLineBox(); } + bool HasInlineFragments() const final; NGPaintFragment* FirstInlineFragment() const final; void SetFirstInlineFragment(NGPaintFragment*) final; wtf_size_t FirstInlineFragmentItemIndex() const final; void ClearFirstInlineFragmentItemIndex() final; void SetFirstInlineFragmentItemIndex(wtf_size_t) final; - // Return true if this inline doesn't occur on any lines, i.e. when it creates - // no fragments. - bool IsEmpty() const { return !FirstLineBox() && !FirstInlineFragment(); } - LayoutBoxModelObject* VirtualContinuation() const final { return Continuation(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc index 7570ca21718..2c8922ef27c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_inline_test.cc @@ -4,9 +4,11 @@ #include "third_party/blink/renderer/core/layout/layout_inline.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" @@ -25,7 +27,9 @@ class ParameterizedLayoutInlineTest : public testing::WithParamInterface<bool>, ParameterizedLayoutInlineTest() : ScopedLayoutNGForTest(GetParam()) {} protected: - bool LayoutNGEnabled() const { return GetParam(); } + bool LayoutNGEnabled() const { + return RuntimeEnabledFeatures::LayoutNGEnabled(); + } }; INSTANTIATE_TEST_SUITE_P(All, ParameterizedLayoutInlineTest, testing::Bool()); @@ -107,6 +111,14 @@ TEST_F(LayoutInlineTest, RegionHitTest) { ToLayoutInline(GetLayoutObjectByElementId("lotsOfBoxes")); ASSERT_TRUE(lots_of_boxes); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + NGInlineCursor cursor; + cursor.MoveTo(*lots_of_boxes); + ASSERT_TRUE(cursor); + EXPECT_EQ(lots_of_boxes, cursor.Current().GetLayoutObject()); + return; + } + HitTestRequest hit_request(HitTestRequest::kTouchEvent | HitTestRequest::kListBased); @@ -676,4 +688,119 @@ TEST_P(ParameterizedLayoutInlineTest, AddAnnotatedRegionsVerticalRL) { EXPECT_TRUE(regions3.IsEmpty()); } +TEST_P(ParameterizedLayoutInlineTest, VisualOverflowRecalcLegacyLayout) { + // "contenteditable" forces us to use legacy layout, other options could be + // using "display: -webkit-box", ruby, etc. + LoadAhem(); + SetBodyInnerHTML(R"HTML( + <style> + body { + margin: 0; + font: 20px/20px Ahem; + } + target { + outline: 50px solid red; + } + </style> + <div contenteditable> + <span id="span">SPAN1</span> + <span id="span2">SPAN2</span> + </div> + )HTML"); + + auto* span = ToLayoutInline(GetLayoutObjectByElementId("span")); + auto* span_element = GetDocument().getElementById("span"); + auto* span2_element = GetDocument().getElementById("span2"); + + span_element->setAttribute(html_names::kStyleAttr, "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(-50, -50, 200, 120), + span->PhysicalVisualOverflowRect()); + + span_element->setAttribute(html_names::kStyleAttr, ""); + span2_element->setAttribute(html_names::kStyleAttr, + "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(0, 0, 100, 20), span->PhysicalVisualOverflowRect()); + + span2_element->setAttribute(html_names::kStyleAttr, ""); + span_element->setAttribute(html_names::kStyleAttr, "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(-50, -50, 200, 120), + span->PhysicalVisualOverflowRect()); + + span_element->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(0, 0, 100, 20), span->PhysicalVisualOverflowRect()); +} + +TEST_P(ParameterizedLayoutInlineTest, VisualOverflowRecalcLayoutNG) { + LoadAhem(); + SetBodyInnerHTML(R"HTML( + <style> + body { + margin: 0; + font: 20px/20px Ahem; + } + target { + outline: 50px solid red; + } + </style> + <div> + <span id="span">SPAN1</span> + <span id="span2">SPAN2</span> + </div> + )HTML"); + + auto* span = ToLayoutInline(GetLayoutObjectByElementId("span")); + auto* span_element = GetDocument().getElementById("span"); + auto* span2_element = GetDocument().getElementById("span2"); + + span_element->setAttribute(html_names::kStyleAttr, "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(-50, -50, 200, 120), + span->PhysicalVisualOverflowRect()); + + span_element->setAttribute(html_names::kStyleAttr, ""); + span2_element->setAttribute(html_names::kStyleAttr, + "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(0, 0, 100, 20), span->PhysicalVisualOverflowRect()); + + span2_element->setAttribute(html_names::kStyleAttr, ""); + span_element->setAttribute(html_names::kStyleAttr, "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(-50, -50, 200, 120), + span->PhysicalVisualOverflowRect()); + + span_element->setAttribute(html_names::kStyleAttr, ""); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(0, 0, 100, 20), span->PhysicalVisualOverflowRect()); +} + +TEST_P(ParameterizedLayoutInlineTest, + VisualOverflowRecalcLegacyLayoutPositionRelative) { + LoadAhem(); + SetBodyInnerHTML(R"HTML( + <style> + body { + margin: 0; + font: 20px/20px Ahem; + } + span { + position: relative; + } + </style> + <span id="span">SPAN</span> + )HTML"); + + auto* span = ToLayoutInline(GetLayoutObjectByElementId("span")); + auto* span_element = GetDocument().getElementById("span"); + + span_element->setAttribute(html_names::kStyleAttr, "outline: 50px solid red"); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(PhysicalRect(-50, -50, 180, 120), + span->PhysicalVisualOverflowRect()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.cc new file mode 100644 index 00000000000..ada7d6b11d0 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.cc @@ -0,0 +1,14 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/layout_inside_list_marker.h" + +namespace blink { + +LayoutInsideListMarker::LayoutInsideListMarker(Element* element) + : LayoutListMarker(element) {} + +LayoutInsideListMarker::~LayoutInsideListMarker() = default; + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.h b/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.h new file mode 100644 index 00000000000..22426dfdec1 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/layout_inside_list_marker.h @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_INSIDE_LIST_MARKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_INSIDE_LIST_MARKER_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_list_marker.h" + +namespace blink { + +// Used to layout the list item's inside marker. +// The LayoutInsideListMarker always has to be a child of a LayoutListItem. +class CORE_EXPORT LayoutInsideListMarker final : public LayoutListMarker { + public: + explicit LayoutInsideListMarker(Element*); + ~LayoutInsideListMarker() override; + + const char* GetName() const override { return "LayoutInsideListMarker"; } + + private: + bool IsOfType(LayoutObjectType type) const override { + return type == kLayoutObjectInsideListMarker || + LayoutListMarker::IsOfType(type); + } +}; + +DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutInsideListMarker, IsInsideListMarker()); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_INSIDE_LIST_MARKER_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_box.cc b/chromium/third_party/blink/renderer/core/layout/layout_list_box.cc deleted file mode 100644 index 1e9c1061abc..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_box.cc +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. - * 2009 Torch Mobile Inc. All rights reserved. - * (http://www.torchmobile.com/) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/core/layout/layout_list_box.h" - -#include "third_party/blink/public/platform/web_scroll_into_view_params.h" -#include "third_party/blink/renderer/core/dom/element_traversal.h" -#include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h" -#include "third_party/blink/renderer/core/html/forms/html_option_element.h" -#include "third_party/blink/renderer/core/html/forms/html_select_element.h" -#include "third_party/blink/renderer/core/html/html_div_element.h" -#include "third_party/blink/renderer/core/paint/paint_layer.h" -#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" -#include "third_party/blink/renderer/core/scroll/scroll_alignment.h" -#include "third_party/blink/renderer/core/scroll/scroll_types.h" - -namespace blink { - -// Default size when the multiple attribute is present but size attribute is -// absent. -const int kDefaultSize = 4; - -const int kDefaultPaddingBottom = 1; - -LayoutListBox::LayoutListBox(Element* element) : LayoutBlockFlow(element) { - DCHECK(element); - DCHECK(element->IsHTMLElement()); - DCHECK(IsA<HTMLSelectElement>(element)); -} - -LayoutListBox::~LayoutListBox() = default; - -inline HTMLSelectElement* LayoutListBox::SelectElement() const { - return To<HTMLSelectElement>(GetNode()); -} - -unsigned LayoutListBox::size() const { - unsigned specified_size = SelectElement()->size(); - if (specified_size >= 1) - return specified_size; - - return kDefaultSize; -} - -LayoutUnit LayoutListBox::DefaultItemHeight() const { - const SimpleFontData* font_data = StyleRef().GetFont().PrimaryFont(); - if (!font_data) - return LayoutUnit(); - return LayoutUnit(font_data->GetFontMetrics().Height() + - kDefaultPaddingBottom); -} - -LayoutUnit LayoutListBox::ItemHeight() const { - // If the intrinsic-inline-size is specified, then we shouldn't ever need to - // get the ItemHeight. - DCHECK(!HasOverrideIntrinsicContentLogicalHeight()); - - HTMLSelectElement* select = SelectElement(); - if (!select) - return LayoutUnit(); - - const auto& items = select->GetListItems(); - if (items.IsEmpty() || ShouldApplySizeContainment()) - return DefaultItemHeight(); - - LayoutUnit max_height; - for (Element* element : items) { - if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(element)) - element = &optgroup->OptGroupLabelElement(); - LayoutObject* layout_object = element->GetLayoutObject(); - LayoutUnit item_height; - if (layout_object && layout_object->IsBox()) - item_height = ToLayoutBox(layout_object)->Size().Height(); - else - item_height = DefaultItemHeight(); - max_height = std::max(max_height, item_height); - } - return max_height; -} - -void LayoutListBox::ComputeLogicalHeight( - LayoutUnit, - LayoutUnit logical_top, - LogicalExtentComputedValues& computed_values) const { - LayoutUnit height; - if (HasOverrideIntrinsicContentLogicalHeight()) { - height = OverrideIntrinsicContentLogicalHeight(); - } else { - height = ItemHeight() * size(); - } - - // FIXME: The item height should have been added before updateLogicalHeight - // was called to avoid this hack. - SetIntrinsicContentLogicalHeight(height); - - height += BorderAndPaddingHeight(); - - LayoutBox::ComputeLogicalHeight(height, logical_top, computed_values); -} - -void LayoutListBox::StopAutoscroll() { - HTMLSelectElement* select = SelectElement(); - if (select->IsDisabledFormControl()) - return; - select->HandleMouseRelease(); -} - -void LayoutListBox::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - LayoutBlockFlow::ComputeIntrinsicLogicalWidths(min_logical_width, - max_logical_width); - if (StyleRef().Width().IsPercentOrCalc()) - min_logical_width = LayoutUnit(); -} - -void LayoutListBox::ScrollToRect(const PhysicalRect& absolute_rect) { - if (HasOverflowClip()) { - DCHECK(Layer()); - DCHECK(Layer()->GetScrollableArea()); - Layer()->GetScrollableArea()->ScrollIntoView( - absolute_rect, WebScrollIntoViewParams( - ScrollAlignment::kAlignToEdgeIfNeeded, - ScrollAlignment::kAlignToEdgeIfNeeded, - kProgrammaticScroll, false, kScrollBehaviorInstant)); - } -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_box.h b/chromium/third_party/blink/renderer/core/layout/layout_list_box.h deleted file mode 100644 index 91e76c1b097..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_box.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of the select element layoutObject in WebCore. - * - * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_LIST_BOX_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_LIST_BOX_H_ - -#include "third_party/blink/renderer/core/layout/layout_block_flow.h" - -namespace blink { - -class HTMLSelectElement; - -class CORE_EXPORT LayoutListBox final : public LayoutBlockFlow { - public: - explicit LayoutListBox(Element*); - ~LayoutListBox() override; - - unsigned size() const; - - // Unlike scrollRectToVisible this will not scroll parent boxes. - void ScrollToRect(const PhysicalRect&); - - const char* GetName() const override { return "LayoutListBox"; } - - private: - HTMLSelectElement* SelectElement() const; - - bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectListBox || LayoutBlockFlow::IsOfType(type); - } - - void ComputeLogicalHeight(LayoutUnit logical_height, - LayoutUnit logical_top, - LogicalExtentComputedValues&) const override; - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; - - void StopAutoscroll() override; - - LayoutUnit DefaultItemHeight() const; - LayoutUnit ItemHeight() const; -}; - -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutListBox, IsListBox()); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_LIST_BOX_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc b/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc index 6f882e11a4a..df0dd904451 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_item.cc @@ -36,7 +36,6 @@ namespace blink { LayoutListItem::LayoutListItem(Element* element) : LayoutBlockFlow(element), - marker_(nullptr), need_block_direction_align_(false) { SetInline(false); @@ -51,13 +50,7 @@ void LayoutListItem::StyleDidChange(StyleDifference diff, StyleImage* current_image = StyleRef().ListStyleImage(); if (StyleRef().ListStyleType() != EListStyleType::kNone || (current_image && !current_image->ErrorOccurred())) { - if (!marker_) - marker_ = LayoutListMarker::CreateAnonymous(this); - marker_->ListItemStyleDidChange(); NotifyOfSubtreeChange(); - } else if (marker_) { - marker_->Destroy(); - marker_ = nullptr; } StyleImage* old_image = old_style ? old_style->ListStyleImage() : nullptr; @@ -70,11 +63,6 @@ void LayoutListItem::StyleDidChange(StyleDifference diff, } void LayoutListItem::WillBeDestroyed() { - if (marker_) { - marker_->Destroy(); - marker_ = nullptr; - } - LayoutBlockFlow::WillBeDestroyed(); if (Style() && StyleRef().ListStyleImage()) @@ -94,7 +82,8 @@ void LayoutListItem::WillBeRemovedFromTree() { } void LayoutListItem::SubtreeDidChange() { - if (!marker_) + LayoutListMarker* marker = Marker(); + if (!marker) return; if (!UpdateMarkerLocation()) @@ -102,8 +91,8 @@ void LayoutListItem::SubtreeDidChange() { // If the marker is inside we need to redo the preferred width calculations // as the size of the item now includes the size of the list marker. - if (marker_->IsInside()) - SetPreferredLogicalWidthsDirty(); + if (marker->IsInsideListMarker()) + SetIntrinsicLogicalWidthsDirty(); } int LayoutListItem::Value() const { @@ -112,13 +101,12 @@ int LayoutListItem::Value() const { } bool LayoutListItem::IsEmpty() const { - return LastChild() == marker_; + return LastChild() == Marker(); } namespace { -LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, - LayoutObject* marker) { +LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr) { LayoutObject* first_child = curr->FirstChild(); if (!first_child) return nullptr; @@ -126,7 +114,7 @@ LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, bool in_quirks_mode = curr->GetDocument().InQuirksMode(); for (LayoutObject* curr_child = first_child; curr_child; curr_child = curr_child->NextSibling()) { - if (curr_child == marker) + if (curr_child->IsOutsideListMarker()) continue; if (curr_child->IsInline() && @@ -150,7 +138,7 @@ LayoutObject* GetParentOfFirstLineBox(LayoutBlockFlow* curr, IsA<HTMLOListElement>(*curr_child->GetNode()))) break; - LayoutObject* line_box = GetParentOfFirstLineBox(child_block_flow, marker); + LayoutObject* line_box = GetParentOfFirstLineBox(child_block_flow); if (line_box) return line_box; } @@ -186,21 +174,27 @@ void ForceLogicalHeight(LayoutObject& layout_object, const Length& height) { // marker_container to 0px; else restore it to LogicalHeight of <li>. bool LayoutListItem::PrepareForBlockDirectionAlign( const LayoutObject* line_box_parent) { - LayoutObject* marker_parent = marker_->Parent(); + LayoutListMarker* marker = Marker(); + LayoutObject* marker_parent = marker->Parent(); + bool is_inside = marker->IsInsideListMarker(); // Deal with the situation of layout tree changed. if (marker_parent && marker_parent->IsAnonymous()) { + bool marker_parent_has_lines = + line_box_parent && line_box_parent->IsDescendantOf(marker_parent); // When list-position-style change from outside to inside, we need to - // restore LogicalHeight to auto. So add IsInside(). - if (marker_->IsInside() || marker_->NextSibling()) { + // restore LogicalHeight to auto. So add is_inside. + if (is_inside || marker_parent_has_lines) { // Set marker_container's LogicalHeight to auto. if (marker_parent->StyleRef().LogicalHeight().IsZero()) ForceLogicalHeight(*marker_parent, Length()); - // If marker_parent isn't the ancestor of line_box_parent, marker might - // generate a new empty line. We need to remove marker here.E.g: - // <li><span><div>text<div><span></li> - if (line_box_parent && !line_box_parent->IsDescendantOf(marker_parent)) { - marker_->Remove(); + // If marker_parent_has_lines and the marker is outside, we need to move + // the marker into another parent with 'height: 0' to avoid generating a + // new empty line in cases like <li><span><div>text<div><span></li> + // If the marker is inside and there are inline contents, we want them to + // share the same block container to avoid a line break between them. + if (is_inside != marker_parent_has_lines) { + marker->Remove(); marker_parent = nullptr; } } else if (line_box_parent) { @@ -211,19 +205,18 @@ bool LayoutListItem::PrepareForBlockDirectionAlign( // Create marker_container, set its height to 0px, and add it to li. if (!marker_parent) { LayoutObject* before_child = FirstNonMarkerChild(this); - if (!marker_->IsInside() && before_child && !before_child->IsInline()) { + if (!is_inside && before_child && !before_child->IsInline()) { // Create marker_container and set its LogicalHeight to 0px. LayoutBlock* marker_container = CreateAnonymousBlock(); if (line_box_parent) ForceLogicalHeight(*marker_container, Length::Fixed(0)); - marker_container->AddChild(marker_, - FirstNonMarkerChild(marker_container)); + marker_container->AddChild(marker, FirstNonMarkerChild(marker_container)); AddChild(marker_container, before_child); } else { - AddChild(marker_, before_child); + AddChild(marker, before_child); } - marker_->UpdateMarginsAndContent(); + marker->UpdateMarginsAndContent(); return true; } return false; @@ -241,13 +234,24 @@ static bool IsFirstLeafChild(LayoutObject* container, LayoutObject* child) { } bool LayoutListItem::UpdateMarkerLocation() { - DCHECK(marker_); + DCHECK(Marker()); - LayoutObject* marker_parent = marker_->Parent(); + LayoutListMarker* marker = Marker(); + LayoutObject* marker_parent = marker->Parent(); LayoutObject* line_box_parent = nullptr; - if (!marker_->IsInside()) - line_box_parent = GetParentOfFirstLineBox(this, marker_); + // Make sure a marker originated by a ::before or ::after precedes the + // generated contents. + if (IsPseudoElement()) { + LayoutObject* first_child = marker_parent->SlowFirstChild(); + if (marker != first_child) { + marker->Remove(); + AddChild(marker, first_child); + } + } + + if (marker->IsOutsideListMarker()) + line_box_parent = GetParentOfFirstLineBox(this); if (line_box_parent && (line_box_parent->HasOverflowClip() || !line_box_parent->IsLayoutBlockFlow() || (line_box_parent->IsBox() && @@ -277,13 +281,13 @@ bool LayoutListItem::UpdateMarkerLocation() { if (!marker_parent || (marker_parent != line_box_parent && NormalChildNeedsLayout())) { - marker_->Remove(); - line_box_parent->AddChild(marker_, FirstNonMarkerChild(line_box_parent)); + marker->Remove(); + line_box_parent->AddChild(marker, FirstNonMarkerChild(line_box_parent)); // TODO(rhogan): line_box_parent and marker_parent may be deleted by // AddChild, so they are not safe to reference here. Once we have a safe way // of referencing them delete marker_parent if it is an empty anonymous // block. - marker_->UpdateMarginsAndContent(); + marker->UpdateMarginsAndContent(); return true; } @@ -309,6 +313,7 @@ void LayoutListItem::ComputeVisualOverflow(bool recompute_floats) { AddVisualOverflowFromFloats(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } @@ -327,7 +332,8 @@ void LayoutListItem::AlignMarkerInBlockDirection() { // layout pass. So if there's no line box in line_box_parent make sure it // back to its original position. bool back_to_original_baseline = false; - LayoutObject* line_box_parent = GetParentOfFirstLineBox(this, marker_); + LayoutListMarker* marker = Marker(); + LayoutObject* line_box_parent = GetParentOfFirstLineBox(this); LayoutBox* line_box_parent_block = nullptr; if (!line_box_parent || !line_box_parent->IsBox()) { back_to_original_baseline = true; @@ -339,12 +345,12 @@ void LayoutListItem::AlignMarkerInBlockDirection() { back_to_original_baseline = true; } - InlineBox* marker_inline_box = marker_->InlineBoxWrapper(); + InlineBox* marker_inline_box = marker->InlineBoxWrapper(); RootInlineBox& marker_root = marker_inline_box->Root(); auto* line_box_parent_block_flow = DynamicTo<LayoutBlockFlow>(line_box_parent_block); if (line_box_parent_block && line_box_parent_block_flow) { - // If marker_ and line_box_parent_block share a same RootInlineBox, no need + // If marker and line_box_parent_block share a same RootInlineBox, no need // to align marker. if (line_box_parent_block_flow->FirstRootBox() == &marker_root) return; @@ -355,7 +361,7 @@ void LayoutListItem::AlignMarkerInBlockDirection() { offset = line_box_parent_block->FirstLineBoxBaseline(); if (back_to_original_baseline || offset == -1) { - line_box_parent_block = marker_->ContainingBlock(); + line_box_parent_block = marker->ContainingBlock(); offset = line_box_parent_block->FirstLineBoxBaseline(); } @@ -369,11 +375,11 @@ void LayoutListItem::AlignMarkerInBlockDirection() { // instead. BaselinePosition is workable when marker is an image. // However, when marker is text, BaselinePosition contains lineheight // information. So use marker_font_metrics.Ascent when marker is text. - if (marker_->IsImage()) { + if (marker->IsImage()) { offset -= marker_inline_box->BaselinePosition(marker_root.BaselineType()); } else { const SimpleFontData* marker_font_data = - marker_->Style(true)->GetFont().PrimaryFont(); + marker->Style(true)->GetFont().PrimaryFont(); if (marker_font_data) { const FontMetrics& marker_font_metrics = marker_font_data->GetFontMetrics(); @@ -382,7 +388,7 @@ void LayoutListItem::AlignMarkerInBlockDirection() { } offset -= marker_inline_box->LogicalTop(); - for (LayoutBox* o = marker_->ParentBox(); o != this; o = o->ParentBox()) { + for (LayoutBox* o = marker->ParentBox(); o != this; o = o->ParentBox()) { offset -= o->LogicalTop(); } @@ -392,24 +398,25 @@ void LayoutListItem::AlignMarkerInBlockDirection() { } void LayoutListItem::UpdateOverflow() { - if (!marker_ || !marker_->Parent() || !marker_->Parent()->IsBox() || - marker_->IsInside() || !marker_->InlineBoxWrapper()) + LayoutListMarker* marker = Marker(); + if (!marker || !marker->Parent() || !marker->Parent()->IsBox() || + marker->IsInsideListMarker() || !marker->InlineBoxWrapper()) return; if (need_block_direction_align_) AlignMarkerInBlockDirection(); - LayoutUnit marker_old_logical_left = marker_->LogicalLeft(); + LayoutUnit marker_old_logical_left = marker->LogicalLeft(); LayoutUnit block_offset; LayoutUnit line_offset; - for (LayoutBox* o = marker_->ParentBox(); o != this; o = o->ParentBox()) { + for (LayoutBox* o = marker->ParentBox(); o != this; o = o->ParentBox()) { block_offset += o->LogicalTop(); line_offset += o->LogicalLeft(); } bool adjust_overflow = false; LayoutUnit marker_logical_left; - InlineBox* marker_inline_box = marker_->InlineBoxWrapper(); + InlineBox* marker_inline_box = marker->InlineBoxWrapper(); RootInlineBox& root = marker_inline_box->Root(); bool hit_self_painting_layer = false; @@ -427,11 +434,11 @@ void LayoutListItem::UpdateOverflow() { // FIXME: Need to account for relative positioning in the layout overflow. if (StyleRef().IsLeftToRightDirection()) { LayoutUnit marker_line_offset = - std::min(marker_->LineOffset(), - LogicalLeftOffsetForLine(marker_->LogicalTop(), + std::min(marker->LineOffset(), + LogicalLeftOffsetForLine(marker->LogicalTop(), kDoNotIndentText, LayoutUnit())); marker_logical_left = marker_line_offset - line_offset - PaddingStart() - - BorderStart() + marker_->MarginStart(); + BorderStart() + marker->MarginStart(); marker_inline_box->MoveInInlineDirection(marker_logical_left - marker_old_logical_left); @@ -468,11 +475,11 @@ void LayoutListItem::UpdateOverflow() { } } else { LayoutUnit marker_line_offset = - std::max(marker_->LineOffset(), - LogicalRightOffsetForLine(marker_->LogicalTop(), + std::max(marker->LineOffset(), + LogicalRightOffsetForLine(marker->LogicalTop(), kDoNotIndentText, LayoutUnit())); marker_logical_left = marker_line_offset - line_offset + PaddingStart() + - BorderStart() + marker_->MarginEnd(); + BorderStart() + marker->MarginEnd(); marker_inline_box->MoveInInlineDirection(marker_logical_left - marker_old_logical_left); @@ -482,11 +489,11 @@ void LayoutListItem::UpdateOverflow() { box->AddReplacedChildrenVisualOverflow(line_top, line_bottom); LayoutRect new_logical_visual_overflow_rect = box->LogicalVisualOverflowRect(line_top, line_bottom); - if (marker_logical_left + marker_->LogicalWidth() > + if (marker_logical_left + marker->LogicalWidth() > new_logical_visual_overflow_rect.MaxX() && !hit_self_painting_layer) { new_logical_visual_overflow_rect.SetWidth( - marker_logical_left + marker_->LogicalWidth() - + marker_logical_left + marker->LogicalWidth() - new_logical_visual_overflow_rect.X()); if (box == root) adjust_overflow = true; @@ -498,10 +505,10 @@ void LayoutListItem::UpdateOverflow() { hit_self_painting_layer = true; LayoutRect new_logical_layout_overflow_rect = box->LogicalLayoutOverflowRect(line_top, line_bottom); - if (marker_logical_left + marker_->LogicalWidth() > + if (marker_logical_left + marker->LogicalWidth() > new_logical_layout_overflow_rect.MaxX()) { new_logical_layout_overflow_rect.SetWidth( - marker_logical_left + marker_->LogicalWidth() - + marker_logical_left + marker->LogicalWidth() - new_logical_layout_overflow_rect.X()); if (box == root) adjust_overflow = true; @@ -518,10 +525,10 @@ void LayoutListItem::UpdateOverflow() { LayoutRect marker_rect( LayoutPoint(marker_logical_left + line_offset, block_offset + marker_inline_box->LogicalTop()), - marker_->Size()); + marker->Size()); if (!StyleRef().IsHorizontalWritingMode()) marker_rect = marker_rect.TransposedRect(); - LayoutBox* object = marker_; + LayoutBox* object = marker; bool found_self_painting_layer = false; do { @@ -549,16 +556,16 @@ void LayoutListItem::Paint(const PaintInfo& paint_info) const { } const String& LayoutListItem::MarkerText() const { - if (marker_) - return marker_->GetText(); + if (LayoutListMarker* marker = Marker()) + return marker->GetText(); return g_null_atom.GetString(); } void LayoutListItem::OrdinalValueChanged() { - if (!marker_) - return; - marker_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - layout_invalidation_reason::kListValueChange); + if (LayoutListMarker* marker = Marker()) { + marker->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( + layout_invalidation_reason::kListValueChange); + } } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_item.h b/chromium/third_party/blink/renderer/core/layout/layout_list_item.h index 3984e8ddabc..b324ba8020a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_item.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_item.h @@ -26,11 +26,10 @@ #include "third_party/blink/renderer/core/html/list_item_ordinal.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/layout_list_marker.h" namespace blink { -class LayoutListMarker; - class LayoutListItem final : public LayoutBlockFlow { public: explicit LayoutListItem(Element*); @@ -41,7 +40,16 @@ class LayoutListItem final : public LayoutBlockFlow { bool IsEmpty() const; - LayoutListMarker* Marker() const { return marker_; } + LayoutListMarker* Marker() const { + Element* list_item = To<Element>(GetNode()); + if (LayoutObject* marker = + list_item->PseudoElementLayoutObject(kPseudoIdMarker)) { + if (marker->IsListMarker()) + return ToLayoutListMarker(marker); + NOTREACHED(); + } + return nullptr; + } ListItemOrdinal& Ordinal() { return ordinal_; } void OrdinalValueChanged(); @@ -80,7 +88,6 @@ class LayoutListItem final : public LayoutBlockFlow { bool PrepareForBlockDirectionAlign(const LayoutObject*); ListItemOrdinal ordinal_; - LayoutListMarker* marker_; bool need_block_direction_align_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc index 7af899cd5b2..0425425e887 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.cc @@ -41,8 +41,8 @@ const int kCMarkerPaddingPx = 7; // Recommended UA margin for list markers. const int kCUAMarkerMarginEm = 1; -LayoutListMarker::LayoutListMarker(LayoutListItem* item) - : LayoutBox(nullptr), list_item_(item), line_offset_() { +LayoutListMarker::LayoutListMarker(Element* element) : LayoutBox(element) { + DCHECK(ListItem()); SetInline(true); SetIsAtomicInlineLevel(true); } @@ -55,11 +55,11 @@ void LayoutListMarker::WillBeDestroyed() { LayoutBox::WillBeDestroyed(); } -LayoutListMarker* LayoutListMarker::CreateAnonymous(LayoutListItem* item) { - Document& document = item->GetDocument(); - LayoutListMarker* layout_object = new LayoutListMarker(item); - layout_object->SetDocumentForAnonymous(&document); - return layout_object; +const LayoutListItem* LayoutListMarker::ListItem() const { + LayoutObject* list_item = GetNode()->parentNode()->GetLayoutObject(); + DCHECK(list_item); + DCHECK(list_item->IsListItem()); + return ToLayoutListItem(list_item); } LayoutSize LayoutListMarker::ImageBulletSize() const { @@ -77,7 +77,8 @@ LayoutSize LayoutListMarker::ImageBulletSize() const { font_data->GetFontMetrics().Ascent() / LayoutUnit(2); return RoundedLayoutSize( image_->ImageSize(GetDocument(), StyleRef().EffectiveZoom(), - LayoutSize(bullet_width, bullet_width))); + LayoutSize(bullet_width, bullet_width), + LayoutObject::ShouldRespectImageOrientation(this))); } void LayoutListMarker::StyleWillChange(StyleDifference diff, @@ -88,7 +89,7 @@ void LayoutListMarker::StyleWillChange(StyleDifference diff, (new_style.ListStyleType() == EListStyleType::kString && new_style.ListStyleStringValue() != StyleRef().ListStyleStringValue()))) { - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kStyleChange); } @@ -127,14 +128,15 @@ void LayoutListMarker::UpdateLayout() { LayoutAnalyzer::Scope analyzer(*this); LayoutUnit block_offset = LogicalTop(); - for (LayoutBox* o = ParentBox(); o && o != ListItem(); o = o->ParentBox()) { + const LayoutListItem* list_item = ListItem(); + for (LayoutBox* o = ParentBox(); o && o != list_item; o = o->ParentBox()) { block_offset += o->LogicalTop(); } - if (ListItem()->StyleRef().IsLeftToRightDirection()) { - line_offset_ = ListItem()->LogicalLeftOffsetForLine( + if (list_item->StyleRef().IsLeftToRightDirection()) { + line_offset_ = list_item->LogicalLeftOffsetForLine( block_offset, kDoNotIndentText, LayoutUnit()); } else { - line_offset_ = ListItem()->LogicalRightOffsetForLine( + line_offset_ = list_item->LogicalRightOffsetForLine( block_offset, kDoNotIndentText, LayoutUnit()); } if (IsImage()) { @@ -145,21 +147,11 @@ void LayoutListMarker::UpdateLayout() { } else { const SimpleFontData* font_data = StyleRef().GetFont().PrimaryFont(); DCHECK(font_data); - SetLogicalWidth(MinPreferredLogicalWidth()); + SetLogicalWidth(PreferredLogicalWidths().min_size); SetLogicalHeight( LayoutUnit(font_data ? font_data->GetFontMetrics().Height() : 0)); } - SetMarginStart(LayoutUnit()); - SetMarginEnd(LayoutUnit()); - - const Length& start_margin = StyleRef().MarginStart(); - const Length& end_margin = StyleRef().MarginEnd(); - if (start_margin.IsFixed()) - SetMarginStart(LayoutUnit(start_margin.Value())); - if (end_margin.IsFixed()) - SetMarginEnd(LayoutUnit(end_margin.Value())); - ClearNeedsLayout(); } @@ -171,7 +163,7 @@ void LayoutListMarker::ImageChanged(WrappedImagePtr o, CanDeferInvalidation) { LayoutSize image_size = IsImage() ? ImageBulletSize() : LayoutSize(); if (Size() != image_size || image_->ErrorOccurred()) { - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kImageChanged); } else { SetShouldDoFullPaintInvalidation(); @@ -179,14 +171,11 @@ void LayoutListMarker::ImageChanged(WrappedImagePtr o, CanDeferInvalidation) { } void LayoutListMarker::UpdateMarginsAndContent() { - if (PreferredLogicalWidthsDirty()) - ComputePreferredLogicalWidths(); - else - UpdateMargins(); + UpdateMargins(PreferredLogicalWidths().min_size); } void LayoutListMarker::UpdateContent() { - DCHECK(PreferredLogicalWidthsDirty()); + DCHECK(IntrinsicLogicalWidthsDirty()); text_ = ""; @@ -202,7 +191,7 @@ void LayoutListMarker::UpdateContent() { break; case ListStyleCategory::kLanguage: text_ = list_marker_text::GetText(StyleRef().ListStyleType(), - list_item_->Value()); + ListItem()->Value()); break; case ListStyleCategory::kStaticString: text_ = StyleRef().ListStyleStringValue(); @@ -214,7 +203,7 @@ String LayoutListMarker::TextAlternative() const { if (GetListStyleCategory() == ListStyleCategory::kStaticString) return text_; UChar suffix = - list_marker_text::Suffix(StyleRef().ListStyleType(), list_item_->Value()); + list_marker_text::Suffix(StyleRef().ListStyleType(), ListItem()->Value()); // Return suffix after the marker text, even in RTL, reflecting speech order. return text_ + suffix + ' '; } @@ -232,7 +221,7 @@ LayoutUnit LayoutListMarker::GetWidthOfText(ListStyleCategory category) const { // TODO(wkorman): Look into constructing a text run for both text and suffix // and painting them together. UChar suffix[2] = { - list_marker_text::Suffix(StyleRef().ListStyleType(), list_item_->Value()), + list_marker_text::Suffix(StyleRef().ListStyleType(), ListItem()->Value()), ' '}; TextRun run = ConstructTextRun(font, suffix, 2, StyleRef(), StyleRef().Direction()); @@ -240,40 +229,36 @@ LayoutUnit LayoutListMarker::GetWidthOfText(ListStyleCategory category) const { return item_width + suffix_space_width; } -void LayoutListMarker::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); - UpdateContent(); +MinMaxSizes LayoutListMarker::ComputeIntrinsicLogicalWidths() const { + DCHECK(IntrinsicLogicalWidthsDirty()); + const_cast<LayoutListMarker*>(this)->UpdateContent(); + MinMaxSizes sizes; if (IsImage()) { LayoutSize image_size(ImageBulletSize()); - min_preferred_logical_width_ = max_preferred_logical_width_ = - StyleRef().IsHorizontalWritingMode() ? image_size.Width() - : image_size.Height(); - ClearPreferredLogicalWidthsDirty(); - UpdateMargins(); - return; - } - - LayoutUnit logical_width; - ListStyleCategory category = GetListStyleCategory(); - switch (category) { - case ListStyleCategory::kNone: - break; - case ListStyleCategory::kSymbol: - logical_width = WidthOfSymbol(StyleRef()); - break; - case ListStyleCategory::kLanguage: - case ListStyleCategory::kStaticString: - logical_width = GetWidthOfText(category); - break; + sizes = StyleRef().IsHorizontalWritingMode() ? image_size.Width() + : image_size.Height(); + } else { + ListStyleCategory category = GetListStyleCategory(); + switch (category) { + case ListStyleCategory::kNone: + break; + case ListStyleCategory::kSymbol: + sizes = WidthOfSymbol(StyleRef()); + break; + case ListStyleCategory::kLanguage: + case ListStyleCategory::kStaticString: + sizes = GetWidthOfText(category); + break; + } } - min_preferred_logical_width_ = logical_width; - max_preferred_logical_width_ = logical_width; - - ClearPreferredLogicalWidthsDirty(); + const_cast<LayoutListMarker*>(this)->UpdateMargins(sizes.min_size); + return sizes; +} - UpdateMargins(); +MinMaxSizes LayoutListMarker::PreferredLogicalWidths() const { + return IntrinsicLogicalWidths(); } LayoutUnit LayoutListMarker::WidthOfSymbol(const ComputedStyle& style) { @@ -285,32 +270,27 @@ LayoutUnit LayoutListMarker::WidthOfSymbol(const ComputedStyle& style) { return LayoutUnit((font_data->GetFontMetrics().Ascent() * 2 / 3 + 1) / 2 + 2); } -void LayoutListMarker::UpdateMargins() { +void LayoutListMarker::UpdateMargins(LayoutUnit marker_inline_size) { LayoutUnit margin_start; LayoutUnit margin_end; const ComputedStyle& style = StyleRef(); - if (IsInside()) { + if (IsInsideListMarker()) { std::tie(margin_start, margin_end) = InlineMarginsForInside(style, IsImage()); } else { std::tie(margin_start, margin_end) = - InlineMarginsForOutside(style, IsImage(), MinPreferredLogicalWidth()); + InlineMarginsForOutside(style, IsImage(), marker_inline_size); } - Length start_length = Length::Fixed(margin_start); - Length end_length = Length::Fixed(margin_end); - - if (start_length != style.MarginStart() || end_length != style.MarginEnd()) { - scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(style); - new_style->SetMarginStart(start_length); - new_style->SetMarginEnd(end_length); - SetStyleInternal(std::move(new_style)); - } + SetMarginStart(margin_start); + SetMarginEnd(margin_end); } std::pair<LayoutUnit, LayoutUnit> LayoutListMarker::InlineMarginsForInside( const ComputedStyle& style, bool is_image) { + if (!style.ContentBehavesAsNormal()) + return {}; if (is_image) return {LayoutUnit(), LayoutUnit(kCMarkerPaddingPx)}; switch (GetListStyleCategory(style.ListStyleType())) { @@ -329,51 +309,31 @@ std::pair<LayoutUnit, LayoutUnit> LayoutListMarker::InlineMarginsForOutside( LayoutUnit marker_inline_size) { LayoutUnit margin_start; LayoutUnit margin_end; - if (style.IsLeftToRightDirection()) { - if (is_image) { - margin_start = -marker_inline_size - kCMarkerPaddingPx; - } else { - switch (GetListStyleCategory(style.ListStyleType())) { - case ListStyleCategory::kNone: - break; - case ListStyleCategory::kSymbol: { - const SimpleFontData* font_data = style.GetFont().PrimaryFont(); - DCHECK(font_data); - if (!font_data) - return {}; - const FontMetrics& font_metrics = font_data->GetFontMetrics(); - int offset = font_metrics.Ascent() * 2 / 3; - margin_start = LayoutUnit(-offset - kCMarkerPaddingPx - 1); - break; - } - default: - margin_start = -marker_inline_size; - } - } - margin_end = -margin_start - marker_inline_size; + if (!style.ContentBehavesAsNormal()) { + margin_start = -marker_inline_size; + } else if (is_image) { + margin_start = -marker_inline_size - kCMarkerPaddingPx; + margin_end = LayoutUnit(kCMarkerPaddingPx); } else { - if (is_image) { - margin_end = LayoutUnit(kCMarkerPaddingPx); - } else { - switch (GetListStyleCategory(style.ListStyleType())) { - case ListStyleCategory::kNone: - break; - case ListStyleCategory::kSymbol: { - const SimpleFontData* font_data = style.GetFont().PrimaryFont(); - DCHECK(font_data); - if (!font_data) - return {}; - const FontMetrics& font_metrics = font_data->GetFontMetrics(); - int offset = font_metrics.Ascent() * 2 / 3; - margin_end = offset + kCMarkerPaddingPx + 1 - marker_inline_size; - break; - } - default: - margin_end = LayoutUnit(); + switch (GetListStyleCategory(style.ListStyleType())) { + case ListStyleCategory::kNone: + break; + case ListStyleCategory::kSymbol: { + const SimpleFontData* font_data = style.GetFont().PrimaryFont(); + DCHECK(font_data); + if (!font_data) + return {}; + const FontMetrics& font_metrics = font_data->GetFontMetrics(); + int offset = font_metrics.Ascent() * 2 / 3; + margin_start = LayoutUnit(-offset - kCMarkerPaddingPx - 1); + margin_end = offset + kCMarkerPaddingPx + 1 - marker_inline_size; + break; } + default: + margin_start = -marker_inline_size; } - margin_start = -margin_end - marker_inline_size; } + DCHECK_EQ(margin_start + margin_end, -marker_inline_size); return {margin_start, margin_end}; } @@ -382,7 +342,7 @@ LayoutUnit LayoutListMarker::LineHeight( LineDirectionMode direction, LinePositionMode line_position_mode) const { if (!IsImage()) - return list_item_->LineHeight(first_line, direction, + return ListItem()->LineHeight(first_line, direction, kPositionOfInteriorLineBoxes); return LayoutBox::LineHeight(first_line, direction, line_position_mode); } @@ -394,7 +354,7 @@ LayoutUnit LayoutListMarker::BaselinePosition( LinePositionMode line_position_mode) const { DCHECK_EQ(line_position_mode, kPositionOnContainingLine); if (!IsImage()) - return list_item_->BaselinePosition(baseline_type, first_line, direction, + return ListItem()->BaselinePosition(baseline_type, first_line, direction, kPositionOfInteriorLineBoxes); return LayoutBox::BaselinePosition(baseline_type, first_line, direction, line_position_mode); @@ -475,11 +435,6 @@ LayoutListMarker::ListStyleCategory LayoutListMarker::GetListStyleCategory( } } -bool LayoutListMarker::IsInside() const { - return list_item_->Ordinal().NotInList() || - StyleRef().ListStylePosition() == EListStylePosition::kInside; -} - LayoutRect LayoutListMarker::GetRelativeMarkerRect() const { if (IsImage()) return LayoutRect(LayoutPoint(), ImageBulletSize()); @@ -535,31 +490,4 @@ LayoutRect LayoutListMarker::RelativeSymbolMarkerRect( return relative_rect; } -void LayoutListMarker::ListItemStyleDidChange() { - Node* list_item = list_item_->GetNode(); - const ComputedStyle* cached_marker_style = - list_item->IsPseudoElement() - ? nullptr - : ToElement(list_item)->CachedStyleForPseudoElement(kPseudoIdMarker); - scoped_refptr<ComputedStyle> new_style; - if (cached_marker_style) { - new_style = ComputedStyle::Clone(*cached_marker_style); - } else { - // The marker always inherits from the list item, regardless of where it - // might end up (e.g., in some deeply nested line box). See CSS3 spec. - new_style = ComputedStyle::Create(); - new_style->InheritFrom(list_item_->StyleRef()); - new_style->SetStyleType(kPseudoIdMarker); - new_style->SetUnicodeBidi(UnicodeBidi::kIsolate); - new_style->SetFontVariantNumericSpacing(FontVariantNumeric::kTabularNums); - } - if (Style()) { - // Reuse the current margins. Otherwise resetting the margins to initial - // values would trigger unnecessary layout. - new_style->SetMarginStart(StyleRef().MarginStart()); - new_style->SetMarginEnd(StyleRef().MarginRight()); - } - SetStyle(std::move(new_style)); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h index 68d1a290b0f..1fee564ee6e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_list_marker.h @@ -31,11 +31,10 @@ namespace blink { class LayoutListItem; -// Used to layout the list item's marker. -// The LayoutListMarker always has to be a child of a LayoutListItem. -class CORE_EXPORT LayoutListMarker final : public LayoutBox { +// This class holds code shared among legacy classes for list markers. +class CORE_EXPORT LayoutListMarker : public LayoutBox { public: - static LayoutListMarker* CreateAnonymous(LayoutListItem*); + explicit LayoutListMarker(Element*); ~LayoutListMarker() override; // Marker text without suffix, e.g. "1". @@ -53,8 +52,6 @@ class CORE_EXPORT LayoutListMarker final : public LayoutBox { ListStyleCategory GetListStyleCategory() const; static ListStyleCategory GetListStyleCategory(EListStyleType); - bool IsInside() const; - void UpdateMarginsAndContent(); // Compute inline margins for 'list-style-position: inside' and 'outside'. @@ -72,26 +69,17 @@ class CORE_EXPORT LayoutListMarker final : public LayoutBox { bool IsImage() const override; const StyleImage* GetImage() const { return image_.Get(); } - const LayoutListItem* ListItem() const { return list_item_; } + const LayoutListItem* ListItem() const; LayoutSize ImageBulletSize() const; - void ListItemStyleDidChange(); - - const char* GetName() const override { return "LayoutListMarker"; } - LayoutUnit LineOffset() const { return line_offset_; } protected: void WillBeDestroyed() override; private: - LayoutListMarker(LayoutListItem*); - - void ComputePreferredLogicalWidths() override; - - bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectListMarker || LayoutBox::IsOfType(type); - } + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; + MinMaxSizes PreferredLogicalWidths() const override; void Paint(const PaintInfo&) const override; @@ -114,21 +102,15 @@ class CORE_EXPORT LayoutListMarker final : public LayoutBox { bool IsText() const { return !IsImage(); } LayoutUnit GetWidthOfText(ListStyleCategory) const; - void UpdateMargins(); + void UpdateMargins(LayoutUnit marker_inline_size); void UpdateContent(); void StyleWillChange(StyleDifference, const ComputedStyle& new_style) override; void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; - bool AnonymousHasStylePropagationOverride() override { return true; } - - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } String text_; Persistent<StyleImage> image_; - LayoutListItem* list_item_; LayoutUnit line_offset_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_media.cc b/chromium/third_party/blink/renderer/core/layout/layout_media.cc index 2764e0b773f..359dc680f00 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_media.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_media.cc @@ -25,6 +25,7 @@ #include "third_party/blink/renderer/core/layout/layout_media.h" +#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/html/media/html_media_element.h" @@ -41,7 +42,7 @@ LayoutMedia::LayoutMedia(HTMLMediaElement* video) : LayoutImage(video) { LayoutMedia::~LayoutMedia() = default; HTMLMediaElement* LayoutMedia::MediaElement() const { - return ToHTMLMediaElement(GetNode()); + return To<HTMLMediaElement>(GetNode()); } void LayoutMedia::UpdateLayout() { @@ -154,9 +155,9 @@ LayoutUnit LayoutMedia::ComputePanelWidth(const LayoutRect& media_rect) const { // TODO(crbug.com/771379): Once we no longer assume that the video is in the // main frame for the visibility calculation below, we will only care about // the video's frame's scrollbar check below. - ScrollbarMode h_mode, v_mode; + mojom::blink::ScrollbarMode h_mode, v_mode; page_view->GetLayoutView()->CalculateScrollbarModes(h_mode, v_mode); - if (h_mode != ScrollbarMode::kAlwaysOff) + if (h_mode != mojom::blink::ScrollbarMode::kAlwaysOff) return media_rect.Width(); // If the video's frame (can be different from main frame if video is in an @@ -165,7 +166,7 @@ LayoutUnit LayoutMedia::ComputePanelWidth(const LayoutRect& media_rect) const { LocalFrameView* media_page_view = media_frame ? media_frame->View() : nullptr; if (media_page_view && media_page_view->GetLayoutView()) { media_page_view->GetLayoutView()->CalculateScrollbarModes(h_mode, v_mode); - if (h_mode != ScrollbarMode::kAlwaysOff) + if (h_mode != mojom::blink::ScrollbarMode::kAlwaysOff) return media_rect.Width(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_media.h b/chromium/third_party/blink/renderer/core/layout/layout_media.h index 98d8a954ffb..d3032678339 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_media.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_media.h @@ -89,8 +89,6 @@ class LayoutMedia : public LayoutImage { LayoutObjectChildList children_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutMedia, IsMedia()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MEDIA_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc deleted file mode 100644 index d10d4e199cd..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.cc +++ /dev/null @@ -1,374 +0,0 @@ -/* - * This file is part of the select element layoutObject in WebCore. - * - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. - * All rights reserved. - * (C) 2009 Torch Mobile Inc. All rights reserved. - * (http://www.torchmobile.com/) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "third_party/blink/renderer/core/layout/layout_menu_list.h" - -#include <math.h> -#include "third_party/blink/public/strings/grit/blink_strings.h" -#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" -#include "third_party/blink/renderer/core/dom/node_computed_style.h" -#include "third_party/blink/renderer/core/frame/local_frame_view.h" -#include "third_party/blink/renderer/core/html/forms/html_option_element.h" -#include "third_party/blink/renderer/core/html/forms/html_select_element.h" -#include "third_party/blink/renderer/core/layout/layout_block_flow.h" -#include "third_party/blink/renderer/core/layout/layout_text.h" -#include "third_party/blink/renderer/core/layout/layout_theme.h" -#include "third_party/blink/renderer/core/paint/paint_layer.h" -#include "third_party/blink/renderer/platform/text/platform_locale.h" - -namespace blink { - -LayoutMenuList::LayoutMenuList(Element* element) - : LayoutFlexibleBox(element), - button_text_(nullptr), - inner_block_(nullptr), - is_empty_(false), - has_updated_active_option_(false), - inner_block_height_(LayoutUnit()), - options_width_(0), - last_active_index_(-1) { - DCHECK(IsA<HTMLSelectElement>(element)); -} - -LayoutMenuList::~LayoutMenuList() = default; - -bool LayoutMenuList::IsChildAllowed(LayoutObject* object, - const ComputedStyle&) const { - // For a size=1 <select>, we only render the active option through the - // anonymous inner_block_ plus button_text_. We do not allow adding layout - // objects for options or optgroups. - return object->IsAnonymous(); -} - -scoped_refptr<ComputedStyle> LayoutMenuList::CreateInnerStyle() { - scoped_refptr<ComputedStyle> inner_style = - ComputedStyle::CreateAnonymousStyleWithDisplay(StyleRef(), - EDisplay::kBlock); - - AdjustInnerStyle(*inner_style); - return inner_style; -} - -void LayoutMenuList::UpdateInnerStyle() { - DCHECK(inner_block_); - scoped_refptr<ComputedStyle> inner_style = - ComputedStyle::Clone(inner_block_->StyleRef()); - AdjustInnerStyle(*inner_style); - inner_block_->SetModifiedStyleOutsideStyleRecalc(std::move(inner_style), - ApplyStyleChanges::kNo); - // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). - SetNeedsPaintPropertyUpdate(); - if (Layer()) - Layer()->SetNeedsCompositingInputsUpdate(); -} - -void LayoutMenuList::CreateInnerBlock() { - if (inner_block_) { - DCHECK_EQ(FirstChild(), inner_block_); - DCHECK(!inner_block_->NextSibling()); - return; - } - - // Create an anonymous block. - LegacyLayout legacy = - ForceLegacyLayout() ? LegacyLayout::kForce : LegacyLayout::kAuto; - DCHECK(!FirstChild()); - inner_block_ = LayoutBlockFlow::CreateAnonymous(&GetDocument(), - CreateInnerStyle(), legacy); - - button_text_ = - LayoutText::CreateEmptyAnonymous(GetDocument(), Style(), legacy); - // We need to set the text explicitly though it was specified in the - // constructor because LayoutText doesn't refer to the text - // specified in the constructor in a case of re-transforming. - inner_block_->AddChild(button_text_); - LayoutFlexibleBox::AddChild(inner_block_); - - // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). - SetNeedsPaintPropertyUpdate(); - if (Layer()) - Layer()->SetNeedsCompositingInputsUpdate(); -} - -bool LayoutMenuList::HasOptionStyleChanged( - const ComputedStyle& inner_style) const { - return option_style_ && - ((option_style_->Direction() != inner_style.Direction() || - option_style_->GetUnicodeBidi() != inner_style.GetUnicodeBidi())); -} - -void LayoutMenuList::AdjustInnerStyle(ComputedStyle& inner_style) const { - inner_style.SetFlexGrow(1); - inner_style.SetFlexShrink(1); - // min-width: 0; is needed for correct shrinking. - inner_style.SetMinWidth(Length::Fixed(0)); - - // Use margin:auto instead of align-items:center to get safe centering, i.e. - // when the content overflows, treat it the same as align-items: flex-start. - // But we only do that for the cases where html.css would otherwise use - // center. - if (StyleRef().AlignItemsPosition() == ItemPosition::kCenter) { - inner_style.SetMarginTop(Length()); - inner_style.SetMarginBottom(Length()); - inner_style.SetAlignSelfPosition(ItemPosition::kFlexStart); - } - - Length padding_start = Length::Fixed( - LayoutTheme::GetTheme().PopupInternalPaddingStart(StyleRef())); - Length padding_end = Length::Fixed( - LayoutTheme::GetTheme().PopupInternalPaddingEnd(GetFrame(), StyleRef())); - inner_style.SetPaddingLeft(StyleRef().Direction() == TextDirection::kLtr - ? padding_start - : padding_end); - inner_style.SetPaddingRight(StyleRef().Direction() == TextDirection::kLtr - ? padding_end - : padding_start); - inner_style.SetPaddingTop(Length::Fixed( - LayoutTheme::GetTheme().PopupInternalPaddingTop(StyleRef()))); - inner_style.SetPaddingBottom(Length::Fixed( - LayoutTheme::GetTheme().PopupInternalPaddingBottom(StyleRef()))); - inner_style.SetTextAlign(StyleRef().IsLeftToRightDirection() - ? ETextAlign::kLeft - : ETextAlign::kRight); - - if (HasOptionStyleChanged(inner_style)) { - inner_block_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - layout_invalidation_reason::kStyleChange); - inner_style.SetDirection(option_style_->Direction()); - inner_style.SetUnicodeBidi(option_style_->GetUnicodeBidi()); - } -} - -HTMLSelectElement* LayoutMenuList::SelectElement() const { - return To<HTMLSelectElement>(GetNode()); -} - -void LayoutMenuList::AddChild(LayoutObject* new_child, - LayoutObject* before_child) { - inner_block_->AddChild(new_child, before_child); - DCHECK_EQ(inner_block_, FirstChild()); - - if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) - cache->ChildrenChanged(this); - - // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). - SetNeedsPaintPropertyUpdate(); - if (Layer()) - Layer()->SetNeedsCompositingInputsUpdate(); -} - -void LayoutMenuList::RemoveChild(LayoutObject* old_child) { - if (old_child == inner_block_ || !inner_block_) { - LayoutFlexibleBox::RemoveChild(old_child); - inner_block_ = nullptr; - } else { - inner_block_->RemoveChild(old_child); - } -} - -void LayoutMenuList::StyleDidChange(StyleDifference diff, - const ComputedStyle* old_style) { - LayoutBlock::StyleDidChange(diff, old_style); - - if (!inner_block_) - CreateInnerBlock(); - - button_text_->SetStyle(Style()); - UpdateInnerStyle(); - UpdateInnerBlockHeight(); -} - -void LayoutMenuList::UpdateInnerBlockHeight() { - const SimpleFontData* font_data = StyleRef().GetFont().PrimaryFont(); - DCHECK(font_data); - inner_block_height_ = (font_data ? font_data->GetFontMetrics().Height() : 0) + - inner_block_->BorderAndPaddingHeight(); -} - -void LayoutMenuList::UpdateOptionsWidth() const { - if (ShouldApplySizeContainment()) { - options_width_ = 0; - return; - } - - float max_option_width = 0; - - for (auto* const option : SelectElement()->GetOptionList()) { - String text = option->TextIndentedToRespectGroupLabel(); - const ComputedStyle* item_style = - option->GetComputedStyle() ? option->GetComputedStyle() : Style(); - item_style->ApplyTextTransform(&text); - // We apply SELECT's style, not OPTION's style because m_optionsWidth is - // used to determine intrinsic width of the menulist box. - TextRun text_run = ConstructTextRun(StyleRef().GetFont(), text, *Style()); - max_option_width = - std::max(max_option_width, StyleRef().GetFont().Width(text_run)); - } - options_width_ = static_cast<int>(ceilf(max_option_width)); -} - -void LayoutMenuList::UpdateFromElement() { - HTMLSelectElement* select = SelectElement(); - HTMLOptionElement* option = select->OptionToBeShown(); - String text = g_empty_string; - option_style_ = nullptr; - - if (select->IsMultiple()) { - unsigned selected_count = 0; - HTMLOptionElement* selected_option_element = nullptr; - for (auto* const option : select->GetOptionList()) { - if (option->Selected()) { - if (++selected_count == 1) - selected_option_element = option; - } - } - - if (selected_count == 1) { - text = selected_option_element->TextIndentedToRespectGroupLabel(); - option_style_ = selected_option_element->GetComputedStyle(); - } else { - Locale& locale = select->GetLocale(); - String localized_number_string = - locale.ConvertToLocalizedNumber(String::Number(selected_count)); - text = locale.QueryString(IDS_FORM_SELECT_MENU_LIST_TEXT, - localized_number_string); - DCHECK(!option_style_); - } - } else { - if (option) { - text = option->TextIndentedToRespectGroupLabel(); - option_style_ = option->GetComputedStyle(); - } - } - - SetText(text.StripWhiteSpace()); - - DidUpdateActiveOption(option); - - DCHECK(inner_block_); - if (HasOptionStyleChanged(inner_block_->StyleRef())) - UpdateInnerStyle(); -} - -void LayoutMenuList::SetText(const String& s) { - if (s.IsEmpty()) { - // FIXME: This is a hack. We need the select to have the same baseline - // positioning as any surrounding text. Wihtout any content, we align the - // bottom of the select to the bottom of the text. With content (In this - // case the faked " ") we correctly align the middle of the select to the - // middle of the text. It should be possible to remove this, just set - // s.impl() into the text and have things align correctly... - // crbug.com/485982 - is_empty_ = true; - button_text_->ForceSetText(StringImpl::Create(" ", 1)); - } else { - is_empty_ = false; - button_text_->ForceSetText(s.Impl()); - } - // LayoutMenuList::ControlClipRect() depends on inner_block_->ContentsSize(). - SetNeedsPaintPropertyUpdate(); - if (Layer()) - Layer()->SetNeedsCompositingInputsUpdate(); -} - -String LayoutMenuList::GetText() const { - return button_text_ && !is_empty_ ? button_text_->GetText() : String(); -} - -PhysicalRect LayoutMenuList::ControlClipRect( - const PhysicalOffset& additional_offset) const { - // Clip to the intersection of the content box and the content box for the - // inner box. This will leave room for the arrows which sit in the inner box - // padding, and if the inner box ever spills out of the outer box, that will - // get clipped too. - PhysicalRect outer_box = PhysicalContentBoxRect(); - outer_box.offset += additional_offset; - - PhysicalRect inner_box(additional_offset + inner_block_->PhysicalLocation() + - PhysicalOffset(inner_block_->PaddingLeft(), - inner_block_->PaddingTop()), - inner_block_->ContentSize()); - - return Intersection(outer_box, inner_box); -} - -void LayoutMenuList::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - UpdateOptionsWidth(); - - max_logical_width = - std::max(options_width_, - LayoutTheme::GetTheme().MinimumMenuListSize(StyleRef())) + - inner_block_->PaddingLeft() + inner_block_->PaddingRight(); - if (!StyleRef().Width().IsPercentOrCalc()) - min_logical_width = max_logical_width; - else - min_logical_width = LayoutUnit(); -} - -void LayoutMenuList::ComputeLogicalHeight( - LayoutUnit logical_height, - LayoutUnit logical_top, - LogicalExtentComputedValues& computed_values) const { - if (StyleRef().HasEffectiveAppearance()) - logical_height = inner_block_height_ + BorderAndPaddingHeight(); - LayoutBox::ComputeLogicalHeight(logical_height, logical_top, computed_values); -} - -void LayoutMenuList::DidSelectOption(HTMLOptionElement* option) { - DidUpdateActiveOption(option); -} - -void LayoutMenuList::DidUpdateActiveOption(HTMLOptionElement* option) { - if (!GetDocument().ExistingAXObjectCache()) - return; - - int option_index = option ? option->index() : -1; - if (last_active_index_ == option_index) - return; - last_active_index_ = option_index; - - // We skip sending accessiblity notifications for the very first option, - // otherwise we get extra focus and select events that are undesired. - if (!has_updated_active_option_) { - has_updated_active_option_ = true; - return; - } - - GetDocument().ExistingAXObjectCache()->HandleUpdateActiveMenuOption( - this, option_index); -} - -LayoutUnit LayoutMenuList::ClientPaddingLeft() const { - return PaddingLeft() + inner_block_->PaddingLeft(); -} - -LayoutUnit LayoutMenuList::ClientPaddingRight() const { - return PaddingRight() + inner_block_->PaddingRight(); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h b/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h deleted file mode 100644 index ba3a7d5a62d..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_menu_list.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of the select element layoutObject in WebCore. - * - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. - * All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MENU_LIST_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_MENU_LIST_H_ - -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/layout_flexible_box.h" -#include "third_party/blink/renderer/platform/geometry/layout_rect.h" - -namespace blink { - -class HTMLOptionElement; -class HTMLSelectElement; -class LayoutText; - -class CORE_EXPORT LayoutMenuList final : public LayoutFlexibleBox { - public: - explicit LayoutMenuList(Element*); - ~LayoutMenuList() override; - - HTMLSelectElement* SelectElement() const; - void DidSelectOption(HTMLOptionElement*); - String GetText() const; - - const char* GetName() const override { return "LayoutMenuList"; } - - LayoutUnit ClientPaddingLeft() const; - LayoutUnit ClientPaddingRight() const; - - private: - bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectMenuList || LayoutFlexibleBox::IsOfType(type); - } - bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override; - - void AddChild(LayoutObject* new_child, - LayoutObject* before_child = nullptr) override; - void RemoveChild(LayoutObject*) override; - bool CreatesAnonymousWrapper() const override { return true; } - - void UpdateFromElement() override; - - PhysicalRect ControlClipRect(const PhysicalOffset&) const override; - bool HasControlClip() const override { return true; } - - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; - void ComputeLogicalHeight(LayoutUnit logical_height, - LayoutUnit logical_top, - LogicalExtentComputedValues&) const override; - - void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; - - bool HasLineIfEmpty() const override { return true; } - - // Flexbox defines baselines differently than regular blocks. - // For backwards compatibility, menulists need to do the regular block - // behavior. - LayoutUnit BaselinePosition(FontBaseline baseline, - bool first_line, - LineDirectionMode direction, - LinePositionMode position) const override { - return LayoutBlock::BaselinePosition(baseline, first_line, direction, - position); - } - LayoutUnit FirstLineBoxBaseline() const override { - return LayoutBlock::FirstLineBoxBaseline(); - } - LayoutUnit InlineBlockBaseline(LineDirectionMode direction) const override { - return LayoutBlock::InlineBlockBaseline(direction); - } - - void CreateInnerBlock(); - scoped_refptr<ComputedStyle> CreateInnerStyle(); - void UpdateInnerStyle(); - void AdjustInnerStyle(ComputedStyle&) const; - bool HasOptionStyleChanged(const ComputedStyle& inner_style) const; - void SetText(const String&); - void UpdateInnerBlockHeight(); - void UpdateOptionsWidth() const; - void SetIndexToSelectOnCancel(int list_index); - - void DidUpdateActiveOption(HTMLOptionElement*); - - LayoutText* button_text_; - LayoutBlock* inner_block_; - - bool is_empty_ : 1; - bool has_updated_active_option_ : 1; - LayoutUnit inner_block_height_; - // m_optionsWidth is calculated and cached on demand. - // updateOptionsWidth() should be called before reading them. - mutable int options_width_; - - int last_active_index_; - - scoped_refptr<const ComputedStyle> option_style_; -}; - -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutMenuList, IsMenuList()); - -} // namespace blink - -#endif diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc index 9b7976066aa..2c6e4fc788e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc @@ -332,7 +332,7 @@ LayoutUnit LayoutMultiColumnFlowThread::MaxColumnLogicalHeight() const { const LayoutBlockFlow* multicol_block = MultiColumnBlockFlow(); const Length& logical_max_height = multicol_block->StyleRef().LogicalMaxHeight(); - if (!logical_max_height.IsMaxSizeNone()) { + if (!logical_max_height.IsNone()) { LayoutUnit resolved_logical_max_height = multicol_block->ComputeContentLogicalHeight( kMaxSize, logical_max_height, LayoutUnit(-1)); @@ -611,7 +611,7 @@ bool LayoutMultiColumnFlowThread::RemoveSpannerPlaceholderIfNoLongerValid( // We may have a new containing block, since we're no longer a spanner. Mark // it for relayout. spanner_object_in_flow_thread->ContainingBlock() - ->SetNeedsLayoutAndPrefWidthsRecalc( + ->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kColumnsChanged); // Now generate a column set for this ex-spanner, if needed and none is there @@ -729,7 +729,7 @@ void LayoutMultiColumnFlowThread::CalculateColumnHeightAvailable() { // have a definite height when they in fact don't. LayoutBlockFlow* container = MultiColumnBlockFlow(); LayoutUnit column_height; - if (container->HasDefiniteLogicalHeight() || container->IsLayoutView()) { + if (container->HasDefiniteLogicalHeight() || IsA<LayoutView>(container)) { LogicalExtentComputedValues computed_values; container->ComputeLogicalHeight(LayoutUnit(), container->LogicalTop(), computed_values); @@ -1332,7 +1332,7 @@ void LayoutMultiColumnFlowThread::ToggleSpannersInSubtree( } } -void LayoutMultiColumnFlowThread::ComputePreferredLogicalWidths() { +MinMaxSizes LayoutMultiColumnFlowThread::PreferredLogicalWidths() const { // The min/max intrinsic widths calculated really tell how much space elements // need when laid out inside the columns. In order to eventually end up with // the desired column width, we need to convert them to values pertaining to @@ -1343,28 +1343,22 @@ void LayoutMultiColumnFlowThread::ComputePreferredLogicalWidths() { multicol_style->HasAutoColumnCount() ? 1 : multicol_style->ColumnCount()); LayoutUnit gap_extra((column_count - 1) * ColumnGap(*multicol_style, LayoutUnit())); + MinMaxSizes sizes; if (flow->HasOverrideIntrinsicContentLogicalWidth()) { - min_preferred_logical_width_ = max_preferred_logical_width_ = - flow->OverrideIntrinsicContentLogicalWidth(); - ClearPreferredLogicalWidthsDirty(); + sizes = flow->OverrideIntrinsicContentLogicalWidth(); } else if (flow->ShouldApplySizeContainment()) { - min_preferred_logical_width_ = max_preferred_logical_width_ = LayoutUnit(); - ClearPreferredLogicalWidthsDirty(); + sizes = LayoutUnit(); } else { - // Calculate and set new min_preferred_logical_width_ and - // max_preferred_logical_width_. - LayoutFlowThread::ComputePreferredLogicalWidths(); + sizes = LayoutFlowThread::PreferredLogicalWidths(); } LayoutUnit column_width; if (multicol_style->HasAutoColumnWidth()) { - min_preferred_logical_width_ = - min_preferred_logical_width_ * column_count + gap_extra; + sizes.min_size = sizes.min_size * column_count + gap_extra; } else { column_width = LayoutUnit(multicol_style->ColumnWidth()); - min_preferred_logical_width_ = - std::min(min_preferred_logical_width_, column_width); + sizes.min_size = std::min(sizes.min_size, column_width); } // Note that if column-count is auto here, we should resolve it to calculate // the maximum intrinsic width, instead of pretending that it's 1. The only @@ -1372,9 +1366,9 @@ void LayoutMultiColumnFlowThread::ComputePreferredLogicalWidths() { // appropriate time or place for layout. The good news is that if height is // unconstrained and there are no explicit breaks, the resolved column-count // really should be 1. - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, column_width) * column_count + - gap_extra; + sizes.max_size = + std::max(sizes.max_size, column_width) * column_count + gap_extra; + return sizes; } void LayoutMultiColumnFlowThread::ComputeLogicalHeight( diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h index e0518872994..e09739b2a21 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h @@ -317,7 +317,7 @@ class CORE_EXPORT LayoutMultiColumnFlowThread final StyleDifference, const ComputedStyle& old_style) override; void ToggleSpannersInSubtree(LayoutBox*); - void ComputePreferredLogicalWidths() override; + MinMaxSizes PreferredLogicalWidths() const override; void ComputeLogicalHeight(LayoutUnit logical_height, LayoutUnit logical_top, LogicalExtentComputedValues&) const final; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.cc b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.cc index 924dd0ea1db..05575c71fc9 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.cc @@ -449,11 +449,8 @@ void LayoutMultiColumnSet::UpdateLayout() { } } -void LayoutMultiColumnSet::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - min_logical_width = flow_thread_->MinPreferredLogicalWidth(); - max_logical_width = flow_thread_->MaxPreferredLogicalWidth(); +MinMaxSizes LayoutMultiColumnSet::ComputeIntrinsicLogicalWidths() const { + return MinMaxSizes(); } void LayoutMultiColumnSet::ComputeLogicalHeight( @@ -534,6 +531,7 @@ void LayoutMultiColumnSet::ComputeVisualOverflow( AddVisualOverflowFromFloats(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.h b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.h index ed5ed54a877..6fdae46ff0c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_set.h @@ -207,8 +207,7 @@ class CORE_EXPORT LayoutMultiColumnSet final : public LayoutBlockFlow { void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void UpdateLayout() override; - void ComputeIntrinsicLogicalWidths(LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const final; + MinMaxSizes ComputeIntrinsicLogicalWidths() const final; void AttachToFlowThread(); void DetachFromFlowThread(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc index d08cd063337..10b19d72df8 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.cc @@ -72,7 +72,7 @@ void LayoutMultiColumnSpannerPlaceholder::InsertedIntoTree() { LayoutBox::InsertedIntoTree(); // The object may previously have been laid out as a non-spanner, but since // it's a spanner now, it needs to be relaid out. - layout_object_in_flow_thread_->SetNeedsLayoutAndPrefWidthsRecalc( + layout_object_in_flow_thread_->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kColumnsChanged); } @@ -83,7 +83,7 @@ void LayoutMultiColumnSpannerPlaceholder::WillBeRemovedFromTree() { // Even if the placeholder is going away, the object in the flow thread // might live on. Since it's not a spanner anymore, it needs to be relaid // out. - ex_spanner->SetNeedsLayoutAndPrefWidthsRecalc( + ex_spanner->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kColumnsChanged); } LayoutBox::WillBeRemovedFromTree(); @@ -101,7 +101,7 @@ void LayoutMultiColumnSpannerPlaceholder::RecalcVisualOverflow() { layout_object_in_flow_thread_->VisualOverflowRect()); } -LayoutUnit LayoutMultiColumnSpannerPlaceholder::MinPreferredLogicalWidth() +MinMaxSizes LayoutMultiColumnSpannerPlaceholder::PreferredLogicalWidths() const { // There should be no contribution from a spanner if the multicol container is // size-contained. Normally we'd stop at the object that has contain:size @@ -111,17 +111,8 @@ LayoutUnit LayoutMultiColumnSpannerPlaceholder::MinPreferredLogicalWidth() // siblings of the flow thread, we need this check. // TODO(crbug.com/953919): What should we return for display-locked content? if (MultiColumnBlockFlow()->ShouldApplySizeContainment()) - return LayoutUnit(); - return layout_object_in_flow_thread_->MinPreferredLogicalWidth(); -} - -LayoutUnit LayoutMultiColumnSpannerPlaceholder::MaxPreferredLogicalWidth() - const { - // See above. - // TODO(crbug.com/953919): What should we return for display-locked content? - if (MultiColumnBlockFlow()->ShouldApplySizeContainment()) - return LayoutUnit(); - return layout_object_in_flow_thread_->MaxPreferredLogicalWidth(); + return MinMaxSizes(); + return layout_object_in_flow_thread_->PreferredLogicalWidths(); } void LayoutMultiColumnSpannerPlaceholder::UpdateLayout() { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h index c4018fbc0ae..a2575a39b8c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h @@ -60,8 +60,7 @@ class LayoutMultiColumnSpannerPlaceholder final : public LayoutBox { void WillBeRemovedFromTree() override; bool NeedsPreferredWidthsRecalculation() const override; void RecalcVisualOverflow() override; - LayoutUnit MinPreferredLogicalWidth() const override; - LayoutUnit MaxPreferredLogicalWidth() const override; + MinMaxSizes PreferredLogicalWidths() const override; void UpdateLayout() override; void ComputeLogicalHeight(LayoutUnit logical_height, LayoutUnit logical_top, @@ -75,6 +74,11 @@ class LayoutMultiColumnSpannerPlaceholder final : public LayoutBox { private: LayoutMultiColumnSpannerPlaceholder(LayoutBox*); + MinMaxSizes ComputeIntrinsicLogicalWidths() const final { + NOTREACHED(); + return MinMaxSizes(); + } + // The actual column-span:all layoutObject inside the flow thread. LayoutBox* layout_object_in_flow_thread_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object.cc b/chromium/third_party/blink/renderer/core/layout/layout_object.cc index a8574b15657..9d6d6f718aa 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object.cc @@ -33,7 +33,7 @@ #include <utility> #include "base/allocator/partition_allocator/partition_alloc.h" -#include "third_party/blink/public/platform/web_scroll_into_view_params.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" @@ -55,11 +55,14 @@ #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" +#include "third_party/blink/renderer/core/html/forms/html_select_element.h" #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/html/html_html_element.h" #include "third_party/blink/renderer/core/html/html_table_cell_element.h" #include "third_party/blink/renderer/core/html/html_table_element.h" +#include "third_party/blink/renderer/core/html/image_document.h" #include "third_party/blink/renderer/core/input/event_handler.h" +#include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/layout_counter.h" @@ -74,6 +77,7 @@ #include "third_party/blink/renderer/core/layout/layout_image_resource_style_image.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" #include "third_party/blink/renderer/core/layout/layout_list_item.h" +#include "third_party/blink/renderer/core/layout/layout_list_marker.h" #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h" #include "third_party/blink/renderer/core/layout/layout_object_factory.h" #include "third_party/blink/renderer/core/layout/layout_table_caption.h" @@ -237,11 +241,10 @@ LayoutObject* LayoutObject::CreateObject(Element* element, case EDisplay::kBlock: case EDisplay::kFlowRoot: case EDisplay::kInlineBlock: + case EDisplay::kListItem: case EDisplay::kMath: case EDisplay::kInlineMath: return LayoutObjectFactory::CreateBlockFlow(*element, style, legacy); - case EDisplay::kListItem: - return LayoutObjectFactory::CreateListItem(*element, style, legacy); case EDisplay::kTable: case EDisplay::kInlineTable: return new LayoutTable(element); @@ -260,9 +263,14 @@ LayoutObject* LayoutObject::CreateObject(Element* element, return LayoutObjectFactory::CreateTableCaption(*element, style, legacy); case EDisplay::kWebkitBox: case EDisplay::kWebkitInlineBox: + if (style.IsDeprecatedWebkitBoxWithVerticalLineClamp() && + RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled()) { + return LayoutObjectFactory::CreateBlockForLineClamp(*element, style, + legacy); + } if (style.IsDeprecatedFlexboxUsingFlexLayout()) return new LayoutFlexibleBox(element); - return new LayoutDeprecatedFlexibleBox(*element); + return new LayoutDeprecatedFlexibleBox(element); case EDisplay::kFlex: case EDisplay::kInlineFlex: UseCounter::Count(element->GetDocument(), WebFeature::kCSSFlexibleBox); @@ -307,6 +315,7 @@ LayoutObject::LayoutObject(Node* node) LayoutObject::~LayoutObject() { #if DCHECK_IS_ON() DCHECK(!has_ax_object_); + DCHECK(BeingDestroyed()); #endif InstanceCounters::DecrementCounter(InstanceCounters::kLayoutObjectCounter); } @@ -361,6 +370,22 @@ bool LayoutObject::RequiresAnonymousTableWrappers( return false; } +#if DCHECK_IS_ON() + +void LayoutObject::AssertClearedPaintInvalidationFlags() const { + if (!PaintInvalidationStateIsDirty() || + PrePaintBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren)) + return; + // NG text objects are exempt, as pre-paint walking doesn't visit those with + // no paint effects (only white-space, for instance). + if (IsText() && IsLayoutNGObject()) + return; + ShowLayoutTreeForThis(); + NOTREACHED(); +} + +#endif // DCHECK_IS_ON() + DISABLE_CFI_PERF void LayoutObject::AddChild(LayoutObject* new_child, LayoutObject* before_child) { @@ -467,20 +492,26 @@ LayoutObject* LayoutObject::NextInPreOrder() const { } bool LayoutObject::HasClipRelatedProperty() const { - // TODO(trchen): Refactor / remove this function. // This function detects a bunch of properties that can potentially affect - // clip inheritance chain. However such generalization is practially useless + // clip inheritance chain. However such generalization is practically useless // because these properties change clip inheritance in different way that // needs to be handled explicitly. // CSS clip applies clip to the current element and all descendants. - // CSS overflow clip applies only to containg-block descendants. + // CSS overflow clip applies only to containing-block descendants. // CSS contain:paint applies to all descendants by making itself a containing // block for all descendants. // CSS clip-path/mask/filter induces a stacking context and applies inherited // clip to that stacking context, while resetting clip for descendants. This // special behavior is already handled elsewhere. - if (HasClip() || HasOverflowClip() || ShouldApplyPaintContainment()) + if (HasClip() || HasOverflowClip()) + return true; + // Paint containment establishes isolation which creates clip isolation nodes. + // Style & Layout containment also establish isolation (see + // |NeedsIsolationNodes| in PaintPropertyTreeBuilder). + if (ShouldApplyPaintContainment() || + (ShouldApplyStyleContainment() && ShouldApplyLayoutContainment())) { return true; + } if (IsBox() && ToLayoutBox(this)->HasControlClip()) return true; return false; @@ -744,19 +775,18 @@ bool LayoutObject::IsFixedPositionObjectInPagedMedia() const { PhysicalRect LayoutObject::ScrollRectToVisible( const PhysicalRect& rect, - const WebScrollIntoViewParams& params) { + mojom::blink::ScrollIntoViewParamsPtr params) { LayoutBox* enclosing_box = EnclosingBox(); if (!enclosing_box) return rect; GetDocument().GetFrame()->GetSmoothScrollSequencer().AbortAnimations(); GetDocument().GetFrame()->GetSmoothScrollSequencer().SetScrollType( - params.GetScrollType()); - WebScrollIntoViewParams new_params(params); - new_params.is_for_scroll_sequence |= - params.GetScrollType() == kProgrammaticScroll; + params->type); + params->is_for_scroll_sequence |= + params->type == mojom::blink::ScrollType::kProgrammatic; PhysicalRect new_location = - enclosing_box->ScrollRectToVisibleRecursive(rect, new_params); + enclosing_box->ScrollRectToVisibleRecursive(rect, std::move(params)); GetDocument().GetFrame()->GetSmoothScrollSequencer().RunQueuedAnimations(); return new_location; @@ -855,6 +885,12 @@ static inline bool ObjectIsRelayoutBoundary(const LayoutObject* object) { // FIXME: In future it may be possible to broaden these conditions in order to // improve performance. + // Positioned objects always have self-painting layers and are safe to use as + // relayout boundaries. + bool is_svg_root = object->IsSVGRoot(); + if (!object->IsPositioned() && !is_svg_root) + return false; + // LayoutInline can't be relayout roots since LayoutBlockFlow is responsible // for layouting them. if (object->IsLayoutInline()) @@ -872,6 +908,17 @@ static inline bool ObjectIsRelayoutBoundary(const LayoutObject* object) { (style->HasAutoLeftAndRight() || style->HasAutoTopAndBottom())) return false; + if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled() && + object->IsLayoutNGObject())) { + // We need to rebuild the entire NG fragment spine all the way from the root + // (or at least the nearest self-painting paint layer), since we traverse + // the fragments, and not objects. Fragment painting is initiated at + // self-painting layers, but we cannot check if it's a self-painting layer + // now, because it may cease to be one during layout (an object with clipped + // overflow that no longer has content that requires it to clip). + return false; + } + if (object->ShouldApplyLayoutContainment() && object->ShouldApplySizeContainment()) return true; @@ -887,7 +934,7 @@ static inline bool ObjectIsRelayoutBoundary(const LayoutObject* object) { if (object->IsTextControl()) return true; - if (object->IsSVGRoot()) + if (is_svg_root) return true; if (!object->HasOverflowClip()) @@ -1016,7 +1063,7 @@ void LayoutObject::MarkContainerChainForLayout(bool schedule_relayout, // Don't mark the outermost object of an unrooted subtree. That object will // be marked when the subtree is added to the document. LayoutObject* container = object->Container(); - if (!container && !object->IsLayoutView()) + if (!container && !IsA<LayoutView>(object)) return; if (!last->IsTextOrSVGChild() && last->StyleRef().HasOutOfFlowPosition()) { object = last->ContainingBlock(); @@ -1112,16 +1159,33 @@ void LayoutObject::CheckBlockPositionedObjectsNeedLayout() { } #endif -void LayoutObject::SetPreferredLogicalWidthsDirty( +void LayoutObject::SetIntrinsicLogicalWidthsDirty( MarkingBehavior mark_parents) { - bitfields_.SetPreferredLogicalWidthsDirty(true); + bitfields_.SetIntrinsicLogicalWidthsDirty(true); if (mark_parents == kMarkContainerChain && (IsText() || !StyleRef().HasOutOfFlowPosition())) - InvalidateContainerPreferredLogicalWidths(); + InvalidateContainerIntrinsicLogicalWidths(); } -void LayoutObject::ClearPreferredLogicalWidthsDirty() { - bitfields_.SetPreferredLogicalWidthsDirty(false); +void LayoutObject::ClearIntrinsicLogicalWidthsDirty() { + bitfields_.SetIntrinsicLogicalWidthsDirty(false); +} + +void LayoutObject::InvalidateSubtreeLayoutForFontUpdates() { + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( + layout_invalidation_reason::kFontsChanged); + for (LayoutObject* child = SlowFirstChild(); child; + child = child->NextSibling()) { + child->InvalidateSubtreeLayoutForFontUpdates(); + } +} + +void LayoutObject::InvalidateIntersectionObserverCachedRects() { + if (GetNode() && GetNode()->IsElementNode()) { + if (auto* data = To<Element>(GetNode())->IntersectionObserverData()) { + data->InvalidateCachedRects(); + } + } } static inline bool NGKeepInvalidatingBeyond(LayoutObject* o) { @@ -1131,28 +1195,30 @@ static inline bool NGKeepInvalidatingBeyond(LayoutObject* o) { // next block container. // Atomic inlines do not have this problem as they are treated like blocks // in this context. + // There's a similar issue for flow thread objects, as they are invisible to + // LayoutNG. if (!RuntimeEnabledFeatures::LayoutNGEnabled()) return false; - if (o->IsLayoutInline() || o->IsText()) + if (o->IsLayoutInline() || o->IsText() || o->IsLayoutFlowThread()) return true; return false; } -inline void LayoutObject::InvalidateContainerPreferredLogicalWidths() { +inline void LayoutObject::InvalidateContainerIntrinsicLogicalWidths() { // In order to avoid pathological behavior when inlines are deeply nested, we // do include them in the chain that we mark dirty (even though they're kind // of irrelevant). LayoutObject* o = IsTableCell() ? ContainingBlock() : Container(); while (o && - (!o->PreferredLogicalWidthsDirty() || NGKeepInvalidatingBeyond(o))) { + (!o->IntrinsicLogicalWidthsDirty() || NGKeepInvalidatingBeyond(o))) { // Don't invalidate the outermost object of an unrooted subtree. That object // will be invalidated when the subtree is added to the document. LayoutObject* container = o->IsTableCell() ? o->ContainingBlock() : o->Container(); - if (!container && !o->IsLayoutView()) + if (!container && !IsA<LayoutView>(o)) break; - o->bitfields_.SetPreferredLogicalWidthsDirty(true); + o->bitfields_.SetIntrinsicLogicalWidthsDirty(true); // A positioned object has no effect on the min/max width of its containing // block ever. We can optimize this case and not go up any further. if (o->StyleRef().HasOutOfFlowPosition()) @@ -1187,24 +1253,6 @@ LayoutObject* LayoutObject::ContainerForFixedPosition( }); } -LayoutBlock* LayoutObject::FindNonAnonymousContainingBlock( - LayoutObject* container, - AncestorSkipInfo* skip_info) { - // For inlines, we return the nearest non-anonymous enclosing - // block. We don't try to return the inline itself. This allows us to avoid - // having a positioned objects list in all LayoutInlines and lets us return a - // strongly-typed LayoutBlock* result from this method. The - // LayoutObject::Container() method can actually be used to obtain the inline - // directly. - if (container && !container->IsLayoutBlock()) - container = container->ContainingBlock(skip_info); - - while (container && container->IsAnonymousBlock()) - container = container->ContainingBlock(skip_info); - - return DynamicTo<LayoutBlock>(container); -} - LayoutBlock* LayoutObject::ContainingBlockForAbsolutePosition( AncestorSkipInfo* skip_info) const { auto* container = ContainerForAbsolutePosition(skip_info); @@ -1250,6 +1298,24 @@ LayoutBlock* LayoutObject::ContainingBlock(AncestorSkipInfo* skip_info) const { return DynamicTo<LayoutBlock>(object); } +LayoutBlock* LayoutObject::FindNonAnonymousContainingBlock( + LayoutObject* container, + AncestorSkipInfo* skip_info) { + // For inlines, we return the nearest non-anonymous enclosing + // block. We don't try to return the inline itself. This allows us to avoid + // having a positioned objects list in all LayoutInlines and lets us return a + // strongly-typed LayoutBlock* result from this method. The + // LayoutObject::Container() method can actually be used to obtain the inline + // directly. + if (container && !container->IsLayoutBlock()) + container = container->ContainingBlock(skip_info); + + while (container && container->IsAnonymousBlock()) + container = container->ContainingBlock(skip_info); + + return DynamicTo<LayoutBlock>(container); +} + bool LayoutObject::ComputeIsFixedContainer(const ComputedStyle* style) const { if (!style) return false; @@ -1267,7 +1333,7 @@ bool LayoutObject::ComputeIsFixedContainer(const ComputedStyle* style) const { // select elements inside that are created by user agent shadow DOM, and we // have (C++) code that assumes that the elements are indeed contained by the // text control. So just make sure this is the case. - if (IsLayoutView() || IsSVGForeignObject() || IsTextControl()) + if (IsA<LayoutView>(this) || IsSVGForeignObject() || IsTextControl()) return true; // https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants if (style->HasTransformRelatedProperty()) { @@ -1334,16 +1400,6 @@ PhysicalRect LayoutObject::AbsoluteBoundingBoxRectForScrollIntoView() const { return rect; } -FloatRect LayoutObject::AbsoluteBoundingBoxRectForRange( - const EphemeralRange& range) { - if (range.IsNull() || !range.StartPosition().ComputeContainerNode()) - return FloatRect(); - - range.GetDocument().UpdateStyleAndLayout(); - - return ComputeTextFloatRect(range); -} - void LayoutObject::AddAbsoluteRectForLayer(IntRect& result) { if (HasLayer()) result.Unite(AbsoluteBoundingBoxRect()); @@ -1412,6 +1468,7 @@ void LayoutObject::RecalcNormalFlowChildVisualOverflowIfNeeded() { } const LayoutBoxModelObject* LayoutObject::EnclosingCompositedContainer() const { + DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()); LayoutBoxModelObject* container = nullptr; // FIXME: CompositingState is not necessarily up to date for many callers of // this function. @@ -1495,7 +1552,7 @@ String LayoutObject::DecoratedName() const { name.Append(" (anonymous)"); // FIXME: Remove the special case for LayoutView here (requires rebaseline of // all tests). - if (IsOutOfFlowPositioned() && !IsLayoutView()) + if (IsOutOfFlowPositioned() && !IsA<LayoutView>(this)) name.Append(" (positioned)"); if (IsRelPositioned()) name.Append(" (relative positioned)"); @@ -1626,14 +1683,12 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath( if (ancestor == this) return true; - const auto* property_container = this; AncestorSkipInfo skip_info(ancestor); - while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) { - property_container = property_container->Container(&skip_info); - if (!property_container || skip_info.AncestorSkipped() || - property_container->FirstFragment().NextFragment()) - return false; - } + PropertyTreeState container_properties = PropertyTreeState::Uninitialized(); + const LayoutObject* property_container = + GetPropertyContainer(&skip_info, &container_properties); + if (!property_container) + return false; // This works because it's not possible to have any intervening clips, // effects, transforms between |this| and |property_container|, and therefore @@ -1643,13 +1698,9 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath( rect.Move(FirstFragment().PaintOffset()); if (property_container != ancestor) { FloatClipRect clip_rect((FloatRect(rect))); - const auto& local_state = - property_container == this - ? FirstFragment().LocalBorderBoxProperties() - : property_container->FirstFragment().ContentsProperties(); intersects = GeometryMapper::LocalToAncestorVisualRect( - local_state, ancestor->FirstFragment().ContentsProperties(), clip_rect, - kIgnorePlatformOverlayScrollbarSize, + container_properties, ancestor->FirstFragment().ContentsProperties(), + clip_rect, kIgnorePlatformOverlayScrollbarSize, (visual_rect_flags & kEdgeInclusive) ? kInclusiveIntersect : kNonInclusiveIntersect); rect = PhysicalRect::EnclosingRect(clip_rect.Rect()); @@ -1707,6 +1758,27 @@ bool LayoutObject::MapToVisualRectInAncestorSpaceInternal( return true; } +const LayoutObject* LayoutObject::GetPropertyContainer( + AncestorSkipInfo* skip_info, + PropertyTreeState* container_properties) const { + const LayoutObject* property_container = this; + while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) { + property_container = property_container->Container(skip_info); + if (!property_container || (skip_info && skip_info->AncestorSkipped()) || + property_container->FirstFragment().NextFragment()) + return nullptr; + } + if (container_properties) { + if (property_container == this) { + *container_properties = FirstFragment().LocalBorderBoxProperties(); + } else { + *container_properties = + property_container->FirstFragment().ContentsProperties(); + } + } + return property_container; +} + HitTestResult LayoutObject::HitTestForOcclusion( const PhysicalRect& hit_rect) const { LocalFrame* frame = GetDocument().GetFrame(); @@ -1893,26 +1965,20 @@ StyleDifference LayoutObject::AdjustStyleDifference( diff.SetNeedsFullLayout(); } - // TODO(wangxianzhu): We may avoid subtree paint invalidation on CSS clip - // change for CAP. - if (diff.CssClipChanged()) - diff.SetNeedsPaintInvalidationSubtree(); - // Optimization: for decoration/color property changes, invalidation is only // needed if we have style or text affected by these properties. - if (diff.TextDecorationOrColorChanged() && - !diff.NeedsFullPaintInvalidation()) { + if (diff.TextDecorationOrColorChanged() && !diff.NeedsPaintInvalidation()) { if (StyleRef().HasBorderColorReferencingCurrentColor() || StyleRef().HasOutlineWithCurrentColor() || StyleRef().HasBackgroundRelatedColorReferencingCurrentColor() || // Skip any text nodes that do not contain text boxes. Whitespace cannot // be skipped or we will miss invalidating decorations (e.g., // underlines). - (IsText() && !IsBR() && ToLayoutText(this)->HasTextBoxes()) || + (IsText() && !IsBR() && ToLayoutText(this)->HasInlineFragments()) || (IsSVG() && StyleRef().SvgStyle().IsFillColorCurrentColor()) || (IsSVG() && StyleRef().SvgStyle().IsStrokeColorCurrentColor()) || IsListMarker() || IsDetailsMarker()) - diff.SetNeedsPaintInvalidationObject(); + diff.SetNeedsPaintInvalidation(); } // The answer to layerTypeRequired() for plugins, iframes, and canvas can @@ -1956,7 +2022,8 @@ void LayoutObject::SetPseudoElementStyle( SetStyle(std::move(pseudo_style)); } -void LayoutObject::MarkContainerChainForOverflowRecalcIfNeeded() { +void LayoutObject::MarkContainerChainForOverflowRecalcIfNeeded( + bool mark_container_chain_layout_overflow_recalc) { LayoutObject* object = this; do { // Cell and row need to propagate the flag to their containing section and @@ -1966,25 +2033,53 @@ void LayoutObject::MarkContainerChainForOverflowRecalcIfNeeded() { ? object->Parent() : object->Container(); if (object) { - object->SetChildNeedsLayoutOverflowRecalc(); - object->MarkSelfPaintingLayerForVisualOverflowRecalc(); + bool already_needs_layout_overflow_recalc = false; + if (mark_container_chain_layout_overflow_recalc) { + already_needs_layout_overflow_recalc = + object->ChildNeedsLayoutOverflowRecalc(); + if (!already_needs_layout_overflow_recalc) + object->SetChildNeedsLayoutOverflowRecalc(); + } + + if (object->HasLayer()) { + auto* box_model_object = ToLayoutBoxModelObject(object); + if (box_model_object->HasSelfPaintingLayer()) { + auto* layer = box_model_object->Layer(); + if (layer->NeedsVisualOverflowRecalc()) { + if (already_needs_layout_overflow_recalc) + return; + } else { + layer->SetNeedsVisualOverflowRecalc(); + } + } + } } } while (object); } -void LayoutObject::SetNeedsVisualOverflowAndPaintInvalidation() { +void LayoutObject::SetNeedsOverflowRecalc( + OverflowRecalcType overflow_recalc_type) { + bool mark_container_chain_layout_overflow_recalc = + !SelfNeedsLayoutOverflowRecalc(); + + if (overflow_recalc_type == + OverflowRecalcType::kLayoutAndVisualOverflowRecalc) { + SetSelfNeedsLayoutOverflowRecalc(); + } + + DCHECK(overflow_recalc_type == + OverflowRecalcType::kOnlyVisualOverflowRecalc || + overflow_recalc_type == + OverflowRecalcType::kLayoutAndVisualOverflowRecalc); SetShouldCheckForPaintInvalidation(); MarkSelfPaintingLayerForVisualOverflowRecalc(); -} - -void LayoutObject::SetNeedsOverflowRecalc() { - bool needed_recalc = SelfNeedsLayoutOverflowRecalc(); - SetSelfNeedsLayoutOverflowRecalc(); - SetNeedsVisualOverflowAndPaintInvalidation(); - if (!needed_recalc) - MarkContainerChainForOverflowRecalcIfNeeded(); + if (mark_container_chain_layout_overflow_recalc) { + MarkContainerChainForOverflowRecalcIfNeeded( + overflow_recalc_type == + OverflowRecalcType::kLayoutAndVisualOverflowRecalc); + } } DISABLE_CFI_PERF @@ -2046,7 +2141,7 @@ void LayoutObject::SetStyle(scoped_refptr<const ComputedStyle> style, if (!diff.NeedsFullLayout()) { if (updated_diff.NeedsFullLayout()) { - SetNeedsLayoutAndPrefWidthsRecalc( + SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kStyleChange); } else if (updated_diff.NeedsPositionedMovementLayout()) { SetNeedsPositionedMovementLayout(); @@ -2060,32 +2155,34 @@ void LayoutObject::SetStyle(scoped_refptr<const ComputedStyle> style, container->SetNeedsOverflowRecalc(); } - if (diff.NeedsRecomputeOverflow() && !NeedsLayout()) { - // TODO(rhogan): Make inlines capable of recomputing overflow too. - if (IsLayoutBlock()) { - SetNeedsOverflowRecalc(); - } else { - SetNeedsLayoutAndPrefWidthsRecalc( + if (diff.NeedsRecomputeVisualOverflow()) { + if (!IsLayoutNGObject() && !IsLayoutBlock() && !NeedsLayout()) { + // TODO(rego): This is still needed because RecalcVisualOverflow() does + // not actually compute the visual overflow for inline elements (legacy + // layout). However in LayoutNG RecalcInlineChildrenInkOverflow() is + // called and visual overflow is recomputed properly so we don't need this + // (see crbug.com/1043927). + SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kStyleChange); + } else { + PaintingLayer()->SetNeedsVisualOverflowRecalc(); + SetShouldCheckForPaintInvalidation(); } } - if (diff.NeedsPaintInvalidationSubtree() || - updated_diff.NeedsPaintInvalidationSubtree()) { - SetSubtreeShouldDoFullPaintInvalidation(); - } else if (diff.NeedsPaintInvalidationObject() || - updated_diff.NeedsPaintInvalidationObject()) { - // TODO(wangxianzhu): For now LayoutSVGRoot::localVisualRect() depends on - // several styles. Refactor to avoid this special case. - if (IsSVGRoot()) + if (diff.NeedsPaintInvalidation() || updated_diff.NeedsPaintInvalidation()) { + if (IsSVGRoot()) { + // LayoutSVGRoot::LocalVisualRect() depends on some styles. SetShouldDoFullPaintInvalidation(); - else + } else { + // We'll set needing geometry change later if the style change does cause + // possible layout change or visual overflow change. SetShouldDoFullPaintInvalidationWithoutGeometryChange(); + } } - if ((diff.NeedsPaintInvalidationObject() || - diff.NeedsPaintInvalidationSubtree()) && - old_style && !old_style->ClipPathDataEquivalent(*style_)) { + if (diff.NeedsPaintInvalidation() && old_style && + !old_style->ClipPathDataEquivalent(*style_)) { InvalidateClipPathCache(); PaintingLayer()->SetNeedsCompositingInputsUpdate(); } @@ -2280,7 +2377,7 @@ void LayoutObject::StyleWillChange(StyleDifference diff, // Elements may inherit touch action from parent frame, so we need to report // touchstart handler if the root layout object has non-auto effective touch // action. - TouchAction old_touch_action = TouchAction::kTouchActionAuto; + TouchAction old_touch_action = TouchAction::kAuto; bool is_document_element = GetNode() && IsDocumentElement(); if (style_) { old_touch_action = is_document_element ? style_->GetEffectiveTouchAction() @@ -2290,11 +2387,11 @@ void LayoutObject::StyleWillChange(StyleDifference diff, ? new_style.GetEffectiveTouchAction() : new_style.GetTouchAction(); if (GetNode() && !GetNode()->IsTextNode() && - (old_touch_action == TouchAction::kTouchActionAuto) != - (new_touch_action == TouchAction::kTouchActionAuto)) { + (old_touch_action == TouchAction::kAuto) != + (new_touch_action == TouchAction::kAuto)) { EventHandlerRegistry& registry = GetDocument().GetFrame()->GetEventHandlerRegistry(); - if (new_touch_action != TouchAction::kTouchActionAuto) { + if (new_touch_action != TouchAction::kAuto) { registry.DidAddEventHandler(*GetNode(), EventHandlerRegistry::kTouchAction); } else { @@ -2342,6 +2439,22 @@ void LayoutObject::SetScrollAnchorDisablingStyleChangedOnAncestor() { } } +static void ClearAncestorScrollAnchors(LayoutObject* layout_object) { + PaintLayer* layer = nullptr; + if (LayoutObject* parent = layout_object->Parent()) + layer = parent->EnclosingLayer(); + + while (layer) { + if (PaintLayerScrollableArea* scrollable_area = + layer->GetScrollableArea()) { + ScrollAnchor* anchor = scrollable_area->GetScrollAnchor(); + DCHECK(anchor); + anchor->Clear(); + } + layer = layer->Parent(); + } +} + void LayoutObject::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { // First assume the outline will be affected. It may be updated when we know @@ -2372,7 +2485,8 @@ void LayoutObject::StyleDidChange(StyleDifference diff, MarkContainerChainForLayout(); } - SetNeedsLayoutAndPrefWidthsRecalc(layout_invalidation_reason::kStyleChange); + SetNeedsLayoutAndIntrinsicWidthsRecalc( + layout_invalidation_reason::kStyleChange); } else if (diff.NeedsPositionedMovementLayout()) { SetNeedsPositionedMovementLayout(); } @@ -2394,7 +2508,7 @@ void LayoutObject::StyleDidChange(StyleDifference diff, } } - if (diff.NeedsFullPaintInvalidation() && old_style) { + if (diff.NeedsPaintInvalidation() && old_style) { if (ResolveColor(*old_style, GetCSSPropertyBackgroundColor()) != ResolveColor(GetCSSPropertyBackgroundColor()) || old_style->BackgroundLayers() != StyleRef().BackgroundLayers()) @@ -2409,6 +2523,10 @@ void LayoutObject::StyleDidChange(StyleDifference diff, AddSubtreePaintPropertyUpdateReason( SubtreePaintPropertyUpdateReason::kTransformStyleChanged); } + + if (old_style && old_style->OverflowAnchor() != StyleRef().OverflowAnchor()) { + ClearAncestorScrollAnchors(this); + } } void LayoutObject::ApplyPseudoElementStyleChanges( @@ -2441,12 +2559,12 @@ void LayoutObject::ApplyFirstLineChanges(const ComputedStyle* old_style) { } } if (!has_diff) { - diff.SetNeedsPaintInvalidationObject(); + diff.SetNeedsPaintInvalidation(); diff.SetNeedsFullLayout(); } - if (BehavesLikeBlockContainer() && (diff.NeedsFullPaintInvalidation() || - diff.TextDecorationOrColorChanged())) { + if (BehavesLikeBlockContainer() && + (diff.NeedsPaintInvalidation() || diff.TextDecorationOrColorChanged())) { if (auto* first_line_container = To<LayoutBlock>(this)->NearestInnerBlockWithFirstLine()) first_line_container->SetShouldDoFullPaintInvalidationForFirstLine(); @@ -2455,7 +2573,8 @@ void LayoutObject::ApplyFirstLineChanges(const ComputedStyle* old_style) { if (diff.NeedsLayout()) { if (diff.NeedsFullLayout()) SetNeedsCollectInlines(); - SetNeedsLayoutAndPrefWidthsRecalc(layout_invalidation_reason::kStyleChange); + SetNeedsLayoutAndIntrinsicWidthsRecalc( + layout_invalidation_reason::kStyleChange); } } @@ -2481,15 +2600,18 @@ void LayoutObject::PropagateStyleToAnonymousChildren() { child_block_flow->IsAnonymousBlockContinuation()) new_style->SetPosition(child->StyleRef().GetPosition()); - if (child->IsLayoutNGListMarker()) - new_style->SetWhiteSpace(child->StyleRef().WhiteSpace()); - UpdateAnonymousChildStyle(child, *new_style); child->SetStyle(std::move(new_style)); } - if (StyleRef().StyleType() == kPseudoIdNone) + PseudoId pseudo_id = StyleRef().StyleType(); + if (pseudo_id == kPseudoIdNone) + return; + + // Don't propagate style from markers with 'content: normal' because it's not + // needed and it would be slow. + if (pseudo_id == kPseudoIdMarker && StyleRef().ContentBehavesAsNormal()) return; // Propagate style from pseudo elements to generated content. We skip children @@ -2681,10 +2803,10 @@ void LayoutObject::MapLocalToAncestor(const LayoutBoxModelObject* ancestor, : TransformState::kFlattenTransform); // If the ancestor is fixed, then the rect is already in its coordinates so // doesn't need viewport-adjusting. + auto* layout_view = DynamicTo<LayoutView>(container); if (ancestor->StyleRef().GetPosition() != EPosition::kFixed && - container->IsLayoutView() && - StyleRef().GetPosition() == EPosition::kFixed) { - transform_state.Move(ToLayoutView(container)->OffsetForFixedPosition()); + layout_view && StyleRef().GetPosition() == EPosition::kFixed) { + transform_state.Move(layout_view->OffsetForFixedPosition()); } return; } @@ -2745,10 +2867,10 @@ void LayoutObject::MapAncestorToLocal(const LayoutBoxModelObject* ancestor, transform_state.Move(-container_offset); // If the ancestor is fixed, then the rect is already in its coordinates so // doesn't need viewport-adjusting. + auto* layout_view = DynamicTo<LayoutView>(container); if (ancestor->StyleRef().GetPosition() != EPosition::kFixed && - container->IsLayoutView() && - StyleRef().GetPosition() == EPosition::kFixed) { - transform_state.Move(ToLayoutView(container)->OffsetForFixedPosition()); + layout_view && StyleRef().GetPosition() == EPosition::kFixed) { + transform_state.Move(layout_view->OffsetForFixedPosition()); } } } @@ -2931,30 +3053,12 @@ bool LayoutObject::IsRooted() const { RespectImageOrientationEnum LayoutObject::ShouldRespectImageOrientation( const LayoutObject* layout_object) { - if (!layout_object) - return kDoNotRespectImageOrientation; - - // Respect the image's orientation if it's being used as a full-page image or - // it's an <img> and the setting to respect it everywhere is set or the <img> - // has image-orientation: from-image style. FIXME: crbug.com/498233 - if (layout_object->GetDocument().IsImageDocument()) - return kRespectImageOrientation; - - if (!IsA<HTMLImageElement>(layout_object->GetNode())) - return kDoNotRespectImageOrientation; - - if (layout_object->GetDocument().GetSettings() && - layout_object->GetDocument() - .GetSettings() - ->GetShouldRespectImageOrientation()) - return kRespectImageOrientation; - - if (layout_object->Style() && - layout_object->StyleRef().RespectImageOrientation() == + if (layout_object && layout_object->Style() && + layout_object->StyleRef().RespectImageOrientation() != kRespectImageOrientation) - return kRespectImageOrientation; + return kDoNotRespectImageOrientation; - return kDoNotRespectImageOrientation; + return kRespectImageOrientation; } LayoutObject* LayoutObject::Container(AncestorSkipInfo* skip_info) const { @@ -2993,7 +3097,7 @@ LayoutObject* LayoutObject::Container(AncestorSkipInfo* skip_info) const { } inline LayoutObject* LayoutObject::ParentCrossingFrames() const { - if (IsLayoutView()) + if (IsA<LayoutView>(this)) return GetFrame()->OwnerLayoutObject(); return Parent(); } @@ -3044,7 +3148,7 @@ void LayoutObject::WillBeDestroyed() { // m_style is null in cases of partial construction. Any handler we added // previously may have already been removed by the Document independently. if (GetNode() && !GetNode()->IsTextNode() && style_ && - style_->GetTouchAction() != TouchAction::kTouchActionAuto) { + style_->GetTouchAction() != TouchAction::kAuto) { EventHandlerRegistry& registry = GetDocument().GetFrame()->GetEventHandlerRegistry(); if (registry.EventHandlerTargets(EventHandlerRegistry::kTouchAction) @@ -3178,6 +3282,11 @@ void LayoutObject::WillBeRemovedFromTree() { } void LayoutObject::SetNeedsPaintPropertyUpdate() { + SetNeedsPaintPropertyUpdatePreservingCachedRects(); + InvalidateIntersectionObserverCachedRects(); +} + +void LayoutObject::SetNeedsPaintPropertyUpdatePreservingCachedRects() { if (bitfields_.NeedsPaintPropertyUpdate()) return; @@ -3287,7 +3396,14 @@ void LayoutObject::DestroyAndCleanupAnonymousWrappers() { } void LayoutObject::Destroy() { + // Mark as being destroyed to avoid trouble with merges in |RemoveChild()| and + // other house keepings. + bitfields_.SetBeingDestroyed(true); WillBeDestroyed(); + DeleteThis(); +} + +void LayoutObject::DeleteThis() { delete this; } @@ -3302,6 +3418,10 @@ CompositingState LayoutObject::GetCompositingState() const { : kNotComposited; } +bool LayoutObject::CanHaveAdditionalCompositingReasons() const { + return false; +} + CompositingReasons LayoutObject::AdditionalCompositingReasons() const { return CompositingReason::kNone; } @@ -3343,17 +3463,12 @@ Node* LayoutObject::NodeForHitTest() const { // If we hit the anonymous layoutObjects inside generated content we should // actually hit the generated content so walk up to the PseudoElement. if (const LayoutObject* parent = Parent()) { - if (parent->IsBeforeOrAfterContent() || + if (parent->IsBeforeOrAfterContent() || parent->IsMarkerContent() || parent->StyleRef().StyleType() == kPseudoIdFirstLetter) { for (; parent; parent = parent->Parent()) { if (Node* node = parent->GetNode()) return node; } - } else if (const LayoutNGListItem* list_item = - LayoutNGListItem::FromMarkerOrMarkerContent(*this)) { - // If this is a list marker, or is inside of a list marker, return the - // list item. - return list_item->GetNode(); } } @@ -3377,9 +3492,8 @@ bool LayoutObject::NodeAtPoint(HitTestResult&, } void LayoutObject::ScheduleRelayout() { - if (IsLayoutView()) { - LocalFrameView* view = ToLayoutView(this)->GetFrameView(); - if (view) + if (auto* layout_view = DynamicTo<LayoutView>(this)) { + if (LocalFrameView* view = layout_view->GetFrameView()) view->ScheduleRelayout(); } else { if (IsRooted()) { @@ -3605,7 +3719,7 @@ Element* LayoutObject::OffsetParent(const Element* base) const { break; if (!IsPositioned() && - (IsA<HTMLTableElement>(*node) || IsHTMLTableCellElement(*node))) + (IsA<HTMLTableElement>(*node) || IsA<HTMLTableCellElement>(*node))) break; // Webkit specific extension where offsetParent stops at zoom level changes. @@ -3696,11 +3810,12 @@ PositionWithAffinity LayoutObject::CreatePositionWithAffinity( if (position.IsNotNull()) return PositionWithAffinity(position); - DCHECK(!GetNode()); + DCHECK(!NonPseudoNode()); return CreatePositionWithAffinity(0); } -CursorDirective LayoutObject::GetCursor(const PhysicalOffset&, Cursor&) const { +CursorDirective LayoutObject::GetCursor(const PhysicalOffset&, + ui::Cursor&) const { return kSetCursorBasedOnStyle; } @@ -4089,6 +4204,15 @@ LayoutUnit LayoutObject::FlipForWritingModeInternal( ->FlipForWritingMode(position, width); } +bool LayoutObject::SelfPaintingLayerNeedsVisualOverflowRecalc() const { + if (HasLayer()) { + auto* box_model_object = ToLayoutBoxModelObject(this); + if (box_model_object->HasSelfPaintingLayer()) + return box_model_object->Layer()->NeedsVisualOverflowRecalc(); + } + return false; +} + void LayoutObject::MarkSelfPaintingLayerForVisualOverflowRecalc() { if (HasLayer()) { auto* box_model_object = ToLayoutBoxModelObject(this); @@ -4097,6 +4221,20 @@ void LayoutObject::MarkSelfPaintingLayerForVisualOverflowRecalc() { } } +bool IsMenuList(const LayoutObject* object) { + if (!object) + return false; + auto* select = DynamicTo<HTMLSelectElement>(object->GetNode()); + return select && select->UsesMenuList(); +} + +bool IsListBox(const LayoutObject* object) { + if (!object) + return false; + auto* select = DynamicTo<HTMLSelectElement>(object->GetNode()); + return select && !select->UsesMenuList(); +} + } // namespace blink #if DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object.h b/chromium/third_party/blink/renderer/core/layout/layout_object.h index 28e6a2fec09..714d1246169 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_object.h @@ -31,6 +31,7 @@ #include "base/auto_reset.h" #include "base/macros.h" +#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/display_lock/display_lock_context.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -45,6 +46,7 @@ #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/layout_object_child_list.h" #include "third_party/blink/renderer/core/layout/map_coordinates_flags.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/ng_outline_type.h" #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h" #include "third_party/blink/renderer/core/layout/subtree_layout_scope.h" @@ -64,10 +66,12 @@ #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" -namespace blink { +namespace ui { +class Cursor; +} +namespace blink { class AffineTransform; -class Cursor; class HitTestLocation; class HitTestRequest; class InlineBox; @@ -89,13 +93,15 @@ class PaintLayer; class PseudoElementStyleRequest; struct PaintInfo; struct PaintInvalidatorContext; -struct WebScrollIntoViewParams; enum VisualRectFlags { kDefaultVisualRectFlags = 0, kEdgeInclusive = 1 << 0, // Use the GeometryMapper fast-path, if possible. kUseGeometryMapper = 1 << 1, + // When mapping to absolute coordinates and the main frame is remote, don't + // apply the main frame root scroller's overflow clip. + kDontApplyMainFrameOverflowClip = 1 << 2, }; enum CursorDirective { kSetCursorBasedOnStyle, kSetCursor, kDoNotSetCursor }; @@ -209,8 +215,8 @@ const int kShowTreeCharacterOffset = 39; // Those widths are used to determine the final layout logical width, which // depends on the layout algorithm used and the available logical width. // -// LayoutObject only has getters for the widths (MinPreferredLogicalWidth and -// MaxPreferredLogicalWidth). However the storage for them is in LayoutBox (see +// LayoutObject only has a getter for the widths (PreferredLogicalWidths). +// However the storage for them is in LayoutBox (see // min_preferred_logical_width_ and max_preferred_logical_width_). This is // because only boxes implementing the full box model have a need for them. // Because LayoutBlockFlow's intrinsic widths rely on the underlying text @@ -219,7 +225,7 @@ const int kShowTreeCharacterOffset = 39; // The 2 widths are computed lazily during layout when the getters are called. // The computation is done by calling ComputePreferredLogicalWidths() behind the // scene. The boolean used to control the lazy recomputation is -// PreferredLogicalWidthsDirty. +// IntrinsicLogicalWidthsDirty. // // See the individual getters below for more details about what each width is. class CORE_EXPORT LayoutObject : public ImageResourceObserver, @@ -374,7 +380,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // TODO(nburris): The returned rect is actually in document coordinates, not // root frame coordinates. PhysicalRect ScrollRectToVisible(const PhysicalRect&, - const WebScrollIntoViewParams&); + mojom::blink::ScrollIntoViewParamsPtr); // Convenience function for getting to the nearest enclosing box of a // LayoutObject. @@ -439,13 +445,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, } } - void AssertClearedPaintInvalidationFlags() const { - if (PaintInvalidationStateIsDirty() && - !PrePaintBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren)) { - ShowLayoutTreeForThis(); - NOTREACHED(); - } - } + void AssertClearedPaintInvalidationFlags() const; void AssertSubtreeClearedPaintInvalidationFlags() const { for (const LayoutObject* layout_object = this; layout_object; @@ -644,6 +644,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, } bool IsFrame() const { return IsOfType(kLayoutObjectFrame); } bool IsFrameSet() const { return IsOfType(kLayoutObjectFrameSet); } + bool IsInsideListMarker() const { + return IsOfType(kLayoutObjectInsideListMarker); + } bool IsLayoutNGBlockFlow() const { return IsOfType(kLayoutObjectNGBlockFlow); } @@ -652,25 +655,27 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, } bool IsLayoutNGMixin() const { return IsOfType(kLayoutObjectNGMixin); } bool IsLayoutNGListItem() const { return IsOfType(kLayoutObjectNGListItem); } - bool IsLayoutNGListMarker() const { - return IsOfType(kLayoutObjectNGListMarker); - } bool IsLayoutNGInsideListMarker() const { return IsOfType(kLayoutObjectNGInsideListMarker); } bool IsLayoutNGListMarkerImage() const { return IsOfType(kLayoutObjectNGListMarkerImage); } + bool IsLayoutNGOutsideListMarker() const { + return IsOfType(kLayoutObjectNGOutsideListMarker); + } bool IsLayoutNGProgress() const { return IsOfType(kLayoutObjectNGProgress); } bool IsLayoutNGText() const { return IsOfType(kLayoutObjectNGText); } bool IsLayoutTableCol() const { return IsOfType(kLayoutObjectLayoutTableCol); } - bool IsListBox() const { return IsOfType(kLayoutObjectListBox); } bool IsListItem() const { return IsOfType(kLayoutObjectListItem); } - bool IsListMarker() const { return IsOfType(kLayoutObjectListMarker); } + bool IsMathML() const { return IsOfType(kLayoutObjectMathML); } + bool IsMathMLRoot() const { return IsOfType(kLayoutObjectMathMLRoot); } bool IsMedia() const { return IsOfType(kLayoutObjectMedia); } - bool IsMenuList() const { return IsOfType(kLayoutObjectMenuList); } + bool IsOutsideListMarker() const { + return IsOfType(kLayoutObjectOutsideListMarker); + } bool IsProgress() const { return IsOfType(kLayoutObjectProgress); } bool IsQuote() const { return IsOfType(kLayoutObjectQuote); } bool IsLayoutButton() const { return IsOfType(kLayoutObjectLayoutButton); } @@ -832,11 +837,8 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bool IsSVGResourceContainer() const { return IsOfType(kLayoutObjectSVGResourceContainer); } - bool IsSVGResourceFilter() const { - return IsOfType(kLayoutObjectSVGResourceFilter); - } - bool IsSVGResourceFilterPrimitive() const { - return IsOfType(kLayoutObjectSVGResourceFilterPrimitive); + bool IsSVGFilterPrimitive() const { + return IsOfType(kLayoutObjectSVGFilterPrimitive); } // FIXME: Those belong into a SVG specific base-class for all layoutObjects @@ -1072,14 +1074,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, } bool NeedsCollectInlines() const { return bitfields_.NeedsCollectInlines(); } - // Return true if the min/max preferred logical widths aren't up-to-date. Note - // that for objects that *don't* need to calculate preferred logical widths - // (e.g. if inline-size is a fixed value, and no other inline lengths are - // intrinsic, and the object isn't a descendant of something that needs + // Return true if the min/max intrinsic logical widths aren't up-to-date. + // Note that for objects that *don't* need to calculate intrinsic logical + // widths (e.g. if inline-size is a fixed value, and no other inline lengths + // are intrinsic, and the object isn't a descendant of something that needs // min/max), this flag will never be cleared (since the values will never be // calculated). - bool PreferredLogicalWidthsDirty() const { - return bitfields_.PreferredLogicalWidthsDirty(); + bool IntrinsicLogicalWidthsDirty() const { + return bitfields_.IntrinsicLogicalWidthsDirty(); } bool NeedsLayoutOverflowRecalc() const { @@ -1343,20 +1345,27 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, void SetChildNeedsLayout(MarkingBehavior = kMarkContainerChain, SubtreeLayoutScope* = nullptr); void SetNeedsPositionedMovementLayout(); - void SetPreferredLogicalWidthsDirty(MarkingBehavior = kMarkContainerChain); - void ClearPreferredLogicalWidthsDirty(); + void SetIntrinsicLogicalWidthsDirty(MarkingBehavior = kMarkContainerChain); + void ClearIntrinsicLogicalWidthsDirty(); - void SetNeedsLayoutAndPrefWidthsRecalc( + void SetNeedsLayoutAndIntrinsicWidthsRecalc( LayoutInvalidationReasonForTracing reason) { SetNeedsLayout(reason); - SetPreferredLogicalWidthsDirty(); + SetIntrinsicLogicalWidthsDirty(); } - void SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + void SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( LayoutInvalidationReasonForTracing reason) { SetNeedsLayoutAndFullPaintInvalidation(reason); - SetPreferredLogicalWidthsDirty(); + SetIntrinsicLogicalWidthsDirty(); } + // Traverses subtree, and marks all layout objects as need relayout, repaint + // and preferred width recalc. Also invalidates shaping on all text nodes. + // TODO(crbug.com/441925): Try to partially invalidate layout on font updates. + virtual void InvalidateSubtreeLayoutForFontUpdates(); + + void InvalidateIntersectionObserverCachedRects(); + void SetPositionState(EPosition position) { DCHECK( (position != EPosition::kAbsolute && position != EPosition::kFixed) || @@ -1368,10 +1377,24 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, void SetFloating(bool is_floating) { bitfields_.SetFloating(is_floating); } void SetInline(bool is_inline) { bitfields_.SetIsInline(is_inline); } + // Return whether we can directly traverse fragments generated for this layout + // object, when it comes to painting, hit-testing and other layout read + // operations. If false is returned, we need to traverse the layout object + // tree instead. + // + // It is not allowed to call this method on a non-LayoutBox object, unless its + // containing block is an NG object (e.g. not allowed to call it on a + // LayoutInline that's contained by a legacy LayoutBlockFlow). + inline bool CanTraversePhysicalFragments() const; + // Returns the associated |NGPaintFragment|. When this is not a |nullptr|, // this is the root of an inline formatting context, laid out by LayoutNG. virtual const NGPaintFragment* PaintFragment() const { return nullptr; } + // Return true if |this| produces one or more inline fragments, including + // whitespace-only text fragments. + virtual bool HasInlineFragments() const { return false; } + // Paint/Physical fragments are not in sync with LayoutObject tree until it is // laid out. For inline, it needs to check if the containing block is // layout-clean. crbug.com/963103 @@ -1479,6 +1502,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, virtual void AddAnnotatedRegions(Vector<AnnotatedRegionValue>&); CompositingState GetCompositingState() const; + + // True for object types which override |AdditionalCompositingReasons|. + virtual bool CanHaveAdditionalCompositingReasons() const; virtual CompositingReasons AdditionalCompositingReasons() const; // |accumulated_offset| is accumulated physical offset of this object from @@ -1563,6 +1589,11 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // See LayoutBlock.h for some extra explanations on containing blocks. LayoutBlock* ContainingBlock(AncestorSkipInfo* = nullptr) const; + // Returns |container|'s containing block. + static LayoutBlock* FindNonAnonymousContainingBlock( + LayoutObject* container, + AncestorSkipInfo* = nullptr); + const LayoutBlock* InclusiveContainingBlock() const; bool CanContainAbsolutePositionObjects() const { @@ -1725,8 +1756,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, virtual void AbsoluteQuads(Vector<FloatQuad>&, MapCoordinatesFlags mode = 0) const {} - static FloatRect AbsoluteBoundingBoxRectForRange(const EphemeralRange&); - // The bounding box (see: absoluteBoundingBoxRect) including all descendant // bounding boxes. IntRect AbsoluteBoundingBoxRectIncludingDescendants() const; @@ -1736,28 +1765,20 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // to any ancestor using, e.g., localToAncestorTransform. virtual FloatRect LocalBoundingBoxRectForAccessibility() const = 0; - // This function returns the minimal logical width this object can have - // without overflowing. This means that all the opportunities for wrapping - // have been taken. + // This function returns the: + // - Minimal logical width this object can have without overflowing. This + // means that all the opportunities for wrapping have been taken. + // - Maximal logical width. // // See INTRINSIC SIZES / PREFERRED LOGICAL WIDTHS above. // - // CSS 2.1 calls this width the "preferred minimum width" (thus this name) - // and "minimum content width" (for table). - // However CSS 3 calls it the "min-content inline size". + // CSS 2.1 calls this width the "preferred minimum width"/"preferred width" + // (thus this name) and "minimum content width" (for table). + // However CSS 3 calls it the "min/max-content inline size". // https://drafts.csswg.org/css-sizing-3/#min-content-inline-size - // TODO(jchaffraix): We will probably want to rename it to match CSS 3. - virtual LayoutUnit MinPreferredLogicalWidth() const { return LayoutUnit(); } - - // This function returns the maximum logical width this object can have. - // - // See INTRINSIC SIZES / PREFERRED LOGICAL WIDTHS above. - // - // CSS 2.1 calls this width the "preferred width". However CSS 3 calls it - // the "max-content inline size". // https://drafts.csswg.org/css-sizing-3/#max-content-inline-size // TODO(jchaffraix): We will probably want to rename it to match CSS 3. - virtual LayoutUnit MaxPreferredLogicalWidth() const { return LayoutUnit(); } + virtual MinMaxSizes PreferredLogicalWidths() const { return MinMaxSizes(); } const ComputedStyle* Style() const { return style_.get(); } @@ -1791,7 +1812,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, return StyleRef().VisitedDependentColor(color_property); } - virtual CursorDirective GetCursor(const PhysicalOffset&, Cursor&) const; + virtual CursorDirective GetCursor(const PhysicalOffset&, ui::Cursor&) const; // Return the LayoutBoxModelObject in the container chain which is responsible // for painting this object. The function crosses frames boundaries so the @@ -1860,6 +1881,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, TransformState&, VisualRectFlags = kDefaultVisualRectFlags) const; + // Returns the nearest ancestor in the containing block chain that + // HasLocalBorderBoxProperties. If AncestorSkipInfo* is non-null and the + // ancestor was skipped, returns nullptr. If PropertyTreeState* is non-null, + // it will be populated with paint property nodes suitable for mapping upward + // from the coordinate system of the property container. + const LayoutObject* GetPropertyContainer(AncestorSkipInfo*, + PropertyTreeState* = nullptr) const; + // Do a rect-based hit test with this object as the stop node. HitTestResult HitTestForOcclusion(const PhysicalRect&) const; HitTestResult HitTestForOcclusion() const { @@ -1879,6 +1908,15 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, return (IsFloating() || IsOutOfFlowPositioned()); } + // Outside list markers are in-flow but behave kind of out-of-flowish. + // We include them here to prevent code like '<li> <ol></ol></li>' from + // generating an anonymous block box for the whitespace between the marker + // and the <ol>. + bool AffectsWhitespaceSiblings() const { + return !IsFloatingOrOutOfFlowPositioned() && + !IsLayoutNGOutsideListMarker() && !IsOutsideListMarker(); + } + bool HasReflection() const { return bitfields_.HasReflection(); } // The current selection state for an object. For blocks, the state refers to @@ -1932,12 +1970,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, void DestroyAndCleanupAnonymousWrappers(); - // While the destroy() method is virtual, this should only be overriden in - // very rare circumstances. - // You want to override willBeDestroyed() instead unless you explicitly need - // to stop this object from being destroyed (for example, - // LayoutEmbeddedContent overrides destroy() for this purpose). - virtual void Destroy(); + void Destroy(); // Virtual function helpers for the deprecated Flexible Box Layout (display: // -webkit-box). @@ -1958,14 +1991,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // There are 3 types of list marker. LayoutNG creates different types for // inside and outside; outside is derived from LayoutBlockFlow, and inside // from LayoutInline. Legacy is derived from LayoutBox. - bool IsListMarkerIncludingNG() const { - return IsListMarker() || IsLayoutNGListMarker(); + bool IsListMarker() const { + return IsOutsideListMarker() || IsInsideListMarker(); } - bool IsLayoutNGListMarkerIncludingInside() const { - return IsLayoutNGListMarker() || IsLayoutNGInsideListMarker(); + bool IsListMarkerIncludingNGOutside() const { + return IsListMarker() || IsLayoutNGOutsideListMarker(); } - bool IsListMarkerIncludingNGInside() const { - return IsListMarker() || IsLayoutNGListMarkerIncludingInside(); + bool IsListMarkerIncludingNGOutsideAndInside() const { + return IsListMarkerIncludingNGOutside() || IsLayoutNGInsideListMarker(); } virtual bool IsCombineText() const { return false; } @@ -2176,8 +2209,12 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // Returns the bounding box of the visual rects of all fragments. IntRect FragmentsVisualRectBoundingBox() const; - void SetNeedsOverflowRecalc(); - void SetNeedsVisualOverflowAndPaintInvalidation(); + enum OverflowRecalcType { + kOnlyVisualOverflowRecalc, + kLayoutAndVisualOverflowRecalc, + }; + void SetNeedsOverflowRecalc( + OverflowRecalcType = OverflowRecalcType::kLayoutAndVisualOverflowRecalc); void InvalidateClipPathCache(); @@ -2189,11 +2226,11 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // (from style) and blocking touch event handlers. TouchAction EffectiveAllowedTouchAction() const { if (InsideBlockingTouchEventHandler()) - return TouchAction::kTouchActionNone; + return TouchAction::kNone; return StyleRef().GetEffectiveTouchAction(); } bool HasEffectiveAllowedTouchAction() const { - return EffectiveAllowedTouchAction() != TouchAction::kTouchActionAuto; + return EffectiveAllowedTouchAction() != TouchAction::kAuto; } // Whether this object's Node has a blocking touch event handler on itself @@ -2239,7 +2276,13 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, } } void SetShouldCheckForPaintInvalidation() { - layout_object_.SetShouldCheckForPaintInvalidation(); + // This method is only intended to be called when visiting this object + // during pre-paint, and as such it should only mark itself, and not the + // entire containing block chain. + DCHECK_EQ(layout_object_.GetDocument().Lifecycle().GetState(), + DocumentLifecycle::kInPrePaint); + layout_object_.bitfields_.SetNeedsPaintOffsetAndVisualRectUpdate(true); + layout_object_.bitfields_.SetShouldCheckForPaintInvalidation(true); } void SetShouldDoFullPaintInvalidation(PaintInvalidationReason reason) { layout_object_.SetShouldDoFullPaintInvalidation(reason); @@ -2272,9 +2315,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, layout_object_.fragment_.SetSelectionVisualRect(r); } - void SetPreviousBackgroundPaintLocation(BackgroundPaintLocation location) { - layout_object_.bitfields_.SetPreviousBackgroundPaintLocation(location); + void SetBackgroundPaintLocation(BackgroundPaintLocation location) { + layout_object_.SetBackgroundPaintLocation(location); } + void UpdatePreviousOutlineMayBeAffectedByDescendants() { layout_object_.SetPreviousOutlineMayBeAffectedByDescendants( layout_object_.OutlineMayBeAffectedByDescendants()); @@ -2303,6 +2347,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, layout_object_.UpdateInsideBlockingTouchEventHandler(inside); } + void InvalidateIntersectionObserverCachedRects() { + layout_object_.InvalidateIntersectionObserverCachedRects(); + } + #if DCHECK_IS_ON() // Same as setNeedsPaintPropertyUpdate() but does not mark ancestors as // having a descendant needing a paint property update. @@ -2352,6 +2400,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // updated, SetNeedsPaintPropertyUpdate marks all ancestors as having a // descendant needing a paint property update too. void SetNeedsPaintPropertyUpdate(); + void SetNeedsPaintPropertyUpdatePreservingCachedRects(); bool NeedsPaintPropertyUpdate() const { return bitfields_.NeedsPaintPropertyUpdate(); } @@ -2387,8 +2436,14 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bool CompositedScrollsWithRespectTo( const LayoutBoxModelObject& paint_invalidation_container) const; - BackgroundPaintLocation PreviousBackgroundPaintLocation() const { - return bitfields_.PreviousBackgroundPaintLocation(); + BackgroundPaintLocation GetBackgroundPaintLocation() const { + return bitfields_.GetBackgroundPaintLocation(); + } + void SetBackgroundPaintLocation(BackgroundPaintLocation location) { + if (GetBackgroundPaintLocation() != location) { + SetBackgroundNeedsFullPaintInvalidation(); + bitfields_.SetBackgroundPaintLocation(location); + } } bool IsBackgroundAttachmentFixedObject() const { @@ -2467,8 +2522,10 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bitfields_.SetHasNonCollapsedBorderDecoration(b); } + bool BeingDestroyed() const { return bitfields_.BeingDestroyed(); } + DisplayLockContext* GetDisplayLockContext() const { - if (!RuntimeEnabledFeatures::DisplayLockingEnabled(&GetDocument())) + if (!RuntimeEnabledFeatures::CSSSubtreeVisibilityEnabled()) return nullptr; auto* element = DynamicTo<Element>(GetNode()); if (!element) @@ -2487,22 +2544,23 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, kLayoutObjectFileUploadControl, kLayoutObjectFrame, kLayoutObjectFrameSet, + kLayoutObjectInsideListMarker, kLayoutObjectLayoutTableCol, - kLayoutObjectListBox, kLayoutObjectListItem, - kLayoutObjectListMarker, + kLayoutObjectMathML, + kLayoutObjectMathMLRoot, kLayoutObjectMedia, - kLayoutObjectMenuList, kLayoutObjectNGBlockFlow, kLayoutObjectNGFieldset, kLayoutObjectNGFlexibleBox, kLayoutObjectNGMixin, kLayoutObjectNGListItem, - kLayoutObjectNGListMarker, kLayoutObjectNGInsideListMarker, + kLayoutObjectNGOutsideListMarker, kLayoutObjectNGListMarkerImage, kLayoutObjectNGProgress, kLayoutObjectNGText, + kLayoutObjectOutsideListMarker, kLayoutObjectProgress, kLayoutObjectQuote, kLayoutObjectLayoutButton, @@ -2549,11 +2607,19 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, kLayoutObjectSVGImage, kLayoutObjectSVGForeignObject, kLayoutObjectSVGResourceContainer, - kLayoutObjectSVGResourceFilter, - kLayoutObjectSVGResourceFilterPrimitive, + kLayoutObjectSVGFilterPrimitive, }; virtual bool IsOfType(LayoutObjectType type) const { return false; } + // While the |DeleteThis()| method is virtual, this should only be overridden + // in very rare circumstances. + // You want to override |WillBeDestroyed()| instead unless you explicitly need + // to stop this object from being destroyed (for example, + // |LayoutEmbeddedContent| overrides |DeleteThis()| for this purpose). + virtual void DeleteThis(); + + void SetBeingDestroyedForTesting() { bitfields_.SetBeingDestroyed(true); } + const ComputedStyle& SlowEffectiveStyle(NGStyleVariant style_variant) const; // Updates only the local style ptr of the object. Does not update the state @@ -2661,10 +2727,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, bitfields_.SetBackgroundIsKnownToBeObscured(b); } - // Returns |container|'s containing block. - static LayoutBlock* FindNonAnonymousContainingBlock( - LayoutObject* container, - AncestorSkipInfo* = nullptr); // Returns ContainerForAbsolutePosition() if it's a LayoutBlock, or the // containing LayoutBlock of it. LayoutBlock* ContainingBlockForAbsolutePosition( @@ -2709,11 +2771,13 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, // scroll anchoring on. void SetScrollAnchorDisablingStyleChangedOnAncestor(); - inline void MarkContainerChainForOverflowRecalcIfNeeded(); + bool SelfPaintingLayerNeedsVisualOverflowRecalc() const; + inline void MarkContainerChainForOverflowRecalcIfNeeded( + bool mark_container_chain_layout_overflow_recalc); inline void SetNeedsPaintOffsetAndVisualRectUpdate(); - inline void InvalidateContainerPreferredLogicalWidths(); + inline void InvalidateContainerIntrinsicLogicalWidths(); const LayoutBoxModelObject* EnclosingCompositedContainer() const; @@ -2810,7 +2874,7 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, needs_simplified_normal_flow_layout_(false), self_needs_layout_overflow_recalc_(false), child_needs_layout_overflow_recalc_(false), - preferred_logical_widths_dirty_(false), + intrinsic_logical_widths_dirty_(false), needs_collect_inlines_(false), should_check_for_paint_invalidation_(true), subtree_should_check_for_paint_invalidation_(false), @@ -2864,11 +2928,12 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, registered_as_first_line_image_observer_(false), is_html_legend_element_(false), has_non_collapsed_border_decoration_(false), + being_destroyed_(false), positioned_state_(kIsStaticallyPositioned), selection_state_(static_cast<unsigned>(SelectionState::kNone)), subtree_paint_property_update_reasons_( static_cast<unsigned>(SubtreePaintPropertyUpdateReason::kNone)), - previous_background_paint_location_(0) {} + background_paint_location_(kBackgroundPaintInGraphicsLayer) {} // Self needs layout for style means that this layout object is marked for a // full layout. This is the default layout but it is expensive as it @@ -2918,12 +2983,12 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ADD_BOOLEAN_BITFIELD(child_needs_layout_overflow_recalc_, ChildNeedsLayoutOverflowRecalc); - // This boolean marks preferred logical widths for lazy recomputation. + // This boolean marks the intrinsic logical widths for lazy recomputation. // // See INTRINSIC SIZES / PREFERRED LOGICAL WIDTHS above about those // widths. - ADD_BOOLEAN_BITFIELD(preferred_logical_widths_dirty_, - PreferredLogicalWidthsDirty); + ADD_BOOLEAN_BITFIELD(intrinsic_logical_widths_dirty_, + IntrinsicLogicalWidthsDirty); // This flag is set on inline container boxes that need to run the // Pre-layout phase in LayoutNG. See NGInlineNode::CollectInlines(). @@ -3124,6 +3189,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ADD_BOOLEAN_BITFIELD(has_non_collapsed_border_decoration_, HasNonCollapsedBorderDecoration); + // True at start of |Destroy()| before calling |WillBeDestroyed()|. + ADD_BOOLEAN_BITFIELD(being_destroyed_, BeingDestroyed); + private: // This is the cached 'position' value of this object // (see ComputedStyle::position). @@ -3134,8 +3202,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, unsigned subtree_paint_property_update_reasons_ : kSubtreePaintPropertyUpdateReasonsBitfieldWidth; - // BackgroundPaintLocation of previous paint invalidation. - unsigned previous_background_paint_location_ : 2; + // Updated during CompositingUpdate in pre-CompositeAfterPaint, or PrePaint + // in CompositeAfterPaint. + unsigned background_paint_location_ : 2; // BackgroundPaintLocation. public: bool IsOutOfFlowPositioned() const { @@ -3202,15 +3271,13 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, static_cast<unsigned>(SubtreePaintPropertyUpdateReason::kNone); } - ALWAYS_INLINE BackgroundPaintLocation - PreviousBackgroundPaintLocation() const { - return static_cast<BackgroundPaintLocation>( - previous_background_paint_location_); + ALWAYS_INLINE BackgroundPaintLocation GetBackgroundPaintLocation() const { + return static_cast<BackgroundPaintLocation>(background_paint_location_); } - ALWAYS_INLINE void SetPreviousBackgroundPaintLocation( + ALWAYS_INLINE void SetBackgroundPaintLocation( BackgroundPaintLocation location) { - previous_background_paint_location_ = static_cast<unsigned>(location); - DCHECK_EQ(location, PreviousBackgroundPaintLocation()); + background_paint_location_ = static_cast<unsigned>(location); + DCHECK_EQ(location, GetBackgroundPaintLocation()); } }; @@ -3290,6 +3357,33 @@ inline bool LayoutObject::IsBeforeOrAfterContent() const { return IsBeforeContent() || IsAfterContent(); } +inline bool LayoutObject::CanTraversePhysicalFragments() const { + if (LIKELY(!RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled())) + return false; + // Non-NG objects should be painted by legacy. + if (!IsLayoutNGObject()) { + if (IsBox()) + return false; + // Non-LayoutBox objects (such as LayoutInline) don't necessarily create NG + // LayoutObjects. If they are laid out by an NG container, though, we may be + // allowed to traverse their fragments. Otherwise, bail now. + if (!IsInLayoutNGInlineFormattingContext()) + return false; + } + // Bail if we have an NGPaintFragment. NGPaintFragment will be removed, and we + // will not attempt to add support for them here. + if (PaintFragment()) + return false; + // We don't support fragmentation traversal inside block fragmentation just + // yet. + if (IsInsideFlowThread()) + return false; + // The NG paint system currently doesn't support table-cells. + if (IsTableCell()) + return false; + return true; +} + // setNeedsLayout() won't cause full paint invalidations as // setNeedsLayoutAndFullPaintInvalidation() does. Otherwise the two methods are // identical. @@ -3428,6 +3522,9 @@ CORE_EXPORT std::ostream& operator<<(std::ostream&, const LayoutObject&); DEFINE_TYPE_CASTS(thisType, LayoutObject, object, object->predicate, \ object.predicate) +bool IsMenuList(const LayoutObject* object); +CORE_EXPORT bool IsListBox(const LayoutObject* object); + } // namespace blink #if DCHECK_IS_ON() diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc index fb9db0a519e..8a71bc3a13b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_child_list.cc @@ -58,7 +58,11 @@ void InvalidateInlineItems(LayoutObject* object) { } } - if (NGPaintFragment* fragment = object->FirstInlineFragment()) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + // TODO(yosin): Tells |NGFragmentItem| about they become not to associate + // to layout object. + object->ClearFirstInlineFragmentItemIndex(); + } else if (NGPaintFragment* fragment = object->FirstInlineFragment()) { // This LayoutObject is not technically destroyed, but further access should // be prohibited when moved to different parent as if it were destroyed. fragment->LayoutObjectWillBeDestroyed(); @@ -70,20 +74,13 @@ void InvalidateInlineItems(LayoutObject* object) { } // namespace void LayoutObjectChildList::DestroyLeftoverChildren() { - while (FirstChild()) { - // List markers are owned by their enclosing list and so don't get destroyed - // by this container. - if (FirstChild()->IsListMarkerIncludingNG()) { - FirstChild()->Remove(); - continue; - } - - // Destroy any anonymous children remaining in the layout tree, as well as - // implicit (shadow) DOM elements like those used in the engine-based text - // fields. - if (FirstChild()->GetNode()) - FirstChild()->GetNode()->SetLayoutObject(nullptr); - FirstChild()->Destroy(); + // Destroy any anonymous children remaining in the layout tree, as well as + // implicit (shadow) DOM elements like those used in the engine-based text + // fields. + while (LayoutObject* child = FirstChild()) { + if (Node* child_node = child->GetNode()) + child_node->SetLayoutObject(nullptr); + child->Destroy(); } } @@ -103,7 +100,7 @@ LayoutObject* LayoutObjectChildList::RemoveChildNode( // issue paint invalidations, so that the area exposed when the child // disappears gets paint invalidated properly. if (notify_layout_object && old_child->EverHadLayout()) { - old_child->SetNeedsLayoutAndPrefWidthsRecalc( + old_child->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kRemovedFromLayout); if (old_child->IsOutOfFlowPositioned() && RuntimeEnabledFeatures::LayoutNGEnabled()) @@ -129,6 +126,8 @@ LayoutObject* LayoutObjectChildList::RemoveChildNode( if (old_child->IsInLayoutNGInlineFormattingContext()) { owner->SetChildNeedsCollectInlines(); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + InvalidateInlineItems(old_child); } } @@ -184,6 +183,12 @@ void LayoutObjectChildList::InsertChildNode(LayoutObject* owner, return; } + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled() && + !owner->DocumentBeingDestroyed() && + new_child->IsInLayoutNGInlineFormattingContext()) { + InvalidateInlineItems(new_child); + } + new_child->SetParent(owner); if (FirstChild() == before_child) @@ -239,7 +244,7 @@ void LayoutObjectChildList::InsertChildNode(LayoutObject* owner, // actually happens. } - new_child->SetNeedsLayoutAndPrefWidthsRecalc( + new_child->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kAddedToLayout); if (new_child->IsOutOfFlowPositioned() && RuntimeEnabledFeatures::LayoutNGEnabled()) diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_factory.cc b/chromium/third_party/blink/renderer/core/layout/layout_object_factory.cc index 00524e1719a..5ec6031ef59 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_factory.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_factory.cc @@ -5,10 +5,15 @@ #include "third_party/blink/renderer/core/layout/layout_object_factory.h" #include "third_party/blink/renderer/core/dom/element.h" +#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" +#include "third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h" #include "third_party/blink/renderer/core/layout/layout_fieldset.h" +#include "third_party/blink/renderer/core/layout/layout_file_upload_control.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" +#include "third_party/blink/renderer/core/layout/layout_inside_list_marker.h" #include "third_party/blink/renderer/core/layout/layout_list_item.h" +#include "third_party/blink/renderer/core/layout/layout_outside_list_marker.h" #include "third_party/blink/renderer/core/layout/layout_table_caption.h" #include "third_party/blink/renderer/core/layout/layout_table_cell.h" #include "third_party/blink/renderer/core/layout/layout_text.h" @@ -24,7 +29,7 @@ #include "third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -74,9 +79,26 @@ LayoutBlockFlow* LayoutObjectFactory::CreateBlockFlow( Node& node, const ComputedStyle& style, LegacyLayout legacy) { + if (style.Display() == EDisplay::kListItem) { + // Create a LayoutBlockFlow with a list marker + return CreateObject<LayoutBlockFlow, LayoutNGListItem, LayoutListItem>( + node, style, legacy); + } + + // Create a plain LayoutBlockFlow return CreateObject<LayoutBlockFlow, LayoutNGBlockFlow>(node, style, legacy); } +// static +LayoutBlock* LayoutObjectFactory::CreateBlockForLineClamp( + Node& node, + const ComputedStyle& style, + LegacyLayout legacy) { + DCHECK(RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled()); + return CreateObject<LayoutBlock, LayoutNGBlockFlow, + LayoutDeprecatedFlexibleBox>(node, style, legacy); +} + LayoutBlock* LayoutObjectFactory::CreateFlexibleBox(Node& node, const ComputedStyle& style, LegacyLayout legacy) { @@ -85,28 +107,20 @@ LayoutBlock* LayoutObjectFactory::CreateFlexibleBox(Node& node, node, style, legacy, disable_ng_for_type); } -LayoutBlockFlow* LayoutObjectFactory::CreateListItem(Node& node, - const ComputedStyle& style, - LegacyLayout legacy) { - return CreateObject<LayoutBlockFlow, LayoutNGListItem, LayoutListItem>( - node, style, legacy); -} - LayoutObject* LayoutObjectFactory::CreateListMarker(Node& node, const ComputedStyle& style, LegacyLayout legacy) { - // TODO(obrufau): allow ::marker pseudo-elements to generate legacy layout. - if (!RuntimeEnabledFeatures::LayoutNGEnabled() || - legacy == LegacyLayout::kForce) - return nullptr; - // TODO(obrufau): markers may be forced to be inside despite having - // `list-style-position: outside`. - if (style.ListStylePosition() == EListStylePosition::kInside) { + const Node* parent = node.parentNode(); + const ComputedStyle* parent_style = parent->GetComputedStyle(); + bool is_inside = + parent_style->ListStylePosition() == EListStylePosition::kInside || + (IsA<HTMLLIElement>(parent) && !parent_style->IsInsideListElement()); + if (is_inside) { return CreateObject<LayoutObject, LayoutNGInsideListMarker, - LayoutNGInsideListMarker>(node, style, legacy); + LayoutInsideListMarker>(node, style, legacy); } - return CreateObject<LayoutObject, LayoutNGListMarker, LayoutNGListMarker>( - node, style, legacy); + return CreateObject<LayoutObject, LayoutNGOutsideListMarker, + LayoutOutsideListMarker>(node, style, legacy); } LayoutTableCaption* LayoutObjectFactory::CreateTableCaption( @@ -132,6 +146,14 @@ LayoutBlock* LayoutObjectFactory::CreateFieldset(Node& node, node, style, legacy, disable_ng_for_type); } +LayoutBlockFlow* LayoutObjectFactory::CreateFileUploadControl( + Node& node, + const ComputedStyle& style, + LegacyLayout legacy) { + return CreateObject<LayoutBlockFlow, LayoutNGBlockFlow, + LayoutFileUploadControl>(node, style, legacy); +} + LayoutText* LayoutObjectFactory::CreateText(Node* node, scoped_refptr<StringImpl> str, LegacyLayout legacy) { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_factory.h b/chromium/third_party/blink/renderer/core/layout/layout_object_factory.h index 4fc7ac0f5af..37428424c92 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_factory.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_factory.h @@ -38,12 +38,12 @@ class LayoutObjectFactory { static LayoutBlockFlow* CreateBlockFlow(Node&, const ComputedStyle&, LegacyLayout); + static LayoutBlock* CreateBlockForLineClamp(Node& node, + const ComputedStyle& style, + LegacyLayout legacy); static LayoutBlock* CreateFlexibleBox(Node&, const ComputedStyle&, LegacyLayout); - static LayoutBlockFlow* CreateListItem(Node&, - const ComputedStyle&, - LegacyLayout); static LayoutObject* CreateListMarker(Node&, const ComputedStyle&, LegacyLayout); @@ -54,6 +54,9 @@ class LayoutObjectFactory { const ComputedStyle&, LegacyLayout); static LayoutBlock* CreateFieldset(Node&, const ComputedStyle&, LegacyLayout); + static LayoutBlockFlow* CreateFileUploadControl(Node& node, + const ComputedStyle& style, + LegacyLayout legacy); static LayoutText* CreateText(Node*, scoped_refptr<StringImpl>, LegacyLayout); static LayoutTextFragment* CreateTextFragment(Node*, StringImpl*, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc index bc06001fd8a..d1d45474fa0 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_object_test.cc @@ -530,6 +530,7 @@ TEST_F(LayoutObjectTest, VisualRect) { class MockLayoutObject : public LayoutObject { public: MockLayoutObject() : LayoutObject(nullptr) {} + ~MockLayoutObject() override { SetBeingDestroyedForTesting(); } MOCK_CONST_METHOD0(VisualRectRespectsVisibility, bool()); private: @@ -856,8 +857,7 @@ TEST_F(LayoutObjectTest, UpdateVisualRectAfterAncestorLayout) { class LayoutObjectSimTest : public SimTest { public: bool DocumentHasTouchActionRegion(const EventHandlerRegistry& registry) { - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); return registry.HasEventHandlers( EventHandlerRegistry::EventHandlerClass::kTouchAction); } @@ -930,8 +930,7 @@ TEST_F(LayoutObjectSimTest, HitTestForOcclusionInIframe) { <div id='target'>target</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); Element* iframe_element = GetDocument().QuerySelector("iframe"); auto* frame_owner_element = To<HTMLFrameOwnerElement>(iframe_element); Document* iframe_doc = frame_owner_element->contentDocument(); @@ -941,8 +940,7 @@ TEST_F(LayoutObjectSimTest, HitTestForOcclusionInIframe) { Element* occluder = GetDocument().getElementById("occluder"); occluder->SetInlineStyleProperty(CSSPropertyID::kMarginTop, "-150px"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); result = target->GetLayoutObject()->HitTestForOcclusion(); EXPECT_EQ(result.InnerNode(), occluder); } @@ -965,8 +963,7 @@ TEST_F(LayoutObjectSimTest, FirstLineBackgroundImage) { <div>To keep the image alive when target is set display: none</div> )HTML"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); auto* target = GetDocument().getElementById("target"); auto* target_object = target->GetLayoutObject(); @@ -998,8 +995,7 @@ TEST_F(LayoutObjectSimTest, FirstLineBackgroundImage) { EXPECT_FALSE(second_line->SlowFirstChild()->ShouldDoFullPaintInvalidation()); target->setAttribute(html_names::kStyleAttr, "display: none"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); target_object = target->GetLayoutObject(); EXPECT_EQ(nullptr, target_object); // The image is still alive because the other div's first line style still @@ -1064,8 +1060,7 @@ TEST_F(LayoutObjectTest, FirstLineBackgroundImageChangeStyleCrash) { UpdateAllLifecyclePhasesForTest(); } -// TODO(rego): Test is failing until we can fix https://crbug.com/941180. -TEST_F(LayoutObjectTest, DISABLED_NeedsLayoutOverflowRecalc) { +TEST_F(LayoutObjectTest, NeedsLayoutOverflowRecalc) { if (!RuntimeEnabledFeatures::LayoutNGEnabled()) return; @@ -1089,7 +1084,7 @@ TEST_F(LayoutObjectTest, DISABLED_NeedsLayoutOverflowRecalc) { EXPECT_FALSE(other->NeedsLayoutOverflowRecalc()); auto* target_element = GetDocument().getElementById("target"); - target_element->SetInnerHTMLFromString("baz"); + target_element->setInnerHTML("baz"); UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(wrapper->NeedsLayoutOverflowRecalc()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.cc new file mode 100644 index 00000000000..41956979cb9 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.cc @@ -0,0 +1,14 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/layout_outside_list_marker.h" + +namespace blink { + +LayoutOutsideListMarker::LayoutOutsideListMarker(Element* element) + : LayoutListMarker(element) {} + +LayoutOutsideListMarker::~LayoutOutsideListMarker() = default; + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.h b/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.h new file mode 100644 index 00000000000..20ff17c14a8 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/layout_outside_list_marker.h @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_OUTSIDE_LIST_MARKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_OUTSIDE_LIST_MARKER_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_list_marker.h" + +namespace blink { + +// Used to layout the list item's outside marker. +// The LayoutOutsideListMarker always has to be a child of a LayoutListItem. +class CORE_EXPORT LayoutOutsideListMarker final : public LayoutListMarker { + public: + explicit LayoutOutsideListMarker(Element*); + ~LayoutOutsideListMarker() override; + + const char* GetName() const override { return "LayoutOutsideListMarker"; } + + private: + bool IsOfType(LayoutObjectType type) const override { + return type == kLayoutObjectOutsideListMarker || + LayoutListMarker::IsOfType(type); + } +}; + +DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutOutsideListMarker, IsOutsideListMarker()); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_OUTSIDE_LIST_MARKER_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc b/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc index b74dbbaca8b..e5b86b519aa 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_replaced.cc @@ -117,7 +117,7 @@ void LayoutReplaced::IntrinsicSizeChanged() { int scaled_height = static_cast<int>(kDefaultHeight * StyleRef().EffectiveZoom()); intrinsic_size_ = LayoutSize(scaled_width, scaled_height); - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kSizeChanged); } @@ -152,7 +152,7 @@ static inline bool LayoutObjectHasAspectRatio( const LayoutObject* layout_object) { DCHECK(layout_object); return layout_object->IsImage() || layout_object->IsCanvas() || - layout_object->IsVideo(); + IsA<LayoutVideo>(layout_object); } void LayoutReplaced::RecalcVisualOverflow() { @@ -724,12 +724,14 @@ void LayoutReplaced::ComputeIntrinsicSizingInfo( static inline LayoutUnit ResolveWidthForRatio(LayoutUnit height, const FloatSize& aspect_ratio) { - return LayoutUnit(height * aspect_ratio.Width() / aspect_ratio.Height()); + return LayoutUnit(height.ToDouble() * aspect_ratio.Width() / + aspect_ratio.Height()); } static inline LayoutUnit ResolveHeightForRatio(LayoutUnit width, const FloatSize& aspect_ratio) { - return LayoutUnit(width * aspect_ratio.Height() / aspect_ratio.Width()); + return LayoutUnit(width.ToDouble() * aspect_ratio.Height() / + aspect_ratio.Width()); } LayoutUnit LayoutReplaced::ComputeConstrainedLogicalWidth( @@ -893,60 +895,45 @@ LayoutUnit LayoutReplaced::ComputeReplacedLogicalHeight( IntrinsicLogicalHeight()); } -void LayoutReplaced::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - min_logical_width = max_logical_width = IntrinsicLogicalWidth(); +MinMaxSizes LayoutReplaced::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth() + IntrinsicLogicalWidth(); + return sizes; } -void LayoutReplaced::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); +MinMaxSizes LayoutReplaced::PreferredLogicalWidths() const { + MinMaxSizes sizes; // We cannot resolve some logical width here (i.e. percent, fill-available or // fit-content) as the available logical width may not be set on our // containing block. const Length& logical_width = StyleRef().LogicalWidth(); if (logical_width.IsPercentOrCalc() || logical_width.IsFillAvailable() || - logical_width.IsFitContent()) - ComputeIntrinsicLogicalWidths(min_preferred_logical_width_, - max_preferred_logical_width_); - else - min_preferred_logical_width_ = max_preferred_logical_width_ = - ComputeReplacedLogicalWidth(kComputePreferred); + logical_width.IsFitContent()) { + sizes = IntrinsicLogicalWidths(); + sizes -= BorderAndPaddingLogicalWidth(); + } else { + sizes = ComputeReplacedLogicalWidth(kComputePreferred); + } const ComputedStyle& style_to_use = StyleRef(); if (style_to_use.LogicalWidth().IsPercentOrCalc() || style_to_use.LogicalMaxWidth().IsPercentOrCalc()) - min_preferred_logical_width_ = LayoutUnit(); + sizes.min_size = LayoutUnit(); if (style_to_use.LogicalMinWidth().IsFixed() && style_to_use.LogicalMinWidth().Value() > 0) { - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); - min_preferred_logical_width_ = - std::max(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); + sizes.Encompass(AdjustContentBoxLogicalWidthForBoxSizing( + style_to_use.LogicalMinWidth().Value())); } if (style_to_use.LogicalMaxWidth().IsFixed()) { - max_preferred_logical_width_ = - std::min(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMaxWidth().Value())); - min_preferred_logical_width_ = - std::min(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMaxWidth().Value())); + sizes.Constrain(AdjustContentBoxLogicalWidthForBoxSizing( + style_to_use.LogicalMaxWidth().Value())); } - LayoutUnit border_and_padding = BorderAndPaddingLogicalWidth(); - min_preferred_logical_width_ += border_and_padding; - max_preferred_logical_width_ += border_and_padding; - - ClearPreferredLogicalWidthsDirty(); + sizes += BorderAndPaddingLogicalWidth(); + return sizes; } static std::pair<LayoutUnit, LayoutUnit> SelectionTopAndBottom( @@ -975,14 +962,15 @@ static std::pair<LayoutUnit, LayoutUnit> SelectionTopAndBottom( // Step 2: Return the logical top and bottom of the line box. // TODO(layout-dev): Use selection top & bottom instead of line's, or decide // if we still want to distinguish line and selection heights in NG. - const ComputedStyle& line_style = line_box.CurrentStyle(); + const ComputedStyle& line_style = line_box.Current().Style(); const WritingMode writing_mode = line_style.GetWritingMode(); const TextDirection text_direction = line_style.Direction(); - const PhysicalOffset line_box_offset = line_box.CurrentOffset(); - const PhysicalSize line_box_size = line_box.CurrentSize(); + const PhysicalOffset line_box_offset = + line_box.Current().OffsetInContainerBlock(); + const PhysicalSize line_box_size = line_box.Current().Size(); const LogicalOffset logical_offset = line_box_offset.ConvertToLogical( writing_mode, text_direction, fragmentainer->Size(), - line_box.CurrentSize()); + line_box.Current().Size()); const LogicalSize logical_size = line_box_size.ConvertToLogical(writing_mode); return {logical_offset.block_offset, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_replaced.h b/chromium/third_party/blink/renderer/core/layout/layout_replaced.h index dfb6906f5d1..d501a40eef6 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_replaced.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_replaced.h @@ -90,11 +90,6 @@ class CORE_EXPORT LayoutReplaced : public LayoutBox { void Paint(const PaintInfo&) const override; - // Replaced objects often have contents to paint. - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } - // This function is public only so we can call it when computing // intrinsic size in LayoutNG. virtual void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const; @@ -136,8 +131,7 @@ class CORE_EXPORT LayoutReplaced : public LayoutBox { void ComputePositionedLogicalHeight( LogicalExtentComputedValues&) const override; - void ComputeIntrinsicLogicalWidths(LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const final; + MinMaxSizes ComputeIntrinsicLogicalWidths() const final; // This function calculates the placement of the replaced contents. It takes // intrinsic size of the replaced contents, stretch to fit CSS content box @@ -164,7 +158,7 @@ class CORE_EXPORT LayoutReplaced : public LayoutBox { } private: - void ComputePreferredLogicalWidths() final; + MinMaxSizes PreferredLogicalWidths() const final; void ComputeIntrinsicSizingInfoForReplacedContent(IntrinsicSizingInfo&) const; FloatSize ConstrainIntrinsicSizeToMinMax(const IntrinsicSizingInfo&) const; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_replaced_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_replaced_test.cc index 5b157b6a6c8..138bee7164b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_replaced_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_replaced_test.cc @@ -26,7 +26,8 @@ TEST_F(LayoutReplacedTest, InvalidateAfterAddingBorderRadius) { target_element->setAttribute(html_names::kStyleAttr, "border-radius: 10px"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(layout_object->NeedsPaintPropertyUpdate()); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_ruby_base.cc b/chromium/third_party/blink/renderer/core/layout/layout_ruby_base.cc index 9b21b0bf71f..1bb9a9e2685 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_ruby_base.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_ruby_base.cc @@ -65,9 +65,9 @@ void LayoutRubyBase::MoveChildren(LayoutRubyBase* to_base, else MoveBlockChildren(to_base, before_child); - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kUnknown); - to_base->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + to_base->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kUnknown); } @@ -152,7 +152,7 @@ void LayoutRubyBase::AdjustInlineDirectionLineBounds( unsigned expansion_opportunity_count, LayoutUnit& logical_left, LayoutUnit& logical_width) const { - int max_preferred_logical_width = MaxPreferredLogicalWidth().ToInt(); + int max_preferred_logical_width = PreferredLogicalWidths().max_size.ToInt(); if (max_preferred_logical_width >= logical_width) return; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_ruby_text.cc b/chromium/third_party/blink/renderer/core/layout/layout_ruby_text.cc index 5990208b8f8..730b2a35e1c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_ruby_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_ruby_text.cc @@ -64,7 +64,7 @@ void LayoutRubyText::AdjustInlineDirectionLineBounds( return LayoutBlockFlow::AdjustInlineDirectionLineBounds( expansion_opportunity_count, logical_left, logical_width); - int max_preferred_logical_width = MaxPreferredLogicalWidth().ToInt(); + int max_preferred_logical_width = PreferredLogicalWidths().max_size.ToInt(); if (max_preferred_logical_width >= logical_width) return; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_search_field.cc b/chromium/third_party/blink/renderer/core/layout/layout_search_field.cc deleted file mode 100644 index 54159e3348f..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_search_field.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. - * (C) 2008 Torch Mobile Inc. All rights reserved. - * (http://www.torchmobile.com/) - * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "third_party/blink/renderer/core/layout/layout_search_field.h" - -#include "third_party/blink/renderer/core/dom/shadow_root.h" -#include "third_party/blink/renderer/core/html/forms/html_input_element.h" -#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" -#include "third_party/blink/renderer/core/input_type_names.h" - -namespace blink { - -LayoutSearchField::LayoutSearchField(HTMLInputElement* element) - : LayoutTextControlSingleLine(element) { - DCHECK_EQ(element->type(), input_type_names::kSearch); -} - -LayoutSearchField::~LayoutSearchField() = default; - -inline Element* LayoutSearchField::SearchDecorationElement() const { - return InputElement()->UserAgentShadowRoot()->getElementById( - shadow_element_names::SearchDecoration()); -} - -inline Element* LayoutSearchField::CancelButtonElement() const { - return InputElement()->UserAgentShadowRoot()->getElementById( - shadow_element_names::ClearButton()); -} - -LayoutUnit LayoutSearchField::ComputeControlLogicalHeight( - LayoutUnit line_height, - LayoutUnit non_content_height) const { - Element* search_decoration = SearchDecorationElement(); - if (LayoutBox* decoration_layout_object = - search_decoration ? search_decoration->GetLayoutBox() : nullptr) { - decoration_layout_object->UpdateLogicalHeight(); - non_content_height = - max(non_content_height, - decoration_layout_object->BorderAndPaddingLogicalHeight() + - decoration_layout_object->MarginLogicalHeight()); - line_height = max(line_height, decoration_layout_object->LogicalHeight()); - } - Element* cancel_button = CancelButtonElement(); - if (LayoutBox* cancel_layout_object = - cancel_button ? cancel_button->GetLayoutBox() : nullptr) { - cancel_layout_object->UpdateLogicalHeight(); - non_content_height = - max(non_content_height, - cancel_layout_object->BorderAndPaddingLogicalHeight() + - cancel_layout_object->MarginLogicalHeight()); - line_height = max(line_height, cancel_layout_object->LogicalHeight()); - } - - return line_height + non_content_height; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_search_field.h b/chromium/third_party/blink/renderer/core/layout/layout_search_field.h deleted file mode 100644 index f9146cab43b..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/layout_search_field.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. - * (http://www.torchmobile.com/) - * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SEARCH_FIELD_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SEARCH_FIELD_H_ - -#include "third_party/blink/renderer/core/layout/layout_text_control_single_line.h" - -namespace blink { - -class HTMLInputElement; - -class LayoutSearchField final : public LayoutTextControlSingleLine { - public: - LayoutSearchField(HTMLInputElement*); - ~LayoutSearchField() override; - - private: - LayoutUnit ComputeControlLogicalHeight( - LayoutUnit line_height, - LayoutUnit non_content_height) const override; - - Element* SearchDecorationElement() const; - Element* CancelButtonElement() const; -}; - -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSearchField, IsTextField()); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_SEARCH_FIELD_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.cc index 3ff579f8fe0..20ab99f71b4 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.cc @@ -6,7 +6,9 @@ #include "cc/layers/heads_up_display_layer.h" #include "cc/layers/picture_layer.h" -#include "third_party/blink/public/platform/web_pointer_event.h" +#include "cc/trees/layer_tree_host.h" +#include "third_party/blink/public/common/input/web_pointer_event.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h" @@ -26,12 +28,20 @@ namespace blink { -static constexpr base::TimeDelta kTimerDelay = - base::TimeDelta::FromMilliseconds(500); -static const float kMovementThreshold = 3.0; // CSS pixels. +using ReattachHook = LayoutShiftTracker::ReattachHook; -static FloatPoint LogicalStart(const FloatRect& rect, - const LayoutObject& object) { +namespace { + +ReattachHook& GetReattachHook() { + DEFINE_STATIC_LOCAL(Persistent<ReattachHook>, hook, + (MakeGarbageCollected<ReattachHook>())); + return *hook; +} + +constexpr base::TimeDelta kTimerDelay = base::TimeDelta::FromMilliseconds(500); +const float kMovementThreshold = 3.0; // CSS pixels. + +FloatPoint LogicalStart(const FloatRect& rect, const LayoutObject& object) { const ComputedStyle* style = object.Style(); DCHECK(style); auto logical = @@ -40,59 +50,67 @@ static FloatPoint LogicalStart(const FloatRect& rect, return FloatPoint(logical.InlineStart(), logical.BlockStart()); } -static float GetMoveDistance(const FloatRect& old_rect, - const FloatRect& new_rect, - const LayoutObject& object) { +float GetMoveDistance(const FloatRect& old_rect, + const FloatRect& new_rect, + const LayoutObject& object) { FloatSize location_delta = LogicalStart(new_rect, object) - LogicalStart(old_rect, object); return std::max(fabs(location_delta.Width()), fabs(location_delta.Height())); } -static bool EqualWithinMovementThreshold(const FloatPoint& a, - const FloatPoint& b, - const LayoutObject& object) { +bool EqualWithinMovementThreshold(const FloatPoint& a, + const FloatPoint& b, + const LayoutObject& object) { float threshold_physical_px = kMovementThreshold * object.StyleRef().EffectiveZoom(); return fabs(a.X() - b.X()) < threshold_physical_px && fabs(a.Y() - b.Y()) < threshold_physical_px; } -static bool SmallerThanRegionGranularity(const FloatRect& rect) { +bool SmallerThanRegionGranularity(const FloatRect& rect) { // The region uses integer coordinates, so the rects are snapped to // pixel boundaries. Ignore rects smaller than half a pixel. return rect.Width() < 0.5 || rect.Height() < 0.5; } -static const PropertyTreeState PropertyTreeStateFor( - const LayoutObject& object) { +const PropertyTreeState PropertyTreeStateFor(const LayoutObject& object) { return object.FirstFragment().LocalBorderBoxProperties(); } -static void RegionToTracedValue(const LayoutShiftRegion& region, - TracedValue& value) { +void RectToTracedValue(const IntRect& rect, + TracedValue& value, + const char* key = nullptr) { + if (key) + value.BeginArray(key); + else + value.BeginArray(); + value.PushInteger(rect.X()); + value.PushInteger(rect.Y()); + value.PushInteger(rect.Width()); + value.PushInteger(rect.Height()); + value.EndArray(); +} + +void RegionToTracedValue(const LayoutShiftRegion& region, TracedValue& value) { Region blink_region; for (IntRect rect : region.GetRects()) blink_region.Unite(Region(rect)); value.BeginArray("region_rects"); - for (const IntRect& rect : blink_region.Rects()) { - value.BeginArray(); - value.PushInteger(rect.X()); - value.PushInteger(rect.Y()); - value.PushInteger(rect.Width()); - value.PushInteger(rect.Height()); - value.EndArray(); - } + for (const IntRect& rect : blink_region.Rects()) + RectToTracedValue(rect, value); value.EndArray(); } #if DCHECK_IS_ON() -static bool ShouldLog(const LocalFrame& frame) { +bool ShouldLog(const LocalFrame& frame) { const String& url = frame.GetDocument()->Url().GetString(); - return !url.StartsWith("chrome-devtools:") && !url.StartsWith("devtools:"); + return !url.StartsWith("devtools:"); } #endif +} // namespace + LayoutShiftTracker::LayoutShiftTracker(LocalFrameView* frame_view) : frame_view_(frame_view), score_(0.0), @@ -200,6 +218,60 @@ void LayoutShiftTracker::ObjectShifted( region_.AddRect(visible_old_rect); region_.AddRect(visible_new_rect); + + if (Node* node = source.GetNode()) { + MaybeRecordAttribution( + {DOMNodeIds::IdForNode(node), visible_old_rect, visible_new_rect}); + } +} + +LayoutShiftTracker::Attribution::Attribution() : node_id(kInvalidDOMNodeId) {} +LayoutShiftTracker::Attribution::Attribution(DOMNodeId node_id_arg, + IntRect old_visual_rect_arg, + IntRect new_visual_rect_arg) + : node_id(node_id_arg), + old_visual_rect(old_visual_rect_arg), + new_visual_rect(new_visual_rect_arg) {} + +LayoutShiftTracker::Attribution::operator bool() const { + return node_id != kInvalidDOMNodeId; +} + +bool LayoutShiftTracker::Attribution::Encloses(const Attribution& other) const { + return old_visual_rect.Contains(other.old_visual_rect) && + new_visual_rect.Contains(other.new_visual_rect); +} + +int LayoutShiftTracker::Attribution::Area() const { + int old_area = old_visual_rect.Width() * old_visual_rect.Height(); + int new_area = new_visual_rect.Width() * new_visual_rect.Height(); + + IntRect intersection = Intersection(old_visual_rect, new_visual_rect); + int shared_area = intersection.Width() * intersection.Height(); + return old_area + new_area - shared_area; +} + +bool LayoutShiftTracker::Attribution::MoreImpactfulThan( + const Attribution& other) const { + return Area() > other.Area(); +} + +void LayoutShiftTracker::MaybeRecordAttribution( + const Attribution& attribution) { + Attribution* smallest = nullptr; + for (auto& slot : attributions_) { + if (!slot || attribution.Encloses(slot)) { + slot = attribution; + return; + } + if (slot.Encloses(attribution)) + return; + if (!smallest || smallest->MoreImpactfulThan(slot)) + smallest = &slot; + } + // No empty slots or redundancies. Replace smallest existing slot if larger. + if (attribution.MoreImpactfulThan(*smallest)) + *smallest = attribution; } void LayoutShiftTracker::NotifyObjectPrePaint( @@ -288,6 +360,7 @@ void LayoutShiftTracker::NotifyPrePaintFinished() { frame_max_distance_ = 0.0; frame_scroll_delta_ = ScrollOffset(); + attributions_.fill(Attribution()); } void LayoutShiftTracker::ReportShift(double score_delta, @@ -378,13 +451,14 @@ void LayoutShiftTracker::UpdateInputTimestamp(base::TimeTicks timestamp) { } } -void LayoutShiftTracker::NotifyScroll(ScrollType scroll_type, +void LayoutShiftTracker::NotifyScroll(mojom::blink::ScrollType scroll_type, ScrollOffset delta) { frame_scroll_delta_ += delta; // Only set observed_input_or_scroll_ for user-initiated scrolls, and not // other scrolls such as hash fragment navigations. - if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) + if (scroll_type == mojom::blink::ScrollType::kUser || + scroll_type == mojom::blink::ScrollType::kCompositor) observed_input_or_scroll_ = true; } @@ -413,20 +487,39 @@ std::unique_ptr<TracedValue> LayoutShiftTracker::PerFrameTraceData( RegionToTracedValue(region_, *value); value->SetBoolean("is_main_frame", frame_view_->GetFrame().IsMainFrame()); value->SetBoolean("had_recent_input", input_detected); + AttributionsToTracedValue(*value); return value; } -void LayoutShiftTracker::SetLayoutShiftRects(const Vector<IntRect>& int_rects) { - // Store the layout shift rects in the HUD layer. - GraphicsLayer* root_graphics_layer = - frame_view_->GetLayoutView()->Compositor()->RootGraphicsLayer(); - if (!root_graphics_layer) +void LayoutShiftTracker::AttributionsToTracedValue(TracedValue& value) const { + const Attribution* it = attributions_.begin(); + if (!*it) return; - cc::Layer* cc_layer = root_graphics_layer->CcLayer(); - if (!cc_layer) - return; - if (cc_layer->layer_tree_host()) { + bool should_include_names; + TRACE_EVENT_CATEGORY_GROUP_ENABLED( + TRACE_DISABLED_BY_DEFAULT("layout_shift.debug"), &should_include_names); + + value.BeginArray("impacted_nodes"); + while (it != attributions_.end() && it->node_id != kInvalidDOMNodeId) { + value.BeginDictionary(); + value.SetInteger("node_id", it->node_id); + RectToTracedValue(it->old_visual_rect, value, "old_rect"); + RectToTracedValue(it->new_visual_rect, value, "new_rect"); + if (should_include_names) { + Node* node = DOMNodeIds::NodeForId(it->node_id); + value.SetString("debug_name", node ? node->DebugName() : ""); + } + value.EndDictionary(); + it++; + } + value.EndArray(); +} + +void LayoutShiftTracker::SetLayoutShiftRects(const Vector<IntRect>& int_rects) { + // Store the layout shift rects in the HUD layer. + auto* cc_layer = frame_view_->RootCcLayer(); + if (cc_layer && cc_layer->layer_tree_host()) { if (!cc_layer->layer_tree_host()->GetDebugState().show_layout_shift_regions) return; if (cc_layer->layer_tree_host()->hud_layer()) { @@ -443,4 +536,61 @@ void LayoutShiftTracker::SetLayoutShiftRects(const Vector<IntRect>& int_rects) { } } +ReattachHook::Scope::Scope(const Node& node) : active_(node.GetLayoutObject()) { + if (active_) { + auto& hook = GetReattachHook(); + outer_ = hook.scope_; + hook.scope_ = this; + } +} + +ReattachHook::Scope::~Scope() { + if (active_) { + auto& hook = GetReattachHook(); + hook.scope_ = outer_; + if (!outer_) + hook.visual_rects_.clear(); + } +} + +void ReattachHook::NotifyDetach(const Node& node) { + auto& hook = GetReattachHook(); + if (!hook.scope_) + return; + auto* layout_object = node.GetLayoutObject(); + if (!layout_object) + return; + auto& map = hook.visual_rects_; + auto& fragment = layout_object->GetMutableForPainting().FirstFragment(); + + // Save the visual rect for restoration on future reattachment. + IntRect visual_rect = fragment.VisualRect(); + if (visual_rect.IsEmpty()) + return; + map.Set(&node, visual_rect); +} + +void ReattachHook::NotifyAttach(const Node& node) { + auto& hook = GetReattachHook(); + if (!hook.scope_) + return; + auto* layout_object = node.GetLayoutObject(); + if (!layout_object) + return; + auto& map = hook.visual_rects_; + auto& fragment = layout_object->GetMutableForPainting().FirstFragment(); + + // Restore the visual rect that was saved during detach. Note: this does not + // affect paint invalidation; we will fully invalidate the new layout object. + auto iter = map.find(&node); + if (iter == map.end()) + return; + IntRect visual_rect = iter->value; + fragment.SetVisualRect(visual_rect); +} + +void ReattachHook::Trace(Visitor* visitor) { + visitor->Trace(visual_rects_); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.h b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.h index 79185e29907..24bbd221df5 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker.h @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/layout_shift_region.h" #include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/platform/geometry/region.h" +#include "third_party/blink/renderer/platform/graphics/dom_node_id.h" #include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -48,7 +49,7 @@ class CORE_EXPORT LayoutShiftTracker { FloatSize paint_offset_delta); void NotifyPrePaintFinished(); void NotifyInput(const WebInputEvent&); - void NotifyScroll(ScrollType, ScrollOffset delta); + void NotifyScroll(mojom::blink::ScrollType, ScrollOffset delta); void NotifyViewportSizeChanged(); bool IsActive(); double Score() const { return score_; } @@ -60,6 +61,31 @@ class CORE_EXPORT LayoutShiftTracker { return most_recent_input_timestamp_; } + // Saves and restores visual rects on layout objects when a layout tree is + // rebuilt by Node::ReattachLayoutTree. + class ReattachHook : public GarbageCollected<ReattachHook> { + public: + ReattachHook() : scope_(nullptr) {} + void Trace(Visitor*); + + class Scope { + public: + Scope(const Node&); + ~Scope(); + + private: + bool active_; + Scope* outer_; + }; + + static void NotifyDetach(const Node&); + static void NotifyAttach(const Node&); + + private: + Scope* scope_; + HeapHashMap<Member<const Node>, IntRect> visual_rects_; + }; + private: void ObjectShifted(const LayoutObject&, const PropertyTreeState&, @@ -70,6 +96,7 @@ class CORE_EXPORT LayoutShiftTracker { void TimerFired(TimerBase*) {} std::unique_ptr<TracedValue> PerFrameTraceData(double score_delta, bool input_detected) const; + void AttributionsToTracedValue(TracedValue&) const; double SubframeWeightingFactor() const; void SetLayoutShiftRects(const Vector<IntRect>& int_rects); void UpdateInputTimestamp(base::TimeTicks timestamp); @@ -132,6 +159,29 @@ class CORE_EXPORT LayoutShiftTracker { // User input includes window resizing but not scrolling. base::TimeTicks most_recent_input_timestamp_; bool most_recent_input_timestamp_initialized_; + + struct Attribution { + DOMNodeId node_id; + IntRect old_visual_rect; + IntRect new_visual_rect; + + Attribution(); + Attribution(DOMNodeId node_id, + IntRect old_visual_rect, + IntRect new_visual_rect); + + explicit operator bool() const; + bool Encloses(const Attribution&) const; + bool MoreImpactfulThan(const Attribution&) const; + int Area() const; + }; + static constexpr int kMaxAttributions = 5; + + void MaybeRecordAttribution(const Attribution&); + + // Nodes that have contributed to the impact region for the current frame, for + // use in trace event. Only populated while tracing. + std::array<Attribution, kMaxAttributions> attributions_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc index 7d60704a20d..4418bcf5202 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc @@ -4,7 +4,7 @@ #include "third_party/blink/renderer/core/layout/layout_shift_tracker.h" -#include "third_party/blink/public/platform/web_mouse_event.h" +#include "third_party/blink/public/common/input/web_mouse_event.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/svg_names.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" @@ -30,14 +30,13 @@ class LayoutShiftTrackerTest : public RenderingTest { void SimulateInput() { GetLayoutShiftTracker().NotifyInput(WebMouseEvent( - WebInputEvent::kMouseDown, WebFloatPoint(), WebFloatPoint(), + WebInputEvent::kMouseDown, gfx::PointF(), gfx::PointF(), WebPointerProperties::Button::kLeft, 0, WebInputEvent::Modifiers::kLeftButtonDown, base::TimeTicks::Now())); } void UpdateAllLifecyclePhases() { - GetFrameView().UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetFrameView().UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); } }; @@ -81,7 +80,8 @@ TEST_F(LayoutShiftTrackerTest, CompositedShiftBeforeFirstPaint) { GetDocument().getElementById("B")->setAttribute(html_names::kClassAttr, AtomicString("tr")); - GetFrameView().UpdateLifecycleToCompositingCleanPlusScrolling(); + GetFrameView().UpdateLifecycleToCompositingCleanPlusScrolling( + DocumentUpdateReason::kTest); GetDocument().getElementById("A")->setAttribute(html_names::kClassAttr, AtomicString("hide")); UpdateAllLifecyclePhases(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider.cc b/chromium/third_party/blink/renderer/core/layout/layout_slider.cc index f41bbd8335c..21dcbb6421d 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider.cc @@ -19,10 +19,7 @@ #include "third_party/blink/renderer/core/layout/layout_slider.h" -#include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/html/forms/html_input_element.h" -#include "third_party/blink/renderer/core/html/forms/slider_thumb_element.h" -#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" #include "third_party/blink/renderer/core/input_type_names.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" @@ -48,23 +45,14 @@ LayoutUnit LayoutSlider::BaselinePosition( return Size().Height() + MarginTop(); } -void LayoutSlider::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { - max_logical_width = +MinMaxSizes LayoutSlider::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth() + ScrollbarLogicalWidth(); + sizes.max_size += LayoutUnit(kDefaultTrackLength * StyleRef().EffectiveZoom()); if (!StyleRef().Width().IsPercentOrCalc()) - min_logical_width = max_logical_width; -} - -inline SliderThumbElement* LayoutSlider::GetSliderThumbElement() const { - return To<SliderThumbElement>( - To<Element>(GetNode())->UserAgentShadowRoot()->getElementById( - shadow_element_names::SliderThumb())); -} - -bool LayoutSlider::InDragMode() const { - return GetSliderThumbElement()->IsActive(); + sizes.min_size = sizes.max_size; + return sizes; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider.h b/chromium/third_party/blink/renderer/core/layout/layout_slider.h index 09d62822dc7..401f30d251b 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider.h @@ -23,11 +23,11 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { class HTMLInputElement; -class SliderThumbElement; class CORE_EXPORT LayoutSlider final : public LayoutFlexibleBox { public: @@ -36,8 +36,6 @@ class CORE_EXPORT LayoutSlider final : public LayoutFlexibleBox { explicit LayoutSlider(HTMLInputElement*); ~LayoutSlider() override; - bool InDragMode() const; - const char* GetName() const override { return "LayoutSlider"; } private: @@ -50,14 +48,15 @@ class CORE_EXPORT LayoutSlider final : public LayoutFlexibleBox { bool first_line, LineDirectionMode, LinePositionMode = kPositionOnContainingLine) const override; - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; - - SliderThumbElement* GetSliderThumbElement() const; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSlider, IsSlider()); +template <> +struct DowncastTraits<LayoutSlider> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsSlider(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc index b88895afa6f..4df8effbab1 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.cc @@ -51,22 +51,13 @@ inline static Decimal SliderPosition(HTMLInputElement* element) { return step_range.ProportionFromValue(step_range.ClampValue(old_value)); } -inline static bool HasVerticalAppearance(HTMLInputElement* input) { - DCHECK(input->GetLayoutObject()); - if (!input->GetLayoutObject() || !input->GetLayoutObject()->Style()) - return false; - const ComputedStyle& slider_style = input->GetLayoutObject()->StyleRef(); - return slider_style.EffectiveAppearance() == kSliderVerticalPart; -} - void LayoutSliderContainer::ComputeLogicalHeight( LayoutUnit logical_height, LayoutUnit logical_top, LogicalExtentComputedValues& computed_values) const { auto* input = To<HTMLInputElement>(GetNode()->OwnerShadowHost()); - bool is_vertical = HasVerticalAppearance(input); - if (input->GetLayoutObject()->IsSlider() && !is_vertical && input->list()) { + if (input->GetLayoutObject()->IsSlider() && input->list()) { int offset_from_center = LayoutTheme::GetTheme().SliderTickOffsetFromTrackCenter(); LayoutUnit track_height; @@ -87,8 +78,6 @@ void LayoutSliderContainer::ComputeLogicalHeight( LayoutBox::ComputeLogicalHeight(track_height, logical_top, computed_values); return; } - if (is_vertical) - logical_height = LayoutUnit(LayoutSlider::kDefaultTrackLength); // FIXME: The trackHeight should have been added before updateLogicalHeight // was called to avoid this hack. @@ -97,9 +86,17 @@ void LayoutSliderContainer::ComputeLogicalHeight( LayoutBox::ComputeLogicalHeight(logical_height, logical_top, computed_values); } +MinMaxSizes LayoutSliderContainer::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += LayoutUnit(LayoutSlider::kDefaultTrackLength * + StyleRef().EffectiveZoom()) + + BorderAndPaddingLogicalWidth(); + return sizes; +} + void LayoutSliderContainer::UpdateLayout() { auto* input = To<HTMLInputElement>(GetNode()->OwnerShadowHost()); - bool is_vertical = HasVerticalAppearance(input); + const bool is_vertical = !StyleRef().IsHorizontalWritingMode(); Element* thumb_element = input->UserAgentShadowRoot()->getElementById( shadow_element_names::SliderThumb()); @@ -131,8 +128,7 @@ void LayoutSliderContainer::UpdateLayout() { LayoutUnit offset(percentage_offset * available_extent); LayoutPoint thumb_location = thumb->Location(); if (is_vertical) { - thumb_location.SetY(thumb_location.Y() + track->ContentHeight() - - thumb->Size().Height() - offset); + thumb_location.SetY(thumb_location.Y() - offset); } else if (StyleRef().IsLeftToRightDirection()) { thumb_location.SetX(thumb_location.X() + offset); } else { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.h b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.h index bab81f70c04..9acb550efe3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_slider_container.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_slider_container.h @@ -46,6 +46,7 @@ class LayoutSliderContainer final : public LayoutFlexibleBox { void ComputeLogicalHeight(LayoutUnit logical_height, LayoutUnit logical_top, LogicalExtentComputedValues&) const override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; private: void UpdateLayout() override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_state.cc b/chromium/third_party/blink/renderer/core/layout/layout_state.cc index fbc642e0cb4..c7258a54f38 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_state.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_state.cc @@ -57,6 +57,11 @@ LayoutState::LayoutState(LayoutBox& layout_object, height_offset_for_table_footers_ = next_->HeightOffsetForTableFooters(); layout_object.View()->PushLayoutState(*this); + if (const AtomicString& named_page = layout_object.StyleRef().Page()) + page_name_ = named_page; + else + page_name_ = next_->page_name_; + if (layout_object.IsLayoutFlowThread()) { // Entering a new pagination context. pagination_offset_ = LayoutSize(); @@ -105,7 +110,7 @@ LayoutState::LayoutState(LayoutObject& root) next_(root.View()->GetLayoutState()), layout_object_(root) { DCHECK(!next_); - DCHECK(!root.IsLayoutView()); + DCHECK(!IsA<LayoutView>(root)); root.View()->PushLayoutState(*this); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_state.h b/chromium/third_party/blink/renderer/core/layout/layout_state.h index eb1b3ce09db..190e5ba2450 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_state.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_state.h @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" +#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" namespace blink { @@ -90,6 +91,8 @@ class LayoutState { height_offset_for_table_footers_ = offset; } + const AtomicString& PageName() const { return page_name_; } + const LayoutSize& PaginationOffset() const { return pagination_offset_; } bool ContainingBlockLogicalWidthChanged() const { return containing_block_logical_width_changed_; @@ -128,6 +131,8 @@ class LayoutState { // paginated layout. LayoutUnit height_offset_for_table_footers_; + AtomicString page_name_; + LayoutObject& layout_object_; DISALLOW_COPY_AND_ASSIGN(LayoutState); }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table.cc b/chromium/third_party/blink/renderer/core/layout/layout_table.cc index 33333b6b88e..2b41ff4843e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table.cc @@ -310,11 +310,10 @@ bool LayoutTable::IsLogicalWidthAuto() const { void LayoutTable::UpdateLogicalWidth() { RecalcSectionsIfNeeded(); - // Recalculate preferred logical widths now, rather than relying on them being - // lazily recalculated, via MinPreferredLogicalWidth() further below. We might - // not even get there. - if (PreferredLogicalWidthsDirty()) - ComputePreferredLogicalWidths(); + // Recalculate the intrinsic logical widths now, rather than relying on them + // being lazily recalculated, via PreferredLogicalWidths() further below. We + // might not even get there. + UpdateCachedIntrinsicLogicalWidthsIfNeeded(); if (IsFlexItemIncludingDeprecatedAndNG() || IsGridItem()) { // TODO(jfernandez): Investigate whether the grid layout algorithm provides @@ -344,6 +343,8 @@ void LayoutTable::UpdateLogicalWidth() { ? PerpendicularContainingBlockLogicalHeight() : available_logical_width; + MinMaxSizes preferred_logical_widths = PreferredLogicalWidths(); + if (!IsLogicalWidthAuto()) { SetLogicalWidth(ConvertStyleLogicalWidthToComputedWidth( StyleRef().LogicalWidth(), container_width_in_inline_direction)); @@ -375,11 +376,11 @@ void LayoutTable::UpdateLogicalWidth() { } // Ensure we aren't bigger than our available width. - LayoutUnit max_width = MaxPreferredLogicalWidth(); + LayoutUnit max_width = preferred_logical_widths.max_size; // scaledWidthFromPercentColumns depends on m_layoutStruct in - // TableLayoutAlgorithmAuto, which maxPreferredLogicalWidth fills in. So - // scaledWidthFromPercentColumns has to be called after - // maxPreferredLogicalWidth. + // TableLayoutAlgorithmAuto, which |PreferredLogicalWidths()| fills in. So + // |ScaledWidthFromPercentColumns()| has to be called after + // |PreferredLogicalWidths()|. LayoutUnit scaled_width = table_layout_->ScaledWidthFromPercentColumns() + BordersPaddingAndSpacingInRowDirection(); max_width = std::max(scaled_width, max_width); @@ -402,8 +403,8 @@ void LayoutTable::UpdateLogicalWidth() { // Ensure we aren't smaller than our min preferred width. This MUST be done // after 'max-width' as we ignore it if it means we wouldn't accommodate our // content. - SetLogicalWidth( - LayoutUnit(std::max(LogicalWidth(), MinPreferredLogicalWidth()).Floor())); + SetLogicalWidth(LayoutUnit( + std::max(LogicalWidth(), preferred_logical_widths.min_size).Floor())); // Ensure we aren't smaller than our min-width style. const Length& style_min_logical_width = StyleRef().LogicalMinWidth(); @@ -431,7 +432,7 @@ void LayoutTable::UpdateLogicalWidth() { // nor what authors expect. // FIXME: When we convert to sub-pixel layout for tables we can remove the int // conversion. http://crbug.com/241198 - DCHECK_GE(LogicalWidth().Floor(), MinPreferredLogicalWidth().Floor()); + DCHECK_GE(LogicalWidth().Floor(), preferred_logical_widths.min_size.Floor()); } // This method takes a ComputedStyle's logical width, min-width, or max-width @@ -439,10 +440,10 @@ void LayoutTable::UpdateLogicalWidth() { LayoutUnit LayoutTable::ConvertStyleLogicalWidthToComputedWidth( const Length& style_logical_width, LayoutUnit available_width) const { - if (style_logical_width.IsIntrinsic()) - return ComputeIntrinsicLogicalWidthUsing( - style_logical_width, available_width, - BordersPaddingAndSpacingInRowDirection()); + if (style_logical_width.IsIntrinsic()) { + return ComputeIntrinsicLogicalWidthUsing(style_logical_width, + available_width); + } // HTML tables' width styles already include borders and paddings, but CSS // tables' width styles do not. @@ -974,6 +975,7 @@ void LayoutTable::ComputeVisualOverflow(bool) { AddVisualOverflowFromTheme(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } @@ -1088,38 +1090,30 @@ void LayoutTable::PaintMask(const PaintInfo& paint_info, TablePainter(*this).PaintMask(paint_info, paint_offset); } -void LayoutTable::ComputeIntrinsicLogicalWidths(LayoutUnit& min_width, - LayoutUnit& max_width) const { +MinMaxSizes LayoutTable::ComputeIntrinsicLogicalWidths() const { RecalcSectionsIfNeeded(); // FIXME: Restructure the table layout code so that we can make this method // const. + MinMaxSizes sizes; const_cast<LayoutTable*>(this)->table_layout_->ComputeIntrinsicLogicalWidths( - min_width, max_width); + sizes.min_size, sizes.max_size); // FIXME: We should include captions widths here like we do in // computePreferredLogicalWidths. + sizes += LayoutUnit(BordersPaddingAndSpacingInRowDirection().ToInt()); + return sizes; } -void LayoutTable::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); - - ComputeIntrinsicLogicalWidths(min_preferred_logical_width_, - max_preferred_logical_width_); - - int borders_padding_and_spacing = - BordersPaddingAndSpacingInRowDirection().ToInt(); - min_preferred_logical_width_ += borders_padding_and_spacing; - max_preferred_logical_width_ += borders_padding_and_spacing; +MinMaxSizes LayoutTable::PreferredLogicalWidths() const { + MinMaxSizes sizes = IntrinsicLogicalWidths(); - table_layout_->ApplyPreferredLogicalWidthQuirks(min_preferred_logical_width_, - max_preferred_logical_width_); + table_layout_->ApplyPreferredLogicalWidthQuirks(sizes.min_size, + sizes.max_size); - for (unsigned i = 0; i < captions_.size(); i++) { - min_preferred_logical_width_ = std::max( - min_preferred_logical_width_, captions_[i]->MinPreferredLogicalWidth()); - // Note: using captions' min-width is intentional here: - max_preferred_logical_width_ = std::max( - max_preferred_logical_width_, captions_[i]->MinPreferredLogicalWidth()); + for (const auto* caption : captions_) { + LayoutUnit min_preferred_logical_width = + caption->PreferredLogicalWidths().min_size; + sizes.Encompass(min_preferred_logical_width); } const ComputedStyle& style_to_use = StyleRef(); @@ -1127,14 +1121,8 @@ void LayoutTable::ComputePreferredLogicalWidths() { // able to use percentage or calc values for min-width. if (style_to_use.LogicalMinWidth().IsFixed() && style_to_use.LogicalMinWidth().Value() > 0) { - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); - min_preferred_logical_width_ = - std::max(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); + sizes.Encompass(AdjustBorderBoxLogicalWidthForBoxSizing( + style_to_use.LogicalMinWidth().Value())); } // FIXME: This should probably be checking for isSpecified since you should be @@ -1142,10 +1130,9 @@ void LayoutTable::ComputePreferredLogicalWidths() { if (style_to_use.LogicalMaxWidth().IsFixed()) { // We don't constrain m_minPreferredLogicalWidth as the table should be at // least the size of its min-content, regardless of 'max-width'. - max_preferred_logical_width_ = - std::min(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMaxWidth().Value())); + sizes.max_size = + std::min(sizes.max_size, AdjustBorderBoxLogicalWidthForBoxSizing( + style_to_use.LogicalMaxWidth().Value())); } // 2 cases need this: @@ -1154,13 +1141,8 @@ void LayoutTable::ComputePreferredLogicalWidths() { // 2. We buggily calculate min > max for some tables with colspans and // percent widths. See fast/table/spans-min-greater-than-max-crash.html and // http://crbug.com/857185 - max_preferred_logical_width_ = - std::max(min_preferred_logical_width_, max_preferred_logical_width_); - - // FIXME: We should be adding borderAndPaddingLogicalWidth here, but - // m_tableLayout->computePreferredLogicalWidths already does, so a bunch of - // tests break doing this naively. - ClearPreferredLogicalWidthsDirty(); + sizes.max_size = std::max(sizes.min_size, sizes.max_size); + return sizes; } LayoutTableSection* LayoutTable::TopNonEmptySection() const { @@ -1865,11 +1847,6 @@ void LayoutTable::UpdateCollapsedOuterBorders() const { max_border_end - collapsed_outer_border_end_; } -bool LayoutTable::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - return LayoutBlock::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() && - !should_paint_all_collapsed_borders_; -} - // LayoutNGTableCellInterface API bool LayoutTable::IsFirstCell(const LayoutNGTableCellInterface& cell) const { const LayoutTableCell& layout_cell = *cell.ToLayoutTableCell(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table.h b/chromium/third_party/blink/renderer/core/layout/layout_table.h index 1c385155762..53978ca750e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table.h @@ -427,7 +427,6 @@ class CORE_EXPORT LayoutTable final : public LayoutBlock, void EnsureIsReadyForPaintInvalidation() override; void InvalidatePaint(const PaintInvalidatorContext&) const override; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; void ColumnStructureChanged(); // LayoutNGTableInterface methods start. @@ -460,9 +459,8 @@ class CORE_EXPORT LayoutTable final : public LayoutBlock, void PaintObject(const PaintInfo&, const PhysicalOffset& paint_offset) const override; void UpdateLayout() override; - void ComputeIntrinsicLogicalWidths(LayoutUnit& min_width, - LayoutUnit& max_width) const override; - void ComputePreferredLogicalWidths() override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; + MinMaxSizes PreferredLogicalWidths() const override; bool NodeAtPoint(HitTestResult&, const HitTestLocation&, const PhysicalOffset& accumulated_offset, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h b/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h index 240de49be66..5ade10c9191 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_caption.h @@ -62,8 +62,6 @@ class LayoutTableCaption : public LayoutBlockFlow { LayoutTable* Table() const; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTableCaption, IsTableCaption()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_TABLE_CAPTION_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc index 02b873d3be5..7d041319196 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.cc @@ -87,12 +87,12 @@ void LayoutTableCell::WillBeRemovedFromTree() { // remove-cell-with-border-box.html only passes with setNeedsLayout but // other places use setChildNeedsLayout. PreviousCell()->SetNeedsLayout(layout_invalidation_reason::kTableChanged); - PreviousCell()->SetPreferredLogicalWidthsDirty(); + PreviousCell()->SetIntrinsicLogicalWidthsDirty(); } if (NextCell()) { // TODO(dgrogan): Same as above re: setChildNeedsLayout vs setNeedsLayout. NextCell()->SetNeedsLayout(layout_invalidation_reason::kTableChanged); - NextCell()->SetPreferredLogicalWidthsDirty(); + NextCell()->SetIntrinsicLogicalWidthsDirty(); } } @@ -100,17 +100,15 @@ unsigned LayoutTableCell::ParseColSpanFromDOM() const { DCHECK(GetNode()); // TODO(dgrogan): HTMLTableCellElement::colSpan() already clamps to something // smaller than maxColumnIndex; can we just DCHECK here? - if (IsHTMLTableCellElement(*GetNode())) - return std::min<unsigned>(ToHTMLTableCellElement(*GetNode()).colSpan(), - kMaxColumnIndex); + if (auto* cell_element = DynamicTo<HTMLTableCellElement>(GetNode())) + return std::min<unsigned>(cell_element->colSpan(), kMaxColumnIndex); return 1; } unsigned LayoutTableCell::ParseRowSpanFromDOM() const { DCHECK(GetNode()); - if (IsHTMLTableCellElement(*GetNode())) - return std::min<unsigned>(ToHTMLTableCellElement(*GetNode()).rowSpan(), - kMaxRowIndex); + if (auto* cell_element = DynamicTo<HTMLTableCellElement>(GetNode())) + return std::min<unsigned>(cell_element->rowSpan(), kMaxRowIndex); return 1; } @@ -123,11 +121,11 @@ void LayoutTableCell::UpdateColAndRowSpanFlags() { void LayoutTableCell::ColSpanOrRowSpanChanged() { DCHECK(GetNode()); - DCHECK(IsHTMLTableCellElement(*GetNode())); + DCHECK(IsA<HTMLTableCellElement>(*GetNode())); UpdateColAndRowSpanFlags(); - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAttributeChanged); if (Parent() && Section()) { Section()->SetNeedsCellRecalc(); @@ -177,7 +175,7 @@ Length LayoutTableCell::LogicalWidthFromColumns( return Length::Fixed(col_width_sum); } -void LayoutTableCell::ComputePreferredLogicalWidths() { +MinMaxSizes LayoutTableCell::PreferredLogicalWidths() const { // The child cells rely on the grids up in the sections to do their // computePreferredLogicalWidths work. Normally the sections are set up // early, as table cells are added, but relayout can cause the cells to be @@ -189,13 +187,14 @@ void LayoutTableCell::ComputePreferredLogicalWidths() { // notional height on the cell, such as can happen when a percent sized image // scales up its width to match the available height. Setting a zero override // height prevents this from happening. + auto* mutable_this = const_cast<LayoutTableCell*>(this); LayoutUnit logical_height = HasOverrideLogicalHeight() ? OverrideLogicalHeight() : LayoutUnit(-1); if (logical_height > -1) - SetOverrideLogicalHeight(LayoutUnit()); - LayoutBlockFlow::ComputePreferredLogicalWidths(); + mutable_this->SetOverrideLogicalHeight(LayoutUnit()); + MinMaxSizes sizes = LayoutBlockFlow::PreferredLogicalWidths(); if (logical_height > -1) - SetOverrideLogicalHeight(logical_height); + mutable_this->SetOverrideLogicalHeight(logical_height); if (GetNode() && StyleRef().AutoWrap()) { // See if nowrap was set. @@ -207,10 +206,11 @@ void LayoutTableCell::ComputePreferredLogicalWidths() { // set on the cell. Even so, it is a WinIE/Moz trait to make the minwidth // of the cell into the fixed width. They do this even in strict mode, so // do not make this a quirk. Affected the top of hiptop.com. - min_preferred_logical_width_ = - std::max(LayoutUnit(w.Value()), min_preferred_logical_width_); + sizes.min_size = std::max(sizes.min_size, LayoutUnit(w.Value())); } } + + return sizes; } void LayoutTableCell::ComputeIntrinsicPadding(int collapsed_height, @@ -476,13 +476,13 @@ void LayoutTableCell::StyleDidChange(StyleDifference diff, // TODO(dgrogan) Add a web test showing that SetChildNeedsLayout is // needed instead of SetNeedsLayout. PreviousCell()->SetChildNeedsLayout(); - PreviousCell()->SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + PreviousCell()->SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); } if (NextCell()) { // TODO(dgrogan) Add a web test showing that SetChildNeedsLayout is // needed instead of SetNeedsLayout. NextCell()->SetChildNeedsLayout(); - NextCell()->SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + NextCell()->SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); } } } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h index cd9a057af30..b2f2dd196e5 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_cell.h @@ -363,9 +363,10 @@ class CORE_EXPORT LayoutTableCell : public LayoutBlockFlow, // LayoutNGTableCellInterface implementation end. + MinMaxSizes PreferredLogicalWidths() const override; + protected: void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; - void ComputePreferredLogicalWidths() override; void InvalidatePaint(const PaintInvalidatorContext&) const override; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_cell_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_cell_test.cc index 77c67634079..0826849fc2e 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_cell_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_cell_test.cc @@ -336,22 +336,26 @@ TEST_F(LayoutTableCellTest, HasNonCollapsedBorderDecoration) { To<Element>(cell->GetNode()) ->setAttribute(html_names::kStyleAttr, "border: 1px solid black"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(cell->HasNonCollapsedBorderDecoration()); To<Element>(cell->Table()->GetNode()) ->setAttribute(html_names::kStyleAttr, "border-collapse: collapse"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(cell->HasNonCollapsedBorderDecoration()); To<Element>(cell->GetNode()) ->setAttribute(html_names::kStyleAttr, "border: 2px solid black"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(cell->HasNonCollapsedBorderDecoration()); To<Element>(cell->Table()->GetNode()) ->setAttribute(html_names::kStyleAttr, ""); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(cell->HasNonCollapsedBorderDecoration()); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_col.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_col.cc index 578d91179f3..e2286b94bfa 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_col.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_col.cc @@ -72,15 +72,14 @@ void LayoutTableCol::StyleDidChange(StyleDifference diff, void LayoutTableCol::UpdateFromElement() { unsigned old_span = span_; - Node* n = GetNode(); - if (IsHTMLTableColElement(n)) { - HTMLTableColElement& tc = ToHTMLTableColElement(*n); - span_ = tc.span(); + + if (auto* tc = DynamicTo<HTMLTableColElement>(GetNode())) { + span_ = tc->span(); } else { span_ = 1; } if (span_ != old_span && Style() && Parent()) { - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kAttributeChanged); } } @@ -107,17 +106,11 @@ bool LayoutTableCol::CanHaveChildren() const { return IsTableColumnGroup(); } -bool LayoutTableCol::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - // LayoutTableCol paints nothing by itself. Its background is painted by - // LayoutTableSection. - return true; -} - -void LayoutTableCol::ClearPreferredLogicalWidthsDirtyBits() { - ClearPreferredLogicalWidthsDirty(); +void LayoutTableCol::ClearIntrinsicLogicalWidthsDirtyBits() { + ClearIntrinsicLogicalWidthsDirty(); for (LayoutObject* child = FirstChild(); child; child = child->NextSibling()) - child->ClearPreferredLogicalWidthsDirty(); + child->ClearIntrinsicLogicalWidthsDirty(); } LayoutTable* LayoutTableCol::Table() const { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_col.h b/chromium/third_party/blink/renderer/core/layout/layout_table_col.h index 56634d3d044..cd905f20d33 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_col.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_col.h @@ -60,7 +60,7 @@ class LayoutTableCol final : public LayoutTableBoxComponent { public: explicit LayoutTableCol(Element*); - void ClearPreferredLogicalWidthsDirtyBits(); + void ClearIntrinsicLogicalWidthsDirtyBits(); // The 'span' attribute in HTML. // For CSS table columns or colgroups, this is always 1. @@ -86,7 +86,15 @@ class LayoutTableCol final : public LayoutTableBoxComponent { return type == kLayoutObjectLayoutTableCol || LayoutBox::IsOfType(type); } void UpdateFromElement() override; - void ComputePreferredLogicalWidths() override { NOTREACHED(); } + + MinMaxSizes PreferredLogicalWidths() const override { + NOTREACHED(); + return MinMaxSizes(); + } + MinMaxSizes ComputeIntrinsicLogicalWidths() const final { + NOTREACHED(); + return MinMaxSizes(); + } void InsertedIntoTree() override; void WillBeRemovedFromTree() override; @@ -95,8 +103,6 @@ class LayoutTableCol final : public LayoutTableBoxComponent { bool CanHaveChildren() const override; PaintLayerType LayerTypeRequired() const override { return kNoPaintLayer; } - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final; - void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; LayoutTable* Table() const final; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_row.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_row.cc index 001ade422da..8dbea5af6e8 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_row.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_row.cc @@ -92,7 +92,7 @@ void LayoutTableRow::StyleDidChange(StyleDifference diff, // TODO(dgrogan) Add a web test showing that SetChildNeedsLayout is // needed instead of SetNeedsLayout. child_box->SetChildNeedsLayout(); - child_box->SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + child_box->SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); } // Most table componenents can rely on LayoutObject::styleDidChange // to mark the container chain dirty. But LayoutTableSection seems @@ -100,7 +100,7 @@ void LayoutTableRow::StyleDidChange(StyleDifference diff, // anything under LayoutTableSection has to restart the propagation // at the table. // TODO(dgrogan): Make LayoutTableSection clear its dirty bit. - table->SetPreferredLogicalWidthsDirty(); + table->SetIntrinsicLogicalWidthsDirty(); } // When a row gets collapsed or uncollapsed, it's necessary to check all the @@ -178,11 +178,11 @@ void LayoutTableRow::AddChild(LayoutObject* child, LayoutObject* before_child) { if (enclosing_table && enclosing_table->ShouldCollapseBorders()) { enclosing_table->InvalidateCollapsedBorders(); if (LayoutTableCell* previous_cell = cell->PreviousCell()) { - previous_cell->SetNeedsLayoutAndPrefWidthsRecalc( + previous_cell->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kTableChanged); } if (LayoutTableCell* next_cell = cell->NextCell()) { - next_cell->SetNeedsLayoutAndPrefWidthsRecalc( + next_cell->SetNeedsLayoutAndIntrinsicWidthsRecalc( layout_invalidation_reason::kTableChanged); } } @@ -376,11 +376,4 @@ void LayoutTableRow::AddVisualOverflowFromCell(const LayoutTableCell* cell) { AddContentsVisualOverflow(cell_visual_overflow_rect); } -bool LayoutTableRow::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - return LayoutTableBoxComponent:: - PaintedOutputOfObjectHasNoEffectRegardlessOfSize() && - // Row paints collapsed borders. - !Table()->HasCollapsedBorders(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_row.h b/chromium/third_party/blink/renderer/core/layout/layout_table_row.h index 7e42e8ef116..82feffcf220 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_row.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_row.h @@ -128,8 +128,6 @@ class CORE_EXPORT LayoutTableRow final : public LayoutTableBoxComponent, bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const override { return false; } - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; - // LayoutNGTableRowInterface methods start. const LayoutNGTableRowInterface* ToLayoutNGTableRowInterface() const final { @@ -153,6 +151,11 @@ class CORE_EXPORT LayoutTableRow final : public LayoutTableBoxComponent, // LayoutNGTableRowInterface methods end. private: + MinMaxSizes ComputeIntrinsicLogicalWidths() const final { + NOTREACHED(); + return MinMaxSizes(); + } + void ComputeVisualOverflow(); void AddLayoutOverflowFromCell(const LayoutTableCell*); void AddVisualOverflowFromCell(const LayoutTableCell*); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc index f3b49bc2729..e109f213be7 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_section.cc @@ -1478,7 +1478,7 @@ void LayoutTableSection::MarkAllCellsWidthsDirtyAndOrNeedsLayout( for (LayoutTableRow* row = FirstRow(); row; row = row->NextRow()) { for (LayoutTableCell* cell = row->FirstCell(); cell; cell = cell->NextCell()) { - cell->SetPreferredLogicalWidthsDirty(); + cell->SetIntrinsicLogicalWidthsDirty(); if (what_to_mark == LayoutTable::kMarkDirtyAndNeedsLayout) cell->SetChildNeedsLayout(); } @@ -2108,13 +2108,4 @@ bool LayoutTableSection::MapToVisualRectInAncestorSpaceInternal( ancestor, transform_state, flags); } -bool LayoutTableSection::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() - const { - // LayoutTableSection paints background from columns. - if (Table()->HasColElements()) - return false; - return LayoutTableBoxComponent:: - PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_section.h b/chromium/third_party/blink/renderer/core/layout/layout_table_section.h index 4bab95705be..2e3495ce6e3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_section.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_section.h @@ -336,6 +336,11 @@ class CORE_EXPORT LayoutTableSection final HitTestAction) override; private: + MinMaxSizes ComputeIntrinsicLogicalWidths() const final { + NOTREACHED(); + return MinMaxSizes(); + } + void ComputeVisualOverflowFromDescendants(); bool IsOfType(LayoutObjectType type) const override { @@ -421,8 +426,6 @@ class CORE_EXPORT LayoutTableSection final // avoid any repeating headers in its table or ancestor tables. int OffsetForRepeatedHeader() const; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override; - bool HeaderGroupShouldRepeat() const { return Table()->Header() == this && GroupShouldRepeat(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_table_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_table_test.cc index 1ea145a99f4..04897e32a31 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_table_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_table_test.cc @@ -37,8 +37,7 @@ TEST_F(LayoutTableTest, OverflowViaOutline) { To<Element>(child->GetNode()) ->setAttribute(html_names::kStyleAttr, "outline: 2px solid black"); - target->GetFrameView()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + target->GetFrameView()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_EQ(LayoutRect(-2, -2, 104, 204), target->SelfVisualOverflowRect()); EXPECT_EQ(LayoutRect(-2, -2, 104, 204), child->SelfVisualOverflowRect()); @@ -358,8 +357,7 @@ TEST_F(LayoutTableTest, VisualOverflowCleared) { EXPECT_EQ(LayoutRect(-3, -3, 66, 66), table->SelfVisualOverflowRect()); To<Element>(table->GetNode()) ->setAttribute(html_names::kStyleAttr, "box-shadow: initial"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); EXPECT_EQ(LayoutRect(0, 0, 50, 50), table->SelfVisualOverflowRect()); } @@ -370,13 +368,15 @@ TEST_F(LayoutTableTest, HasNonCollapsedBorderDecoration) { To<Element>(table->GetNode()) ->setAttribute(html_names::kStyleAttr, "border: 1px solid black"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_TRUE(table->HasNonCollapsedBorderDecoration()); To<Element>(table->GetNode()) ->setAttribute(html_names::kStyleAttr, "border: 1px solid black; border-collapse: collapse"); - GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); + GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); EXPECT_FALSE(table->HasNonCollapsedBorderDecoration()); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text.cc b/chromium/third_party/blink/renderer/core/layout/layout_text.cc index f2c80c747a2..b1ccf91480c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text.cc @@ -29,6 +29,7 @@ #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/text.h" #include "third_party/blink/renderer/core/editing/ephemeral_range.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" @@ -181,7 +182,8 @@ void LayoutText::StyleDidChange(StyleDifference diff, // We do have to schedule layouts, though, since a style change can force us // to need to relayout. if (diff.NeedsFullLayout()) { - SetNeedsLayoutAndPrefWidthsRecalc(layout_invalidation_reason::kStyleChange); + SetNeedsLayoutAndIntrinsicWidthsRecalc( + layout_invalidation_reason::kStyleChange); known_to_have_no_overflow_and_no_fallback_fonts_ = false; } @@ -221,13 +223,20 @@ void LayoutText::RemoveAndDestroyTextBoxes() { for (InlineTextBox* box : TextBoxes()) box->Remove(); } else { - if (NGPaintFragment* first_inline_fragment = FirstInlineFragment()) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (has_abstract_inline_text_box_) + ClearFirstInlineFragmentItemIndex(); + } else if (NGPaintFragment* first_inline_fragment = + FirstInlineFragment()) { first_inline_fragment->LayoutObjectWillBeDestroyed(); SetFirstInlineFragment(nullptr); } if (Parent()) Parent()->DirtyLinesFromChangedChild(this); } + } else if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (has_abstract_inline_text_box_) + ClearFirstInlineFragmentItemIndex(); } else if (NGPaintFragment* first_inline_fragment = FirstInlineFragment()) { // Still do this to clear the global hash map in NGAbstractInlineTextBox. SetFirstInlineFragment(nullptr); @@ -265,29 +274,42 @@ void LayoutText::RemoveTextBox(InlineTextBox* box) { void LayoutText::DeleteTextBoxes() { if (!IsInLayoutNGInlineFormattingContext()) - MutableTextBoxes().DeleteLineBoxes(); + return MutableTextBoxes().DeleteLineBoxes(); + DetachAbstractInlineTextBoxesIfNeeded(); } -void LayoutText::SetFirstInlineFragment(NGPaintFragment* first_fragment) { - CHECK(IsInLayoutNGInlineFormattingContext()); - // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of - // |!fragment|. - DCHECK(!first_fragment || - !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); +void LayoutText::DetachAbstractInlineTextBoxes() { // TODO(layout-dev): Because We should call |WillDestroy()| once for // associated fragments, when you reuse fragments, you should construct // NGAbstractInlineTextBox for them. - if (has_abstract_inline_text_box_) { + DCHECK(has_abstract_inline_text_box_); + has_abstract_inline_text_box_ = false; + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { for (NGPaintFragment* fragment : NGPaintFragment::InlineFragmentsFor(this)) NGAbstractInlineTextBox::WillDestroy(fragment); - has_abstract_inline_text_box_ = false; + return; } + // TODO(yosin): Make sure we call this function within valid containg block + // of |this|. + NGInlineCursor cursor; + for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) + NGAbstractInlineTextBox::WillDestroy(cursor); +} + +void LayoutText::SetFirstInlineFragment(NGPaintFragment* first_fragment) { + CHECK(IsInLayoutNGInlineFormattingContext()); + // TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of + // |!fragment|. + DCHECK(!first_fragment || + !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DetachAbstractInlineTextBoxesIfNeeded(); first_paint_fragment_ = first_fragment; } void LayoutText::ClearFirstInlineFragmentItemIndex() { CHECK(IsInLayoutNGInlineFormattingContext()) << *this; DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DetachAbstractInlineTextBoxesIfNeeded(); first_fragment_item_index_ = 0u; } @@ -296,7 +318,10 @@ void LayoutText::SetFirstInlineFragmentItemIndex(wtf_size_t index) { // TODO(yosin): Call |NGAbstractInlineTextBox::WillDestroy()|. DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); DCHECK_NE(index, 0u); - first_fragment_item_index_ = index; + DetachAbstractInlineTextBoxesIfNeeded(); + // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|, + // we should enable below. + // first_fragment_item_index_ = index; } void LayoutText::InLayoutNGInlineFormattingContextWillChange(bool new_value) { @@ -305,9 +330,17 @@ void LayoutText::InLayoutNGInlineFormattingContextWillChange(bool new_value) { // Because |first_paint_fragment_| and |text_boxes_| are union, when one is // deleted, the other should be initialized to nullptr. DCHECK(new_value ? !first_paint_fragment_ : !text_boxes_.First()); + + // Because there are no inline boxes associated to this text, we should not + // have abstract inline text boxes too. + DCHECK(!has_abstract_inline_text_box_); } Vector<LayoutText::TextBoxInfo> LayoutText::GetTextBoxInfo() const { + // This function may kick the layout (e.g., |LocalRect()|), but Inspector may + // call this function outside of the layout phase. + FontCachePurgePreventer fontCachePurgePreventer; + Vector<TextBoxInfo> results; if (const NGOffsetMapping* mapping = GetNGOffsetMapping()) { bool in_hidden_for_paint = false; @@ -317,7 +350,7 @@ Vector<LayoutText::TextBoxInfo> LayoutText::GetTextBoxInfo() const { // TODO(yosin): We should introduce // |NGPhysicalTextFragment::IsTruncated()| to skip them instead of using // |IsHiddenForPaint()| with ordering of fragments. - if (cursor.IsHiddenForPaint()) { + if (cursor.Current().IsHiddenForPaint()) { in_hidden_for_paint = true; } else if (in_hidden_for_paint) { // Because of we finished original fragments (not painted), we should @@ -326,31 +359,31 @@ Vector<LayoutText::TextBoxInfo> LayoutText::GetTextBoxInfo() const { } // We don't put generated texts, e.g. ellipsis, hyphen, etc. not in text // content, into results. Note: CSS "content" aren't categorized this. - if (cursor.IsGeneratedTextType()) + if (cursor.Current().IsGeneratedTextType()) continue; // When the corresponding DOM range contains collapsed whitespaces, NG // produces one fragment but legacy produces multiple text boxes broken at // collapsed whitespaces. We break the fragment at collapsed whitespaces // to match the legacy output. + const NGTextOffset offset = cursor.Current().TextOffset(); for (const NGOffsetMappingUnit& unit : - mapping->GetMappingUnitsForTextContentOffsetRange( - cursor.CurrentTextStartOffset(), - cursor.CurrentTextEndOffset())) { + mapping->GetMappingUnitsForTextContentOffsetRange(offset.start, + offset.end)) { DCHECK_EQ(unit.GetLayoutObject(), this); if (unit.GetType() == NGOffsetMappingUnitType::kCollapsed) continue; // [clamped_start, clamped_end] of |fragment| matches a legacy text box. const unsigned clamped_start = - std::max(unit.TextContentStart(), cursor.CurrentTextStartOffset()); + std::max(unit.TextContentStart(), offset.start); const unsigned clamped_end = - std::min(unit.TextContentEnd(), cursor.CurrentTextEndOffset()); + std::min(unit.TextContentEnd(), offset.end); DCHECK_LT(clamped_start, clamped_end); const unsigned box_length = clamped_end - clamped_start; // Compute rect of the legacy text box. LayoutRect rect = cursor.CurrentLocalRect(clamped_start, clamped_end).ToLayoutRect(); - rect.MoveBy(cursor.CurrentOffset().ToLayoutPoint()); + rect.MoveBy(cursor.Current().OffsetInContainerBlock().ToLayoutPoint()); // Compute start of the legacy text box. if (unit.AssociatedNode()) { @@ -380,15 +413,13 @@ Vector<LayoutText::TextBoxInfo> LayoutText::GetTextBoxInfo() const { return results; } -bool LayoutText::HasTextBoxes() const { - if (RuntimeEnabledFeatures::LayoutNGEnabled()) { - auto fragments = NGPaintFragment::InlineFragmentsFor(this); - if (fragments.IsInLayoutNGInlineFormattingContext()) - return !(fragments.begin() == fragments.end()); - // When legacy is forced, IsInLayoutNGInlineFormattingContext is false, - // and we fall back to normal HasTextBox - return FirstTextBox(); - } +bool LayoutText::HasInlineFragments() const { + if (IsInLayoutNGInlineFormattingContext()) { + NGInlineCursor cursor; + cursor.MoveTo(*this); + return cursor.IsNotNull(); + } + return FirstTextBox(); } @@ -473,10 +504,10 @@ void LayoutText::CollectLineBoxRects(const PhysicalRectCollector& yield, for (; cursor; cursor.MoveToNextForSameLayoutObject()) { if (UNLIKELY(option != ClippingOption::kNoClipping)) { DCHECK_EQ(option, ClippingOption::kClipToEllipsis); - if (cursor.IsHiddenForPaint()) + if (cursor.Current().IsHiddenForPaint()) continue; } - yield(cursor.CurrentRect()); + yield(cursor.Current().RectInContainerBlock()); } return; } @@ -585,13 +616,13 @@ void LayoutText::AbsoluteQuadsForRange(Vector<FloatQuad>& quads, block_for_flipping = ContainingBlock(); NGInlineCursor cursor; for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { - const NGTextOffset offset = cursor.CurrentTextOffset(); + const NGTextOffset offset = cursor.Current().TextOffset(); if (start > offset.end || end < offset.start) continue; const unsigned clamped_start = std::max(start, offset.start); const unsigned clamped_end = std::min(end, offset.end); PhysicalRect rect = cursor.CurrentLocalRect(clamped_start, clamped_end); - rect.Move(cursor.CurrentOffset()); + rect.Move(cursor.Current().OffsetInContainerBlock()); const FloatQuad quad = LocalRectToAbsoluteQuad(rect); if (clamped_start < clamped_end) { quads.push_back(quad); @@ -979,7 +1010,7 @@ void LayoutText::TrimmedPrefWidths(LayoutUnit lead_width_layout_unit, if (!collapse_white_space) strip_front_spaces = false; - if (has_tab_ || PreferredLogicalWidthsDirty()) + if (has_tab_ || IntrinsicLogicalWidthsDirty()) ComputePreferredLogicalWidths(lead_width); has_breakable_start = !strip_front_spaces && has_breakable_start_; @@ -1070,14 +1101,14 @@ void LayoutText::TrimmedPrefWidths(LayoutUnit lead_width_layout_unit, } float LayoutText::MinLogicalWidth() const { - if (PreferredLogicalWidthsDirty()) + if (IntrinsicLogicalWidthsDirty()) const_cast<LayoutText*>(this)->ComputePreferredLogicalWidths(0); return min_width_; } float LayoutText::MaxLogicalWidth() const { - if (PreferredLogicalWidthsDirty()) + if (IntrinsicLogicalWidthsDirty()) const_cast<LayoutText*>(this)->ComputePreferredLogicalWidths(0); return max_width_; @@ -1173,7 +1204,7 @@ void LayoutText::ComputePreferredLogicalWidths( float lead_width, HashSet<const SimpleFontData*>& fallback_fonts, FloatRect& glyph_bounds) { - DCHECK(has_tab_ || PreferredLogicalWidthsDirty() || + DCHECK(has_tab_ || IntrinsicLogicalWidthsDirty() || !known_to_have_no_overflow_and_no_fallback_fonts_); min_width_ = 0; @@ -1523,7 +1554,7 @@ void LayoutText::ComputePreferredLogicalWidths( known_to_have_no_overflow_and_no_fallback_fonts_ = fallback_fonts.IsEmpty() && glyph_overflow.IsApproximatelyZero(); - ClearPreferredLogicalWidthsDirty(); + ClearIntrinsicLogicalWidthsDirty(); } bool LayoutText::IsAllCollapsibleWhitespace() const { @@ -1564,7 +1595,7 @@ UChar32 LayoutText::FirstCharacterAfterWhitespaceCollapsing() const { NGInlineCursor cursor; cursor.MoveTo(*this); if (cursor) { - const StringView text = cursor.CurrentText(); + const StringView text = cursor.Current().Text(cursor); return text.length() ? text.CodepointAt(0) : 0; } } @@ -1580,7 +1611,7 @@ UChar32 LayoutText::LastCharacterAfterWhitespaceCollapsing() const { NGInlineCursor cursor; cursor.MoveTo(*this); if (cursor) { - const StringView text = cursor.CurrentText(); + const StringView text = cursor.Current().Text(cursor); return text.length() ? text.CodepointAt(text.length() - 1) : 0; } } @@ -1588,12 +1619,15 @@ UChar32 LayoutText::LastCharacterAfterWhitespaceCollapsing() const { } PhysicalOffset LayoutText::FirstLineBoxTopLeft() const { - if (const NGPaintFragment* fragment = FirstInlineFragment()) { + if (IsInLayoutNGInlineFormattingContext()) { // TODO(kojii): Some clients call this against dirty-tree, but NG fragments // are not safe to read for dirty-tree. crbug.com/963103 if (UNLIKELY(!IsFirstInlineFragmentSafe())) return PhysicalOffset(); - return fragment->InlineOffsetToContainerBox(); + NGInlineCursor cursor; + cursor.MoveTo(*this); + return cursor ? cursor.Current().OffsetInContainerBlock() + : PhysicalOffset(); } if (const auto* text_box = FirstTextBox()) { LayoutPoint location = text_box->Location(); @@ -1798,7 +1832,7 @@ static inline bool IsInlineFlowOrEmptyText(const LayoutObject* o) { } OnlyWhitespaceOrNbsp LayoutText::ContainsOnlyWhitespaceOrNbsp() const { - return PreferredLogicalWidthsDirty() ? OnlyWhitespaceOrNbsp::kUnknown + return IntrinsicLogicalWidthsDirty() ? OnlyWhitespaceOrNbsp::kUnknown : static_cast<OnlyWhitespaceOrNbsp>( contains_only_whitespace_or_nbsp_); } @@ -1889,10 +1923,10 @@ void LayoutText::ForceSetText(scoped_refptr<StringImpl> text) { void LayoutText::TextDidChange() { // If preferredLogicalWidthsDirty() of an orphan child is true, // LayoutObjectChildList::insertChildNode() fails to set true to owner. - // To avoid that, we call setNeedsLayoutAndPrefWidthsRecalc() only if this - // LayoutText has parent. + // To avoid that, we call SetNeedsLayoutAndIntrinsicWidthsRecalc() only if + // this LayoutText has parent. if (Parent()) { - SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( layout_invalidation_reason::kTextChanged); } TextDidChangeWithoutInvalidation(); @@ -1918,6 +1952,14 @@ void LayoutText::TextDidChangeWithoutInvalidation() { SetNeedsCollectInlines(); } +void LayoutText::InvalidateSubtreeLayoutForFontUpdates() { + known_to_have_no_overflow_and_no_fallback_fonts_ = false; + valid_ng_items_ = false; + SetNeedsCollectInlines(); + SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( + layout_invalidation_reason::kFontsChanged); +} + void LayoutText::DirtyOrDeleteLineBoxesIfNeeded(bool full_layout) { if (full_layout) DeleteTextBoxes(); @@ -1999,7 +2041,7 @@ float LayoutText::Width(unsigned from, if (!StyleRef().PreserveNewline() && !from && len == TextLength()) { if (fallback_fonts) { DCHECK(glyph_bounds); - if (PreferredLogicalWidthsDirty() || + if (IntrinsicLogicalWidthsDirty() || !known_to_have_no_overflow_and_no_fallback_fonts_) { const_cast<LayoutText*>(this)->ComputePreferredLogicalWidths( 0, *fallback_fonts, *glyph_bounds); @@ -2110,14 +2152,14 @@ PhysicalRect LayoutText::LocalSelectionVisualRect() const { PhysicalRect rect; NGInlineCursor cursor(*RootInlineFormattingContext()); for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { - if (cursor.IsHiddenForPaint()) + if (cursor.Current().IsHiddenForPaint()) continue; const LayoutSelectionStatus status = frame_selection.ComputeLayoutSelectionStatus(cursor); if (status.start == status.end) continue; PhysicalRect item_rect = ComputeLocalSelectionRectForText(cursor, status); - item_rect.offset += cursor.CurrentOffset(); + item_rect.offset += cursor.Current().OffsetInContainerBlock(); rect.Unite(item_rect); } return rect; @@ -2415,12 +2457,10 @@ void LayoutText::MomentarilyRevealLastTypedCharacter( } scoped_refptr<AbstractInlineTextBox> LayoutText::FirstAbstractInlineTextBox() { - if (RuntimeEnabledFeatures::LayoutNGEnabled()) { - auto fragments = NGPaintFragment::InlineFragmentsFor(this); - if (!fragments.IsEmpty() && - fragments.IsInLayoutNGInlineFormattingContext()) { - return NGAbstractInlineTextBox::GetOrCreate(fragments.front()); - } + if (IsInLayoutNGInlineFormattingContext()) { + NGInlineCursor cursor; + cursor.MoveTo(*this); + return NGAbstractInlineTextBox::GetOrCreate(cursor); } return LegacyAbstractInlineTextBox::GetOrCreate(LineLayoutText(this), FirstTextBox()); @@ -2430,7 +2470,8 @@ void LayoutText::InvalidateDisplayItemClients( PaintInvalidationReason invalidation_reason) const { ObjectPaintInvalidator paint_invalidator(*this); - if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled()) { + if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled() && + !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { auto fragments = NGPaintFragment::InlineFragmentsFor(this); if (fragments.IsInLayoutNGInlineFormattingContext()) { for (NGPaintFragment* fragment : fragments) { @@ -2445,7 +2486,7 @@ void LayoutText::InvalidateDisplayItemClients( NGInlineCursor cursor; for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) { paint_invalidator.InvalidateDisplayItemClient( - *cursor.CurrentDisplayItemClient(), invalidation_reason); + *cursor.Current().GetDisplayItemClient(), invalidation_reason); } return; } @@ -2467,8 +2508,11 @@ PhysicalRect LayoutText::DebugRect() const { DOMNodeId LayoutText::EnsureNodeId() { if (node_id_ == kInvalidDOMNodeId) { - if (auto* content_capture_manager = GetContentCaptureManager()) - node_id_ = content_capture_manager->GetNodeId(*GetNode()); + auto* content_capture_manager = GetContentCaptureManager(); + if (content_capture_manager) { + content_capture_manager->ScheduleTaskIfNeeded(); + node_id_ = DOMNodeIds::IdForNode(GetNode()); + } } return node_id_; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text.h b/chromium/third_party/blink/renderer/core/layout/layout_text.h index 3548afaf77a..17ca490ad20 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text.h @@ -97,6 +97,7 @@ class CORE_EXPORT LayoutText : public LayoutObject { void AttachTextBox(InlineTextBox*); void RemoveTextBox(InlineTextBox*); + bool HasInlineFragments() const final; NGPaintFragment* FirstInlineFragment() const final; void SetFirstInlineFragment(NGPaintFragment*) final; wtf_size_t FirstInlineFragmentItemIndex() const final; @@ -218,10 +219,6 @@ class CORE_EXPORT LayoutText : public LayoutObject { InlineTextBox* FirstTextBox() const { return TextBoxes().First(); } InlineTextBox* LastTextBox() const { return TextBoxes().Last(); } - // True if we have inline text box children which implies rendered text (or - // whitespace) output. - bool HasTextBoxes() const; - // TODO(layoutng) Legacy-only implementation of HasTextBoxes. // All callers should call HasTextBoxes instead, and take NG into account. bool HasLegacyTextBoxes() const { return FirstTextBox(); } @@ -331,6 +328,10 @@ class CORE_EXPORT LayoutText : public LayoutObject { } virtual base::span<NGInlineItem>* GetNGInlineItems() { return nullptr; } + void InvalidateSubtreeLayoutForFontUpdates() override; + + void DetachAbstractInlineTextBoxesIfNeeded(); + protected: void WillBeDestroyed() override; @@ -439,6 +440,7 @@ class CORE_EXPORT LayoutText : public LayoutObject { private: ContentCaptureManager* GetContentCaptureManager(); + void DetachAbstractInlineTextBoxes(); // Used for LayoutNG with accessibility. True if inline fragments are // associated to |NGAbstractInlineTextBox|. @@ -517,6 +519,11 @@ inline float LayoutText::HyphenWidth(const Font& font, style, direction)); } +inline void LayoutText::DetachAbstractInlineTextBoxesIfNeeded() { + if (UNLIKELY(has_abstract_inline_text_box_)) + DetachAbstractInlineTextBoxes(); +} + DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutText, IsText()); inline LayoutText* Text::GetLayoutObject() const { diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc index 62c19718366..cb823919580 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_combine.cc @@ -177,7 +177,7 @@ void LayoutTextCombine::UpdateFontStyleForCombinedText() { FontSelector* font_selector = style->GetFont().GetFontSelector(); // Need to change font orientation to horizontal. - bool should_update_font = style->SetFontDescription(description); + style->SetFontDescription(description); if (combined_text_width_ <= em_width) { scale_x_ = 1.0f; @@ -187,14 +187,13 @@ void LayoutTextCombine::UpdateFontStyleForCombinedText() { kQuarterWidth}; for (size_t i = 0; i < base::size(kWidthVariants); ++i) { description.SetWidthVariant(kWidthVariants[i]); - Font compressed_font = Font(description); - compressed_font.Update(font_selector); + Font compressed_font(description, font_selector); float run_width = compressed_font.Width(run); if (run_width <= em_width) { combined_text_width_ = run_width; // Replace my font with the new one. - should_update_font = style->SetFontDescription(description); + style->SetFontDescription(description); break; } } @@ -209,9 +208,6 @@ void LayoutTextCombine::UpdateFontStyleForCombinedText() { scale_x_ = 1.0f; } } - - if (should_update_font) - style->GetFont().Update(font_selector); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc index 966bfa81583..cce0469a265 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control.cc @@ -208,69 +208,25 @@ float LayoutTextControl::GetAvgCharWidth(const AtomicString& family) const { return font.Width(text_run); } -void LayoutTextControl::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { +MinMaxSizes LayoutTextControl::ComputeIntrinsicLogicalWidths() const { + MinMaxSizes sizes; + sizes += BorderAndPaddingLogicalWidth(); + // Use average character width. Matches IE. AtomicString family = StyleRef().GetFont().GetFontDescription().Family().Family(); - max_logical_width = PreferredContentLogicalWidth( + sizes.max_size += PreferredContentLogicalWidth( const_cast<LayoutTextControl*>(this)->GetAvgCharWidth(family)); if (InnerEditorElement()) { if (LayoutBox* inner_editor_layout_box = - InnerEditorElement()->GetLayoutBox()) - max_logical_width += inner_editor_layout_box->PaddingStart() + - inner_editor_layout_box->PaddingEnd(); + InnerEditorElement()->GetLayoutBox()) { + sizes.max_size += inner_editor_layout_box->PaddingStart() + + inner_editor_layout_box->PaddingEnd(); + } } if (!StyleRef().LogicalWidth().IsPercentOrCalc()) - min_logical_width = max_logical_width; -} - -void LayoutTextControl::ComputePreferredLogicalWidths() { - DCHECK(PreferredLogicalWidthsDirty()); - - min_preferred_logical_width_ = LayoutUnit(); - max_preferred_logical_width_ = LayoutUnit(); - const ComputedStyle& style_to_use = StyleRef(); - - if (style_to_use.LogicalWidth().IsFixed() && - style_to_use.LogicalWidth().Value() >= 0) - min_preferred_logical_width_ = max_preferred_logical_width_ = - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalWidth().Value()); - else - ComputeIntrinsicLogicalWidths(min_preferred_logical_width_, - max_preferred_logical_width_); - - if (style_to_use.LogicalMinWidth().IsFixed() && - style_to_use.LogicalMinWidth().Value() > 0) { - max_preferred_logical_width_ = - std::max(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); - min_preferred_logical_width_ = - std::max(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMinWidth().Value())); - } - - if (style_to_use.LogicalMaxWidth().IsFixed()) { - max_preferred_logical_width_ = - std::min(max_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMaxWidth().Value())); - min_preferred_logical_width_ = - std::min(min_preferred_logical_width_, - AdjustContentBoxLogicalWidthForBoxSizing( - style_to_use.LogicalMaxWidth().Value())); - } - - LayoutUnit to_add = BorderAndPaddingLogicalWidth(); - - min_preferred_logical_width_ += to_add; - max_preferred_logical_width_ += to_add; - - ClearPreferredLogicalWidthsDirty(); + sizes.min_size = sizes.max_size; + return sizes; } void LayoutTextControl::AddOutlineRects(Vector<PhysicalRect>& rects, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control.h index 4c6b2bfb358..b3298fc3835 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control.h @@ -75,22 +75,13 @@ class CORE_EXPORT LayoutTextControl : public LayoutBlockFlow { SubtreeLayoutScope&) override; LayoutUnit FirstLineBoxBaseline() const override; - // We need to override this function because we don't want overflow:hidden on - // an <input> to affect the baseline calculation. This is necessary because we - // are an inline-block element as an implementation detail which would - // normally be affected by this. - bool ShouldIgnoreOverflowPropertyForInlineBlockBaseline() const override { - return true; - } bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectTextControl || LayoutBlockFlow::IsOfType(type); } private: - void ComputeIntrinsicLogicalWidths(LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const final; - void ComputePreferredLogicalWidths() final; + MinMaxSizes ComputeIntrinsicLogicalWidths() const final; void RemoveLeftoverAnonymousBlock(LayoutBlock*) final {} void AddOutlineRects(Vector<PhysicalRect>&, @@ -100,33 +91,10 @@ class CORE_EXPORT LayoutTextControl : public LayoutBlockFlow { bool CanBeProgramaticallyScrolled() const final { return true; } }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTextControl, IsTextControl()); - -// LayoutObject for our inner container, for <search> and others. -// We can't use LayoutFlexibleBox directly, because flexboxes have a different -// baseline definition, and then inputs of different types wouldn't line up -// anymore. -class LayoutTextControlInnerContainer final : public LayoutFlexibleBox { - public: - explicit LayoutTextControlInnerContainer(Element* element) - : LayoutFlexibleBox(element) {} - ~LayoutTextControlInnerContainer() override = default; - - LayoutUnit BaselinePosition(FontBaseline baseline, - bool first_line, - LineDirectionMode direction, - LinePositionMode position) const override { - return LayoutBlock::BaselinePosition(baseline, first_line, direction, - position); - } - LayoutUnit FirstLineBoxBaseline() const override { - return LayoutBlock::FirstLineBoxBaseline(); - } - LayoutUnit InlineBlockBaseline(LineDirectionMode direction) const override { - return LayoutBlock::InlineBlockBaseline(direction); - } - bool ShouldIgnoreOverflowPropertyForInlineBlockBaseline() const override { - return true; +template <> +struct DowncastTraits<LayoutTextControl> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsTextControl(); } }; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h index 35fff854e5b..21111aa1a63 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_multi_line.h @@ -66,8 +66,6 @@ class LayoutTextControlMultiLine final : public LayoutTextControl { LayoutUnit ScrollHeight() const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTextControlMultiLine, IsTextArea()); - } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc index 6f1f6bc19ae..b2c0cebcab0 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.cc @@ -197,17 +197,6 @@ void LayoutTextControlSingleLine::CapsLockStateMayHaveChanged() { } } -bool LayoutTextControlSingleLine::HasControlClip() const { - return true; -} - -PhysicalRect LayoutTextControlSingleLine::ControlClipRect( - const PhysicalOffset& additional_offset) const { - PhysicalRect clip_rect = PhysicalPaddingBoxRect(); - clip_rect.offset += additional_offset; - return clip_rect; -} - LayoutUnit LayoutTextControlSingleLine::PreferredContentLogicalWidth( float char_width) const { int factor; @@ -249,14 +238,6 @@ LayoutUnit LayoutTextControlSingleLine::ComputeControlLogicalHeight( return line_height + non_content_height; } -void LayoutTextControlSingleLine::Autoscroll(const PhysicalOffset& position) { - LayoutBox* layout_object = InnerEditorElement()->GetLayoutBox(); - if (!layout_object) - return; - - layout_object->Autoscroll(position); -} - LayoutUnit LayoutTextControlSingleLine::ScrollWidth() const { // If in preview state, fake the scroll width to prevent that any information // about the suggested content can be derived from the size. @@ -309,6 +290,7 @@ void LayoutTextControlSingleLine::ComputeVisualOverflow( AddVisualOverflowFromFloats(); if (VisualOverflowRect() != previous_visual_overflow_rect) { + InvalidateIntersectionObserverCachedRects(); SetShouldCheckForPaintInvalidation(); GetFrameView()->SetIntersectionObservationState(LocalFrameView::kDesired); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h index a49de236a8d..59323298ca3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line.h @@ -31,6 +31,12 @@ namespace blink { class HTMLInputElement; +// LayoutObject for text-field <input>s. +// +// This class inherits from LayoutTextControl and LayoutBlockFlow. If we'd like +// to change the base class, we need to make sure that +// ShouldIgnoreOverflowPropertyForInlineBlockBaseline flag works with the new +// base class. class LayoutTextControlSingleLine : public LayoutTextControl { public: LayoutTextControlSingleLine(HTMLInputElement*); @@ -47,8 +53,6 @@ class LayoutTextControlSingleLine : public LayoutTextControl { HTMLInputElement* InputElement() const; private: - bool HasControlClip() const final; - PhysicalRect ControlClipRect(const PhysicalOffset&) const final; bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectTextField || LayoutTextControl::IsOfType(type); } @@ -61,8 +65,6 @@ class LayoutTextControlSingleLine : public LayoutTextControl { const PhysicalOffset& accumulated_offset, HitTestAction) final; - void Autoscroll(const PhysicalOffset&) final; - // Subclassed to forward to our inner div. LayoutUnit ScrollWidth() const final; LayoutUnit ScrollHeight() const final; @@ -87,16 +89,18 @@ class LayoutTextControlSingleLine : public LayoutTextControl { bool should_draw_caps_lock_indicator_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutTextControlSingleLine, IsTextField()); +template <> +struct DowncastTraits<LayoutTextControlSingleLine> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsTextField(); + } +}; // ---------------------------- class LayoutTextControlInnerEditor : public LayoutBlockFlow { public: LayoutTextControlInnerEditor(Element* element) : LayoutBlockFlow(element) {} - bool ShouldIgnoreOverflowPropertyForInlineBlockBaseline() const override { - return true; - } private: bool IsIntrinsicallyScrollable( @@ -105,7 +109,6 @@ class LayoutTextControlInnerEditor : public LayoutBlockFlow { } bool ScrollsOverflowX() const override { return HasOverflowClip(); } bool ScrollsOverflowY() const override { return false; } - bool HasLineIfEmpty() const override { return true; } }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc index a0533fd75c5..a04b29a50f4 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_single_line_test.cc @@ -6,6 +6,7 @@ #include "build/build_config.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -23,21 +24,28 @@ TEST_F(LayoutTextControlSingleLineTest, VisualOverflowCleared) { <input id=input type="text"></input. )HTML"); auto* input = - ToLayoutTextControlSingleLine(GetLayoutObjectByElementId("input")); + To<LayoutTextControlSingleLine>(GetLayoutObjectByElementId("input")); + if (::features::IsFormControlsRefreshEnabled()) { + EXPECT_EQ(LayoutRect(-3, -3, 74, 72), input->SelfVisualOverflowRect()); + } else { #if defined(OS_MACOSX) - EXPECT_EQ(LayoutRect(-3, -3, 72, 72), input->SelfVisualOverflowRect()); + EXPECT_EQ(LayoutRect(-3, -3, 72, 72), input->SelfVisualOverflowRect()); #else - EXPECT_EQ(LayoutRect(-3, -3, 70, 72), input->SelfVisualOverflowRect()); + EXPECT_EQ(LayoutRect(-3, -3, 70, 72), input->SelfVisualOverflowRect()); #endif + } To<Element>(input->GetNode()) ->setAttribute(html_names::kStyleAttr, "box-shadow: initial"); - GetDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); + if (::features::IsFormControlsRefreshEnabled()) { + EXPECT_EQ(LayoutRect(0, 0, 58, 56), input->SelfVisualOverflowRect()); + } else { #if defined(OS_MACOSX) - EXPECT_EQ(LayoutRect(0, 0, 56, 56), input->SelfVisualOverflowRect()); + EXPECT_EQ(LayoutRect(0, 0, 56, 56), input->SelfVisualOverflowRect()); #else - EXPECT_EQ(LayoutRect(0, 0, 54, 56), input->SelfVisualOverflowRect()); + EXPECT_EQ(LayoutRect(0, 0, 54, 56), input->SelfVisualOverflowRect()); #endif + } } } // anonymous namespace diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_control_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_control_test.cc index 18d55934be9..b8446166f0c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_control_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_control_test.cc @@ -44,7 +44,8 @@ TEST_F(LayoutTextControlTest, EXPECT_FALSE(selectedText->ShouldInvalidateSelection()); inputElement->setAttribute(html_names::kClassAttr, "pseudoSelection"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(selectedText->ShouldInvalidateSelection()); UpdateAllLifecyclePhasesForTest(); @@ -69,7 +70,8 @@ TEST_F(LayoutTextControlTest, EXPECT_FALSE(selectedText->ShouldInvalidateSelection()); inputElement->setAttribute(html_names::kClassAttr, "pseudoSelection"); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(selectedText->ShouldInvalidateSelection()); UpdateAllLifecyclePhasesForTest(); @@ -94,7 +96,8 @@ TEST_F(LayoutTextControlTest, EXPECT_FALSE(selectedText->ShouldInvalidateSelection()); inputElement->removeAttribute(html_names::kClassAttr); - GetDocument().View()->UpdateLifecycleToLayoutClean(); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); EXPECT_TRUE(selectedText->ShouldInvalidateSelection()); UpdateAllLifecyclePhasesForTest(); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc index e8bacc966f2..3405d85c0ae 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc @@ -17,7 +17,7 @@ class LayoutTextFragmentTest : public RenderingTest { protected: void SetUp() override { RenderingTest::SetUp(); - GetDocument().head()->SetInnerHTMLFromString( + GetDocument().head()->setInnerHTML( "<style>#target::first-letter{color:red}</style>"); } @@ -52,7 +52,9 @@ class ParameterizedLayoutTextFragmentTest ParameterizedLayoutTextFragmentTest() : ScopedLayoutNGForTest(GetParam()) {} protected: - bool LayoutNGEnabled() const { return GetParam(); } + bool LayoutNGEnabled() const { + return RuntimeEnabledFeatures::LayoutNGEnabled(); + } }; INSTANTIATE_TEST_SUITE_P(All, diff --git a/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc index ac8fb58a804..ec907acd7fa 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_text_test.cc @@ -78,7 +78,9 @@ class ParameterizedLayoutTextTest : public testing::WithParamInterface<bool>, ParameterizedLayoutTextTest() : ScopedLayoutNGForTest(GetParam()) {} protected: - bool LayoutNGEnabled() const { return GetParam(); } + bool LayoutNGEnabled() const { + return RuntimeEnabledFeatures::LayoutNGEnabled(); + } }; INSTANTIATE_TEST_SUITE_P(All, ParameterizedLayoutTextTest, testing::Bool()); @@ -252,7 +254,7 @@ TEST_P(ParameterizedLayoutTextTest, CharacterAfterWhitespaceCollapsing) { SetBodyInnerHTML("a <span id=target> </span>b"); layout_text = GetLayoutTextById("target"); - DCHECK(!layout_text->HasTextBoxes()); + DCHECK(!layout_text->HasInlineFragments()); EXPECT_EQ(0, layout_text->FirstCharacterAfterWhitespaceCollapsing()); EXPECT_EQ(0, layout_text->LastCharacterAfterWhitespaceCollapsing()); @@ -268,7 +270,7 @@ TEST_P(ParameterizedLayoutTextTest, CharacterAfterWhitespaceCollapsing) { EXPECT_EQ(' ', layout_text->LastCharacterAfterWhitespaceCollapsing()); layout_text = ToLayoutText(GetLayoutObjectByElementId("target")->NextSibling()); - DCHECK(!layout_text->HasTextBoxes()); + DCHECK(!layout_text->HasInlineFragments()); EXPECT_EQ(0, layout_text->FirstCharacterAfterWhitespaceCollapsing()); EXPECT_EQ(0, layout_text->LastCharacterAfterWhitespaceCollapsing()); diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme.cc b/chromium/third_party/blink/renderer/core/layout/layout_theme.cc index 488f6f7d225..b1cbf5326ba 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme.cc @@ -30,7 +30,7 @@ #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" -#include "third_party/blink/renderer/core/fileapi/file_list.h" +#include "third_party/blink/renderer/core/fileapi/file.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/html/forms/html_data_list_element.h" @@ -55,12 +55,11 @@ #include "third_party/blink/renderer/core/style/computed_style_initial_values.h" #include "third_party/blink/renderer/platform/file_metadata.h" #include "third_party/blink/renderer/platform/fonts/font_selector.h" -#include "third_party/blink/renderer/platform/fonts/string_truncator.h" #include "third_party/blink/renderer/platform/graphics/touch_action.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" -#include "third_party/blink/renderer/platform/text/platform_locale.h" #include "third_party/blink/renderer/platform/web_test_support.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "ui/base/ui_base_features.h" #include "ui/native_theme/native_theme.h" // The methods in this file are shared by all themes on every platform. @@ -164,9 +163,6 @@ ControlPart LayoutTheme::AdjustAppearanceWithElementType( const ComputedStyle& style, const Element* element) { ControlPart part = style.EffectiveAppearance(); - if (!RuntimeEnabledFeatures::RestrictedWebkitAppearanceEnabled()) - return part; - if (!element) return kNoControlPart; @@ -186,6 +182,7 @@ ControlPart LayoutTheme::AdjustAppearanceWithElementType( // Aliases of 'auto'. // https://drafts.csswg.org/css-ui-4/#typedef-appearance-compat-auto + case kAutoPart: case kCheckboxPart: case kRadioPart: case kPushButtonPart: @@ -262,6 +259,7 @@ void LayoutTheme::AdjustStyle(ComputedStyle& style, Element* e) { ControlPart part = AdjustAppearanceWithAuthorStyle( AdjustAppearanceWithElementType(style, e), style); style.SetEffectiveAppearance(part); + DCHECK_NE(part, kAutoPart); if (part == kNoControlPart) return; @@ -297,6 +295,19 @@ void LayoutTheme::AdjustStyle(ComputedStyle& style, Element* e) { } String LayoutTheme::ExtraDefaultStyleSheet() { + if (RuntimeEnabledFeatures::LayoutNGForControlsEnabled()) { + return String(R"CSS( +input[type="file" i] { + overflow: hidden; + text-overflow: ellipsis; + white-space: pre; +} + +input[type="file" i]::-webkit-file-upload-button { + margin-inline-end: 4px; +} +)CSS"); + } return g_empty_string; } @@ -612,7 +623,12 @@ void LayoutTheme::AdjustButtonStyle(ComputedStyle& style) const {} void LayoutTheme::AdjustInnerSpinButtonStyle(ComputedStyle&) const {} -void LayoutTheme::AdjustMenuListStyle(ComputedStyle&, Element*) const {} +void LayoutTheme::AdjustMenuListStyle(ComputedStyle& style, Element*) const { + // Menulists should have visible overflow + // https://bugs.webkit.org/show_bug.cgi?id=21287 + style.SetOverflowX(EOverflow::kVisible); + style.SetOverflowY(EOverflow::kVisible); +} base::TimeDelta LayoutTheme::AnimationRepeatIntervalForProgressBar() const { return base::TimeDelta(); @@ -634,11 +650,15 @@ void LayoutTheme::AdjustSliderContainerStyle(ComputedStyle& style, if (e && (e->ShadowPseudoId() == "-webkit-media-slider-container" || e->ShadowPseudoId() == "-webkit-slider-container")) { if (style.EffectiveAppearance() == kSliderVerticalPart) { - style.SetTouchAction(TouchAction::kTouchActionPanX); + style.SetTouchAction(TouchAction::kPanX); style.SetEffectiveAppearance(kNoControlPart); + style.SetWritingMode(WritingMode::kVerticalRl); + // It's always in RTL because the slider value increases up even in LTR. + style.SetDirection(TextDirection::kRtl); } else { - style.SetTouchAction(TouchAction::kTouchActionPanY); + style.SetTouchAction(TouchAction::kPanY); style.SetEffectiveAppearance(kNoControlPart); + style.SetWritingMode(WritingMode::kHorizontalTb); } } } @@ -749,7 +769,7 @@ Color LayoutTheme::SystemColor(CSSValueID css_value_id, case CSSValueID::kButtonshadow: return 0xFF888888; case CSSValueID::kButtontext: - return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000; + return color_scheme == WebColorScheme::kDark ? 0xFFAAAAAA : 0xFF000000; case CSSValueID::kCaptiontext: return color_scheme == WebColorScheme::kDark ? 0xFFFFFFFF : 0xFF000000; case CSSValueID::kField: @@ -855,27 +875,16 @@ Color LayoutTheme::FocusRingColor() const { : GetTheme().PlatformFocusRingColor(); } -String LayoutTheme::FileListNameForWidth(Locale& locale, - const FileList* file_list, - const Font& font, - int width) const { - if (width <= 0) - return String(); - - String string; - if (file_list->IsEmpty()) { - string = locale.QueryString(IDS_FORM_FILE_NO_FILE_LABEL); - } else if (file_list->length() == 1) { - string = file_list->item(0)->name(); - } else { - return StringTruncator::RightTruncate( - locale.QueryString(IDS_FORM_FILE_MULTIPLE_UPLOAD, - locale.ConvertToLocalizedNumber( - String::Number(file_list->length()))), - width, font); - } +bool LayoutTheme::DelegatesMenuListRendering() const { + return delegates_menu_list_rendering_; +} + +void LayoutTheme::SetDelegatesMenuListRenderingForTesting(bool flag) { + delegates_menu_list_rendering_ = flag; +} - return StringTruncator::CenterTruncate(string, width, font); +String LayoutTheme::DisplayNameForFile(const File& file) const { + return file.name(); } bool LayoutTheme::ShouldOpenPickerWithF4Key() const { @@ -884,7 +893,7 @@ bool LayoutTheme::ShouldOpenPickerWithF4Key() const { bool LayoutTheme::SupportsCalendarPicker(const AtomicString& type) const { DCHECK(RuntimeEnabledFeatures::InputMultipleFieldsUIEnabled()); - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled() && + if (features::IsFormControlsRefreshEnabled() && type == input_type_names::kTime) return true; @@ -993,12 +1002,6 @@ void LayoutTheme::AdjustRadioStyleUsingFallbackTheme( style.ResetBorder(); } -Color LayoutTheme::RootElementColor(WebColorScheme color_scheme) const { - if (color_scheme == WebColorScheme::kDark) - return Color::kWhite; - return ComputedStyleInitialValues::InitialColor(); -} - LengthBox LayoutTheme::ControlPadding(ControlPart part, const FontDescription&, const Length& zoomed_box_top, @@ -1053,4 +1056,12 @@ void LayoutTheme::AdjustControlPartStyle(ComputedStyle& style) { } } +bool LayoutTheme::HasCustomFocusRingColor() const { + return has_custom_focus_ring_color_; +} + +Color LayoutTheme::GetCustomFocusRingColor() const { + return custom_focus_ring_color_; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme.h b/chromium/third_party/blink/renderer/core/layout/layout_theme.h index 0c92b958f8f..9ffb65caaab 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme.h @@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/platform/fonts/font_description.h" #include "third_party/blink/renderer/platform/fonts/font_selection_types.h" +#include "third_party/blink/renderer/platform/geometry/int_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/geometry/length_box.h" #include "third_party/blink/renderer/platform/geometry/length_size.h" @@ -42,12 +43,11 @@ namespace blink { class ComputedStyle; class Element; -class FileList; -class Font; +class File; class FontDescription; class HTMLInputElement; +class IntRect; class LengthSize; -class Locale; class LocalFrame; class Node; class ThemePainter; @@ -170,22 +170,18 @@ class CORE_EXPORT LayoutTheme : public RefCounted<LayoutTheme> { WebColorScheme color_scheme) const; virtual bool IsFocusRingOutset() const; - Color FocusRingColor() const; + virtual Color FocusRingColor() const; virtual Color PlatformFocusRingColor() const { return Color(0, 0, 0); } void SetCustomFocusRingColor(const Color&); static Color TapHighlightColor(); - // Root element text color. It can be different from the initial color in - // other color schemes than the light theme. - Color RootElementColor(WebColorScheme) const; - virtual Color PlatformTapHighlightColor() const { return LayoutTheme::kDefaultTapHighlightColor; } virtual Color PlatformDefaultCompositionBackgroundColor() const { return kDefaultCompositionBackgroundColor; } - virtual void PlatformColorsDidChange(); + void PlatformColorsDidChange(); virtual void ColorSchemeDidChange(); void SetCaretBlinkInterval(base::TimeDelta); @@ -236,16 +232,16 @@ class CORE_EXPORT LayoutTheme : public RefCounted<LayoutTheme> { virtual bool ShouldHaveSpinButton(HTMLInputElement*) const; // Functions for <select> elements. - virtual bool DelegatesMenuListRendering() const { return false; } + virtual bool DelegatesMenuListRendering() const; + // This function has no effect for LayoutThemeAndroid, of which + // DelegatesMenuListRendering() always returns true. + void SetDelegatesMenuListRenderingForTesting(bool flag); virtual bool PopsMenuByArrowKeys() const { return false; } virtual bool PopsMenuBySpaceKey() const { return false; } virtual bool PopsMenuByReturnKey() const { return false; } virtual bool PopsMenuByAltDownUpOrF4Key() const { return false; } - virtual String FileListNameForWidth(Locale&, - const FileList*, - const Font&, - int width) const; + virtual String DisplayNameForFile(const File& file) const; virtual bool ShouldOpenPickerWithF4Key() const; @@ -356,6 +352,10 @@ class CORE_EXPORT LayoutTheme : public RefCounted<LayoutTheme> { static bool IsSpinUpButtonPartHovered(const Node*); static bool IsReadOnlyControl(const Node*); + protected: + bool HasCustomFocusRingColor() const; + Color GetCustomFocusRingColor() const; + private: // This function is to be implemented in your platform-specific theme // implementation to hand back the appropriate platform theme. @@ -372,6 +372,8 @@ class CORE_EXPORT LayoutTheme : public RefCounted<LayoutTheme> { base::TimeDelta caret_blink_interval_ = base::TimeDelta::FromMilliseconds(500); + bool delegates_menu_list_rendering_ = false; + // This color is expected to be drawn on a semi-transparent overlay, // making it more transparent than its alpha value indicates. static const RGBA32 kDefaultTapHighlightColor = 0x66000000; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc index 8c38b306a47..806f976d723 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.cc @@ -36,6 +36,7 @@ #include "third_party/blink/renderer/platform/data_resource_helper.h" #include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "ui/base/ui_base_features.h" namespace blink { @@ -45,10 +46,16 @@ static const float kDefaultCancelButtonSize = 9; static const float kMinCancelButtonSize = 5; static const float kMaxCancelButtonSize = 21; -LayoutThemeDefault::LayoutThemeDefault() - : LayoutTheme(), - caret_blink_interval_(LayoutTheme::CaretBlinkInterval()), - painter_(*this) {} +base::TimeDelta LayoutThemeDefault::caret_blink_interval_; + +Color LayoutThemeDefault::active_selection_background_color_ = 0xff1e90ff; +Color LayoutThemeDefault::active_selection_foreground_color_ = Color::kBlack; +Color LayoutThemeDefault::inactive_selection_background_color_ = 0xffc8c8c8; +Color LayoutThemeDefault::inactive_selection_foreground_color_ = 0xff323232; + +LayoutThemeDefault::LayoutThemeDefault() : LayoutTheme(), painter_(*this) { + caret_blink_interval_ = LayoutTheme::CaretBlinkInterval(); +} LayoutThemeDefault::~LayoutThemeDefault() = default; @@ -94,7 +101,7 @@ String LayoutThemeDefault::ExtraDefaultStyleSheet() { String windows_style_sheet = UncompressResourceAsASCIIString(IDR_UASTYLE_THEME_WIN_CSS); String controls_refresh_style_sheet = - RuntimeEnabledFeatures::FormControlsRefreshEnabled() + features::IsFormControlsRefreshEnabled() ? UncompressResourceAsASCIIString( IDR_UASTYLE_THEME_CONTROLS_REFRESH_CSS) : String(); @@ -154,14 +161,14 @@ Color LayoutThemeDefault::PlatformInactiveSelectionForegroundColor( } IntSize LayoutThemeDefault::SliderTickSize() const { - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) + if (features::IsFormControlsRefreshEnabled()) return IntSize(1, 4); else return IntSize(1, 6); } int LayoutThemeDefault::SliderTickOffsetFromTrackCenter() const { - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) + if (features::IsFormControlsRefreshEnabled()) return 7; else return -16; @@ -294,7 +301,8 @@ void LayoutThemeDefault::AdjustSearchFieldCancelButtonStyle( } void LayoutThemeDefault::AdjustMenuListStyle(ComputedStyle& style, - Element*) const { + Element* element) const { + LayoutTheme::AdjustMenuListStyle(style, element); // Height is locked to auto on all browsers. style.SetLineHeight(ComputedStyleInitialValues::InitialLineHeight()); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h index 397383f420e..50588077553 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_default.h @@ -150,12 +150,13 @@ class CORE_EXPORT LayoutThemeDefault : public LayoutTheme { int MenuListInternalPadding(const ComputedStyle&, int padding) const; static const RGBA32 kDefaultTapHighlightColor = 0x2e000000; // 18% black. - base::TimeDelta caret_blink_interval_; - Color active_selection_background_color_ = 0xff1e90ff; - Color active_selection_foreground_color_ = Color::kBlack; - Color inactive_selection_background_color_ = 0xffc8c8c8; - Color inactive_selection_foreground_color_ = 0xff323232; + static base::TimeDelta caret_blink_interval_; + + static Color active_selection_background_color_; + static Color active_selection_foreground_color_; + static Color inactive_selection_background_color_; + static Color inactive_selection_foreground_color_; ThemePainterDefault painter_; // Cached values for crbug.com/673754. diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h index ae22fe65b67..43219d5853f 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.h @@ -29,7 +29,6 @@ #include "base/mac/scoped_nsobject.h" #import "third_party/blink/renderer/core/layout/layout_theme.h" #import "third_party/blink/renderer/core/paint/theme_painter_mac.h" -#import "third_party/blink/renderer/platform/wtf/hash_map.h" @class BlinkLayoutThemeNotificationObserver; @@ -67,8 +66,6 @@ class LayoutThemeMac final : public LayoutTheme { return part == kListboxPart ? kSmallScrollbar : kRegularScrollbar; } - void PlatformColorsDidChange() override; - // System fonts. void SystemFont(CSSValueID system_font_id, FontSelectionValue& font_slope, @@ -276,10 +273,7 @@ class LayoutThemeMac final : public LayoutTheme { private: const int* ProgressBarHeights() const; const int* ProgressBarMargins(NSControlSize) const; - String FileListNameForWidth(Locale&, - const FileList*, - const Font&, - int width) const override; + String DisplayNameForFile(const File& file) const override; String ExtraDefaultStyleSheet() override; bool ThemeDrawsFocusRing(const ComputedStyle&) const override; @@ -289,8 +283,6 @@ class LayoutThemeMac final : public LayoutTheme { mutable base::scoped_nsobject<NSSearchFieldCell> search_; mutable base::scoped_nsobject<NSTextFieldCell> text_field_; - mutable HashMap<CSSValueID, RGBA32> system_color_cache_; - base::scoped_nsobject<BlinkLayoutThemeNotificationObserver> notification_observer_; diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm index 09eb072a39f..f9912f0c745 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_mac.mm @@ -31,21 +31,21 @@ #import "third_party/blink/public/resources/grit/blink_resources.h" #import "third_party/blink/public/strings/grit/blink_strings.h" #import "third_party/blink/renderer/core/css_value_keywords.h" -#import "third_party/blink/renderer/core/fileapi/file_list.h" +#import "third_party/blink/renderer/core/fileapi/file.h" #import "third_party/blink/renderer/core/html_names.h" #import "third_party/blink/renderer/core/layout/layout_progress.h" #import "third_party/blink/renderer/core/layout/layout_theme_default.h" #import "third_party/blink/renderer/core/layout/layout_view.h" #import "third_party/blink/renderer/core/style/shadow_list.h" #import "third_party/blink/renderer/platform/data_resource_helper.h" -#import "third_party/blink/renderer/platform/fonts/string_truncator.h" #import "third_party/blink/renderer/platform/graphics/bitmap_image.h" #import "third_party/blink/renderer/platform/mac/block_exceptions.h" #import "third_party/blink/renderer/platform/mac/color_mac.h" #import "third_party/blink/renderer/platform/mac/web_core_ns_cell_extras.h" #import "third_party/blink/renderer/platform/runtime_enabled_features.h" -#import "third_party/blink/renderer/platform/text/platform_locale.h" #import "third_party/blink/renderer/platform/web_test_support.h" +#include "ui/base/ui_base_features.h" +#include "ui/native_theme/native_theme.h" // This is a view whose sole purpose is to tell AppKit that it's flipped. @interface BlinkFlippedControl : NSControl @@ -146,6 +146,27 @@ class LayoutThemeMacRefresh final : public LayoutThemeDefault { static scoped_refptr<LayoutTheme> Create() { return base::AdoptRef(new LayoutThemeMacRefresh()); } + + Color PlatformActiveSelectionBackgroundColor( + WebColorScheme color_scheme) const override; + Color PlatformInactiveSelectionBackgroundColor( + WebColorScheme color_scheme) const override; + Color PlatformActiveSelectionForegroundColor( + WebColorScheme color_scheme) const override; + Color PlatformSpellingMarkerUnderlineColor() const override; + Color PlatformGrammarMarkerUnderlineColor() const override; + Color FocusRingColor() const override; + String DisplayNameForFile(const File& file) const override { + if (file.GetUserVisibility() == File::kIsUserVisible) + return [[NSFileManager defaultManager] displayNameAtPath:file.GetPath()]; + return file.name(); + } + bool PopsMenuByArrowKeys() const override; + + protected: + // Controls color values returned from FocusRingColor(). + bool UsesTestModeFocusRingColor() const; + bool IsAccentColorCustomized(WebColorScheme color_scheme) const; }; // Inflate an IntRect to account for specific padding around margins. @@ -162,13 +183,13 @@ bool FontSizeMatchesToControlSize(const ComputedStyle& style) { return false; } -Color GetSystemColor(MacSystemColorID color_id) { +Color GetSystemColor(MacSystemColorID color_id, WebColorScheme color_scheme) { // In tests, a WebSandboxSupport may not be set up. Just return a dummy // color, in this case, black. auto* sandbox_support = Platform::Current()->GetSandboxSupport(); if (!sandbox_support) return Color(); - return sandbox_support->GetSystemColor(color_id); + return sandbox_support->GetSystemColor(color_id, color_scheme); } // Helper functions used by a bunch of different control parts. @@ -225,12 +246,14 @@ LayoutThemeMac::~LayoutThemeMac() { Color LayoutThemeMac::PlatformActiveSelectionBackgroundColor( WebColorScheme color_scheme) const { - return GetSystemColor(MacSystemColorID::kSelectedTextBackground); + return GetSystemColor(MacSystemColorID::kSelectedTextBackground, + color_scheme); } Color LayoutThemeMac::PlatformInactiveSelectionBackgroundColor( WebColorScheme color_scheme) const { - return GetSystemColor(MacSystemColorID::kSecondarySelectedControl); + return GetSystemColor(MacSystemColorID::kSecondarySelectedControl, + color_scheme); } Color LayoutThemeMac::PlatformActiveSelectionForegroundColor( @@ -240,7 +263,8 @@ Color LayoutThemeMac::PlatformActiveSelectionForegroundColor( Color LayoutThemeMac::PlatformActiveListBoxSelectionBackgroundColor( WebColorScheme color_scheme) const { - return GetSystemColor(MacSystemColorID::kAlternateSelectedControl); + return GetSystemColor(MacSystemColorID::kAlternateSelectedControl, + color_scheme); } Color LayoutThemeMac::PlatformActiveListBoxSelectionForegroundColor( @@ -276,6 +300,88 @@ Color LayoutThemeMac::PlatformInactiveListBoxSelectionBackgroundColor( return PlatformInactiveSelectionBackgroundColor(color_scheme); } +Color LayoutThemeMacRefresh::PlatformActiveSelectionBackgroundColor( + WebColorScheme color_scheme) const { + return GetSystemColor(MacSystemColorID::kSelectedTextBackground, + color_scheme); +} + +Color LayoutThemeMacRefresh::PlatformInactiveSelectionBackgroundColor( + WebColorScheme color_scheme) const { + return GetSystemColor(MacSystemColorID::kSecondarySelectedControl, + color_scheme); +} + +Color LayoutThemeMacRefresh::PlatformActiveSelectionForegroundColor( + WebColorScheme color_scheme) const { + return Color::kBlack; +} + +Color LayoutThemeMacRefresh::PlatformSpellingMarkerUnderlineColor() const { + return Color(251, 45, 29); +} + +Color LayoutThemeMacRefresh::PlatformGrammarMarkerUnderlineColor() const { + return Color(107, 107, 107); +} + +bool LayoutThemeMacRefresh::IsAccentColorCustomized( + WebColorScheme color_scheme) const { + if (@available(macOS 10.14, *)) { + static const Color kControlBlueAccentColor = + GetSystemColor(MacSystemColorID::kControlAccentBlueColor, color_scheme); + if (kControlBlueAccentColor == + GetSystemColor(MacSystemColorID::kControlAccentColor, color_scheme)) { + return false; + } + } else { + if (NSBlueControlTint == [[NSUserDefaults standardUserDefaults] + integerForKey:@"AppleAquaColorVariant"]) { + return false; + } + } + return true; +} + +Color LayoutThemeMacRefresh::FocusRingColor() const { + static const RGBA32 kDefaultFocusRingColor = 0xFF101010; + if (UsesTestModeFocusRingColor()) { + return HasCustomFocusRingColor() ? GetCustomFocusRingColor() + : kDefaultFocusRingColor; + } + + if (ui::NativeTheme::GetInstanceForWeb()->UsesHighContrastColors()) { + // When high contrast is enabled, #101010 should be used. + return Color(0xFF101010); + } + + // TODO(crbug.com/929098) Need to pass an appropriate color scheme here. + WebColorScheme color_scheme = ComputedStyle::InitialStyle().UsedColorScheme(); + + Color keyboard_focus_indicator = + GetSystemColor(MacSystemColorID::kKeyboardFocusIndicator, color_scheme); + // Take the RGB values from the keyboard_focus_indicator color, but use a + // different alpha value to avoid having a color too light. + Color focus_ring = + Color(keyboard_focus_indicator.Red(), keyboard_focus_indicator.Green(), + keyboard_focus_indicator.Blue(), /*alpha=*/166); + if (!HasCustomFocusRingColor()) + return focus_ring; + // Use the custom focus ring color when the system accent color wasn't + // changed. + if (!IsAccentColorCustomized(color_scheme)) + return GetCustomFocusRingColor(); + return focus_ring; +} + +bool LayoutThemeMacRefresh::UsesTestModeFocusRingColor() const { + return WebTestSupport::IsRunningWebTest(); +} + +bool LayoutThemeMacRefresh::PopsMenuByArrowKeys() const { + return true; +} + static FontSelectionValue ToFontWeight(NSInteger app_kit_font_weight) { DCHECK_GT(app_kit_font_weight, 0); DCHECK_LT(app_kit_font_weight, 15); @@ -334,138 +440,96 @@ void LayoutThemeMac::SystemFont(CSSValueID system_font_id, font_family = font_family_names::kSystemUi; } -void LayoutThemeMac::PlatformColorsDidChange() { - system_color_cache_.clear(); - LayoutTheme::PlatformColorsDidChange(); -} - Color LayoutThemeMac::SystemColor(CSSValueID css_value_id, WebColorScheme color_scheme) const { - { - HashMap<CSSValueID, RGBA32>::iterator it = - system_color_cache_.find(css_value_id); - if (it != system_color_cache_.end()) - return it->value; - } + if (!Platform::Current()->GetSandboxSupport()) + return LayoutTheme::SystemColor(css_value_id, color_scheme); - Color color; - bool needs_fallback = false; switch (css_value_id) { case CSSValueID::kActiveborder: - color = GetSystemColor(MacSystemColorID::kKeyboardFocusIndicator); - break; + return GetSystemColor(MacSystemColorID::kKeyboardFocusIndicator, + color_scheme); case CSSValueID::kActivecaption: - color = GetSystemColor(MacSystemColorID::kWindowFrameText); - break; + return GetSystemColor(MacSystemColorID::kWindowFrameText, color_scheme); case CSSValueID::kAppworkspace: - color = GetSystemColor(MacSystemColorID::kHeader); - break; + return GetSystemColor(MacSystemColorID::kHeader, color_scheme); case CSSValueID::kBackground: - // Use theme independent default - needs_fallback = true; + // Use theme independent default. break; case CSSValueID::kButtonface: - color = GetSystemColor(MacSystemColorID::kControlBackground); - break; + return GetSystemColor(MacSystemColorID::kControlBackground, color_scheme); case CSSValueID::kButtonhighlight: - color = GetSystemColor(MacSystemColorID::kControlHighlight); - break; + return GetSystemColor(MacSystemColorID::kControlHighlight, color_scheme); case CSSValueID::kButtonshadow: - color = GetSystemColor(MacSystemColorID::kControlShadow); - break; + return GetSystemColor(MacSystemColorID::kControlShadow, color_scheme); case CSSValueID::kButtontext: - color = GetSystemColor(MacSystemColorID::kControlText); - break; + return GetSystemColor(MacSystemColorID::kControlText, color_scheme); case CSSValueID::kCaptiontext: - color = GetSystemColor(MacSystemColorID::kText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); case CSSValueID::kField: - color = GetSystemColor(MacSystemColorID::kControlBackground); - break; + return GetSystemColor(MacSystemColorID::kControlBackground, color_scheme); case CSSValueID::kFieldtext: - color = GetSystemColor(MacSystemColorID::kText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); case CSSValueID::kGraytext: - color = GetSystemColor(MacSystemColorID::kDisabledControlText); - break; + return GetSystemColor(MacSystemColorID::kDisabledControlText, + color_scheme); case CSSValueID::kHighlight: - color = GetSystemColor(MacSystemColorID::kSelectedTextBackground); - break; + return GetSystemColor(MacSystemColorID::kSelectedTextBackground, + color_scheme); case CSSValueID::kHighlighttext: - color = GetSystemColor(MacSystemColorID::kSelectedText); - break; + return GetSystemColor(MacSystemColorID::kSelectedText, color_scheme); case CSSValueID::kInactiveborder: - color = GetSystemColor(MacSystemColorID::kControlBackground); - break; + return GetSystemColor(MacSystemColorID::kControlBackground, color_scheme); case CSSValueID::kInactivecaption: - color = GetSystemColor(MacSystemColorID::kControlBackground); - break; + return GetSystemColor(MacSystemColorID::kControlBackground, color_scheme); case CSSValueID::kInactivecaptiontext: - color = GetSystemColor(MacSystemColorID::kText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); case CSSValueID::kInfobackground: // There is no corresponding NSColor for this so we use a hard coded // value. - color = 0xFFFBFCC5; - break; + return 0xFFFBFCC5; case CSSValueID::kInfotext: - color = GetSystemColor(MacSystemColorID::kText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); case CSSValueID::kMenu: - color = GetSystemColor(MacSystemColorID::kMenuBackground); - break; + return GetSystemColor(MacSystemColorID::kMenuBackground, color_scheme); case CSSValueID::kMenutext: - color = GetSystemColor(MacSystemColorID::kSelectedMenuItemText); - break; + return GetSystemColor(MacSystemColorID::kSelectedMenuItemText, + color_scheme); case CSSValueID::kScrollbar: - color = GetSystemColor(MacSystemColorID::kScrollBar); - break; + return GetSystemColor(MacSystemColorID::kScrollBar, color_scheme); case CSSValueID::kText: - color = GetSystemColor(MacSystemColorID::kText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); case CSSValueID::kThreeddarkshadow: - color = GetSystemColor(MacSystemColorID::kControlDarkShadow); - break; + return GetSystemColor(MacSystemColorID::kControlDarkShadow, color_scheme); case CSSValueID::kThreedshadow: - color = GetSystemColor(MacSystemColorID::kShadow); - break; + return GetSystemColor(MacSystemColorID::kShadow, color_scheme); case CSSValueID::kThreedface: // We use this value instead of NSColor's controlColor to avoid website // incompatibilities. We may want to change this to use the NSColor in // future. - color = 0xFFC0C0C0; - break; + return 0xFFC0C0C0; case CSSValueID::kThreedhighlight: - color = GetSystemColor(MacSystemColorID::kHighlight); - break; + return GetSystemColor(MacSystemColorID::kHighlight, color_scheme); case CSSValueID::kThreedlightshadow: - color = GetSystemColor(MacSystemColorID::kControlLightHighlight); - break; + return GetSystemColor(MacSystemColorID::kControlLightHighlight, + color_scheme); case CSSValueID::kWebkitFocusRingColor: - color = GetSystemColor(MacSystemColorID::kKeyboardFocusIndicator); - break; + return GetSystemColor(MacSystemColorID::kKeyboardFocusIndicator, + color_scheme); case CSSValueID::kWindow: case CSSValueID::kCanvas: - color = GetSystemColor(MacSystemColorID::kWindowBackground); - break; + return GetSystemColor(MacSystemColorID::kWindowBackground, color_scheme); case CSSValueID::kWindowframe: - color = GetSystemColor(MacSystemColorID::kWindowFrame); - break; + return GetSystemColor(MacSystemColorID::kWindowFrame, color_scheme); case CSSValueID::kWindowtext: + return GetSystemColor(MacSystemColorID::kWindowFrameText, color_scheme); case CSSValueID::kCanvastext: - color = GetSystemColor(MacSystemColorID::kWindowFrameText); - break; + return GetSystemColor(MacSystemColorID::kText, color_scheme); default: - needs_fallback = true; break; } - if (needs_fallback) - color = LayoutTheme::SystemColor(css_value_id, color_scheme); - - system_color_cache_.Set(css_value_id, color.Rgb()); - - return color; + return LayoutTheme::SystemColor(css_value_id, color_scheme); } bool LayoutThemeMac::IsControlStyled(ControlPart part, @@ -656,10 +720,7 @@ void LayoutThemeMac::SetFontFromControlSize(ComputedStyle& style, // Reset line height. style.SetLineHeight(ComputedStyleInitialValues::InitialLineHeight()); - // TODO(esprehn): The fontSelector manual management is buggy and error prone. - FontSelector* font_selector = style.GetFont().GetFontSelector(); - if (style.SetFontDescription(font_description)) - style.GetFont().Update(font_selector); + style.SetFontDescription(font_description); } NSControlSize LayoutThemeMac::ControlSizeForSystemFont( @@ -715,6 +776,7 @@ static const IntSize* MenuListButtonSizes() { void LayoutThemeMac::AdjustMenuListStyle(ComputedStyle& style, Element* e) const { + LayoutTheme::AdjustMenuListStyle(style, e); NSControlSize control_size = ControlSizeForFont(style); style.ResetBorder(); @@ -1003,32 +1065,10 @@ NSTextFieldCell* LayoutThemeMac::TextField() const { return text_field_; } -String LayoutThemeMac::FileListNameForWidth(Locale& locale, - const FileList* file_list, - const Font& font, - int width) const { - if (width <= 0) - return String(); - - String str_to_truncate; - if (file_list->IsEmpty()) { - str_to_truncate = locale.QueryString(IDS_FORM_FILE_NO_FILE_LABEL); - } else if (file_list->length() == 1) { - File* file = file_list->item(0); - if (file->GetUserVisibility() == File::kIsUserVisible) - str_to_truncate = [[NSFileManager defaultManager] - displayNameAtPath:(file_list->item(0)->GetPath())]; - else - str_to_truncate = file->name(); - } else { - return StringTruncator::RightTruncate( - locale.QueryString(IDS_FORM_FILE_MULTIPLE_UPLOAD, - locale.ConvertToLocalizedNumber( - String::Number(file_list->length()))), - width, font); - } - - return StringTruncator::CenterTruncate(str_to_truncate, width, font); +String LayoutThemeMac::DisplayNameForFile(const File& file) const { + if (file.GetUserVisibility() == File::kIsUserVisible) + return [[NSFileManager defaultManager] displayNameAtPath:file.GetPath()]; + return file.name(); } NSView* FlippedView() { @@ -1037,7 +1077,7 @@ NSView* FlippedView() { } LayoutTheme& LayoutTheme::NativeTheme() { - if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) { + if (features::IsFormControlsRefreshEnabled()) { DEFINE_STATIC_REF(LayoutTheme, layout_theme, (LayoutThemeMacRefresh::Create())); return *layout_theme; @@ -1052,7 +1092,8 @@ scoped_refptr<LayoutTheme> LayoutThemeMac::Create() { } bool LayoutThemeMac::UsesTestModeFocusRingColor() const { - return WebTestSupport::IsRunningWebTest(); + return WebTestSupport::IsRunningWebTest() || + !Platform::Current()->GetSandboxSupport(); } NSView* LayoutThemeMac::DocumentView() const { @@ -1475,8 +1516,7 @@ void LayoutThemeMac::AdjustControlPartStyle(ComputedStyle& style) { style.SetLineHeight(ComputedStyleInitialValues::InitialLineHeight()); // Now update our font. - if (style.SetFontDescription(control_font)) - style.GetFont().Update(nullptr); + style.SetFontDescription(control_font); } break; } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_theme_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_theme_test.cc index ef113db19ff..7461a3457ca 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_theme_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_theme_test.cc @@ -24,15 +24,17 @@ namespace blink { class LayoutThemeTest : public PageTestBase, - private ScopedCSSColorSchemeForTest { + private ScopedCSSColorSchemeForTest, + private ScopedCSSColorSchemeUARenderingForTest { protected: - LayoutThemeTest() : ScopedCSSColorSchemeForTest(true) {} + LayoutThemeTest() + : ScopedCSSColorSchemeForTest(true), + ScopedCSSColorSchemeUARenderingForTest(true) {} void SetHtmlInnerHTML(const char* html_content); }; void LayoutThemeTest::SetHtmlInnerHTML(const char* html_content) { - GetDocument().documentElement()->SetInnerHTMLFromString( - String::FromUTF8(html_content)); + GetDocument().documentElement()->setInnerHTML(String::FromUTF8(html_content)); UpdateAllLifecyclePhasesForTest(); } @@ -78,57 +80,8 @@ TEST_F(LayoutThemeTest, ChangeFocusRingColor) { EXPECT_EQ(custom_color, OutlineColor(span)); } -TEST_F(LayoutThemeTest, RootElementColor) { - EXPECT_EQ(Color::kBlack, - LayoutTheme::GetTheme().RootElementColor(WebColorScheme::kLight)); - EXPECT_EQ(Color::kWhite, - LayoutTheme::GetTheme().RootElementColor(WebColorScheme::kDark)); -} - -TEST_F(LayoutThemeTest, RootElementColorChange) { - SetHtmlInnerHTML(R"HTML( - <style> - :root { color-scheme: light dark } - #initial { color: initial } - </style> - <div id="initial"></div> - )HTML"); - - Element* initial = GetDocument().getElementById("initial"); - ASSERT_TRUE(initial); - ASSERT_TRUE(GetDocument().documentElement()); - const ComputedStyle* document_element_style = - GetDocument().documentElement()->GetComputedStyle(); - ASSERT_TRUE(document_element_style); - EXPECT_EQ(Color::kBlack, document_element_style->VisitedDependentColor( - GetCSSPropertyColor())); - - const ComputedStyle* initial_style = initial->GetComputedStyle(); - ASSERT_TRUE(initial_style); - EXPECT_EQ(Color::kBlack, - initial_style->VisitedDependentColor(GetCSSPropertyColor())); - - // Change color scheme to dark. - ColorSchemeHelper color_scheme_helper; - color_scheme_helper.SetPreferredColorScheme(GetDocument(), - PreferredColorScheme::kDark); - UpdateAllLifecyclePhasesForTest(); - - document_element_style = GetDocument().documentElement()->GetComputedStyle(); - ASSERT_TRUE(document_element_style); - EXPECT_EQ(Color::kWhite, document_element_style->VisitedDependentColor( - GetCSSPropertyColor())); - - initial_style = initial->GetComputedStyle(); - ASSERT_TRUE(initial_style); - // Theming does not change the initial value for color, only the UA style for - // the root element. - EXPECT_EQ(Color::kBlack, - initial_style->VisitedDependentColor(GetCSSPropertyColor())); -} - -// The expectations are based on LayoutThemeDefault::SystemColor. -// LayoutThemeMac doesn't use that code path. +// The expectations in the tests below are relying on LayoutThemeDefault. +// LayoutThemeMac doesn't inherit from that class. #if !defined(OS_MACOSX) TEST_F(LayoutThemeTest, SystemColorWithColorScheme) { SetHtmlInnerHTML(R"HTML( @@ -150,9 +103,8 @@ TEST_F(LayoutThemeTest, SystemColorWithColorScheme) { style->VisitedDependentColor(GetCSSPropertyColor())); // Change color scheme to dark. - ColorSchemeHelper color_scheme_helper; - color_scheme_helper.SetPreferredColorScheme(GetDocument(), - PreferredColorScheme::kDark); + ColorSchemeHelper color_scheme_helper(GetDocument()); + color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark); UpdateAllLifecyclePhasesForTest(); style = dark_element->GetComputedStyle(); @@ -160,6 +112,32 @@ TEST_F(LayoutThemeTest, SystemColorWithColorScheme) { EXPECT_EQ(Color(0x44, 0x44, 0x44), style->VisitedDependentColor(GetCSSPropertyColor())); } + +TEST_F(LayoutThemeTest, SetSelectionColors) { + LayoutTheme::GetTheme().SetSelectionColors(Color::kBlack, Color::kBlack, + Color::kBlack, Color::kBlack); + EXPECT_EQ(Color::kBlack, + LayoutTheme::GetTheme().ActiveSelectionForegroundColor( + WebColorScheme::kLight)); + { + // Enabling MobileLayoutTheme switches which instance is returned from + // LayoutTheme::GetTheme(). Devtools expect SetSelectionColors() to affect + // both LayoutTheme instances. + ScopedMobileLayoutThemeForTest scope(true); + EXPECT_EQ(Color::kBlack, + LayoutTheme::GetTheme().ActiveSelectionForegroundColor( + WebColorScheme::kLight)); + + LayoutTheme::GetTheme().SetSelectionColors(Color::kWhite, Color::kWhite, + Color::kWhite, Color::kWhite); + EXPECT_EQ(Color::kWhite, + LayoutTheme::GetTheme().ActiveSelectionForegroundColor( + WebColorScheme::kLight)); + } + EXPECT_EQ(Color::kWhite, + LayoutTheme::GetTheme().ActiveSelectionForegroundColor( + WebColorScheme::kLight)); +} #endif // !defined(OS_MACOSX) } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc index 47bdaee8cb0..28cc1cbd7c3 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_tree_as_text.cc @@ -49,7 +49,7 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" +#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h" @@ -487,8 +487,7 @@ static void WriteTextFragment(WTF::TextStream& ts, const NGTextFragment fragment(paint_fragment->Style().GetWritingMode(), *physical_text_fragment); WriteTextFragment(ts, paint_fragment->GetLayoutObject(), - PhysicalRect(paint_fragment->InlineOffsetToContainerBox(), - paint_fragment->Size()), + paint_fragment->RectInContainerBlock(), paint_fragment->Style(), physical_text_fragment->Text(), fragment.InlineSize()); return; @@ -499,8 +498,8 @@ static void WriteTextFragment(WTF::TextStream& ts, item.Type() == NGFragmentItem::kGeneratedText); const LayoutUnit inline_size = item.IsHorizontal() ? item.Size().width : item.Size().height; - WriteTextFragment(ts, item.GetLayoutObject(), item.Rect(), item.Style(), - item.Text(cursor.Items()), inline_size); + WriteTextFragment(ts, item.GetLayoutObject(), item.RectInContainerBlock(), + item.Style(), item.Text(cursor.Items()), inline_size); } static void WritePaintProperties(WTF::TextStream& ts, @@ -610,7 +609,8 @@ void Write(WTF::TextStream& ts, FrameView* frame_view = ToLayoutEmbeddedContent(o).ChildFrameView(); if (auto* local_frame_view = DynamicTo<LocalFrameView>(frame_view)) { if (auto* layout_view = local_frame_view->GetLayoutView()) { - layout_view->GetDocument().UpdateStyleAndLayout(); + layout_view->GetDocument().UpdateStyleAndLayout( + DocumentUpdateReason::kTest); if (auto* layer = layout_view->Layer()) { LayoutTreeAsText::WriteLayers(ts, layer, layer, indent + 1, behavior); } @@ -900,7 +900,8 @@ String ExternalRepresentation(LocalFrame* frame, LayoutAsTextBehavior behavior, const PaintLayer* marked_layer) { if (!(behavior & kLayoutAsTextDontUpdateLayout)) { - bool success = frame->View()->UpdateAllLifecyclePhasesExceptPaint(); + bool success = frame->View()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kTest); DCHECK(success); }; @@ -931,8 +932,9 @@ String ExternalRepresentation(LocalFrame* frame, String ExternalRepresentation(Element* element, LayoutAsTextBehavior behavior) { // Doesn't support printing mode. DCHECK(!(behavior & kLayoutAsTextPrintingMode)); - if (!(behavior & kLayoutAsTextDontUpdateLayout)) - element->GetDocument().UpdateStyleAndLayout(); + if (!(behavior & kLayoutAsTextDontUpdateLayout)) { + element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); + } LayoutObject* layout_object = element->GetLayoutObject(); if (!layout_object || !layout_object->IsBox()) @@ -958,11 +960,14 @@ static void WriteCounterValuesFromChildren(WTF::TextStream& stream, } String CounterValueForElement(Element* element) { - element->GetDocument().UpdateStyleAndLayout(); + element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); WTF::TextStream stream; bool is_first_counter = true; - // The counter layoutObjects should be children of :before or :after - // pseudo-elements. + // The counter LayoutObjects should be children of ::marker, ::before or + // ::after pseudo-elements. + if (LayoutObject* marker = + element->PseudoElementLayoutObject(kPseudoIdMarker)) + WriteCounterValuesFromChildren(stream, marker, is_first_counter); if (LayoutObject* before = element->PseudoElementLayoutObject(kPseudoIdBefore)) WriteCounterValuesFromChildren(stream, before, is_first_counter); @@ -972,14 +977,16 @@ String CounterValueForElement(Element* element) { } String MarkerTextForListItem(Element* element) { - element->GetDocument().UpdateStyleAndLayout(); + element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); LayoutObject* layout_object = element->GetLayoutObject(); if (layout_object) { if (layout_object->IsListItem()) return ToLayoutListItem(layout_object)->MarkerText(); - if (layout_object->IsLayoutNGListItem()) - return ToLayoutNGListItem(layout_object)->MarkerTextWithoutSuffix(); + if (layout_object->IsLayoutNGListItem()) { + if (LayoutObject* marker = ToLayoutNGListItem(layout_object)->Marker()) + return ListMarker::Get(marker)->MarkerTextWithoutSuffix(*marker); + } } return String(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_video.cc b/chromium/third_party/blink/renderer/core/layout/layout_video.cc index 5b910056fbe..5a71ef5b8ce 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_video.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_video.cc @@ -28,7 +28,6 @@ #include "third_party/blink/public/platform/web_size.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h" -#include "third_party/blink/renderer/core/html/media/media_element_parser_helpers.h" #include "third_party/blink/renderer/core/paint/video_painter.h" namespace blink { @@ -62,7 +61,7 @@ void LayoutVideo::UpdateIntrinsicSize(bool is_in_layout) { return; SetIntrinsicSize(size); - SetPreferredLogicalWidthsDirty(); + SetIntrinsicLogicalWidthsDirty(); if (!is_in_layout) { SetNeedsLayoutAndFullPaintInvalidation( layout_invalidation_reason::kSizeChanged); @@ -73,9 +72,10 @@ LayoutSize LayoutVideo::CalculateIntrinsicSize() { HTMLVideoElement* video = VideoElement(); DCHECK(video); - if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() && - !video->GetOverriddenIntrinsicSize().IsEmpty()) - return LayoutSize(video->GetOverriddenIntrinsicSize()); + if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) { + if (video->IsDefaultIntrinsicSize()) + return DefaultSize(); + } // Spec text from 4.8.6 // @@ -91,7 +91,7 @@ LayoutSize LayoutVideo::CalculateIntrinsicSize() { WebMediaPlayer* web_media_player = MediaElement()->GetWebMediaPlayer(); if (web_media_player && video->getReadyState() >= HTMLVideoElement::kHaveMetadata) { - IntSize size = web_media_player->NaturalSize(); + IntSize size(web_media_player->NaturalSize()); if (!size.IsEmpty()) return LayoutSize(size); } @@ -189,7 +189,7 @@ bool LayoutVideo::SupportsAcceleratedRendering() const { } CompositingReasons LayoutVideo::AdditionalCompositingReasons() const { - HTMLMediaElement* element = ToHTMLMediaElement(GetNode()); + auto* element = To<HTMLMediaElement>(GetNode()); if (element->IsFullscreen() && element->UsesOverlayFullscreenVideo()) return CompositingReason::kVideo; @@ -199,13 +199,4 @@ CompositingReasons LayoutVideo::AdditionalCompositingReasons() const { return CompositingReason::kNone; } -void LayoutVideo::UpdateAfterLayout() { - LayoutBox::UpdateAfterLayout(); - // Report violation of unsized-media policy. - if (auto* video_element = DynamicTo<HTMLVideoElement>(GetNode())) { - media_element_parser_helpers::ReportUnsizedMediaViolation( - this, video_element->IsDefaultIntrinsicSize()); - } -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_video.h b/chromium/third_party/blink/renderer/core/layout/layout_video.h index 66d2550ef51..ed6ce6aa006 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_video.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_video.h @@ -27,6 +27,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_VIDEO_H_ #include "third_party/blink/renderer/core/layout/layout_media.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -50,8 +51,6 @@ class LayoutVideo final : public LayoutMedia { void IntrinsicSizeChanged() override; - void UpdateAfterLayout() override; - bool ComputeShouldClipOverflow() const final { return true; } private: @@ -77,6 +76,7 @@ class LayoutVideo final : public LayoutMedia { LayoutUnit estimated_used_width = LayoutUnit()) const override; LayoutUnit MinimumReplacedHeight() const override; + bool CanHaveAdditionalCompositingReasons() const override { return true; } CompositingReasons AdditionalCompositingReasons() const override; void UpdatePlayer(bool is_in_layout); @@ -84,7 +84,10 @@ class LayoutVideo final : public LayoutMedia { LayoutSize cached_image_size_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutVideo, IsVideo()); +template <> +struct DowncastTraits<LayoutVideo> { + static bool AllowFrom(const LayoutObject& object) { return object.IsVideo(); } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_view.cc b/chromium/third_party/blink/renderer/core/layout/layout_view.cc index f1af97319ad..65ecfe44e87 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_view.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_view.cc @@ -24,6 +24,7 @@ #include <inttypes.h> #include "build/build_config.h" +#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_screen_info.h" #include "third_party/blink/renderer/core/dom/document.h" @@ -34,6 +35,7 @@ #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/html/html_iframe_element.h" +#include "third_party/blink/renderer/core/html/plugin_document.h" #include "third_party/blink/renderer/core/input/event_handler.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" @@ -96,22 +98,20 @@ LayoutView::LayoutView(Document* document) : LayoutBlockFlow(document), frame_view_(document->View()), layout_state_(nullptr), - // TODO(pdr): This should be null if CompositeAfterPaintEnabled() is true. - compositor_(std::make_unique<PaintLayerCompositor>(*this)), + compositor_(RuntimeEnabledFeatures::CompositeAfterPaintEnabled() + ? nullptr + : std::make_unique<PaintLayerCompositor>(*this)), layout_quote_head_(nullptr), layout_counter_count_(0), hit_test_count_(0), hit_test_cache_hits_(0), hit_test_cache_(MakeGarbageCollected<HitTestCache>()), - autosize_h_scrollbar_mode_(ScrollbarMode::kAuto), - autosize_v_scrollbar_mode_(ScrollbarMode::kAuto) { + autosize_h_scrollbar_mode_(mojom::blink::ScrollbarMode::kAuto), + autosize_v_scrollbar_mode_(mojom::blink::ScrollbarMode::kAuto) { // init LayoutObject attributes SetInline(false); - min_preferred_logical_width_ = LayoutUnit(); - max_preferred_logical_width_ = LayoutUnit(); - - SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); SetPositionState(EPosition::kAbsolute); // to 0,0 :) @@ -135,7 +135,8 @@ bool LayoutView::HitTest(const HitTestLocation& location, // Note that if an iframe has its render pipeline throttled, it will not // update layout here, and it will also not propagate the hit test into the // iframe's inner document. - if (!GetFrameView()->UpdateAllLifecyclePhasesExceptPaint()) + if (!GetFrameView()->UpdateAllLifecyclePhasesExceptPaint( + DocumentUpdateReason::kHitTest)) return false; HitTestLatencyRecorder hit_test_latency_recorder( result.GetHitTestRequest().AllowsChildFrameContent()); @@ -240,7 +241,8 @@ bool LayoutView::CanHaveChildren() const { // the PluginDocument's <embed> element to have an EmbeddedContentView, which // it acquires during LocalFrameView::UpdatePlugins, which operates on the // <embed> element's layout object (LayoutEmbeddedObject). - if (GetDocument().IsPluginDocument() || GetDocument().IsForExternalHandler()) + if (IsA<PluginDocument>(GetDocument()) || + GetDocument().IsForExternalHandler()) return true; return !owner->IsDisplayNone(); } @@ -308,8 +310,7 @@ void LayoutView::UpdateLayout() { SetPageLogicalHeight(LayoutUnit()); if (PageLogicalHeight() && ShouldUsePrintingLayout()) { - min_preferred_logical_width_ = max_preferred_logical_width_ = - LogicalWidth(); + intrinsic_logical_widths_ = LogicalWidth(); if (!fragmentation_context_) { fragmentation_context_ = std::make_unique<ViewFragmentationContext>(*this); @@ -528,7 +529,8 @@ bool LayoutView::MapToVisualRectInAncestorSpaceInternal( if (!owner) { PhysicalRect rect = PhysicalRect::EnclosingRect( transform_state.LastPlanarQuad().BoundingBox()); - bool retval = GetFrameView()->MapToVisualRectInRemoteRootFrame(rect); + bool retval = GetFrameView()->MapToVisualRectInRemoteRootFrame( + rect, !(visual_rect_flags & kDontApplyMainFrameOverflowClip)); transform_state.SetQuad(FloatQuad(FloatRect(rect))); return retval; } @@ -617,15 +619,17 @@ PhysicalRect LayoutView::OverflowClipRect( return rect; } -void LayoutView::SetAutosizeScrollbarModes(ScrollbarMode h_mode, - ScrollbarMode v_mode) { - DCHECK_EQ(v_mode == ScrollbarMode::kAuto, h_mode == ScrollbarMode::kAuto); +void LayoutView::SetAutosizeScrollbarModes(mojom::blink::ScrollbarMode h_mode, + mojom::blink::ScrollbarMode v_mode) { + DCHECK_EQ(v_mode == mojom::blink::ScrollbarMode::kAuto, + h_mode == mojom::blink::ScrollbarMode::kAuto); autosize_v_scrollbar_mode_ = v_mode; autosize_h_scrollbar_mode_ = h_mode; } -void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, - ScrollbarMode& v_mode) const { +void LayoutView::CalculateScrollbarModes( + mojom::blink::ScrollbarMode& h_mode, + mojom::blink::ScrollbarMode& v_mode) const { #define RETURN_SCROLLBAR_MODE(mode) \ { \ h_mode = v_mode = mode; \ @@ -634,8 +638,8 @@ void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, // FrameViewAutoSizeInfo manually controls the appearance of the main frame's // scrollbars so defer to those if we're in AutoSize mode. - if (AutosizeVerticalScrollbarMode() != ScrollbarMode::kAuto || - AutosizeHorizontalScrollbarMode() != ScrollbarMode::kAuto) { + if (AutosizeVerticalScrollbarMode() != mojom::blink::ScrollbarMode::kAuto || + AutosizeHorizontalScrollbarMode() != mojom::blink::ScrollbarMode::kAuto) { h_mode = AutosizeHorizontalScrollbarMode(); v_mode = AutosizeVerticalScrollbarMode(); return; @@ -643,19 +647,19 @@ void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, LocalFrame* frame = GetFrame(); if (!frame) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); if (FrameOwner* owner = frame->Owner()) { // Setting scrolling="no" on an iframe element disables scrolling. - if (owner->ScrollingMode() == ScrollbarMode::kAlwaysOff) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + if (owner->ScrollbarMode() == mojom::blink::ScrollbarMode::kAlwaysOff) + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); } Document& document = GetDocument(); if (Node* body = document.body()) { // Framesets can't scroll. if (body->GetLayoutObject() && body->GetLayoutObject()->IsFrameSet()) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); } if (document.IsCapturingLayout()) { @@ -663,40 +667,40 @@ void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, // displayed. // TODO(szager): Figure out the right behavior when printing an overflowing // iframe. https://bugs.chromium.org/p/chromium/issues/detail?id=777528 - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); } if (LocalFrameView* frameView = GetFrameView()) { // Scrollbars can be disabled by LocalFrameView::setCanHaveScrollbars. if (!frameView->CanHaveScrollbars()) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); } Element* viewportDefiningElement = document.ViewportDefiningElement(); if (!viewportDefiningElement) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAuto); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto); LayoutObject* viewport = viewportDefiningElement->GetLayoutObject(); if (!viewport) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAuto); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto); const ComputedStyle* style = viewport->Style(); if (!style) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAuto); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto); if (viewport->IsSVGRoot()) { // Don't allow overflow to affect <img> and css backgrounds if (ToLayoutSVGRoot(viewport)->IsEmbeddedThroughSVGImage()) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAuto); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto); // FIXME: evaluate if we can allow overflow for these cases too. // Overflow is always hidden when stand-alone SVG documents are embedded. if (ToLayoutSVGRoot(viewport) ->IsEmbeddedThroughFrameContainingSVGDocument()) - RETURN_SCROLLBAR_MODE(ScrollbarMode::kAlwaysOff); + RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff); } - h_mode = v_mode = ScrollbarMode::kAuto; + h_mode = v_mode = mojom::blink::ScrollbarMode::kAuto; EOverflow overflow_x = style->OverflowX(); EOverflow overflow_y = style->OverflowY(); @@ -709,25 +713,19 @@ void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, } if (!shouldIgnoreOverflowHidden) { if (overflow_x == EOverflow::kHidden) - h_mode = ScrollbarMode::kAlwaysOff; + h_mode = mojom::blink::ScrollbarMode::kAlwaysOff; if (overflow_y == EOverflow::kHidden) - v_mode = ScrollbarMode::kAlwaysOff; + v_mode = mojom::blink::ScrollbarMode::kAlwaysOff; } if (overflow_x == EOverflow::kScroll) - h_mode = ScrollbarMode::kAlwaysOn; + h_mode = mojom::blink::ScrollbarMode::kAlwaysOn; if (overflow_y == EOverflow::kScroll) - v_mode = ScrollbarMode::kAlwaysOn; + v_mode = mojom::blink::ScrollbarMode::kAlwaysOn; #undef RETURN_SCROLLBAR_MODE } -void LayoutView::MayUpdateHoverWhenContentUnderMouseChanged( - EventHandler& event_handler) { - event_handler.MayUpdateHoverWhenContentUnderMouseChanged( - MouseEventManager::UpdateHoverReason::kScrollOffsetChanged); -} - PhysicalRect LayoutView::DocumentRect() const { return FlipForWritingMode(LayoutOverflowRect()); } @@ -813,7 +811,6 @@ bool LayoutView::UsesCompositing() const { } PaintLayerCompositor* LayoutView::Compositor() { - DCHECK(compositor_); return compositor_.get(); } diff --git a/chromium/third_party/blink/renderer/core/layout/layout_view.h b/chromium/third_party/blink/renderer/core/layout/layout_view.h index 38b4e574759..10753eb247a 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_view.h +++ b/chromium/third_party/blink/renderer/core/layout/layout_view.h @@ -23,6 +23,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_VIEW_H_ #include <memory> +#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/hit_test_cache.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" @@ -31,6 +32,7 @@ #include "third_party/blink/renderer/core/scroll/scrollable_area.h" #include "third_party/blink/renderer/platform/graphics/scroll_types.h" #include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -164,18 +166,17 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { kIgnorePlatformOverlayScrollbarSize) const override; // If either direction has a non-auto mode, the other must as well. - void SetAutosizeScrollbarModes(ScrollbarMode h_mode, ScrollbarMode v_mode); - ScrollbarMode AutosizeHorizontalScrollbarMode() const { + void SetAutosizeScrollbarModes(mojom::blink::ScrollbarMode h_mode, + mojom::blink::ScrollbarMode v_mode); + mojom::blink::ScrollbarMode AutosizeHorizontalScrollbarMode() const { return autosize_h_scrollbar_mode_; } - ScrollbarMode AutosizeVerticalScrollbarMode() const { + mojom::blink::ScrollbarMode AutosizeVerticalScrollbarMode() const { return autosize_v_scrollbar_mode_; } - void CalculateScrollbarModes(ScrollbarMode& h_mode, - ScrollbarMode& v_mode) const; - - void MayUpdateHoverWhenContentUnderMouseChanged(EventHandler&) override; + void CalculateScrollbarModes(mojom::blink::ScrollbarMode& h_mode, + mojom::blink::ScrollbarMode& v_mode) const; LayoutState* GetLayoutState() const { return layout_state_; } @@ -277,6 +278,10 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { previous_background_rect_ = r; } + void MapAncestorToLocal(const LayoutBoxModelObject*, + TransformState&, + MapCoordinatesFlags) const override; + private: void MapLocalToAncestor(const LayoutBoxModelObject* ancestor, TransformState&, @@ -285,10 +290,6 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { const LayoutObject* PushMappingToContainer( const LayoutBoxModelObject* ancestor_to_stop_at, LayoutGeometryMap&) const override; - void MapAncestorToLocal(const LayoutBoxModelObject*, - TransformState&, - MapCoordinatesFlags) const override; - bool CanHaveChildren() const override; void UpdateBlockLayout(bool relayout_children) override; @@ -338,15 +339,20 @@ class CORE_EXPORT LayoutView final : public LayoutBlockFlow { // FrameViewAutoSizeInfo controls scrollbar appearance manually rather than // relying on layout. These members are used to override the ScrollbarModes // calculated from style. kScrollbarAuto disables the override. - ScrollbarMode autosize_h_scrollbar_mode_; - ScrollbarMode autosize_v_scrollbar_mode_; + mojom::blink::ScrollbarMode autosize_h_scrollbar_mode_; + mojom::blink::ScrollbarMode autosize_v_scrollbar_mode_; Vector<IntRect> tickmarks_override_; mutable PhysicalRect previous_background_rect_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutView, IsLayoutView()); +template <> +struct DowncastTraits<LayoutView> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsLayoutView(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/layout_view_test.cc b/chromium/third_party/blink/renderer/core/layout/layout_view_test.cc index 1f69fd44cdc..94dbb303112 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_view_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_view_test.cc @@ -57,7 +57,7 @@ TEST_F(LayoutViewTest, DisplayNoneFrame) { EXPECT_FALSE(view->CanHaveChildren()); EXPECT_FALSE(frame_doc->documentElement()->GetComputedStyle()); - frame_doc->body()->SetInnerHTMLFromString(R"HTML( + frame_doc->body()->setInnerHTML(R"HTML( <div id="div"></div> )HTML"); @@ -74,11 +74,11 @@ class LayoutViewHitTestTest : public testing::WithParamInterface<HitTestConfig>, public RenderingTest { public: LayoutViewHitTestTest() - : ScopedLayoutNGForTest(LayoutNG()), + : ScopedLayoutNGForTest(GetParam().layout_ng), RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {} protected: - bool LayoutNG() { return GetParam().layout_ng; } + bool LayoutNG() { return RuntimeEnabledFeatures::LayoutNGEnabled(); } bool IsAndroidOrWindowsEditingBehavior() { // TODO(crbug.com/971414): For now LayoutNG always uses Android/Windows // behavior for ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(). diff --git a/chromium/third_party/blink/renderer/core/layout/layout_vtt_cue.cc b/chromium/third_party/blink/renderer/core/layout/layout_vtt_cue.cc index fa49a3ec625..f882270bd8c 100644 --- a/chromium/third_party/blink/renderer/core/layout/layout_vtt_cue.cc +++ b/chromium/third_party/blink/renderer/core/layout/layout_vtt_cue.cc @@ -329,8 +329,7 @@ IntRect LayoutVTTCue::ComputeControlsRect() const { // the MediaControls. DCHECK(Parent()->GetNode()->IsTextTrackContainer()); - HTMLMediaElement* media_element = - ToHTMLMediaElement(Parent()->Parent()->GetNode()); + auto* media_element = To<HTMLMediaElement>(Parent()->Parent()->GetNode()); DCHECK(media_element); MediaControls* controls = media_element->GetMediaControls(); diff --git a/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc b/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc index 1907ede364d..371d92665ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box.cc @@ -305,6 +305,8 @@ bool LegacyAbstractInlineTextBox::IsLineBreak() const { const NGOffsetMapping* LegacyAbstractInlineTextBox::GetOffsetMapping() const { const auto* text_node = DynamicTo<Text>(GetNode()); + if (!text_node) + return nullptr; LayoutBlockFlow& block_flow = *NGOffsetMapping::GetInlineFormattingContextOf( *text_node->GetLayoutObject()); diff --git a/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc b/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc index d63206fe779..5067af15179 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/line/abstract_inline_text_box_test.cc @@ -20,7 +20,9 @@ class AbstractInlineTextBoxTest : public testing::WithParamInterface<bool>, AbstractInlineTextBoxTest() : ScopedLayoutNGForTest(GetParam()) {} protected: - bool LayoutNGEnabled() const { return GetParam(); } + bool LayoutNGEnabled() const { + return RuntimeEnabledFeatures::LayoutNGEnabled(); + } }; INSTANTIATE_TEST_SUITE_P(All, AbstractInlineTextBoxTest, testing::Bool()); @@ -116,9 +118,10 @@ TEST_P(AbstractInlineTextBoxTest, GetTextWithLineBreakAtTrailingWhiteSpace) { TEST_P(AbstractInlineTextBoxTest, GetTextOffsetInContainer) { // "
" is a Line Feed ("\n"). - SetBodyInnerHTML( - R"HTML(<style>p { white-space: pre-line; }</style> - <p id="paragraph">First sentence of the paragraph. Second sentence of the paragraph.</p>)HTML"); + SetBodyInnerHTML(R"HTML( + <style>p { white-space: pre-line; }</style> + <p id="paragraph">First sentence of the paragraph. Second sentence of the paragraph. </p> + <br id='br'>)HTML"); const Element& paragraph = *GetDocument().getElementById("paragraph"); LayoutText& layout_text = @@ -145,6 +148,13 @@ TEST_P(AbstractInlineTextBoxTest, GetTextOffsetInContainer) { inline_text_box = inline_text_box->NextInlineTextBox()->NextInlineTextBox(); EXPECT_EQ("the paragraph.", inline_text_box->GetText()); EXPECT_EQ(52u, inline_text_box->TextOffsetInContainer(0)); + + // Ensure that calling TextOffsetInContainer on a br gives the correct result. + const Element& br_element = *GetDocument().getElementById("br"); + LayoutText& br_text = *ToLayoutText(br_element.GetLayoutObject()); + inline_text_box = br_text.FirstAbstractInlineTextBox(); + EXPECT_EQ("\n", inline_text_box->GetText()); + EXPECT_EQ(0u, inline_text_box->TextOffsetInContainer(0)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/line/inline_box.cc b/chromium/third_party/blink/renderer/core/layout/line/inline_box.cc index e9fb4e46810..234f99caab9 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/inline_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/line/inline_box.cc @@ -20,6 +20,7 @@ #include "third_party/blink/renderer/core/layout/line/inline_box.h" #include "base/allocator/partition_allocator/partition_alloc.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/layout/api/line_layout_api_shim.h" #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h" #include "third_party/blink/renderer/core/layout/hit_test_location.h" @@ -100,6 +101,12 @@ IntRect InlineBox::PartialInvalidationVisualRect() const { return GetLineLayoutItem().PartialInvalidationVisualRectForInlineBox(); } +DOMNodeId InlineBox::OwnerNodeId() const { + return GetLineLayoutItem().GetNode() + ? DOMNodeIds::IdForNode(GetLineLayoutItem().GetNode()) + : kInvalidDOMNodeId; +} + #if DCHECK_IS_ON() void InlineBox::ShowTreeForThis() const { GetLineLayoutItem().ShowTreeForThis(); diff --git a/chromium/third_party/blink/renderer/core/layout/line/inline_box.h b/chromium/third_party/blink/renderer/core/layout/line/inline_box.h index 2d5dc2026b9..07c06a0bc5c 100644 --- a/chromium/third_party/blink/renderer/core/layout/line/inline_box.h +++ b/chromium/third_party/blink/renderer/core/layout/line/inline_box.h @@ -131,6 +131,7 @@ class CORE_EXPORT InlineBox : public DisplayItemClient { String DebugName() const override; IntRect VisualRect() const override; IntRect PartialInvalidationVisualRect() const override; + DOMNodeId OwnerNodeId() const override; bool IsText() const { return bitfields_.IsText(); } void SetIsText(bool is_text) { bitfields_.SetIsText(is_text); } diff --git a/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc b/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc index ae0b43acf1f..22aefbe8f04 100644 --- a/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/map_coordinates_test.cc @@ -363,7 +363,7 @@ TEST_F(MapCoordinatesTest, FixedPos) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); PhysicalOffset mapped_point = MapLocalToAncestor(target, view, PhysicalOffset()); @@ -423,7 +423,7 @@ TEST_F(MapCoordinatesTest, FixedPosAuto) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); PhysicalOffset mapped_point = MapLocalToAncestor(target, target->ContainingBlock(), PhysicalOffset()); @@ -489,7 +489,7 @@ TEST_F(MapCoordinatesTest, FixedPosInFixedPos) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); PhysicalOffset mapped_point = MapLocalToAncestor(target, view, PhysicalOffset()); @@ -549,10 +549,10 @@ TEST_F(MapCoordinatesTest, FixedPosInFixedPosScrollView) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0.0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); @@ -584,10 +584,10 @@ TEST_F(MapCoordinatesTest, FixedPosInAbsolutePosScrollView) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0.0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); @@ -615,8 +615,8 @@ TEST_F(MapCoordinatesTest, FixedPosInTransform) { <div class='spacer'></div> )HTML"); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0.0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); @@ -626,7 +626,7 @@ TEST_F(MapCoordinatesTest, FixedPosInTransform) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); PhysicalOffset mapped_point = MapLocalToAncestor(target, view, PhysicalOffset()); @@ -655,8 +655,8 @@ TEST_F(MapCoordinatesTest, FixedPosInContainPaint) { <div class='spacer'></div> )HTML"); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0.0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); @@ -666,7 +666,7 @@ TEST_F(MapCoordinatesTest, FixedPosInContainPaint) { LayoutBox* body = container->ParentBox(); LayoutBox* html = body->ParentBox(); LayoutBox* view = html->ParentBox(); - ASSERT_TRUE(view->IsLayoutView()); + ASSERT_TRUE(IsA<LayoutView>(view)); PhysicalOffset mapped_point = MapLocalToAncestor(target, view, PhysicalOffset()); @@ -700,7 +700,7 @@ TEST_F(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) { "position:fixed}</style><div id=target></div>"); GetDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 1000), kProgrammaticScroll); + ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* target = ChildDocument().getElementById("target"); @@ -731,9 +731,8 @@ TEST_F(MapCoordinatesTest, IFrameTransformed) { UpdateAllLifecyclePhasesForTest(); ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 1000), kProgrammaticScroll); - ChildDocument().View()->UpdateAllLifecyclePhases( - DocumentLifecycle::LifecycleUpdateReason::kTest); + ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); + ChildDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); Element* target = ChildDocument().getElementById("target"); ASSERT_TRUE(target); @@ -768,7 +767,7 @@ TEST_F(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) { UpdateAllLifecyclePhasesForTest(); ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0.0, 1000), kProgrammaticScroll); + ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* target = ChildDocument().getElementById("target"); diff --git a/chromium/third_party/blink/renderer/core/layout/min_max_size_test.cc b/chromium/third_party/blink/renderer/core/layout/min_max_size_test.cc index af5cb1e84b0..31171af4b41 100644 --- a/chromium/third_party/blink/renderer/core/layout/min_max_size_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/min_max_size_test.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "testing/gtest/include/gtest/gtest.h" @@ -11,7 +11,7 @@ namespace blink { namespace { TEST(NGUnitsTest, ShrinkToFit) { - MinMaxSize sizes; + MinMaxSizes sizes; sizes.min_size = LayoutUnit(100); sizes.max_size = LayoutUnit(200); diff --git a/chromium/third_party/blink/renderer/core/layout/min_max_size.cc b/chromium/third_party/blink/renderer/core/layout/min_max_sizes.cc index e6980104b3c..15f5bf6aca8 100644 --- a/chromium/third_party/blink/renderer/core/layout/min_max_size.cc +++ b/chromium/third_party/blink/renderer/core/layout/min_max_sizes.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include <algorithm> namespace blink { -std::ostream& operator<<(std::ostream& stream, const MinMaxSize& value) { +std::ostream& operator<<(std::ostream& stream, const MinMaxSizes& value) { return stream << "(" << value.min_size << ", " << value.max_size << ")"; } diff --git a/chromium/third_party/blink/renderer/core/layout/min_max_size.h b/chromium/third_party/blink/renderer/core/layout/min_max_sizes.h index 4233e6c79a1..721ecec1738 100644 --- a/chromium/third_party/blink/renderer/core/layout/min_max_size.h +++ b/chromium/third_party/blink/renderer/core/layout/min_max_sizes.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZE_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZE_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZES_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZES_H_ #include <algorithm> @@ -15,12 +15,12 @@ namespace blink { // A struct that holds a pair of two sizes, a "min" size and a "max" size. // Useful for holding a {min,max}-content size pair or a // {min,max}-{width,height}. -struct CORE_EXPORT MinMaxSize { +struct CORE_EXPORT MinMaxSizes { LayoutUnit min_size; LayoutUnit max_size; // Make sure that our min/max sizes are at least as large as |other|. - void Encompass(const MinMaxSize& other) { + void Encompass(const MinMaxSizes& other) { min_size = std::max(min_size, other.min_size); max_size = std::max(max_size, other.max_size); } @@ -50,30 +50,30 @@ struct CORE_EXPORT MinMaxSize { return std::max(min_size, std::min(size, max_size)); } - bool operator==(const MinMaxSize& other) const { + bool operator==(const MinMaxSizes& other) const { return min_size == other.min_size && max_size == other.max_size; } void operator=(LayoutUnit value) { min_size = max_size = value; } - MinMaxSize& operator+=(MinMaxSize extra) { + MinMaxSizes& operator+=(MinMaxSizes extra) { min_size += extra.min_size; max_size += extra.max_size; return *this; } - MinMaxSize& operator+=(const LayoutUnit length) { + MinMaxSizes& operator+=(const LayoutUnit length) { min_size += length; max_size += length; return *this; } - MinMaxSize& operator-=(const LayoutUnit length) { + MinMaxSizes& operator-=(const LayoutUnit length) { min_size -= length; max_size -= length; return *this; } }; -CORE_EXPORT std::ostream& operator<<(std::ostream&, const MinMaxSize&); +CORE_EXPORT std::ostream& operator<<(std::ostream&, const MinMaxSizes&); } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZE_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_MIN_MAX_SIZES_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc index 3ac1c1fbcb5..b662f819135 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc @@ -13,6 +13,8 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_function.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_intrinsic_sizes_callback.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_intrinsic_sizes_result_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_layout_callback.h" #include "third_party/blink/renderer/bindings/core/v8/v8_no_argument_constructor.h" #include "third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h" @@ -23,20 +25,41 @@ #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_edges.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h" -#include "third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" #include "third_party/blink/renderer/platform/bindings/microtask.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h" #include "third_party/blink/renderer/platform/bindings/v8_object_constructor.h" +#include "third_party/blink/renderer/platform/heap/heap.h" namespace blink { +namespace { + +void GatherChildren(const NGBlockNode& node, + CustomLayoutScope* custom_layout_scope, + HeapVector<Member<CustomLayoutChild>>* children) { + // TODO(ikilpatrick): Determine if knowing the size of the array ahead of + // time improves performance in any noticeable way. + for (NGLayoutInputNode child = node.FirstChild(); child; + child = child.NextSibling()) { + if (child.IsOutOfFlowPositioned()) + continue; + + CustomLayoutChild* layout_child = child.GetCustomLayoutChild(); + layout_child->SetCustomLayoutToken(custom_layout_scope->Token()); + DCHECK(layout_child); + children->push_back(layout_child); + } +} + +} // anonymous namespace + CSSLayoutDefinition::CSSLayoutDefinition( ScriptState* script_state, V8NoArgumentConstructor* constructor, - V8Function* intrinsic_sizes, + V8IntrinsicSizesCallback* intrinsic_sizes, V8LayoutCallback* layout, const Vector<CSSPropertyID>& native_invalidation_properties, const Vector<AtomicString>& custom_invalidation_properties, @@ -44,7 +67,7 @@ CSSLayoutDefinition::CSSLayoutDefinition( const Vector<AtomicString>& child_custom_invalidation_properties) : script_state_(script_state), constructor_(constructor), - unused_intrinsic_sizes_(intrinsic_sizes), + intrinsic_sizes_(intrinsic_sizes), layout_(layout), native_invalidation_properties_(native_invalidation_properties), custom_invalidation_properties_(custom_invalidation_properties), @@ -66,8 +89,9 @@ bool CSSLayoutDefinition::Instance::Layout( const NGBlockNode& node, const LogicalSize& border_box_size, const NGBoxStrut& border_scrollbar_padding, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, CustomLayoutScope* custom_layout_scope, - FragmentResultOptions* fragment_result_options, + FragmentResultOptions*& fragment_result_options, scoped_refptr<SerializedScriptValue>* fragment_result_data) { ScriptState* script_state = definition_->GetScriptState(); v8::Isolate* isolate = script_state->GetIsolate(); @@ -77,19 +101,8 @@ bool CSSLayoutDefinition::Instance::Layout( ScriptState::Scope scope(script_state); - // TODO(ikilpatrick): Determine if knowing the size of the array ahead of - // time improves performance in any noticeable way. HeapVector<Member<CustomLayoutChild>> children; - for (NGLayoutInputNode child = node.FirstChild(); child; - child = child.NextSibling()) { - if (child.IsOutOfFlowPositioned()) - continue; - - CustomLayoutChild* layout_child = child.GetCustomLayoutChild(); - layout_child->SetCustomLayoutToken(custom_layout_scope->Token()); - DCHECK(layout_child); - children.push_back(layout_child); - } + GatherChildren(node, custom_layout_scope, &children); CustomLayoutEdges* edges = MakeGarbageCollected<CustomLayoutEdges>(border_scrollbar_padding); @@ -121,18 +134,20 @@ bool CSSLayoutDefinition::Instance::Layout( v8::Local<v8::Value> v8_return_value = return_value.V8Value(); if (v8_return_value.IsEmpty() || !v8_return_value->IsPromise()) { - execution_context->AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript, - mojom::ConsoleMessageLevel::kInfo, - "The layout function must be async or return a " - "promise, falling back to block layout.")); + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "The layout function must be async or return a " + "promise, falling back to block layout.")); return false; } // Run the work queue until exhaustion. while (!custom_layout_scope->Queue()->IsEmpty()) { - for (auto& task : *custom_layout_scope->Queue()) - task.Run(space, node.Style()); + for (auto& task : *custom_layout_scope->Queue()) { + task.Run(space, node.Style(), + child_percentage_resolution_block_size_for_min_max); + } custom_layout_scope->Queue()->clear(); { v8::MicrotasksScope microtasks_scope(isolate, microtask_queue, @@ -149,27 +164,28 @@ bool CSSLayoutDefinition::Instance::Layout( v8::Local<v8::Promise>::Cast(v8_return_value); if (v8_result_promise->State() != v8::Promise::kFulfilled) { - execution_context->AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript, - mojom::ConsoleMessageLevel::kInfo, - "The layout function promise must resolve, " - "falling back to block layout.")); + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "The layout function promise must resolve, " + "falling back to block layout.")); return false; } v8::Local<v8::Value> inner_value = v8_result_promise->Result(); // Attempt to convert the result. - V8FragmentResultOptions::ToImpl(isolate, inner_value, fragment_result_options, - exception_state); + fragment_result_options = + NativeValueTraits<FragmentResultOptions>::NativeValue( + isolate, inner_value, exception_state); if (exception_state.HadException()) { V8ScriptRunner::ReportException(isolate, exception_state.GetException()); exception_state.ClearException(); - execution_context->AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript, - mojom::ConsoleMessageLevel::kInfo, - "Unable to parse the layout function " - "result, falling back to block layout.")); + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "Unable to parse the layout function " + "result, falling back to block layout.")); return false; } @@ -188,11 +204,114 @@ bool CSSLayoutDefinition::Instance::Layout( if (exception_state.HadException()) { V8ScriptRunner::ReportException(isolate, exception_state.GetException()); exception_state.ClearException(); - execution_context->AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript, - mojom::ConsoleMessageLevel::kInfo, - "Unable to serialize the data provided in the " - "result, falling back to block layout.")); + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "Unable to serialize the data provided in the " + "result, falling back to block layout.")); + return false; + } + + return true; +} + +bool CSSLayoutDefinition::Instance::IntrinsicSizes( + const NGConstraintSpace& space, + const Document& document, + const NGBlockNode& node, + const LogicalSize& border_box_size, + const NGBoxStrut& border_scrollbar_padding, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, + CustomLayoutScope* custom_layout_scope, + IntrinsicSizesResultOptions*& intrinsic_sizes_result_options) { + ScriptState* script_state = definition_->GetScriptState(); + v8::Isolate* isolate = script_state->GetIsolate(); + + if (!script_state->ContextIsValid()) + return false; + + ScriptState::Scope scope(script_state); + + HeapVector<Member<CustomLayoutChild>> children; + GatherChildren(node, custom_layout_scope, &children); + + CustomLayoutEdges* edges = + MakeGarbageCollected<CustomLayoutEdges>(border_scrollbar_padding); + + // TODO(ikilpatrick): Instead of creating a new style_map each time here, + // store on LayoutCustom, and update when the style changes. + StylePropertyMapReadOnly* style_map = + MakeGarbageCollected<PrepopulatedComputedStylePropertyMap>( + document, node.Style(), definition_->native_invalidation_properties_, + definition_->custom_invalidation_properties_); + + ScriptValue return_value; + if (!definition_->intrinsic_sizes_ + ->Invoke(instance_.NewLocal(isolate), children, edges, style_map) + .To(&return_value)) + return false; + + ExecutionContext* execution_context = ExecutionContext::From(script_state); + v8::MicrotaskQueue* microtask_queue = ToMicrotaskQueue(execution_context); + DCHECK(microtask_queue); + + ExceptionState exception_state(isolate, ExceptionState::kExecutionContext, + "CSSLayoutAPI", "IntrinsicSizes"); + + v8::Local<v8::Value> v8_return_value = return_value.V8Value(); + if (v8_return_value.IsEmpty() || !v8_return_value->IsPromise()) { + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "The intrinsicSizes function must be async or return a " + "promise, falling back to block layout.")); + return false; + } + + // Run the work queue until exhaustion. + while (!custom_layout_scope->Queue()->IsEmpty()) { + for (auto& task : *custom_layout_scope->Queue()) { + task.Run(space, node.Style(), + child_percentage_resolution_block_size_for_min_max); + } + custom_layout_scope->Queue()->clear(); + { + v8::MicrotasksScope microtasks_scope(isolate, microtask_queue, + v8::MicrotasksScope::kRunMicrotasks); + } + } + + if (exception_state.HadException()) { + ReportException(&exception_state); + return false; + } + + v8::Local<v8::Promise> v8_result_promise = + v8::Local<v8::Promise>::Cast(v8_return_value); + + if (v8_result_promise->State() != v8::Promise::kFulfilled) { + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "The intrinsicSizes function promise must resolve, " + "falling back to block layout.")); + return false; + } + v8::Local<v8::Value> inner_value = v8_result_promise->Result(); + + // Attempt to convert the result. + intrinsic_sizes_result_options = + NativeValueTraits<IntrinsicSizesResultOptions>::NativeValue( + isolate, inner_value, exception_state); + + if (exception_state.HadException()) { + V8ScriptRunner::ReportException(isolate, exception_state.GetException()); + exception_state.ClearException(); + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kJavaScript, + mojom::ConsoleMessageLevel::kInfo, + "Unable to parse the intrinsicSizes function " + "result, falling back to block layout.")); return false; } @@ -209,7 +328,7 @@ void CSSLayoutDefinition::Instance::ReportException( // again (as the callbacks are invoked directly by the UA). V8ScriptRunner::ReportException(isolate, exception_state->GetException()); exception_state->ClearException(); - execution_context->AddConsoleMessage(ConsoleMessage::Create( + execution_context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( mojom::ConsoleMessageSource::kJavaScript, mojom::ConsoleMessageLevel::kInfo, "The layout function failed, falling back to block layout.")); @@ -234,14 +353,14 @@ CSSLayoutDefinition::Instance* CSSLayoutDefinition::CreateInstance() { return MakeGarbageCollected<Instance>(this, instance.V8Value()); } -void CSSLayoutDefinition::Instance::Trace(blink::Visitor* visitor) { +void CSSLayoutDefinition::Instance::Trace(Visitor* visitor) { visitor->Trace(definition_); visitor->Trace(instance_); } void CSSLayoutDefinition::Trace(Visitor* visitor) { visitor->Trace(constructor_); - visitor->Trace(unused_intrinsic_sizes_); + visitor->Trace(intrinsic_sizes_); visitor->Trace(layout_); visitor->Trace(script_state_); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h index b8690a9e84f..4c1ff77f733 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h @@ -17,13 +17,15 @@ namespace blink { class CustomLayoutScope; class FragmentResultOptions; +class IntrinsicSizesResultOptions; +class LayoutUnit; struct LogicalSize; class NGBlockNode; struct NGBoxStrut; class NGConstraintSpace; class ScriptState; class SerializedScriptValue; -class V8Function; +class V8IntrinsicSizesCallback; class V8LayoutCallback; class V8NoArgumentConstructor; @@ -36,7 +38,7 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>, CSSLayoutDefinition( ScriptState*, V8NoArgumentConstructor* constructor, - V8Function* intrinsic_sizes, + V8IntrinsicSizesCallback* intrinsic_sizes, V8LayoutCallback* layout, const Vector<CSSPropertyID>& native_invalidation_properties, const Vector<AtomicString>& custom_invalidation_properties, @@ -53,16 +55,30 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>, // Runs the web developer defined layout, returns true if everything // succeeded. It populates the FragmentResultOptions dictionary, and // fragment_result_data. - bool Layout(const NGConstraintSpace&, - const Document&, - const NGBlockNode&, - const LogicalSize& border_box_size, - const NGBoxStrut& border_scrollbar_padding, - CustomLayoutScope*, - FragmentResultOptions*, - scoped_refptr<SerializedScriptValue>* fragment_result_data); - - void Trace(blink::Visitor*); + bool Layout( + const NGConstraintSpace&, + const Document&, + const NGBlockNode&, + const LogicalSize& border_box_size, + const NGBoxStrut& border_scrollbar_padding, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, + CustomLayoutScope*, + FragmentResultOptions*&, + scoped_refptr<SerializedScriptValue>* fragment_result_data); + + // Runs the web developer defined intrinsicSizes, returns true if everything + // succeeded. It populates the IntrinsicSizesResultOptions dictionary. + bool IntrinsicSizes( + const NGConstraintSpace&, + const Document&, + const NGBlockNode&, + const LogicalSize& border_box_size, + const NGBoxStrut& border_scrollbar_padding, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, + CustomLayoutScope*, + IntrinsicSizesResultOptions*&); + + void Trace(Visitor*); private: void ReportException(ExceptionState*); @@ -90,7 +106,7 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>, ScriptState* GetScriptState() const { return script_state_; } - virtual void Trace(blink::Visitor* visitor); + virtual void Trace(Visitor* visitor); const char* NameInHeapSnapshot() const override { return "CSSLayoutDefinition"; @@ -103,7 +119,7 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>, // sizes function, and layout function alive. It participates in wrapper // tracing as it holds onto V8 wrappers. Member<V8NoArgumentConstructor> constructor_; - Member<V8Function> unused_intrinsic_sizes_; + Member<V8IntrinsicSizesCallback> intrinsic_sizes_; Member<V8LayoutCallback> layout_; // If a constructor call ever fails, we'll refuse to create any more diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.cc new file mode 100644 index 00000000000..7dd9c992f83 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.cc @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h" + +#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h" + +namespace blink { + +CustomIntrinsicSizes::CustomIntrinsicSizes(CustomLayoutChild* child, + CustomLayoutToken* token, + double min_content_size, + double max_content_size) + : child_(child), + token_(token), + min_content_size_(min_content_size), + max_content_size_(max_content_size) {} + +const NGLayoutInputNode& CustomIntrinsicSizes::GetLayoutNode() const { + return child_->GetLayoutNode(); +} + +void CustomIntrinsicSizes::Trace(Visitor* visitor) { + visitor->Trace(child_); + visitor->Trace(token_); + ScriptWrappable::Trace(visitor); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h new file mode 100644 index 00000000000..1bfa9cea738 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h @@ -0,0 +1,53 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_CUSTOM_CUSTOM_INTRINSIC_SIZES_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_CUSTOM_CUSTOM_INTRINSIC_SIZES_H_ + +#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h" +#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" + +namespace blink { + +class NGLayoutInputNode; +class CustomLayoutChild; + +// This represents the result of intrinsicSizes (on a LayoutChild). +// +// This should mirror the information in a MinMaxSize, and it has the +// additional capability that it is exposed to web developers. +class CustomIntrinsicSizes : public ScriptWrappable { + DEFINE_WRAPPERTYPEINFO(); + + public: + CustomIntrinsicSizes(CustomLayoutChild*, + CustomLayoutToken*, + double min_content_size, + double max_content_size); + ~CustomIntrinsicSizes() override = default; + + CustomIntrinsicSizes(const CustomIntrinsicSizes&) = delete; + CustomIntrinsicSizes& operator=(const CustomIntrinsicSizes&) = delete; + + double minContentSize() const { return min_content_size_; } + double maxContentSize() const { return max_content_size_; } + + const NGLayoutInputNode& GetLayoutNode() const; + + bool IsValid() const { return token_->IsValid(); } + + void Trace(Visitor*) override; + + private: + Member<CustomLayoutChild> child_; + Member<CustomLayoutToken> token_; + + // The min and max content sizes on this object should never change. + const double min_content_size_; + const double max_content_size_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_CUSTOM_CUSTOM_INTRINSIC_SIZES_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc index 043110aec8f..11a2762ad9d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc @@ -11,9 +11,14 @@ #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" namespace blink { +namespace { +const char kInvalidLayoutChild[] = "The LayoutChild is not valid."; +} // namespace + CustomLayoutChild::CustomLayoutChild(const CSSLayoutDefinition& definition, NGLayoutInputNode node) : node_(node), @@ -23,6 +28,24 @@ CustomLayoutChild::CustomLayoutChild(const CSSLayoutDefinition& definition, definition.ChildNativeInvalidationProperties(), definition.ChildCustomInvalidationProperties())) {} +ScriptPromise CustomLayoutChild::intrinsicSizes( + ScriptState* script_state, + ExceptionState& exception_state) { + // A layout child may be invalid if it has been removed from the tree (it is + // possible for a web developer to hold onto a LayoutChild object after its + // underlying LayoutObject has been destroyed). + if (!node_ || !token_->IsValid()) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + kInvalidLayoutChild); + return ScriptPromise(); + } + + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); + CustomLayoutScope::Current()->Queue()->emplace_back( + this, token_, resolver, CustomLayoutWorkTask::TaskType::kIntrinsicSizes); + return resolver->Promise(); +} + ScriptPromise CustomLayoutChild::layoutNextFragment( ScriptState* script_state, const CustomLayoutConstraintsOptions* options, @@ -31,10 +54,9 @@ ScriptPromise CustomLayoutChild::layoutNextFragment( // possible for a web developer to hold onto a LayoutChild object after its // underlying LayoutObject has been destroyed). if (!node_ || !token_->IsValid()) { - return ScriptPromise::RejectWithDOMException( - script_state, - MakeGarbageCollected<DOMException>(DOMExceptionCode::kInvalidStateError, - "The LayoutChild is not valid.")); + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + kInvalidLayoutChild); + return ScriptPromise(); } // Serialize the provided data if needed. @@ -54,11 +76,12 @@ ScriptPromise CustomLayoutChild::layoutNextFragment( auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); CustomLayoutScope::Current()->Queue()->emplace_back( - this, token_, resolver, options, std::move(constraint_data)); + this, token_, resolver, options, std::move(constraint_data), + CustomLayoutWorkTask::TaskType::kLayoutFragment); return resolver->Promise(); } -void CustomLayoutChild::Trace(blink::Visitor* visitor) { +void CustomLayoutChild::Trace(Visitor* visitor) { visitor->Trace(style_map_); visitor->Trace(token_); ScriptWrappable::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h index 5b7c1c435f3..7943adc0ac3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h @@ -16,6 +16,7 @@ namespace blink { class CSSLayoutDefinition; class CustomLayoutConstraintsOptions; class CustomLayoutToken; +class ExceptionState; // Represents a "CSS box" for use by a web developer. This is passed into the // web developer defined layout and intrinsicSizes functions so that they can @@ -32,6 +33,7 @@ class CustomLayoutChild : public ScriptWrappable { // LayoutChild.idl PrepopulatedComputedStylePropertyMap* styleMap() const { return style_map_; } + ScriptPromise intrinsicSizes(ScriptState*, ExceptionState&); ScriptPromise layoutNextFragment(ScriptState*, const CustomLayoutConstraintsOptions*, ExceptionState&); @@ -44,7 +46,7 @@ class CustomLayoutChild : public ScriptWrappable { void SetCustomLayoutToken(CustomLayoutToken* token) { token_ = token; } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: NGLayoutInputNode node_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc index ec4f27e6957..268d0e72343 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc @@ -25,6 +25,13 @@ CustomLayoutConstraints::CustomLayoutConstraints( CustomLayoutConstraints::~CustomLayoutConstraints() = default; +base::Optional<double> CustomLayoutConstraints::fixedBlockSize() const { + // Check if we've been passed an indefinite block-size. + if (fixed_block_size_ < 0.0) + return base::nullopt; + return fixed_block_size_; +} + double CustomLayoutConstraints::fixedBlockSize(bool& is_null) const { // Check if we've been passed an indefinite block-size. if (fixed_block_size_ < 0.0) { @@ -50,7 +57,7 @@ ScriptValue CustomLayoutConstraints::data(ScriptState* script_state) const { layout_worklet_world_v8_data_.NewLocal(script_state->GetIsolate())); } -void CustomLayoutConstraints::Trace(blink::Visitor* visitor) { +void CustomLayoutConstraints::Trace(Visitor* visitor) { visitor->Trace(layout_worklet_world_v8_data_); ScriptWrappable::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h index 1c66b7c437f..42ce5a8fa06 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h @@ -29,10 +29,12 @@ class CustomLayoutConstraints : public ScriptWrappable { // LayoutConstraints.idl double fixedInlineSize() const { return fixed_inline_size_; } - double fixedBlockSize(bool& is_null) const; + base::Optional<double> fixedBlockSize() const; + // TODO(crbug.com/1060971): Remove |is_null| version. + double fixedBlockSize(bool& is_null) const; // DEPRECATED ScriptValue data(ScriptState*) const; - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: double fixed_inline_size_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc index 1019fd242bb..476d990a0b4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc @@ -15,12 +15,14 @@ CustomLayoutFragment::CustomLayoutFragment( CustomLayoutToken* token, scoped_refptr<const NGLayoutResult> layout_result, const LogicalSize& size, + const base::Optional<LayoutUnit> baseline, v8::Isolate* isolate) : child_(child), token_(token), layout_result_(std::move(layout_result)), inline_size_(size.inline_size.ToDouble()), - block_size_(size.block_size.ToDouble()) { + block_size_(size.block_size.ToDouble()), + baseline_(baseline) { // Immediately store the result data, so that it remains immutable between // layout calls to the child. if (SerializedScriptValue* data = layout_result_->CustomLayoutData()) @@ -36,6 +38,11 @@ const NGLayoutInputNode& CustomLayoutFragment::GetLayoutNode() const { return child_->GetLayoutNode(); } +double CustomLayoutFragment::baseline(bool& is_null) const { + is_null = !baseline_.has_value(); + return baseline_.value_or(0.0); +} + ScriptValue CustomLayoutFragment::data(ScriptState* script_state) const { // "data" is *only* exposed to the LayoutWorkletGlobalScope, and we are able // to return the same deserialized object. We don't need to check which world @@ -51,7 +58,7 @@ ScriptValue CustomLayoutFragment::data(ScriptState* script_state) const { layout_worklet_world_v8_data_.NewLocal(script_state->GetIsolate())); } -void CustomLayoutFragment::Trace(blink::Visitor* visitor) { +void CustomLayoutFragment::Trace(Visitor* visitor) { visitor->Trace(child_); visitor->Trace(token_); visitor->Trace(layout_worklet_world_v8_data_); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h index bc9cb8ee03e..29adc05f5a9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h @@ -39,6 +39,7 @@ class CustomLayoutFragment : public ScriptWrappable { CustomLayoutToken*, scoped_refptr<const NGLayoutResult>, const LogicalSize& size, + const base::Optional<LayoutUnit> baseline, v8::Isolate*); ~CustomLayoutFragment() override = default; @@ -51,6 +52,10 @@ class CustomLayoutFragment : public ScriptWrappable { void setInlineOffset(double inline_offset) { inline_offset_ = inline_offset; } void setBlockOffset(double block_offset) { block_offset_ = block_offset; } + base::Optional<double> baseline() const { return baseline_; } + // TODO(crbug.com/1060971): Remove |is_null| version. + double baseline(bool& is_null) const; // DEPRECATED + ScriptValue data(ScriptState*) const; const NGLayoutResult& GetLayoutResult() const; @@ -58,7 +63,7 @@ class CustomLayoutFragment : public ScriptWrappable { bool IsValid() const { return token_->IsValid(); } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: Member<CustomLayoutChild> child_; @@ -88,6 +93,9 @@ class CustomLayoutFragment : public ScriptWrappable { double inline_offset_ = 0; double block_offset_ = 0; + // The first-line baseline. + const base::Optional<double> baseline_; + TraceWrapperV8Reference<v8::Value> layout_worklet_world_v8_data_; DISALLOW_COPY_AND_ASSIGN(CustomLayoutFragment); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h index 319ea6b778b..3cc70061f06 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h @@ -64,7 +64,7 @@ class CustomLayoutScope { CustomLayoutScope* prev_scope_; CustomLayoutWorkQueue queue_; - Member<CustomLayoutToken> token_; + CustomLayoutToken* token_; }; inline bool CustomLayoutToken::IsValid() const { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc index b23852b8139..3e9dc834b29 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc @@ -5,37 +5,68 @@ #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" +#include "third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h" -#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints_options.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" namespace blink { +CustomLayoutWorkTask::CustomLayoutWorkTask(CustomLayoutChild* child, + CustomLayoutToken* token, + ScriptPromiseResolver* resolver, + const TaskType type) + : CustomLayoutWorkTask(child, token, resolver, nullptr, nullptr, type) {} + CustomLayoutWorkTask::CustomLayoutWorkTask( CustomLayoutChild* child, CustomLayoutToken* token, ScriptPromiseResolver* resolver, const CustomLayoutConstraintsOptions* options, - scoped_refptr<SerializedScriptValue> constraint_data) + scoped_refptr<SerializedScriptValue> constraint_data, + const TaskType type) : child_(child), token_(token), resolver_(resolver), options_(options), - constraint_data_(std::move(constraint_data)) {} + constraint_data_(std::move(constraint_data)), + type_(type) {} CustomLayoutWorkTask::~CustomLayoutWorkTask() = default; -void CustomLayoutWorkTask::Run(const NGConstraintSpace& parent_space, - const ComputedStyle& parent_style) { +void CustomLayoutWorkTask::Run( + const NGConstraintSpace& parent_space, + const ComputedStyle& parent_style, + const LayoutUnit child_percentage_resolution_block_size_for_min_max) { DCHECK(token_->IsValid()); - NGLayoutInputNode node = child_->GetLayoutNode(); - NGConstraintSpaceBuilder builder(parent_space, node.Style().GetWritingMode(), + NGLayoutInputNode child = child_->GetLayoutNode(); + + if (type_ == CustomLayoutWorkTask::TaskType::kIntrinsicSizes) { + RunIntrinsicSizesTask(parent_style, + child_percentage_resolution_block_size_for_min_max, + child); + } else { + DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kLayoutFragment); + RunLayoutFragmentTask(parent_space, parent_style, child); + } +} + +void CustomLayoutWorkTask::RunLayoutFragmentTask( + const NGConstraintSpace& parent_space, + const ComputedStyle& parent_style, + NGLayoutInputNode child) { + DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kLayoutFragment); + DCHECK(options_ && resolver_); + + NGConstraintSpaceBuilder builder(parent_space, child.Style().GetWritingMode(), /* is_new_fc */ true); - SetOrthogonalFallbackInlineSizeIfNeeded(parent_style, node, &builder); + SetOrthogonalFallbackInlineSizeIfNeeded(parent_style, child, &builder); bool is_fixed_inline_size = false; bool is_fixed_block_size = false; @@ -88,24 +119,40 @@ void CustomLayoutWorkTask::Run(const NGConstraintSpace& parent_space, percentage_size.block_size = kIndefiniteSize; } - builder.SetTextDirection(node.Style().Direction()); + builder.SetTextDirection(child.Style().Direction()); builder.SetAvailableSize(available_size); builder.SetPercentageResolutionSize(percentage_size); builder.SetReplacedPercentageResolutionSize(percentage_size); - builder.SetIsShrinkToFit(node.Style().LogicalWidth().IsAuto()); + builder.SetIsShrinkToFit(child.Style().LogicalWidth().IsAuto()); builder.SetIsFixedInlineSize(is_fixed_inline_size); builder.SetIsFixedBlockSize(is_fixed_block_size); - if (node.IsLayoutNGCustom()) + builder.SetNeedsBaseline(true); + if (child.IsLayoutNGCustom()) builder.SetCustomLayoutData(std::move(constraint_data_)); auto space = builder.ToConstraintSpace(); - auto result = To<NGBlockNode>(node).Layout(space, nullptr /* break_token */); + auto result = To<NGBlockNode>(child).Layout(space, nullptr /* break_token */); - LogicalSize size = result->PhysicalFragment().Size().ConvertToLogical( - parent_space.GetWritingMode()); + NGBoxFragment fragment(parent_space.GetWritingMode(), + parent_space.Direction(), + To<NGPhysicalBoxFragment>(result->PhysicalFragment())); resolver_->Resolve(MakeGarbageCollected<CustomLayoutFragment>( - child_, token_, std::move(result), size, + child_, token_, std::move(result), fragment.Size(), fragment.Baseline(), resolver_->GetScriptState()->GetIsolate())); } +void CustomLayoutWorkTask::RunIntrinsicSizesTask( + const ComputedStyle& parent_style, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, + NGLayoutInputNode child) { + DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kIntrinsicSizes); + DCHECK(resolver_); + + MinMaxSizesInput input(child_percentage_resolution_block_size_for_min_max); + MinMaxSizes sizes = + ComputeMinAndMaxContentContribution(parent_style, child, input); + resolver_->Resolve(MakeGarbageCollected<CustomIntrinsicSizes>( + child_, token_, sizes.min_size, sizes.max_size)); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h index 5038048ed31..7aacc7fce5c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h @@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_CUSTOM_CUSTOM_LAYOUT_WORK_TASK_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_CUSTOM_CUSTOM_LAYOUT_WORK_TASK_H_ -#include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_custom_layout_constraints_options.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -14,7 +14,9 @@ namespace blink { class ComputedStyle; class CustomLayoutChild; class CustomLayoutToken; +class LayoutUnit; class NGConstraintSpace; +class NGLayoutInputNode; class SerializedScriptValue; class ScriptPromiseResolver; @@ -22,16 +24,30 @@ class ScriptPromiseResolver; // intrinsic-sizes. class CustomLayoutWorkTask { public: + enum TaskType { + kLayoutFragment, + kIntrinsicSizes, + }; + + // Used when resolving a promise with intrinsic-sizes. + CustomLayoutWorkTask(CustomLayoutChild*, + CustomLayoutToken*, + ScriptPromiseResolver*, + const TaskType type); + + // Used when resolving a promise with a fragment. CustomLayoutWorkTask(CustomLayoutChild*, CustomLayoutToken*, ScriptPromiseResolver*, const CustomLayoutConstraintsOptions*, - scoped_refptr<SerializedScriptValue> constraint_data); + scoped_refptr<SerializedScriptValue> constraint_data, + const TaskType type); ~CustomLayoutWorkTask(); // Runs this work task. void Run(const NGConstraintSpace& parent_space, - const ComputedStyle& parent_style); + const ComputedStyle& parent_style, + const LayoutUnit child_percentage_resolution_block_size_for_min_max); private: Persistent<CustomLayoutChild> child_; @@ -39,6 +55,15 @@ class CustomLayoutWorkTask { Persistent<ScriptPromiseResolver> resolver_; Persistent<const CustomLayoutConstraintsOptions> options_; scoped_refptr<SerializedScriptValue> constraint_data_; + TaskType type_; + + void RunLayoutFragmentTask(const NGConstraintSpace& parent_space, + const ComputedStyle& parent_style, + NGLayoutInputNode child); + void RunIntrinsicSizesTask( + const ComputedStyle& parent_style, + const LayoutUnit child_percentage_resolution_block_size_for_min_max, + NGLayoutInputNode child); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.cc index 3d60b887d1c..c68765b9107 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.cc @@ -33,7 +33,7 @@ bool DocumentLayoutDefinition::IsEqual(const CSSLayoutDefinition& other) { other.ChildCustomInvalidationProperties(); } -void DocumentLayoutDefinition::Trace(blink::Visitor* visitor) { +void DocumentLayoutDefinition::Trace(Visitor* visitor) { visitor->Trace(layout_definition_); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.h index 2a8e359b1cf..0c2d8bce246 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.h @@ -37,7 +37,7 @@ class DocumentLayoutDefinition final return registered_definitions_count_; } - virtual void Trace(blink::Visitor*); + virtual void Trace(Visitor*); private: bool IsEqual(const CSSLayoutDefinition&); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.idl index 376cc5be01c..28867d89dc6 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.idl +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.idl @@ -6,6 +6,7 @@ dictionary FragmentResultOptions { double autoBlockSize = 0; + double baseline; sequence<LayoutFragment> childFragments = []; any data = null; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes.idl new file mode 100644 index 00000000000..5864a0720a0 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes.idl @@ -0,0 +1,15 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://drafts.css-houdini.org/css-layout-api/#intrinsicsizes + +[ + Exposed=LayoutWorklet, + ImplementedAs=CustomIntrinsicSizes, + RuntimeEnabled=CSSLayoutAPI +] +interface IntrinsicSizes { + readonly attribute double minContentSize; + readonly attribute double maxContentSize; +}; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes_result_options.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes_result_options.idl new file mode 100644 index 00000000000..aa3ae7d453b --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes_result_options.idl @@ -0,0 +1,10 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// https://drafts.css-houdini.org/css-layout-api/#dictdef-intrinsicsizesresultoptions + +dictionary IntrinsicSizesResultOptions { + double minContentSize = 0; + double maxContentSize = 0; +}; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_child.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_child.idl index 3940849802c..76ba3c49d42 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_child.idl +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_child.idl @@ -12,7 +12,7 @@ interface LayoutChild { readonly attribute StylePropertyMapReadOnly styleMap; - // IntrinsicSizesRequest intrinsicSizes(); - [CallWith=ScriptState, RaisesException] Promise<LayoutFragment> layoutNextFragment(optional CustomLayoutConstraintsOptions options); + [CallWith=ScriptState, RaisesException] Promise<IntrinsicSizes> intrinsicSizes(); + [CallWith=ScriptState, RaisesException] Promise<LayoutFragment> layoutNextFragment(optional CustomLayoutConstraintsOptions options = {}); }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_fragment.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_fragment.idl index e142bcd24b3..110744df579 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_fragment.idl +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_fragment.idl @@ -16,5 +16,7 @@ interface LayoutFragment { attribute double inlineOffset; attribute double blockOffset; + readonly attribute double? baseline; + [CallWith=ScriptState] readonly attribute any data; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.cc index 254c5354891..b49d3c51ad4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.cc @@ -15,6 +15,27 @@ LayoutNGCustom::LayoutNGCustom(Element* element) DCHECK(element); } +void LayoutNGCustom::AddChild(LayoutObject* new_child, + LayoutObject* before_child) { + // Only use the block-flow AddChild logic when we are unloaded, i.e. we + // should behave exactly like a block-flow. + if (state_ == kUnloaded) { + LayoutNGBlockFlow::AddChild(new_child, before_child); + return; + } + LayoutBlock::AddChild(new_child, before_child); +} + +void LayoutNGCustom::RemoveChild(LayoutObject* child) { + // Only use the block-flow RemoveChild logic when we are unloaded, i.e. we + // should behave exactly like a block-flow. + if (state_ == kUnloaded) { + LayoutNGBlockFlow::RemoveChild(child); + return; + } + LayoutBlock::RemoveChild(child); +} + void LayoutNGCustom::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { if (state_ == kUnloaded) { @@ -35,6 +56,11 @@ void LayoutNGCustom::StyleDidChange(StyleDifference diff, } } + // Make our children "block-level" before invoking StyleDidChange. As the + // current multi-col logic may invoke a call to AddChild, failing a DCHECK. + if (state_ != kUnloaded) + SetChildrenInline(false); + // TODO(ikilpatrick): Investigate reducing the properties which // LayoutNGBlockFlow::StyleDidChange invalidates upon. (For example margins). LayoutNGBlockFlow::StyleDidChange(diff, old_style); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h index 3b80651d205..95f0f3d0dae 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h @@ -30,6 +30,9 @@ class LayoutNGCustom final : public LayoutNGBlockFlow { bool IsLoaded() { return state_ != kUnloaded; } + void AddChild(LayoutObject* new_child, LayoutObject* before_child) override; + void RemoveChild(LayoutObject* child) override; + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; private: diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.cc index 6986d303d97..71e6bb85888 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.cc @@ -45,7 +45,7 @@ LayoutWorkletGlobalScopeProxy* LayoutWorklet::Proxy() { return LayoutWorkletGlobalScopeProxy::From(FindAvailableGlobalScope()); } -void LayoutWorklet::Trace(blink::Visitor* visitor) { +void LayoutWorklet::Trace(Visitor* visitor) { visitor->Trace(document_definition_map_); visitor->Trace(pending_layout_registry_); Worklet::Trace(visitor); @@ -59,8 +59,9 @@ bool LayoutWorklet::NeedsToCreateGlobalScope() { WorkletGlobalScopeProxy* LayoutWorklet::CreateGlobalScope() { DCHECK(NeedsToCreateGlobalScope()); return MakeGarbageCollected<LayoutWorkletGlobalScopeProxy>( - To<Document>(GetExecutionContext())->GetFrame(), ModuleResponsesMap(), - pending_layout_registry_, GetNumberOfGlobalScopes() + 1); + To<LocalDOMWindow>(GetExecutionContext())->GetFrame(), + ModuleResponsesMap(), pending_layout_registry_, + GetNumberOfGlobalScopes() + 1); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h index 3c433e4920b..19d91dd9a97 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h" #include "third_party/blink/renderer/core/workers/worklet.h" #include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/supplementable.h" namespace blink { @@ -46,7 +47,7 @@ class CORE_EXPORT LayoutWorklet : public Worklet, void AddPendingLayout(const AtomicString& name, Node*); LayoutWorkletGlobalScopeProxy* Proxy(); - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; protected: // TODO(ikilpatrick): Make selection of the global scope non-deterministic. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc index a953e2ef0e8..77870594359 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h" #include "third_party/blink/renderer/bindings/core/v8/v8_function.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_intrinsic_sizes_callback.h" #include "third_party/blink/renderer/bindings/core/v8/v8_layout_callback.h" #include "third_party/blink/renderer/bindings/core/v8/v8_no_argument_constructor.h" #include "third_party/blink/renderer/bindings/core/v8/v8_object_parser.h" @@ -36,12 +37,12 @@ LayoutWorkletGlobalScope* LayoutWorkletGlobalScope::Create( auto* isolate = ToIsolate(frame); auto microtask_queue = v8::MicrotaskQueue::New(isolate, v8::MicrotasksPolicy::kScoped); - auto* agent = Agent::CreateForWorkerOrWorklet( - isolate, - creation_params->agent_cluster_id.is_empty() - ? base::UnguessableToken::Create() - : creation_params->agent_cluster_id, - std::move(microtask_queue)); + auto* agent = + MakeGarbageCollected<Agent>(isolate, + creation_params->agent_cluster_id.is_empty() + ? base::UnguessableToken::Create() + : creation_params->agent_cluster_id, + std::move(microtask_queue)); auto* global_scope = MakeGarbageCollected<LayoutWorkletGlobalScope>( frame, std::move(creation_params), reporting_proxy, pending_layout_registry, agent); @@ -103,7 +104,8 @@ void LayoutWorkletGlobalScope::registerLayout( Vector<AtomicString> custom_invalidation_properties; if (!V8ObjectParser::ParseCSSPropertyList( - current_context, layout_ctor->CallbackObject(), "inputProperties", + current_context, GetFrame()->DomWindow(), + layout_ctor->CallbackObject(), "inputProperties", &native_invalidation_properties, &custom_invalidation_properties, &exception_state)) return; @@ -112,8 +114,9 @@ void LayoutWorkletGlobalScope::registerLayout( Vector<AtomicString> child_custom_invalidation_properties; if (!V8ObjectParser::ParseCSSPropertyList( - current_context, layout_ctor->CallbackObject(), - "childInputProperties", &child_native_invalidation_properties, + current_context, GetFrame()->DomWindow(), + layout_ctor->CallbackObject(), "childInputProperties", + &child_native_invalidation_properties, &child_custom_invalidation_properties, &exception_state)) return; @@ -126,7 +129,8 @@ void LayoutWorkletGlobalScope::registerLayout( retriever.GetMethodOrThrow("intrinsicSizes", exception_state); if (exception_state.HadException()) return; - V8Function* intrinsic_sizes = V8Function::Create(v8_intrinsic_sizes); + V8IntrinsicSizesCallback* intrinsic_sizes = + V8IntrinsicSizesCallback::Create(v8_intrinsic_sizes); v8::Local<v8::Function> v8_layout = retriever.GetMethodOrThrow("layout", exception_state); @@ -141,8 +145,7 @@ void LayoutWorkletGlobalScope::registerLayout( child_custom_invalidation_properties); layout_definitions_.Set(name, definition); - LayoutWorklet* layout_worklet = - LayoutWorklet::From(*GetFrame()->GetDocument()->domWindow()); + LayoutWorklet* layout_worklet = LayoutWorklet::From(*GetFrame()->DomWindow()); LayoutWorklet::DocumentDefinitionMap* document_definition_map = layout_worklet->GetDocumentDefinitionMap(); if (document_definition_map->Contains(name)) { @@ -177,7 +180,7 @@ CSSLayoutDefinition* LayoutWorkletGlobalScope::FindDefinition( return layout_definitions_.at(name); } -void LayoutWorkletGlobalScope::Trace(blink::Visitor* visitor) { +void LayoutWorkletGlobalScope::Trace(Visitor* visitor) { visitor->Trace(layout_definitions_); visitor->Trace(pending_layout_registry_); WorkletGlobalScope::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h index 642e9af4527..d920e67f8d3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h @@ -45,7 +45,7 @@ class CORE_EXPORT LayoutWorkletGlobalScope final : public WorkletGlobalScope { CSSLayoutDefinition* FindDefinition(const AtomicString& name); - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: // https://drafts.css-houdini.org/css-layout-api/#layout-definitions diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.idl b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.idl index 5ce4982e351..63a233f5461 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.idl +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.idl @@ -15,3 +15,7 @@ // Blink-specific type for layout function // https://drafts.css-houdini.org/css-layout-api/#layout-definition-layout-function callback LayoutCallback = any (sequence<LayoutChild> children, LayoutEdges edges, LayoutConstraints constraints, StylePropertyMapReadOnly styleMap); + +// Blink-specific type for intrinsicSizes function +// https://drafts.css-houdini.org/css-layout-api/#layout-definition-intrinsic-sizes-function +callback IntrinsicSizesCallback = any (sequence<LayoutChild> children, LayoutEdges edges, StylePropertyMapReadOnly styleMap); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc index 781452633d4..b71ceac3b78 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h" #include "third_party/blink/renderer/core/loader/worker_fetch_context.h" @@ -40,15 +41,16 @@ LayoutWorkletGlobalScopeProxy::LayoutWorkletGlobalScopeProxy( StringView("LayoutWorklet #") + String::Number(global_scope_number); auto creation_params = std::make_unique<GlobalScopeCreationParams>( - document->Url(), mojom::ScriptType::kModule, - OffMainThreadWorkerScriptFetchOption::kEnabled, global_scope_name, - document->UserAgent(), frame->Client()->CreateWorkerFetchContext(), + document->Url(), mojom::blink::ScriptType::kModule, global_scope_name, + document->UserAgent(), frame->Client()->UserAgentMetadata(), + frame->Client()->CreateWorkerFetchContext(), document->GetContentSecurityPolicy()->Headers(), document->GetReferrerPolicy(), document->GetSecurityOrigin(), document->IsSecureContext(), document->GetHttpsState(), nullptr /* worker_clients */, frame->Client()->CreateWorkerContentSettingsClient(), - document->AddressSpace(), OriginTrialContext::GetTokens(document).get(), + document->GetSecurityContext().AddressSpace(), + OriginTrialContext::GetTokens(frame->DomWindow()).get(), base::UnguessableToken::Create(), nullptr /* worker_settings */, kV8CacheOptionsDefault, module_responses_map, mojo::NullRemote() /* browser_interface_broker */, @@ -92,7 +94,7 @@ CSSLayoutDefinition* LayoutWorkletGlobalScopeProxy::FindDefinition( return global_scope_->FindDefinition(name); } -void LayoutWorkletGlobalScopeProxy::Trace(blink::Visitor* visitor) { +void LayoutWorkletGlobalScopeProxy::Trace(Visitor* visitor) { visitor->Trace(global_scope_); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h index 46837b1ea3a..de993758af3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h @@ -46,7 +46,7 @@ class CORE_EXPORT LayoutWorkletGlobalScopeProxy LayoutWorkletGlobalScope* global_scope() const { return global_scope_.Get(); } - void Trace(blink::Visitor*) override; + void Trace(Visitor*) override; private: std::unique_ptr<MainThreadWorkletReportingProxy> reporting_proxy_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.cc index 2e7297a2fd7..c763349959b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.cc @@ -5,11 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_intrinsic_sizes_result_options.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h" #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h" -#include "third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.h" #include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h" #include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" @@ -30,13 +31,55 @@ NGCustomLayoutAlgorithm::NGCustomLayoutAlgorithm( container_builder_.SetIsNewFormattingContext( params.space.IsNewFormattingContext()); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); + const NGConstraintSpace& space = ConstraintSpace(); + child_percentage_resolution_block_size_for_min_max_ = + CalculateChildPercentageBlockSizeForMinMax( + space, Node(), border_padding_, + space.PercentageResolutionBlockSize()); } -base::Optional<MinMaxSize> NGCustomLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { - // TODO(ikilpatrick): Invoke the web-developer defined "intrinsicSizes" - // method. - return FallbackMinMaxSize(input); +base::Optional<MinMaxSizes> NGCustomLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + if (!Node().IsCustomLayoutLoaded()) + return FallbackMinMaxSizes(input); + + ScriptForbiddenScope::AllowUserAgentScript allow_script; + CustomLayoutScope scope; + + const AtomicString& name = Style().DisplayLayoutCustomName(); + const Document& document = Node().GetDocument(); + LayoutWorklet* worklet = LayoutWorklet::From(*document.domWindow()); + CSSLayoutDefinition* definition = worklet->Proxy()->FindDefinition(name); + + // TODO(ikilpatrick): Cache the instance of the layout class. + CSSLayoutDefinition::Instance* instance = definition->CreateInstance(); + + if (!instance) { + // TODO(ikilpatrick): Report this error to the developer. + return FallbackMinMaxSizes(input); + } + + IntrinsicSizesResultOptions* intrinsic_sizes_result_options = nullptr; + if (!instance->IntrinsicSizes( + ConstraintSpace(), document, Node(), + container_builder_.InitialBorderBoxSize(), border_scrollbar_padding_, + child_percentage_resolution_block_size_for_min_max_, &scope, + intrinsic_sizes_result_options)) { + // TODO(ikilpatrick): Report this error to the developer. + return FallbackMinMaxSizes(input); + } + + MinMaxSizes sizes; + sizes.max_size = LayoutUnit::FromDoubleRound( + intrinsic_sizes_result_options->maxContentSize()); + sizes.min_size = std::min( + sizes.max_size, LayoutUnit::FromDoubleRound( + intrinsic_sizes_result_options->minContentSize())); + + sizes.min_size.ClampNegativeToZero(); + sizes.max_size.ClampNegativeToZero(); + + return sizes; } scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() { @@ -61,13 +104,13 @@ scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() { return FallbackLayout(); } - FragmentResultOptions* fragment_result_options = - FragmentResultOptions::Create(); + FragmentResultOptions* fragment_result_options = nullptr; scoped_refptr<SerializedScriptValue> fragment_result_data; - if (!instance->Layout(ConstraintSpace(), document, Node(), - container_builder_.InitialBorderBoxSize(), - border_scrollbar_padding_, &scope, - fragment_result_options, &fragment_result_data)) { + if (!instance->Layout( + ConstraintSpace(), document, Node(), + container_builder_.InitialBorderBoxSize(), border_scrollbar_padding_, + child_percentage_resolution_block_size_for_min_max_, &scope, + fragment_result_options, &fragment_result_data)) { // TODO(ikilpatrick): Report this error to the developer. return FallbackLayout(); } @@ -123,6 +166,12 @@ scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() { LayoutUnit block_size = ComputeBlockSizeForFragment( ConstraintSpace(), Style(), border_padding_, auto_block_size); + if (fragment_result_options->hasBaseline()) { + LayoutUnit baseline = + LayoutUnit::FromDoubleRound(fragment_result_options->baseline()); + container_builder_.SetBaseline(baseline); + } + container_builder_.SetCustomLayoutData(std::move(fragment_result_data)); container_builder_.SetIntrinsicBlockSize(auto_block_size); container_builder_.SetBlockSize(block_size); @@ -151,10 +200,10 @@ void NGCustomLayoutAlgorithm::AddAnyOutOfFlowPositionedChildren( } } -base::Optional<MinMaxSize> NGCustomLayoutAlgorithm::FallbackMinMaxSize( - const MinMaxSizeInput& input) const { +base::Optional<MinMaxSizes> NGCustomLayoutAlgorithm::FallbackMinMaxSizes( + const MinMaxSizesInput& input) const { NGBlockLayoutAlgorithm algorithm(params_); - return algorithm.ComputeMinMaxSize(input); + return algorithm.ComputeMinMaxSizes(input); } scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::FallbackLayout() { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h index 7725387252a..5a43340b4a0 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h @@ -20,18 +20,20 @@ class CORE_EXPORT NGCustomLayoutAlgorithm public: NGCustomLayoutAlgorithm(const NGLayoutAlgorithmParams& params); - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; scoped_refptr<const NGLayoutResult> Layout() override; private: void AddAnyOutOfFlowPositionedChildren(NGLayoutInputNode* child); - base::Optional<MinMaxSize> FallbackMinMaxSize(const MinMaxSizeInput&) const; + base::Optional<MinMaxSizes> FallbackMinMaxSizes( + const MinMaxSizesInput&) const; scoped_refptr<const NGLayoutResult> FallbackLayout(); const NGLayoutAlgorithmParams& params_; const NGBoxStrut border_padding_; const NGBoxStrut border_scrollbar_padding_; + LayoutUnit child_percentage_resolution_block_size_for_min_max_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.cc index 4239b4da920..644f324ac9c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.cc @@ -42,7 +42,7 @@ void PendingLayoutRegistry::AddPendingLayout(const AtomicString& name, set->insert(node); } -void PendingLayoutRegistry::Trace(blink::Visitor* visitor) { +void PendingLayoutRegistry::Trace(Visitor* visitor) { visitor->Trace(pending_layouts_); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h b/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h index ba74e988d1b..8afe379befd 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h @@ -23,7 +23,7 @@ class PendingLayoutRegistry : public GarbageCollected<PendingLayoutRegistry> { void NotifyLayoutReady(const AtomicString& name); void AddPendingLayout(const AtomicString& name, Node*); - void Trace(blink::Visitor*); + void Trace(Visitor*); private: // This is a map of Nodes which are waiting for a CSSLayoutDefinition to be diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc index 0672d751fc4..250278fdfba 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc @@ -96,17 +96,6 @@ bool NGLayoutOpportunity::IsBlockDeltaBelowShapes( return true; } -NGLineLayoutOpportunity NGLayoutOpportunity::ComputeLineLayoutOpportunity( - const NGConstraintSpace& space, - LayoutUnit line_block_size, - LayoutUnit block_delta) const { - return NGLineLayoutOpportunity( - ComputeLineLeftOffset(space, line_block_size, block_delta), - ComputeLineRightOffset(space, line_block_size, block_delta), - rect.LineStartOffset(), rect.LineEndOffset(), - rect.BlockStartOffset() + block_delta, line_block_size); -} - LayoutUnit NGLayoutOpportunity::ComputeLineLeftOffset( const NGConstraintSpace& space, LayoutUnit line_block_size, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h index abf46f88fff..58734340c0b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h @@ -47,9 +47,15 @@ struct CORE_EXPORT NGLayoutOpportunity { // Calculates a line layout opportunity which takes into account any shapes // which may affect the available inline size for the line breaker. NGLineLayoutOpportunity ComputeLineLayoutOpportunity( - const NGConstraintSpace&, + const NGConstraintSpace& space, LayoutUnit line_block_size, - LayoutUnit block_delta) const; + LayoutUnit block_delta) const { + return NGLineLayoutOpportunity( + ComputeLineLeftOffset(space, line_block_size, block_delta), + ComputeLineRightOffset(space, line_block_size, block_delta), + rect.LineStartOffset(), rect.LineEndOffset(), + rect.BlockStartOffset() + block_delta, line_block_size); + } private: LayoutUnit ComputeLineLeftOffset(const NGConstraintSpace&, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h index f5c78a92dad..3c01e9f6085 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h @@ -118,6 +118,10 @@ struct CORE_EXPORT NGLineBoxStrut { LayoutUnit InlineSum() const { return inline_start + inline_end; } LayoutUnit BlockSum() const { return line_over + line_under; } + bool IsEmpty() const { + return !inline_start && !inline_end && !line_over && !line_under; + } + bool operator==(const NGLineBoxStrut& other) const { return inline_start == other.inline_start && inline_end == other.inline_end && line_over == other.line_over && diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h index 8236afa14ee..759d7091b66 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h @@ -26,8 +26,7 @@ struct CORE_EXPORT NGMarginStrut { // See comment inside NGBlockLayoutAlgorithm for when this occurs. bool is_quirky_container_start = false; - // If set, we will discard all adjoining margins, which is the - // effect of -webkit-margin-collapse:discard. + // If set, we will discard all adjoining margins. bool discard_margins = false; // Appends negative or positive value to the current margin strut. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc index bd55b43700a..da28e31bfcf 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc @@ -47,6 +47,20 @@ class LayoutNGTextTest : public PageTestBase { } }; +TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendBidi) { + if (!RuntimeEnabledFeatures::LayoutNGEnabled()) + return; + + SetBodyInnerHTML(u"<div dir=rtl id=target>\u05D0\u05D1\u05BC\u05D2</div>"); + Text& text = To<Text>(*GetElementById("target")->firstChild()); + text.appendData(u"\u05D0\u05D1\u05BC\u05D2"); + + EXPECT_EQ(String(u"*{'\u05D0\u05D1\u05BC\u05D2\u05D0\u05D1\u05BC\u05D2', " + u"ShapeResult=0+8}\n") + .Utf8(), + GetItemsAsString(*text.GetLayoutObject())); +} + TEST_F(LayoutNGTextTest, SetTextWithOffsetAppendControl) { if (!RuntimeEnabledFeatures::LayoutNGEnabled()) return; @@ -136,8 +150,11 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL) { Text& text = To<Text>(*GetElementById("target")->firstChild()); text.deleteData(2, 2, ASSERT_NO_EXCEPTION); // remove "23" - EXPECT_EQ("*{'0 4', ShapeResult=0+3}\n", - GetItemsAsString(*text.GetLayoutObject())); + EXPECT_EQ( + "*{'0', ShapeResult=0+1}\n" + "*{' ', ShapeResult=1+1}\n" + "*{'4', ShapeResult=2+1}\n", + GetItemsAsString(*text.GetLayoutObject())); } // http://crbug.com/1000685 @@ -149,7 +166,26 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL2) { Text& text = To<Text>(*GetElementById("target")->firstChild()); text.deleteData(0, 1, ASSERT_NO_EXCEPTION); // remove "0" - EXPECT_EQ("*{'(xy)5', ShapeResult=0+5}\n", + EXPECT_EQ( + "*{'(', ShapeResult=0+1}\n" + "*{'xy', ShapeResult=1+2}\n" + "*{')', ShapeResult=3+1}\n" + "*{'5', ShapeResult=4+1}\n", + GetItemsAsString(*text.GetLayoutObject())); +} + +// http://crbug.com/1039143 +TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteWithBidiControl) { + if (!RuntimeEnabledFeatures::LayoutNGEnabled()) + return; + + // In text content, we have bidi control codes: + // U+2066 U+2069 \n U+2066 abc U+2066 + SetBodyInnerHTML(u"<pre><b id=target dir=ltr>\nabc</b></pre>"); + Text& text = To<Text>(*GetElementById("target")->firstChild()); + text.deleteData(0, 1, ASSERT_NO_EXCEPTION); // remove "\n" + + EXPECT_EQ("LayoutText has NeedsCollectInlines", GetItemsAsString(*text.GetLayoutObject())); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc index 8a5f0a7201d..34643cd55f2 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc @@ -5,7 +5,10 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" @@ -16,38 +19,90 @@ namespace blink { -NGAbstractInlineTextBox::FragmentToNGAbstractInlineTextBoxHashMap* - NGAbstractInlineTextBox::g_abstract_inline_text_box_map_ = nullptr; +namespace { + +// Mapping from NGFragmentItem/NGPaintFragment to NGAbstractInlineTextBox +// TODO(yosin): Once we get rid of |NGPaintFragment|, we should not use +// template class for |NGAbstractInlineTextBoxCache|. +template <typename Fragment> +class NGAbstractInlineTextBoxCache final { + public: + static scoped_refptr<AbstractInlineTextBox> GetOrCreate( + const Fragment& fragment) { + if (!s_instance_) + s_instance_ = new NGAbstractInlineTextBoxCache(); + return s_instance_->GetOrCreateInternal(fragment); + } + + static void WillDestroy(const Fragment* fragment) { + if (!s_instance_) + return; + s_instance_->WillDestroyInternal(fragment); + } + + private: + scoped_refptr<AbstractInlineTextBox> GetOrCreateInternal( + const Fragment& fragment) { + const auto it = map_.find(&fragment); + LayoutText* const layout_text = + ToLayoutText(fragment.GetMutableLayoutObject()); + if (it != map_.end()) { + CHECK(layout_text->HasAbstractInlineTextBox()); + return it->value; + } + scoped_refptr<AbstractInlineTextBox> obj = base::AdoptRef( + new NGAbstractInlineTextBox(LineLayoutText(layout_text), fragment)); + map_.Set(&fragment, obj); + layout_text->SetHasAbstractInlineTextBox(); + return obj; + } + + void WillDestroyInternal(const Fragment* fragment) { + const auto it = map_.find(fragment); + if (it == map_.end()) + return; + it->value->Detach(); + map_.erase(fragment); + } + + static NGAbstractInlineTextBoxCache* s_instance_; + + HashMap<const Fragment*, scoped_refptr<AbstractInlineTextBox>> map_; +}; + +template <typename Fragment> +NGAbstractInlineTextBoxCache<Fragment>* + NGAbstractInlineTextBoxCache<Fragment>::s_instance_ = nullptr; + +} // namespace scoped_refptr<AbstractInlineTextBox> NGAbstractInlineTextBox::GetOrCreate( - const NGPaintFragment& fragment) { - DCHECK(fragment.GetLayoutObject()->IsText()) << fragment.GetLayoutObject(); - if (!g_abstract_inline_text_box_map_) { - g_abstract_inline_text_box_map_ = - new FragmentToNGAbstractInlineTextBoxHashMap(); + const NGInlineCursor& cursor) { + if (const NGPaintFragment* paint_fragment = cursor.CurrentPaintFragment()) { + return NGAbstractInlineTextBoxCache<NGPaintFragment>::GetOrCreate( + *paint_fragment); } - const auto it = g_abstract_inline_text_box_map_->find(&fragment); - LayoutText* const layout_text = - ToLayoutText(fragment.GetMutableLayoutObject()); - if (it != g_abstract_inline_text_box_map_->end()) { - CHECK(layout_text->HasAbstractInlineTextBox()); - return it->value; + if (const NGFragmentItem* fragment_item = cursor.CurrentItem()) { + return NGAbstractInlineTextBoxCache<NGFragmentItem>::GetOrCreate( + *fragment_item); } - scoped_refptr<AbstractInlineTextBox> obj = base::AdoptRef( - new NGAbstractInlineTextBox(LineLayoutText(layout_text), fragment)); - g_abstract_inline_text_box_map_->Set(&fragment, obj); - layout_text->SetHasAbstractInlineTextBox(); - return obj; + return nullptr; } -void NGAbstractInlineTextBox::WillDestroy(NGPaintFragment* fragment) { - if (!g_abstract_inline_text_box_map_) - return; - const auto it = g_abstract_inline_text_box_map_->find(fragment); - if (it != g_abstract_inline_text_box_map_->end()) { - it->value->Detach(); - g_abstract_inline_text_box_map_->erase(fragment); +void NGAbstractInlineTextBox::WillDestroy(const NGInlineCursor& cursor) { + if (const NGPaintFragment* paint_fragment = cursor.CurrentPaintFragment()) { + return NGAbstractInlineTextBoxCache<NGPaintFragment>::WillDestroy( + paint_fragment); + } + if (const NGFragmentItem* fragment_item = cursor.CurrentItem()) { + return NGAbstractInlineTextBoxCache<NGFragmentItem>::WillDestroy( + fragment_item); } + NOTREACHED(); +} + +void NGAbstractInlineTextBox::WillDestroy(const NGPaintFragment* fragment) { + NGAbstractInlineTextBoxCache<NGPaintFragment>::WillDestroy(fragment); } NGAbstractInlineTextBox::NGAbstractInlineTextBox( @@ -57,6 +112,13 @@ NGAbstractInlineTextBox::NGAbstractInlineTextBox( DCHECK(fragment_->PhysicalFragment().IsText()) << fragment_; } +NGAbstractInlineTextBox::NGAbstractInlineTextBox( + LineLayoutText line_layout_item, + const NGFragmentItem& fragment_item) + : AbstractInlineTextBox(line_layout_item), fragment_item_(&fragment_item) { + DCHECK(fragment_item_->IsText()) << fragment_item_; +} + NGAbstractInlineTextBox::~NGAbstractInlineTextBox() { DCHECK(!fragment_); } @@ -70,107 +132,128 @@ void NGAbstractInlineTextBox::Detach() { fragment_ = nullptr; } -const NGPhysicalTextFragment& NGAbstractInlineTextBox::PhysicalTextFragment() - const { - return To<NGPhysicalTextFragment>(fragment_->PhysicalFragment()); +NGInlineCursor NGAbstractInlineTextBox::GetCursor() const { + if (!fragment_item_) + return NGInlineCursor(); + NGInlineCursor cursor; + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + cursor.MoveTo(*fragment_item_); + else + cursor.MoveTo(*fragment_); + DCHECK(!cursor.Current().GetLayoutObject()->NeedsLayout()); + return cursor; +} + +NGInlineCursor NGAbstractInlineTextBox::GetCursorOnLine() const { + NGInlineCursor current = GetCursor(); + NGInlineCursor line_box = current; + line_box.MoveToContainingLine(); + NGInlineCursor cursor = line_box.CursorForDescendants(); + cursor.MoveTo(current); + return cursor; } -bool NGAbstractInlineTextBox::NeedsLayout() const { - return fragment_->GetLayoutObject()->NeedsLayout(); +String NGAbstractInlineTextBox::GetTextContent() const { + const NGInlineCursor& cursor = GetCursor(); + if (cursor.Current().IsGeneratedTextType()) + return cursor.Current().Text(cursor).ToString(); + if (const NGPaintFragment* paint_fragment = cursor.CurrentPaintFragment()) { + return To<NGPhysicalTextFragment>(paint_fragment->PhysicalFragment()) + .TextContent(); + } + return cursor.Items().Text(cursor.Current().UsesFirstLineStyle()); } bool NGAbstractInlineTextBox::NeedsTrailingSpace() const { - if (!fragment_->Style().CollapseWhiteSpace()) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor.Current().Style().CollapseWhiteSpace()) return false; - const NGPaintFragment& line_box = *fragment_->ContainerLineBox(); - if (!To<NGPhysicalLineBoxFragment>(line_box.PhysicalFragment()) - .HasSoftWrapToNextLine()) + NGInlineCursor line_box = cursor; + line_box.MoveToContainingLine(); + if (!line_box.HasSoftWrapToNextLine()) return false; - const NGPhysicalTextFragment& text_fragment = PhysicalTextFragment(); - if (text_fragment.EndOffset() >= text_fragment.TextContent().length()) + const String text_content = GetTextContent(); + const unsigned end_offset = cursor.Current().TextEndOffset(); + if (end_offset >= text_content.length()) return false; - if (text_fragment.TextContent()[text_fragment.EndOffset()] != ' ') + if (text_content[end_offset] != ' ') return false; - const NGInlineBreakToken& break_token = *To<NGInlineBreakToken>( - To<NGPhysicalLineBoxFragment>(line_box.PhysicalFragment()).BreakToken()); + const NGInlineBreakToken* break_token = line_box.Current().InlineBreakToken(); + DCHECK(break_token); // TODO(yosin): We should support OOF fragments between |fragment_| and // break token. - if (break_token.TextOffset() != text_fragment.EndOffset() + 1) + if (break_token->TextOffset() != end_offset + 1) return false; // Check a character in text content after |fragment_| comes from same // layout text of |fragment_|. - const NGOffsetMapping* mapping = - NGOffsetMapping::GetFor(fragment_->GetLayoutObject()); + const LayoutObject* const layout_object = cursor.Current().GetLayoutObject(); + const NGOffsetMapping* mapping = NGOffsetMapping::GetFor(layout_object); // TODO(kojii): There's not much we can do for dirty-tree. crbug.com/946004 if (!mapping) return false; const base::span<const NGOffsetMappingUnit> mapping_units = - mapping->GetMappingUnitsForTextContentOffsetRange( - text_fragment.EndOffset(), text_fragment.EndOffset() + 1); + mapping->GetMappingUnitsForTextContentOffsetRange(end_offset, + end_offset + 1); if (mapping_units.begin() == mapping_units.end()) return false; const NGOffsetMappingUnit& mapping_unit = mapping_units.front(); - return mapping_unit.GetLayoutObject() == fragment_->GetLayoutObject(); -} - -const NGPaintFragment* -NGAbstractInlineTextBox::NextTextFragmentForSameLayoutObject() const { - const auto fragments = - NGPaintFragment::InlineFragmentsFor(fragment_->GetLayoutObject()); - const auto it = - std::find_if(fragments.begin(), fragments.end(), - [&](const auto& sibling) { return fragment_ == sibling; }); - DCHECK(it != fragments.end()); - const auto next_it = std::next(it); - return next_it == fragments.end() ? nullptr : *next_it; + return mapping_unit.GetLayoutObject() == layout_object; } scoped_refptr<AbstractInlineTextBox> NGAbstractInlineTextBox::NextInlineTextBox() const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return nullptr; - DCHECK(!NeedsLayout()); - const NGPaintFragment* next_fragment = NextTextFragmentForSameLayoutObject(); - if (!next_fragment) + NGInlineCursor next; + next.MoveTo(*cursor.Current().GetLayoutObject()); + while (next != cursor) + next.MoveToNextForSameLayoutObject(); + next.MoveToNextForSameLayoutObject(); + if (!next) return nullptr; - return GetOrCreate(*next_fragment); + return GetOrCreate(next); } LayoutRect NGAbstractInlineTextBox::LocalBounds() const { - if (!fragment_ || !GetLineLayoutItem()) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return LayoutRect(); - return LayoutRect(fragment_->InlineOffsetToContainerBox().ToLayoutPoint(), - fragment_->Size().ToLayoutSize()); + return cursor.Current().RectInContainerBlock().ToLayoutRect(); } unsigned NGAbstractInlineTextBox::Len() const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return 0; if (NeedsTrailingSpace()) - return PhysicalTextFragment().TextLength() + 1; - return PhysicalTextFragment().TextLength(); + return cursor.Current().Text(cursor).length() + 1; + return cursor.Current().Text(cursor).length(); } unsigned NGAbstractInlineTextBox::TextOffsetInContainer(unsigned offset) const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return 0; - return PhysicalTextFragment().StartOffset() + offset; + return cursor.Current().TextStartOffset() + offset; } AbstractInlineTextBox::Direction NGAbstractInlineTextBox::GetDirection() const { - if (!fragment_ || !GetLineLayoutItem()) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return kLeftToRight; - const TextDirection text_direction = - PhysicalTextFragment().ResolvedDirection(); + const TextDirection text_direction = cursor.Current().ResolvedDirection(); if (GetLineLayoutItem().Style()->IsHorizontalWritingMode()) return IsLtr(text_direction) ? kLeftToRight : kRightToLeft; return IsLtr(text_direction) ? kTopToBottom : kBottomToTop; } void NGAbstractInlineTextBox::CharacterWidths(Vector<float>& widths) const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return; - if (!PhysicalTextFragment().TextShapeResult()) { + const ShapeResultView* shape_result_view = cursor.Current().TextShapeResult(); + if (!shape_result_view) { // When |fragment_| for BR, we don't have shape result. // "aom-computed-boolean-properties.html" reaches here. widths.resize(Len()); @@ -178,8 +261,8 @@ void NGAbstractInlineTextBox::CharacterWidths(Vector<float>& widths) const { } // TODO(layout-dev): Add support for IndividualCharacterRanges to // ShapeResultView to avoid the copy below. - auto shape_result = - PhysicalTextFragment().TextShapeResult()->CreateShapeResult(); + scoped_refptr<ShapeResult> shape_result = + shape_result_view->CreateShapeResult(); Vector<CharacterRange> ranges; shape_result->IndividualCharacterRanges(&ranges); widths.ReserveCapacity(ranges.size()); @@ -193,10 +276,11 @@ void NGAbstractInlineTextBox::CharacterWidths(Vector<float>& widths) const { } String NGAbstractInlineTextBox::GetText() const { - if (!fragment_ || !GetLineLayoutItem()) - return String(); + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) + return g_empty_string; - String result = PhysicalTextFragment().Text().ToString(); + String result = cursor.Current().Text(cursor).ToString(); // For compatibility with |InlineTextBox|, we should have a space character // for soft line break. @@ -217,56 +301,51 @@ String NGAbstractInlineTextBox::GetText() const { } bool NGAbstractInlineTextBox::IsFirst() const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return true; - DCHECK(!NeedsLayout()); - const auto fragments = - NGPaintFragment::InlineFragmentsFor(fragment_->GetLayoutObject()); - return fragment_ == &fragments.front(); + NGInlineCursor first_fragment; + first_fragment.MoveTo(*cursor.Current().GetLayoutObject()); + return cursor == first_fragment; } bool NGAbstractInlineTextBox::IsLast() const { - if (!fragment_) + const NGInlineCursor& cursor = GetCursor(); + if (!cursor) return true; - DCHECK(!NeedsLayout()); - const auto fragments = - NGPaintFragment::InlineFragmentsFor(fragment_->GetLayoutObject()); - return fragment_ == &fragments.back(); + NGInlineCursor last_fragment; + last_fragment.MoveTo(*cursor.Current().GetLayoutObject()); + last_fragment.MoveToLastForSameLayoutObject(); + return cursor == last_fragment; } scoped_refptr<AbstractInlineTextBox> NGAbstractInlineTextBox::NextOnLine() const { - if (!fragment_) + NGInlineCursor cursor = GetCursorOnLine(); + if (!cursor) return nullptr; - DCHECK(!NeedsLayout()); - DCHECK(fragment_->ContainerLineBox()); - NGPaintFragmentTraversal cursor(*fragment_->ContainerLineBox(), *fragment_); - for (cursor.MoveToNext(); !cursor.IsAtEnd(); cursor.MoveToNext()) { - if (cursor->GetLayoutObject()->IsText()) - return GetOrCreate(*cursor); + for (cursor.MoveToNext(); cursor; cursor.MoveToNext()) { + if (cursor.Current().GetLayoutObject()->IsText()) + return GetOrCreate(cursor); } return nullptr; } scoped_refptr<AbstractInlineTextBox> NGAbstractInlineTextBox::PreviousOnLine() const { - if (!fragment_) + NGInlineCursor cursor = GetCursorOnLine(); + if (!cursor) return nullptr; - DCHECK(!NeedsLayout()); - DCHECK(fragment_->ContainerLineBox()); - NGPaintFragmentTraversal cursor(*fragment_->ContainerLineBox(), *fragment_); - for (cursor.MoveToPrevious(); !cursor.IsAtEnd(); cursor.MoveToPrevious()) { - if (cursor->GetLayoutObject()->IsText()) - return GetOrCreate(*cursor); + for (cursor.MoveToPrevious(); cursor; cursor.MoveToPrevious()) { + if (cursor.Current().GetLayoutObject()->IsText()) + return GetOrCreate(cursor); } return nullptr; } bool NGAbstractInlineTextBox::IsLineBreak() const { - if (!fragment_) - return false; - DCHECK(!NeedsLayout()); - return PhysicalTextFragment().IsLineBreak(); + const NGInlineCursor& cursor = GetCursor(); + return cursor && cursor.Current().IsLineBreak(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h index 1f1d57dcef5..85fec4b6631 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h @@ -9,34 +9,36 @@ namespace blink { +class NGFragmentItem; +class NGInlineCursor; class NGPaintFragment; -class NGPhysicalTextFragment; // The implementation of |AbstractInlineTextBox| for LayoutNG. // See also |LegacyAbstractInlineTextBox| for legacy layout. class CORE_EXPORT NGAbstractInlineTextBox final : public AbstractInlineTextBox { private: // Returns existing or newly created |NGAbstractInlineTextBox|. - // * |fragment| should be attached to |NGPhysicalTextFragment|. + // * |cursor| should be attached to |NGPhysicalTextFragment|. static scoped_refptr<AbstractInlineTextBox> GetOrCreate( - const NGPaintFragment& fragment); - static void WillDestroy(NGPaintFragment*); + const NGInlineCursor& cursor); + static void WillDestroy(const NGInlineCursor& cursor); + static void WillDestroy(const NGPaintFragment* fragment); friend class LayoutText; - friend class NGPaintFragment; public: - ~NGAbstractInlineTextBox() final; - - private: NGAbstractInlineTextBox(LineLayoutText line_layout_item, const NGPaintFragment& fragment); + NGAbstractInlineTextBox(LineLayoutText line_layout_item, + const NGFragmentItem& fragment); - const NGPhysicalTextFragment& PhysicalTextFragment() const; - bool NeedsLayout() const; + ~NGAbstractInlineTextBox() final; + + private: + NGInlineCursor GetCursor() const; + NGInlineCursor GetCursorOnLine() const; + String GetTextContent() const; bool NeedsTrailingSpace() const; - // Returns next fragment associated to |LayoutText|. - const NGPaintFragment* NextTextFragmentForSameLayoutObject() const; // Implementations of AbstractInlineTextBox member functions. void Detach() final; @@ -53,12 +55,10 @@ class CORE_EXPORT NGAbstractInlineTextBox final : public AbstractInlineTextBox { scoped_refptr<AbstractInlineTextBox> PreviousOnLine() const final; bool IsLineBreak() const final; - const NGPaintFragment* fragment_; - - using FragmentToNGAbstractInlineTextBoxHashMap = - HashMap<const NGPaintFragment*, scoped_refptr<AbstractInlineTextBox>>; - static FragmentToNGAbstractInlineTextBoxHashMap* - g_abstract_inline_text_box_map_; + union { + const NGPaintFragment* fragment_; + const NGFragmentItem* fragment_item_; + }; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.cc deleted file mode 100644 index f9059f05c5f..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" - -#include "third_party/blink/renderer/core/layout/layout_block.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" -#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" - -namespace blink { - -const unsigned NGBaselineRequest::kTypeIdCount; -const LayoutUnit NGBaselineList::kEmptyOffset; - -bool NGBaselineRequest::operator==(const NGBaselineRequest& other) const { - return algorithm_type_ == other.algorithm_type_ && - baseline_type_ == other.baseline_type_; -} - -bool NGBaselineRequestList::operator==( - const NGBaselineRequestList& other) const { - return type_id_mask_ == other.type_id_mask_; -} - -void NGBaselineRequestList::push_back(const NGBaselineRequest& request) { - type_id_mask_ |= 1 << request.TypeId(); -} - -void NGBaselineRequestList::AppendVector( - const NGBaselineRequestList& requests) { - type_id_mask_ |= requests.type_id_mask_; -} - -bool NGBaseline::ShouldPropagateBaselines(const NGLayoutInputNode node) { - if (node.IsInline()) - return true; - - return ShouldPropagateBaselines(node.GetLayoutBox()); -} - -bool NGBaseline::ShouldPropagateBaselines(LayoutBox* layout_box) { - // Test if this node should use its own box to synthesize the baseline. - if (!layout_box->IsLayoutBlock() || - layout_box->IsFloatingOrOutOfFlowPositioned() || - layout_box->IsWritingModeRoot()) - return false; - - // If this node is LayoutBlock that uses old layout, this may be a subclass - // that overrides baseline functions. Propagate baseline requests so that we - // call virtual functions. - if (!NGBlockNode(layout_box).CanUseNewLayout()) - return true; - - return true; -} - -NGBaselineList::NGBaselineList() { - std::fill(std::begin(offsets_), std::end(offsets_), kEmptyOffset); -} - -bool NGBaselineList::IsEmpty() const { - for (LayoutUnit offset : offsets_) { - if (offset != kEmptyOffset) - return false; - } - return true; -} - -base::Optional<LayoutUnit> NGBaselineList::Offset( - const NGBaselineRequest request) const { - LayoutUnit offset = offsets_[request.TypeId()]; - if (offset != kEmptyOffset) - return offset; - return base::nullopt; -} - -void NGBaselineList::emplace_back(NGBaselineRequest request, - LayoutUnit offset) { - // Round LayoutUnit::Min() because we use it for an empty value. - DCHECK_EQ(kEmptyOffset, LayoutUnit::Min()) - << "Change the rounding if kEmptyOffset was changed"; - if (UNLIKELY(offset == LayoutUnit::Min())) - offset = LayoutUnit::NearlyMin(); - DCHECK_NE(offset, kEmptyOffset); - offsets_[request.TypeId()] = offset; -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h deleted file mode 100644 index 64426201e06..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_BASELINE_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_BASELINE_H_ - -#include "base/optional.h" -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/fonts/font_baseline.h" -#include "third_party/blink/renderer/platform/geometry/layout_unit.h" -#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" - -namespace blink { - -class LayoutBox; -class NGLayoutInputNode; - -enum class NGBaselineAlgorithmType { - // Compute baselines for atomic inlines. - kAtomicInline, - // Compute baseline of first line box. - kFirstLine -}; - -// Baselines are products of layout. -// To compute baseline, add requests to NGConstraintSpace and run Layout(). -class CORE_EXPORT NGBaselineRequest { - DISALLOW_NEW(); - - public: - NGBaselineRequest(NGBaselineAlgorithmType algorithm_type, - FontBaseline baseline_type) - : algorithm_type_(static_cast<unsigned>(algorithm_type)), - baseline_type_(static_cast<unsigned>(baseline_type)) {} - - NGBaselineAlgorithmType AlgorithmType() const { - return static_cast<NGBaselineAlgorithmType>(algorithm_type_); - } - - FontBaseline BaselineType() const { - return static_cast<FontBaseline>(baseline_type_); - } - - bool operator==(const NGBaselineRequest& other) const; - bool operator!=(const NGBaselineRequest& other) const { - return !(*this == other); - } - - private: - // TypeId is an integer that identifies all combinations of - // |NGBaselineRequest|. Visible only to |NGBaselineRequestList| and - // |NGBaselineList|. - static constexpr unsigned kTypeIdCount = 4; - unsigned TypeId() const { return algorithm_type_ | (baseline_type_ << 1); } - static NGBaselineRequest FromTypeId(unsigned type_id) { - DCHECK_LE(type_id, kTypeIdCount); - return NGBaselineRequest(static_cast<NGBaselineAlgorithmType>(type_id & 1), - static_cast<FontBaseline>((type_id >> 1) & 1)); - } - friend class NGBaselineList; - friend class NGBaselineRequestList; - friend class NGBaselineTest; - - unsigned algorithm_type_ : 1; // NGBaselineAlgorithmType - unsigned baseline_type_ : 1; // FontBaseline -}; - -// A list of |NGBaselineRequest| in a packed format, with similar interface as -// |Vector|. -class CORE_EXPORT NGBaselineRequestList { - DISALLOW_NEW(); - - public: - NGBaselineRequestList() = default; - - bool IsEmpty() const { return !type_id_mask_; } - - bool operator==(const NGBaselineRequestList& other) const; - - void push_back(const NGBaselineRequest& request); - void AppendVector(const NGBaselineRequestList& requests); - - class const_iterator { - DISALLOW_NEW(); - - public: - const_iterator() : type_id_(NGBaselineRequest::kTypeIdCount), mask_(0) {} - explicit const_iterator(unsigned mask) : type_id_(0), mask_(mask) { - if (!(mask_ & 1)) - ++(*this); - } - - const NGBaselineRequest operator*() const { - return NGBaselineRequest::FromTypeId(type_id_); - } - bool operator!=(const const_iterator& other) const { - return type_id_ != other.type_id_; - } - void operator++() { - while (type_id_ < NGBaselineRequest::kTypeIdCount) { - ++type_id_; - mask_ >>= 1; - if (mask_ & 1) - break; - } - } - - private: - unsigned type_id_; - unsigned mask_; - }; - - const_iterator begin() const { return const_iterator(type_id_mask_); } - const_iterator end() const { return const_iterator(); } - - private: - // Serialize/deserialize to a bit fields. - static constexpr unsigned kSerializedBits = NGBaselineRequest::kTypeIdCount; - unsigned Serialize() const { return type_id_mask_; } - explicit NGBaselineRequestList(unsigned serialized) - : type_id_mask_(serialized) {} - friend class NGConstraintSpace; - friend class NGConstraintSpaceBuilder; - - unsigned type_id_mask_ = 0; -}; - -// Represents a computed baseline position. -struct CORE_EXPORT NGBaseline { - NGBaselineRequest request; - LayoutUnit offset; - - // @return if the node needs to propagate baseline requests/results. - static bool ShouldPropagateBaselines(const NGLayoutInputNode); - static bool ShouldPropagateBaselines(LayoutBox*); -}; - -// A list of |NGBaseline| in a packed format, with similar interface as -// |Vector|. -class CORE_EXPORT NGBaselineList { - DISALLOW_NEW(); - - public: - NGBaselineList(); - - bool IsEmpty() const; - - base::Optional<LayoutUnit> Offset(const NGBaselineRequest request) const; - - void emplace_back(NGBaselineRequest request, LayoutUnit offset); - -#if DCHECK_IS_ON() - bool operator==(const NGBaselineList& other) const { - for (wtf_size_t i = 0; i < NGBaselineRequest::kTypeIdCount; ++i) { - if (offsets_[i] != other.offsets_[i]) - return false; - } - - return true; - } -#endif - - class const_iterator { - public: - explicit const_iterator(unsigned type_id, const LayoutUnit* offset) - : type_id_(type_id), offset_(offset) { - DCHECK(offset); - if (*offset == kEmptyOffset) - ++(*this); - } - const_iterator() - : type_id_(NGBaselineRequest::kTypeIdCount), offset_(nullptr) {} - - const NGBaseline operator*() const { - return NGBaseline{NGBaselineRequest::FromTypeId(type_id_), *offset_}; - } - bool operator!=(const const_iterator& other) const { - return type_id_ != other.type_id_; - } - void operator++() { - while (type_id_ < NGBaselineRequest::kTypeIdCount) { - ++type_id_; - ++offset_; - if (type_id_ < NGBaselineRequest::kTypeIdCount && - *offset_ != kEmptyOffset) - break; - } - } - - private: - unsigned type_id_; - const LayoutUnit* offset_; - }; - - const_iterator begin() const { return const_iterator(0, offsets_); } - const_iterator end() const { return const_iterator(); } - - private: - static constexpr LayoutUnit kEmptyOffset = LayoutUnit::Min(); - - LayoutUnit offsets_[NGBaselineRequest::kTypeIdCount]; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_BASELINE_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc deleted file mode 100644 index 5278637e097..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" - -namespace blink { - -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; - -class NGBaselineTest : public testing::Test { - public: - static NGBaselineRequest RequestFromTypeId(unsigned type_id) { - return NGBaselineRequest::FromTypeId(type_id); - } - - static Vector<NGBaselineRequest> ToList(NGBaselineRequestList requests) { - Vector<NGBaselineRequest> list; - for (const NGBaselineRequest request : requests) - list.push_back(request); - return list; - } -}; - -struct NGBaselineRequestListTestData { - unsigned count; - unsigned type_ids[4]; -} baseline_request_list_test_data[] = { - {0, {}}, {1, {0}}, {1, {1}}, {1, {2}}, - {1, {3}}, {2, {0, 1}}, {2, {0, 2}}, {2, {1, 3}}, - {3, {0, 1, 2}}, {3, {0, 2, 3}}, {3, {1, 2, 3}}, {4, {0, 1, 2, 3}}, -}; - -class NGBaselineRequestListDataTest - : public NGBaselineTest, - public testing::WithParamInterface<NGBaselineRequestListTestData> {}; - -INSTANTIATE_TEST_SUITE_P(NGBaselineTest, - NGBaselineRequestListDataTest, - testing::ValuesIn(baseline_request_list_test_data)); - -TEST_P(NGBaselineRequestListDataTest, Data) { - const auto& data = GetParam(); - NGBaselineRequestList requests; - Vector<NGBaselineRequest> expected; - for (unsigned i = 0; i < data.count; i++) { - NGBaselineRequest request = RequestFromTypeId(data.type_ids[i]); - requests.push_back(request); - expected.push_back(request); - } - - EXPECT_EQ(requests.IsEmpty(), !data.count); - - Vector<NGBaselineRequest> actual = ToList(requests); - EXPECT_THAT(actual, expected); -} - -TEST_F(NGBaselineTest, BaselineList) { - NGBaselineList list; - EXPECT_TRUE(list.IsEmpty()); - - NGBaselineRequest request(NGBaselineAlgorithmType::kFirstLine, - FontBaseline::kAlphabeticBaseline); - list.emplace_back(request, LayoutUnit(123)); - EXPECT_FALSE(list.IsEmpty()); - EXPECT_EQ(list.Offset(request), LayoutUnit(123)); - EXPECT_FALSE(list.Offset({NGBaselineAlgorithmType::kFirstLine, - FontBaseline::kIdeographicBaseline})); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc index b01d591bf48..ed88fbe192b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc @@ -90,11 +90,11 @@ CaretPositionResolution TryResolveCaretPositionInTextFragment( const NGInlineCursor& cursor, unsigned offset, TextAffinity affinity) { - if (cursor.IsGeneratedText()) + if (cursor.Current().IsGeneratedText()) return CaretPositionResolution(); const NGOffsetMapping& mapping = - *NGOffsetMapping::GetFor(cursor.CurrentLayoutObject()); + *NGOffsetMapping::GetFor(cursor.Current().GetLayoutObject()); // A text fragment natually allows caret placement in offset range // [StartOffset(), EndOffset()], i.e., from before the first character to @@ -106,12 +106,13 @@ CaretPositionResolution TryResolveCaretPositionInTextFragment( // Note that we don't ignore other characters that are not in fragments. For // example, a trailing space of a line is not in any fragment, but its two // sides are still different caret positions, so we don't ignore it. - const unsigned start_offset = cursor.CurrentTextStartOffset(); - const unsigned end_offset = cursor.CurrentTextEndOffset(); + const NGTextOffset current_offset = cursor.Current().TextOffset(); + const unsigned start_offset = current_offset.start; + const unsigned end_offset = current_offset.end; if (offset < start_offset && !mapping.HasBidiControlCharactersOnly(offset, start_offset)) return CaretPositionResolution(); - if (offset > cursor.CurrentTextEndOffset() && + if (offset > current_offset.end && !mapping.HasBidiControlCharactersOnly(end_offset, offset)) return CaretPositionResolution(); @@ -129,7 +130,7 @@ CaretPositionResolution TryResolveCaretPositionInTextFragment( return {ResolutionType::kResolved, candidate}; } - if (offset == end_offset && !cursor.IsLineBreak() && + if (offset == end_offset && !cursor.Current().IsLineBreak() && CanResolveCaretPositionAfterFragment(cursor, affinity)) { return {ResolutionType::kResolved, candidate}; } @@ -157,7 +158,7 @@ CaretPositionResolution TryResolveCaretPositionByBoxFragmentSide( const NGInlineCursor& cursor, unsigned offset, TextAffinity affinity) { - const Node* const node = cursor.CurrentNode(); + const Node* const node = cursor.Current().GetNode(); // There is no caret position at a pseudo or generated box side. if (!node || node->IsPseudoElement()) { // TODO(xiaochengh): This leads to false negatives for, e.g., RUBY, where an @@ -192,9 +193,9 @@ CaretPositionResolution TryResolveCaretPositionWithFragment( const NGInlineCursor& cursor, unsigned offset, TextAffinity affinity) { - if (cursor.IsText()) + if (cursor.Current().IsText()) return TryResolveCaretPositionInTextFragment(cursor, offset, affinity); - if (cursor.IsAtomicInline()) + if (cursor.Current().IsAtomicInline()) return TryResolveCaretPositionByBoxFragmentSide(cursor, offset, affinity); return CaretPositionResolution(); } @@ -207,8 +208,9 @@ bool NeedsBidiAdjustment(const NGCaretPosition& caret_position) { if (caret_position.position_type != NGCaretPositionType::kAtTextOffset) return true; DCHECK(caret_position.text_offset.has_value()); - const unsigned start_offset = caret_position.cursor.CurrentTextStartOffset(); - const unsigned end_offset = caret_position.cursor.CurrentTextEndOffset(); + const NGTextOffset offset = caret_position.cursor.Current().TextOffset(); + const unsigned start_offset = offset.start; + const unsigned end_offset = offset.end; DCHECK_GE(*caret_position.text_offset, start_offset); DCHECK_LE(*caret_position.text_offset, end_offset); // Bidi adjustment is needed only for caret positions at bidi boundaries. @@ -232,10 +234,10 @@ bool IsUpstreamAfterLineBreak(const NGCaretPosition& caret_position) { DCHECK(caret_position.cursor.IsNotNull()); DCHECK(caret_position.text_offset.has_value()); - if (!caret_position.cursor.IsLineBreak()) + if (!caret_position.cursor.Current().IsLineBreak()) return false; return *caret_position.text_offset == - caret_position.cursor.CurrentTextEndOffset(); + caret_position.cursor.Current().TextEndOffset(); } NGCaretPosition BetterCandidateBetween(const NGCaretPosition& current, @@ -320,22 +322,24 @@ PositionWithAffinity NGCaretPosition::ToPositionInDOMTreeWithAffinity() const { return PositionWithAffinity(); switch (position_type) { case NGCaretPositionType::kBeforeBox: - if (cursor.CurrentNode()) + if (cursor.Current().GetNode()) return PositionWithAffinity(); - return PositionWithAffinity(Position::BeforeNode(*cursor.CurrentNode()), - TextAffinity::kDownstream); + return PositionWithAffinity( + Position::BeforeNode(*cursor.Current().GetNode()), + TextAffinity::kDownstream); case NGCaretPositionType::kAfterBox: - if (cursor.CurrentNode()) + if (cursor.Current().GetNode()) return PositionWithAffinity(); - return PositionWithAffinity(Position::AfterNode(*cursor.CurrentNode()), - TextAffinity::kUpstreamIfPossible); + return PositionWithAffinity( + Position::AfterNode(*cursor.Current().GetNode()), + TextAffinity::kUpstreamIfPossible); case NGCaretPositionType::kAtTextOffset: - // In case of ::first-letter, |cursor.CurrentNode()| is null. + // In case of ::first-letter, |cursor.Current().GetNode()| is null. DCHECK(text_offset.has_value()); const NGOffsetMapping* mapping = - NGOffsetMapping::GetFor(cursor.CurrentLayoutObject()); + NGOffsetMapping::GetFor(cursor.Current().GetLayoutObject()); const TextAffinity affinity = - *text_offset == cursor.CurrentTextEndOffset() + *text_offset == cursor.Current().TextEndOffset() ? TextAffinity::kUpstreamIfPossible : TextAffinity::kDownstream; const Position position = affinity == TextAffinity::kDownstream diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc index 7977e5227cf..bccd83f3551 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc @@ -17,28 +17,29 @@ namespace { PhysicalRect ComputeLocalCaretRectByBoxSide(const NGInlineCursor& cursor, NGCaretPositionType position_type) { - const bool is_horizontal = cursor.CurrentStyle().IsHorizontalWritingMode(); + const bool is_horizontal = cursor.Current().Style().IsHorizontalWritingMode(); NGInlineCursor line_box(cursor); line_box.MoveToContainingLine(); DCHECK(line_box); const PhysicalOffset offset_to_line_box = - cursor.CurrentOffset() - line_box.CurrentOffset(); - LayoutUnit caret_height = is_horizontal ? line_box.CurrentSize().height - : line_box.CurrentSize().width; + cursor.Current().OffsetInContainerBlock() - + line_box.Current().OffsetInContainerBlock(); + LayoutUnit caret_height = is_horizontal ? line_box.Current().Size().height + : line_box.Current().Size().width; LayoutUnit caret_top = is_horizontal ? -offset_to_line_box.top : -offset_to_line_box.left; const LocalFrameView* frame_view = - cursor.CurrentLayoutObject()->GetDocument().View(); + cursor.Current().GetLayoutObject()->GetDocument().View(); LayoutUnit caret_width = frame_view->CaretWidth(); - const bool is_ltr = IsLtr(cursor.CurrentResolvedDirection()); + const bool is_ltr = IsLtr(cursor.Current().ResolvedDirection()); LayoutUnit caret_left; if (is_ltr != (position_type == NGCaretPositionType::kBeforeBox)) { if (is_horizontal) - caret_left = cursor.CurrentSize().width - caret_width; + caret_left = cursor.Current().Size().width - caret_width; else - caret_left = cursor.CurrentSize().height - caret_width; + caret_left = cursor.Current().Size().height - caret_width; } if (!is_horizontal) { @@ -53,22 +54,22 @@ PhysicalRect ComputeLocalCaretRectByBoxSide(const NGInlineCursor& cursor, PhysicalRect ComputeLocalCaretRectAtTextOffset(const NGInlineCursor& cursor, unsigned offset) { - DCHECK(cursor.IsText()); - DCHECK_GE(offset, cursor.CurrentTextStartOffset()); - DCHECK_LE(offset, cursor.CurrentTextEndOffset()); + DCHECK(cursor.Current().IsText()); + DCHECK_GE(offset, cursor.Current().TextStartOffset()); + DCHECK_LE(offset, cursor.Current().TextEndOffset()); const LocalFrameView* frame_view = - cursor.CurrentLayoutObject()->GetDocument().View(); + cursor.Current().GetLayoutObject()->GetDocument().View(); LayoutUnit caret_width = frame_view->CaretWidth(); - const bool is_horizontal = cursor.CurrentStyle().IsHorizontalWritingMode(); + const bool is_horizontal = cursor.Current().Style().IsHorizontalWritingMode(); - LayoutUnit caret_height = - is_horizontal ? cursor.CurrentSize().height : cursor.CurrentSize().width; + LayoutUnit caret_height = is_horizontal ? cursor.Current().Size().height + : cursor.Current().Size().width; LayoutUnit caret_top; LayoutUnit caret_left = cursor.InlinePositionForOffset(offset); - if (!cursor.IsLineBreak()) + if (!cursor.Current().IsLineBreak()) caret_left -= caret_width / 2; if (!is_horizontal) { @@ -77,16 +78,17 @@ PhysicalRect ComputeLocalCaretRectAtTextOffset(const NGInlineCursor& cursor, } // Adjust the location to be relative to the inline formatting context. - PhysicalOffset caret_location = - PhysicalOffset(caret_left, caret_top) + cursor.CurrentOffset(); + PhysicalOffset caret_location = PhysicalOffset(caret_left, caret_top) + + cursor.Current().OffsetInContainerBlock(); const PhysicalSize caret_size(caret_width, caret_height); const NGPhysicalBoxFragment& fragmentainer = - *cursor.CurrentLayoutObject()->ContainingBlockFlowFragment(); + *cursor.Current().GetLayoutObject()->ContainingBlockFlowFragment(); NGInlineCursor line_box(cursor); line_box.MoveToContainingLine(); - const PhysicalOffset line_box_offset = line_box.CurrentOffset(); - const PhysicalRect line_box_rect(line_box_offset, line_box.CurrentSize()); + const PhysicalOffset line_box_offset = + line_box.Current().OffsetInContainerBlock(); + const PhysicalRect line_box_rect(line_box_offset, line_box.Current().Size()); // For horizontal text, adjust the location in the x direction to ensure that // it completely falls in the union of line box and containing block, and @@ -116,17 +118,17 @@ LocalCaretRect ComputeLocalCaretRect(const NGCaretPosition& caret_position) { return LocalCaretRect(); const LayoutObject* layout_object = - caret_position.cursor.CurrentLayoutObject(); + caret_position.cursor.Current().GetLayoutObject(); switch (caret_position.position_type) { case NGCaretPositionType::kBeforeBox: case NGCaretPositionType::kAfterBox: { - DCHECK(!caret_position.cursor.IsText()); + DCHECK(!caret_position.cursor.Current().IsText()); const PhysicalRect fragment_local_rect = ComputeLocalCaretRectByBoxSide( caret_position.cursor, caret_position.position_type); return {layout_object, fragment_local_rect}; } case NGCaretPositionType::kAtTextOffset: { - DCHECK(caret_position.cursor.IsText()); + DCHECK(caret_position.cursor.Current().IsText()); DCHECK(caret_position.text_offset.has_value()); const PhysicalRect caret_rect = ComputeLocalCaretRectAtTextOffset( caret_position.cursor, *caret_position.text_offset); @@ -151,12 +153,12 @@ LocalCaretRect ComputeLocalSelectionRect( DCHECK(line_box); PhysicalRect rect = caret_rect.rect; - if (caret_position.cursor.CurrentStyle().IsHorizontalWritingMode()) { - rect.SetY(line_box.CurrentOffset().top); - rect.SetHeight(line_box.CurrentSize().height); + if (caret_position.cursor.Current().Style().IsHorizontalWritingMode()) { + rect.SetY(line_box.Current().OffsetInContainerBlock().top); + rect.SetHeight(line_box.Current().Size().height); } else { - rect.SetX(line_box.CurrentOffset().left); - rect.SetHeight(line_box.CurrentSize().width); + rect.SetX(line_box.Current().OffsetInContainerBlock().left); + rect.SetHeight(line_box.Current().Size().width); } return {caret_rect.layout_object, rect}; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc index c184209b8f2..63a23dff82b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc @@ -9,13 +9,14 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.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_item.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" namespace blink { NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text) : layout_object_(text.GetLayoutObject()), - text_({text.TextShapeResult(), text.StartOffset(), text.EndOffset()}), + text_({text.TextShapeResult(), text.TextOffset()}), rect_({PhysicalOffset(), text.Size()}), type_(kText), sub_type_(static_cast<unsigned>(text.TextType())), @@ -23,12 +24,12 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text) is_generated_text_(text.IsGeneratedText()), is_hidden_for_paint_(text.IsHiddenForPaint()), text_direction_(static_cast<unsigned>(text.ResolvedDirection())), - ink_overflow_computed_(false) { - DCHECK_LE(text_.start_offset, text_.end_offset); + ink_overflow_computed_(false), + is_first_for_node_(text.IsFirstForNode()) { #if DCHECK_IS_ON() if (text_.shape_result) { - DCHECK_EQ(text_.shape_result->StartIndex(), text_.start_offset); - DCHECK_EQ(text_.shape_result->EndIndex(), text_.end_offset); + DCHECK_EQ(text_.shape_result->StartIndex(), StartOffset()); + DCHECK_EQ(text_.shape_result->EndIndex(), EndOffset()); } #endif if (text.TextType() == NGPhysicalTextFragment::kGeneratedText) { @@ -38,6 +39,7 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text) // |generated_text_.text_| instead copying, |generated_text_.text = ...|. new (&generated_text_.text) String(text.Text().ToString()); } + DCHECK(!IsFormattingContextRoot()); } NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line, @@ -46,22 +48,45 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line, line_({&line, item_count}), rect_({PhysicalOffset(), line.Size()}), type_(kLine), + sub_type_(static_cast<unsigned>(line.LineBoxType())), style_variant_(static_cast<unsigned>(line.StyleVariant())), is_hidden_for_paint_(false), text_direction_(static_cast<unsigned>(line.BaseDirection())), - ink_overflow_computed_(false) {} + ink_overflow_computed_(false), + is_first_for_node_(true) { + DCHECK(!IsFormattingContextRoot()); +} NGFragmentItem::NGFragmentItem(const NGPhysicalBoxFragment& box, - wtf_size_t item_count, TextDirection resolved_direction) : layout_object_(box.GetLayoutObject()), - box_({&box, item_count}), + box_({&box, 1}), rect_({PhysicalOffset(), box.Size()}), type_(kBox), style_variant_(static_cast<unsigned>(box.StyleVariant())), is_hidden_for_paint_(box.IsHiddenForPaint()), text_direction_(static_cast<unsigned>(resolved_direction)), - ink_overflow_computed_(false) {} + ink_overflow_computed_(false), + is_first_for_node_(box.IsFirstForNode()) { + DCHECK_EQ(IsFormattingContextRoot(), box.IsFormattingContextRoot()); +} + +NGFragmentItem::NGFragmentItem(const NGInlineItem& inline_item, + const PhysicalSize& size) + : layout_object_(inline_item.GetLayoutObject()), + box_({nullptr, 1}), + rect_({PhysicalOffset(), size}), + type_(kBox), + style_variant_(static_cast<unsigned>(inline_item.StyleVariant())), + is_hidden_for_paint_(false), + text_direction_(static_cast<unsigned>(TextDirection::kLtr)), + ink_overflow_computed_(false), + is_first_for_node_(true) { + DCHECK_EQ(inline_item.Type(), NGInlineItem::kOpenTag); + DCHECK(layout_object_); + DCHECK(layout_object_->IsLayoutInline()); + DCHECK(!IsFormattingContextRoot()); +} NGFragmentItem::~NGFragmentItem() { switch (Type()) { @@ -80,12 +105,32 @@ NGFragmentItem::~NGFragmentItem() { } } -bool NGFragmentItem::HasSameParent(const NGFragmentItem& other) const { +bool NGFragmentItem::IsSiblingOf(const NGFragmentItem& other) const { if (!GetLayoutObject()) return !other.GetLayoutObject(); if (!other.GetLayoutObject()) return false; - return GetLayoutObject()->Parent() == other.GetLayoutObject()->Parent(); + if (GetLayoutObject()->Parent() == other.GetLayoutObject()->Parent()) + return true; + // To traverse list marker and line box of <li> with |MoveToNextSibling()|, + // we think list marker and <li> are sibling. + // See hittesting/culled-inline-crash.html (skip list marker) + // See fast/events/onclick-list-marker.html (hit on list marker) + if (IsListMarker()) + return GetLayoutObject()->Parent() == other.GetLayoutObject(); + if (other.IsListMarker()) + return other.GetLayoutObject()->Parent() == GetLayoutObject(); + return false; +} + +bool NGFragmentItem::IsInlineBox() const { + if (Type() == kBox) { + if (const NGPhysicalBoxFragment* box = BoxFragment()) + return box->IsInlineBox(); + DCHECK(GetLayoutObject()->IsLayoutInline()); + return true; + } + return false; } bool NGFragmentItem::IsAtomicInline() const { @@ -96,11 +141,16 @@ bool NGFragmentItem::IsAtomicInline() const { return false; } -bool NGFragmentItem::IsEmptyLineBox() const { - // TODO(yosin): Implement |NGFragmentItem::IsEmptyLineBox()|. +bool NGFragmentItem::IsFloating() const { + if (const NGPhysicalBoxFragment* box = BoxFragment()) + return box->IsFloating(); return false; } +bool NGFragmentItem::IsEmptyLineBox() const { + return LineBoxType() == NGLineBoxType::kEmptyLineBox; +} + bool NGFragmentItem::IsGeneratedText() const { if (Type() == kText || Type() == kGeneratedText) return is_generated_text_; @@ -109,8 +159,7 @@ bool NGFragmentItem::IsGeneratedText() const { } bool NGFragmentItem::IsListMarker() const { - // TODO(yosin): Implement |NGFragmentItem::IsListMarker()|. - return false; + return layout_object_ && layout_object_->IsLayoutNGOutsideListMarker(); } bool NGFragmentItem::HasOverflowClip() const { @@ -162,11 +211,6 @@ PhysicalRect NGFragmentItem::InkOverflow() const { return container_ink_overflow.SelfAndContentsInkOverflow(); } -PositionWithAffinity NGFragmentItem::PositionForPoint( - const PhysicalOffset&) const { - return PositionWithAffinity(); -} - const ShapeResultView* NGFragmentItem::TextShapeResult() const { if (Type() == kText) return text_.shape_result.get(); @@ -176,29 +220,19 @@ const ShapeResultView* NGFragmentItem::TextShapeResult() const { return nullptr; } -unsigned NGFragmentItem::StartOffset() const { +NGTextOffset NGFragmentItem::TextOffset() const { if (Type() == kText) - return text_.start_offset; + return text_.text_offset; if (Type() == kGeneratedText) - return 0; + return {0, generated_text_.text.length()}; NOTREACHED(); - return 0; -} - -unsigned NGFragmentItem::EndOffset() const { - if (Type() == kText) - return text_.end_offset; - if (Type() == kGeneratedText) - return generated_text_.text.length(); - NOTREACHED(); - return 0; + return {}; } StringView NGFragmentItem::Text(const NGFragmentItems& items) const { if (Type() == kText) { - DCHECK_LE(text_.start_offset, text_.end_offset); - return StringView(items.Text(UsesFirstLineStyle()), text_.start_offset, - text_.end_offset - text_.start_offset); + return StringView(items.Text(UsesFirstLineStyle()), text_.text_offset.start, + text_.text_offset.Length()); } if (Type() == kGeneratedText) return GeneratedText(); @@ -209,8 +243,8 @@ StringView NGFragmentItem::Text(const NGFragmentItems& items) const { NGTextFragmentPaintInfo NGFragmentItem::TextPaintInfo( const NGFragmentItems& items) const { if (Type() == kText) { - return {items.Text(UsesFirstLineStyle()), text_.start_offset, - text_.end_offset, text_.shape_result.get()}; + return {items.Text(UsesFirstLineStyle()), text_.text_offset.start, + text_.text_offset.end, text_.shape_result.get()}; } if (Type() == kGeneratedText) { return {generated_text_.text, 0, generated_text_.text.length(), @@ -231,6 +265,24 @@ TextDirection NGFragmentItem::ResolvedDirection() const { } String NGFragmentItem::DebugName() const { + // TODO(yosin): Once |NGPaintFragment| is removed, we should get rid of + // following if-statements. + // For ease of rebasing, we use same |DebugName()| as |NGPaintFrgment|. + if (Type() == NGFragmentItem::kBox) { + StringBuilder name; + name.Append("NGPhysicalBoxFragment "); + name.Append(layout_object_->DebugName()); + return name.ToString(); + } + if (Type() == NGFragmentItem::kText) { + StringBuilder name; + name.Append("NGPhysicalTextFragment '"); + name.Append(Text(*layout_object_->ContainingBlockFlowFragment()->Items())); + name.Append('\''); + return name.ToString(); + } + if (Type() == NGFragmentItem::kLine) + return "NGPhysicalLineBoxFragment"; return "NGFragmentItem"; } @@ -241,17 +293,28 @@ IntRect NGFragmentItem::VisualRect() const { return GetLayoutObject()->VisualRectForInlineBox(); } +IntRect NGFragmentItem::PartialInvalidationVisualRect() const { + // TODO(yosin): Need to reconsider the storage of |VisualRect|, to integrate + // better with |FragmentData| and to avoid dependency to |LayoutObject|. + DCHECK(GetLayoutObject()); + return GetLayoutObject()->PartialInvalidationVisualRectForInlineBox(); +} + PhysicalRect NGFragmentItem::LocalVisualRectFor( const LayoutObject& layout_object) { DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()); PhysicalRect visual_rect; - for (const NGFragmentItem& item : ItemsFor(layout_object)) { + NGInlineCursor cursor; + for (cursor.MoveTo(layout_object); cursor; + cursor.MoveToNextForSameLayoutObject()) { + DCHECK(cursor.Current().Item()); + const NGFragmentItem& item = *cursor.Current().Item(); if (UNLIKELY(item.IsHiddenForPaint())) continue; PhysicalRect child_visual_rect = item.SelfInkOverflow(); - child_visual_rect.offset += item.Offset(); + child_visual_rect.offset += item.OffsetInContainerBlock(); visual_rect.Unite(child_visual_rect); } return visual_rect; @@ -269,7 +332,7 @@ PhysicalRect NGFragmentItem::RecalcInkOverflowForCursor( if (item->HasSelfPaintingLayer()) continue; if (!child_rect.IsEmpty()) { - child_rect.offset += item->Offset(); + child_rect.offset += item->OffsetInContainerBlock(); contents_ink_overflow.Unite(child_rect); } } @@ -322,14 +385,22 @@ void NGFragmentItem::RecalcInkOverflow( cursor->MoveToNextSibling(); PhysicalRect contents_rect = RecalcInkOverflowForCursor(&descendants_cursor); + // |contents_rect| is relative to the inline formatting context. Make it + // relative to |this|. + contents_rect.offset -= OffsetInContainerBlock(); + // Compute the self ink overflow. PhysicalRect self_rect; if (Type() == kLine) { // Line boxes don't have self overflow. Compute content overflow only. *self_and_contents_rect_out = contents_rect; - } else if (const NGPhysicalBoxFragment* box_fragment = BoxFragment()) { - DCHECK(box_fragment->IsInlineBox()); - self_rect = box_fragment->ComputeSelfInkOverflow(); + } else if (Type() == kBox) { + if (const NGPhysicalBoxFragment* box_fragment = BoxFragment()) { + DCHECK(box_fragment->IsInlineBox()); + self_rect = box_fragment->ComputeSelfInkOverflow(); + } else { + self_rect = LocalRect(); + } *self_and_contents_rect_out = UnionRect(self_rect, contents_rect); } else { NOTREACHED(); @@ -403,49 +474,6 @@ unsigned NGFragmentItem::TextOffsetForPoint( return inline_offset <= size.inline_size / 2 ? StartOffset() : EndOffset(); } -NGFragmentItem::ItemsForLayoutObject NGFragmentItem::ItemsFor( - const LayoutObject& layout_object) { - DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()); - DCHECK(layout_object.IsText() || layout_object.IsLayoutInline() || - (layout_object.IsBox() && layout_object.IsInline())); - - if (const LayoutBlockFlow* block_flow = - layout_object.RootInlineFormattingContext()) { - if (const NGPhysicalBoxFragment* fragment = block_flow->CurrentFragment()) { - if (wtf_size_t index = layout_object.FirstInlineFragmentItemIndex()) { - const auto& items = fragment->Items()->Items(); - return ItemsForLayoutObject(items, index, items[index].get()); - } - // TODO(yosin): Once we update all usages of |FirstInlineFragment()|, - // we should get rid of below code. - if (const NGFragmentItems* items = fragment->Items()) { - for (unsigned i = 0; i < items->Items().size(); ++i) { - const NGFragmentItem* item = items->Items()[i].get(); - if (item->GetLayoutObject() == &layout_object) - return ItemsForLayoutObject(items->Items(), i, item); - } - } - } - } - - return ItemsForLayoutObject(); -} - -NGFragmentItem::ItemsForLayoutObject::Iterator& -NGFragmentItem::ItemsForLayoutObject::Iterator::operator++() { - // TODO(kojii): This is a hot function needed by paint and several other - // operations. Make this fast, by not iterating. - if (!current_) - return *this; - if (!current_->delta_to_next_for_same_layout_object_) { - current_ = nullptr; - return *this; - } - index_ += current_->delta_to_next_for_same_layout_object_; - current_ = (*items_)[index_].get(); - return *this; -} - std::ostream& operator<<(std::ostream& ostream, const NGFragmentItem& item) { ostream << "{"; switch (item.Type()) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h index 112464bdc50..c07af90d3d8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h" #include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h" #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h" @@ -18,6 +19,7 @@ namespace blink { class NGFragmentItems; class NGInlineBreakToken; +class NGInlineItem; struct NGTextFragmentPaintInfo; // This class represents a text run or a box in an inline formatting context. @@ -32,8 +34,7 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { // TODO(kojii): |start_offset| and |end_offset| should match to the offset // in |shape_result|. Consider if we should remove them, or if keeping them // is easier. - const unsigned start_offset; - const unsigned end_offset; + const NGTextOffset text_offset; }; // Represents text generated by the layout engine, e.g., hyphen or ellipsis. struct GeneratedTextItem { @@ -64,8 +65,8 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { // TODO(kojii): Should be able to create without once creating fragments. NGFragmentItem(const NGPhysicalTextFragment& text); NGFragmentItem(const NGPhysicalBoxFragment& box, - wtf_size_t item_count, TextDirection resolved_direction); + NGFragmentItem(const NGInlineItem& inline_item, const PhysicalSize& size); NGFragmentItem(const NGPhysicalLineBoxFragment& line, wtf_size_t item_count); ~NGFragmentItem() final; @@ -74,11 +75,29 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { bool IsText() const { return Type() == kText || Type() == kGeneratedText; } bool IsContainer() const { return Type() == kBox || Type() == kLine; } + bool IsInlineBox() const; bool IsAtomicInline() const; + bool IsFloating() const; bool IsEmptyLineBox() const; bool IsHiddenForPaint() const { return is_hidden_for_paint_; } bool IsListMarker() const; + // Return true if this is the first fragment generated from a node. + bool IsFirstForNode() const { + DCHECK(Type() != kLine); + DCHECK(!IsInlineBox() || BoxFragment()); + return is_first_for_node_; + } + + // Return true if this is the last fragment generated from a node. + bool IsLastForNode() const { + // TODO(layout-dev): This doesn't work if the LayoutObject continues in a + // next fragmentainer (we get a false negative here then). + DCHECK(Type() != kLine); + DCHECK(!IsInlineBox() || BoxFragment()); + return !DeltaToNextForSameLayoutObject(); + } + NGStyleVariant StyleVariant() const { return static_cast<NGStyleVariant>(style_variant_); } @@ -100,15 +119,15 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { } Node* GetNode() const { return layout_object_->GetNode(); } Node* NodeForHitTest() const { return layout_object_->NodeForHitTest(); } - bool HasSameParent(const NGFragmentItem& other) const; + bool IsSiblingOf(const NGFragmentItem& other) const; wtf_size_t DeltaToNextForSameLayoutObject() const { return delta_to_next_for_same_layout_object_; } void SetDeltaToNextForSameLayoutObject(wtf_size_t delta); - const PhysicalRect& Rect() const { return rect_; } - const PhysicalOffset& Offset() const { return rect_.offset; } + const PhysicalRect& RectInContainerBlock() const { return rect_; } + const PhysicalOffset& OffsetInContainerBlock() const { return rect_.offset; } const PhysicalSize& Size() const { return rect_.size; } PhysicalRect LocalRect() const { return {PhysicalOffset(), Size()}; } void SetOffset(const PhysicalOffset& offset) { rect_.offset = offset; } @@ -128,6 +147,10 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { return 0; } bool HasChildren() const { return DescendantsCount() > 1; } + void SetDescendantsCount(wtf_size_t count) { + CHECK_EQ(Type(), kBox); + box_.descendants_count = count; + } // Returns |NGPhysicalBoxFragment| if one is associated with this item. const NGPhysicalBoxFragment* BoxFragment() const { @@ -158,58 +181,19 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { return nullptr; } - NGTextFragmentPaintInfo TextPaintInfo(const NGFragmentItems& items) const; + using NGLineBoxType = NGPhysicalLineBoxFragment::NGLineBoxType; + NGLineBoxType LineBoxType() const { + if (Type() == kLine) + return static_cast<NGLineBoxType>(sub_type_); + NOTREACHED() << this; + return NGLineBoxType::kNormalLineBox; + } // DisplayItemClient overrides String DebugName() const override; IntRect VisualRect() const override; + IntRect PartialInvalidationVisualRect() const override; - // Find |NGFragmentItem|s that are associated with a |LayoutObject|. - class CORE_EXPORT ItemsForLayoutObject { - STACK_ALLOCATED(); - - public: - ItemsForLayoutObject() = default; - ItemsForLayoutObject(const Vector<std::unique_ptr<NGFragmentItem>>& items, - unsigned first_index, - const NGFragmentItem* first_item) - : items_(&items), first_item_(first_item), first_index_(first_index) {} - - bool IsEmpty() const { return !items_; } - - class CORE_EXPORT Iterator { - public: - Iterator(const Vector<std::unique_ptr<NGFragmentItem>>* items, - unsigned index, - const NGFragmentItem* item) - : current_(item), items_(items), index_(index) {} - const NGFragmentItem& operator*() const { return *current_; } - const NGFragmentItem& operator->() const { return *current_; } - Iterator& operator++(); - bool operator==(const Iterator& other) const { - return current_ == other.current_; - } - bool operator!=(const Iterator& other) const { - return current_ != other.current_; - } - - private: - const NGFragmentItem* current_; - const Vector<std::unique_ptr<NGFragmentItem>>* items_; - unsigned index_; - }; - using iterator = Iterator; - iterator begin() const { - return Iterator(items_, first_index_, first_item_); - } - iterator end() const { return Iterator(nullptr, 0, nullptr); } - - private: - const Vector<std::unique_ptr<NGFragmentItem>>* items_; - const NGFragmentItem* first_item_; - unsigned first_index_; - }; - static ItemsForLayoutObject ItemsFor(const LayoutObject& layout_object); static PhysicalRect LocalVisualRectFor(const LayoutObject& layout_object); // Re-compute the ink overflow for the |cursor| until its end. @@ -292,16 +276,21 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { return TextType() == NGTextType::kSymbolMarker; } - const ShapeResultView* TextShapeResult() const; + bool IsFormattingContextRoot() const { + return BoxFragment() && !IsInlineBox(); + } - unsigned StartOffset() const; - unsigned EndOffset() const; - unsigned TextLength() const { return EndOffset() - StartOffset(); } + const ShapeResultView* TextShapeResult() const; + NGTextOffset TextOffset() const; + unsigned StartOffset() const { return TextOffset().start; } + unsigned EndOffset() const { return TextOffset().end; } + unsigned TextLength() const { return TextOffset().Length(); } StringView Text(const NGFragmentItems& items) const; String GeneratedText() const { DCHECK_EQ(Type(), kGeneratedText); return generated_text_.text; } + NGTextFragmentPaintInfo TextPaintInfo(const NGFragmentItems& items) const; // Compute the inline position from text offset, in logical coordinate // relative to this fragment. @@ -338,7 +327,6 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { // Converts the given point, relative to the fragment itself, into a position // in DOM tree. - PositionWithAffinity PositionForPoint(const PhysicalOffset&) const; PositionWithAffinity PositionForPointInText( const PhysicalOffset& point, const NGInlineCursor& cursor) const; @@ -370,7 +358,7 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { // Note: We should not add |bidi_level_| because it is used only for layout. unsigned type_ : 2; // ItemType - unsigned sub_type_ : 3; // NGTextType + unsigned sub_type_ : 3; // NGTextType or NGLineBoxType unsigned style_variant_ : 2; // NGStyleVariant // TODO(yosin): We'll remove |is_generated_text_| field when we construct // |NGFragmentItem| without |NGPhysicalTextFragment| because usage of this @@ -383,6 +371,8 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { // Used only when |IsText()| to avoid re-computing ink overflow. unsigned ink_overflow_computed_ : 1; + + unsigned is_first_for_node_ : 1; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc index bc573b0d0d1..3e8f4853a6b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc @@ -8,6 +8,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -22,9 +23,12 @@ class NGFragmentItemTest : public NGLayoutTest, Vector<const NGFragmentItem*> ItemsForAsVector( const LayoutObject& layout_object) { - const auto items = NGFragmentItem::ItemsFor(layout_object); Vector<const NGFragmentItem*> list; - for (const NGFragmentItem& item : items) { + NGInlineCursor cursor; + for (cursor.MoveTo(layout_object); cursor; + cursor.MoveToNextForSameLayoutObject()) { + DCHECK(cursor.Current().Item()); + const NGFragmentItem& item = *cursor.Current().Item(); EXPECT_EQ(item.GetLayoutObject(), &layout_object); list.push_back(&item); } @@ -67,12 +71,12 @@ TEST_F(NGFragmentItemTest, BasicText) { const NGFragmentItem& text1 = *items_for_text[0]; EXPECT_EQ(text1.Type(), NGFragmentItem::kText); EXPECT_EQ(text1.GetLayoutObject(), layout_text); - EXPECT_EQ(text1.Offset(), PhysicalOffset()); + EXPECT_EQ(text1.OffsetInContainerBlock(), PhysicalOffset()); const NGFragmentItem& text2 = *items_for_text[1]; EXPECT_EQ(text2.Type(), NGFragmentItem::kText); EXPECT_EQ(text2.GetLayoutObject(), layout_text); - EXPECT_EQ(text2.Offset(), PhysicalOffset(0, 10)); + EXPECT_EQ(text2.OffsetInContainerBlock(), PhysicalOffset(0, 10)); EXPECT_EQ(IntRect(0, 0, 70, 20), layout_text->FragmentsVisualRectBoundingBox()); @@ -108,7 +112,6 @@ TEST_F(NGFragmentItemTest, BasicInlineBox) { ASSERT_NE(span1, nullptr); Vector<const NGFragmentItem*> items_for_span1 = ItemsForAsVector(*span1); EXPECT_EQ(items_for_span1.size(), 2u); - EXPECT_EQ(IntRect(0, 0, 80, 20), span1->FragmentsVisualRectBoundingBox()); // "span2" doesn't wrap, produces only one fragment. @@ -116,8 +119,52 @@ TEST_F(NGFragmentItemTest, BasicInlineBox) { ASSERT_NE(span2, nullptr); Vector<const NGFragmentItem*> items_for_span2 = ItemsForAsVector(*span2); EXPECT_EQ(items_for_span2.size(), 1u); + EXPECT_EQ(IntRect(0, 20, 80, 10), span2->FragmentsVisualRectBoundingBox()); +} +// Same as |BasicInlineBox| but `<span>`s do not have background. +// They will not need box fragments, but all operations should work the same. +TEST_F(NGFragmentItemTest, CulledInlineBox) { + LoadAhem(); + SetBodyInnerHTML(R"HTML( + <style> + html, body { + margin: 0; + font-family: Ahem; + font-size: 10px; + line-height: 1; + } + #container { + width: 10ch; + } + </style> + <div id="container"> + 000 + <span id="span1">1234 5678</span> + 999 + <span id="span2">12345678</span> + </div> + )HTML"); + + // "span1" wraps, produces two fragments. + const LayoutObject* span1 = GetLayoutObjectByElementId("span1"); + ASSERT_NE(span1, nullptr); + Vector<const NGFragmentItem*> items_for_span1 = ItemsForAsVector(*span1); + EXPECT_EQ(items_for_span1.size(), 2u); + EXPECT_EQ(IntRect(0, 0, 80, 20), span1->FragmentsVisualRectBoundingBox()); + + // "span2" doesn't wrap, produces only one fragment. + const LayoutObject* span2 = GetLayoutObjectByElementId("span2"); + ASSERT_NE(span2, nullptr); + Vector<const NGFragmentItem*> items_for_span2 = ItemsForAsVector(*span2); + EXPECT_EQ(items_for_span2.size(), 1u); EXPECT_EQ(IntRect(0, 20, 80, 10), span2->FragmentsVisualRectBoundingBox()); + + // Except that they do not produce box fragments. + for (const NGFragmentItem* item : items_for_span1) + EXPECT_EQ(item->BoxFragment(), nullptr); + for (const NGFragmentItem* item : items_for_span2) + EXPECT_EQ(item->BoxFragment(), nullptr); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc index 87aba81920f..794995bd7bc 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc @@ -13,4 +13,35 @@ NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder) text_content_(std::move(builder->text_content_)), first_line_text_content_(std::move(builder->first_line_text_content_)) {} +// static +void NGFragmentItems::AssociateWithLayoutObject( + Vector<std::unique_ptr<NGFragmentItem>>* items) { + // items_[0] can be: + // - kBox for list marker, e.g. <li>abc</li> + // - kLine for line, e.g. <div>abc</div> + // Calling get() is necessary below because operator<< in std::unique_ptr is + // a C++20 feature. + // TODO(https://crbug.com/980914): Drop .get() once we move to C++20. + DCHECK(items->IsEmpty() || (*items)[0]->IsContainer()) << (*items)[0].get(); + HashMap<const LayoutObject*, wtf_size_t> last_fragment_map; + for (wtf_size_t index = 1u; index < items->size(); ++index) { + const NGFragmentItem& item = *(*items)[index]; + if (item.Type() == NGFragmentItem::kLine) + continue; + LayoutObject* const layout_object = item.GetMutableLayoutObject(); + DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << item; + auto insert_result = last_fragment_map.insert(layout_object, index); + if (insert_result.is_new_entry) { + layout_object->SetFirstInlineFragmentItemIndex(index); + continue; + } + const wtf_size_t last_index = insert_result.stored_value->value; + insert_result.stored_value->value = index; + DCHECK_GT(last_index, 0u) << item; + DCHECK_LT(last_index, items->size()); + DCHECK_LT(last_index, index); + (*items)[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index); + } +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h index fe62f758e81..76c95f180a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h @@ -28,6 +28,9 @@ class CORE_EXPORT NGFragmentItems { return UNLIKELY(first_line) ? first_line_text_content_ : text_content_; } + static void AssociateWithLayoutObject( + Vector<std::unique_ptr<NGFragmentItem>>* items); + private: // TODO(kojii): inline capacity TBD. Vector<std::unique_ptr<NGFragmentItem>> items_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc index fd54d34056f..243c9d3622c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc @@ -30,8 +30,8 @@ void NGFragmentItemsBuilder::SetCurrentLine( void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line, const LogicalOffset& offset) { DCHECK_EQ(items_.size(), offsets_.size()); -#if DCHECK_IS_ON() DCHECK(!is_converted_to_physical_); +#if DCHECK_IS_ON() DCHECK_EQ(current_line_fragment_, &line); #endif @@ -65,36 +65,40 @@ void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line, void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) { DCHECK_EQ(items_.size(), offsets_.size()); + DCHECK(!is_converted_to_physical_); for (Child* child_iter = child_begin; child_iter != child_end;) { Child& child = *child_iter; if (const NGPhysicalTextFragment* text = child.fragment.get()) { items_.push_back(std::make_unique<NGFragmentItem>(*text)); - offsets_.push_back(child.offset); + offsets_.push_back(child.rect.offset); ++child_iter; continue; } - if (child.layout_result) { + if (child.layout_result || child.inline_item) { // Create an item if this box has no inline children. - const NGPhysicalBoxFragment& box = - To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment()); - // Floats are in the fragment tree, not in the fragment item list. - DCHECK(!box.IsFloating()); + std::unique_ptr<NGFragmentItem> item; + if (child.layout_result) { + const NGPhysicalBoxFragment& box = + To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment()); + item = std::make_unique<NGFragmentItem>(box, child.ResolvedDirection()); + } else { + DCHECK(child.inline_item); + item = std::make_unique<NGFragmentItem>( + *child.inline_item, + ToPhysicalSize(child.rect.size, + child.inline_item->Style()->GetWritingMode())); + } + // Take the fast path when we know |child| does not have child items. if (child.children_count <= 1) { - // Compute |has_floating_descendants_for_paint_| to optimize tree - // traversal in paint. - if (!has_floating_descendants_for_paint_ && box.IsFloating()) - has_floating_descendants_for_paint_ = true; - - DCHECK(child.HasBidiLevel()); - items_.push_back(std::make_unique<NGFragmentItem>( - box, 1, DirectionFromLevel(child.bidi_level))); - offsets_.push_back(child.offset); + items_.push_back(std::move(item)); + offsets_.push_back(child.rect.offset); ++child_iter; continue; } + DCHECK(!item->IsFloating()); // Children of inline boxes are flattened and added to |items_|, with the // count of descendant items to preserve the tree structure. @@ -102,7 +106,7 @@ void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) { // Add an empty item so that the start of the box can be set later. wtf_size_t box_start_index = items_.size(); items_.Grow(box_start_index + 1); - offsets_.push_back(child.offset); + offsets_.push_back(child.rect.offset); // Add all children, including their desendants, skipping this item. CHECK_GE(child.children_count, 1u); // 0 will loop infinitely. @@ -116,9 +120,8 @@ void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) { wtf_size_t item_count = items_.size() - box_start_index; // Create an item for the start of the box. - DCHECK(child.HasBidiLevel()); - items_[box_start_index] = std::make_unique<NGFragmentItem>( - box, item_count, DirectionFromLevel(child.bidi_level)); + item->SetDescendantsCount(item_count); + items_[box_start_index] = std::move(item); continue; } @@ -132,23 +135,36 @@ void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) { void NGFragmentItemsBuilder::AddListMarker( const NGPhysicalBoxFragment& marker_fragment, const LogicalOffset& offset) { + DCHECK(!is_converted_to_physical_); + // Resolved direction matters only for inline items, and outside list markers // are not inline. const TextDirection resolved_direction = TextDirection::kLtr; items_.push_back( - std::make_unique<NGFragmentItem>(marker_fragment, 1, resolved_direction)); + std::make_unique<NGFragmentItem>(marker_fragment, resolved_direction)); offsets_.push_back(offset); } +const Vector<std::unique_ptr<NGFragmentItem>>& NGFragmentItemsBuilder::Items( + WritingMode writing_mode, + TextDirection direction, + const PhysicalSize& outer_size) { + ConvertToPhysical(writing_mode, direction, outer_size); + return items_; +} + // Convert internal logical offsets to physical. Items are kept with logical // offset until outer box size is determined. void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode, TextDirection direction, const PhysicalSize& outer_size) { CHECK_EQ(items_.size(), offsets_.size()); -#if DCHECK_IS_ON() - DCHECK(!is_converted_to_physical_); -#endif + if (is_converted_to_physical_) + return; + + // Children of lines have line-relative offsets. Use line-writing mode to + // convert their logical offsets. + const WritingMode line_writing_mode = ToLineWritingMode(writing_mode); std::unique_ptr<NGFragmentItem>* item_iter = items_.begin(); const LogicalOffset* offset = offsets_.begin(); @@ -165,7 +181,7 @@ void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode, unsigned descendants_count = item->DescendantsCount(); DCHECK(descendants_count); if (descendants_count) { - const PhysicalRect line_box_bounds = item->Rect(); + const PhysicalRect line_box_bounds = item->RectInContainerBlock(); while (--descendants_count) { ++offset; ++item_iter; @@ -175,7 +191,7 @@ void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode, // Use `kLtr` because inline items are after bidi-reoder, and that // their offset is visual, not logical. item->SetOffset( - offset->ConvertToPhysical(writing_mode, TextDirection::kLtr, + offset->ConvertToPhysical(line_writing_mode, TextDirection::kLtr, line_box_bounds.size, item->Size()) + line_box_bounds.offset); } @@ -183,9 +199,17 @@ void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode, } } -#if DCHECK_IS_ON() is_converted_to_physical_ = true; -#endif +} + +base::Optional<LogicalOffset> NGFragmentItemsBuilder::LogicalOffsetFor( + const LayoutObject& layout_object) const { + DCHECK_EQ(items_.size(), offsets_.size()); + for (const std::unique_ptr<NGFragmentItem>& item : items_) { + if (item->GetLayoutObject() == &layout_object) + return offsets_[&item - items_.begin()]; + } + return base::nullopt; } void NGFragmentItemsBuilder::ToFragmentItems(WritingMode writing_mode, @@ -193,39 +217,8 @@ void NGFragmentItemsBuilder::ToFragmentItems(WritingMode writing_mode, const PhysicalSize& outer_size, void* data) { ConvertToPhysical(writing_mode, direction, outer_size); - AssociateNextForSameLayoutObject(); + NGFragmentItems::AssociateWithLayoutObject(&items_); new (data) NGFragmentItems(this); } -void NGFragmentItemsBuilder::AssociateNextForSameLayoutObject() { - // items_[0] can be: - // - kBox for list marker, e.g. <li>abc</li> - // - kLine for line, e.g. <div>abc</div> - // Calling get() is necessary below because operator<< in std::unique_ptr is - // a C++20 feature. - // TODO(https://crbug.com/980914): Drop .get() once we move to C++20. - DCHECK(items_.IsEmpty() || items_[0]->IsContainer()) << items_[0].get(); - HashMap<const LayoutObject*, wtf_size_t> last_fragment_map; - for (wtf_size_t index = 1u; index < items_.size(); ++index) { - const NGFragmentItem& item = *items_[index]; - if (item.Type() == NGFragmentItem::kLine) - continue; - LayoutObject* const layout_object = item.GetMutableLayoutObject(); - DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << item; - auto insert_result = last_fragment_map.insert(layout_object, index); - if (insert_result.is_new_entry) { - // TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|, - // we should enable below. - // layout_object->SetFirstInlineFragmentItemIndex(index); - continue; - } - const wtf_size_t last_index = insert_result.stored_value->value; - insert_result.stored_value->value = index; - DCHECK_GT(last_index, 0u) << item; - DCHECK_LT(last_index, items_.size()); - DCHECK_LT(last_index, index); - items_[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index); - } -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h index eef2fc77231..84be85e4132 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h @@ -59,10 +59,21 @@ class CORE_EXPORT NGFragmentItemsBuilder { void AddListMarker(const NGPhysicalBoxFragment& marker_fragment, const LogicalOffset& offset); + // Find |LogicalOffset| of the first |NGFragmentItem| for |LayoutObject|. + base::Optional<LogicalOffset> LogicalOffsetFor(const LayoutObject&) const; + + // Converts the |NGFragmentItem| vector to the physical coordinate space and + // returns the result. This should only be used for determining the inline + // containing block geometry for OOF-positioned nodes. + // + // Once this method has been called, new items cannot be added. + const Vector<std::unique_ptr<NGFragmentItem>>& + Items(WritingMode, TextDirection, const PhysicalSize& outer_size); + // Build a |NGFragmentItems|. The builder cannot build twice because data set // to this builder may be cleared. - void ToFragmentItems(WritingMode writing_mode, - TextDirection direction, + void ToFragmentItems(WritingMode, + TextDirection, const PhysicalSize& outer_size, void* data); @@ -73,8 +84,6 @@ class CORE_EXPORT NGFragmentItemsBuilder { TextDirection direction, const PhysicalSize& outer_size); - void AssociateNextForSameLayoutObject(); - Vector<std::unique_ptr<NGFragmentItem>> items_; Vector<LogicalOffset> offsets_; String text_content_; @@ -84,10 +93,10 @@ class CORE_EXPORT NGFragmentItemsBuilder { ChildList current_line_; bool has_floating_descendants_for_paint_ = false; + bool is_converted_to_physical_ = false; #if DCHECK_IS_ON() const NGPhysicalLineBoxFragment* current_line_fragment_ = nullptr; - bool is_converted_to_physical_ = false; #endif friend class NGFragmentItems; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc index f2c2f03927e..cc0d8d8d3b4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc @@ -105,7 +105,8 @@ bool NGInlineBoxState::CanAddTextOfStyle( NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( const ComputedStyle& line_style, FontBaseline baseline_type, - bool line_height_quirk) { + bool line_height_quirk, + NGLineBoxFragmentBuilder::ChildList* line_box) { if (stack_.IsEmpty()) { // For the first line, push a box state for the line itself. stack_.resize(1); @@ -114,7 +115,9 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( } else { // For the following lines, clear states that are not shared across lines. for (NGInlineBoxState& box : stack_) { - box.fragment_start = 0; + box.fragment_start = line_box->size(); + if (&box != stack_.begin()) + AddBoxFragmentPlaceholder(&box, line_box, baseline_type); if (!line_height_quirk) box.metrics = box.text_metrics; else @@ -133,15 +136,15 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( DCHECK(box_data_list_.IsEmpty()); // Initialize the box state for the line box. - NGInlineBoxState& line_box = LineBoxState(); - if (line_box.style != &line_style) { - line_box.style = &line_style; + NGInlineBoxState& line_box_state = LineBoxState(); + if (line_box_state.style != &line_style) { + line_box_state.style = &line_style; // Use a "strut" (a zero-width inline box with the element's font and // line height properties) as the initial metrics for the line box. // https://drafts.csswg.org/css2/visudet.html#strut if (!line_height_quirk) - line_box.ComputeTextMetrics(line_style, baseline_type); + line_box_state.ComputeTextMetrics(line_style, baseline_type); } return &stack_.back(); @@ -150,31 +153,32 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag( const NGInlineItem& item, const NGInlineItemResult& item_result, - const NGLineBoxFragmentBuilder::ChildList& line_box) { - DCHECK(item.Style()); - NGInlineBoxState* box = OnOpenTag(*item.Style(), line_box); - box->item = &item; - - if (item.ShouldCreateBoxFragment()) - box->SetNeedsBoxFragment(); - - // Compute box properties regardless of needs_box_fragment since close tag may - // also set needs_box_fragment. - box->has_start_edge = item_result.has_edge; - box->margin_inline_start = item_result.margins.inline_start; - box->margin_inline_end = item_result.margins.inline_end; - box->borders = item_result.borders; - box->padding = item_result.padding; + FontBaseline baseline_type, + NGLineBoxFragmentBuilder::ChildList* line_box) { + NGInlineBoxState* box = + OnOpenTag(item, item_result, baseline_type, *line_box); + box->needs_box_fragment = item.ShouldCreateBoxFragment(); + AddBoxFragmentPlaceholder(box, line_box, baseline_type); return box; } NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag( - const ComputedStyle& style, + const NGInlineItem& item, + const NGInlineItemResult& item_result, + FontBaseline baseline_type, const NGLineBoxFragmentBuilder::ChildList& line_box) { + DCHECK(item.Style()); + const ComputedStyle& style = *item.Style(); stack_.resize(stack_.size() + 1); NGInlineBoxState* box = &stack_.back(); box->fragment_start = line_box.size(); box->style = &style; + box->item = &item; + box->has_start_edge = item_result.has_edge; + box->margin_inline_start = item_result.margins.inline_start; + box->margin_inline_end = item_result.margins.inline_end; + box->borders = item_result.borders; + box->padding = item_result.padding; return box; } @@ -209,9 +213,9 @@ void NGInlineLayoutStateStack::OnEndPlaceItems( // Copy the final offset to |box_data_list_|. for (BoxData& box_data : box_data_list_) { const NGLineBoxFragmentBuilder::Child& placeholder = - (*line_box)[box_data.fragment_end]; - DCHECK(!placeholder.HasFragment()); - box_data.offset = placeholder.offset; + (*line_box)[box_data.fragment_start]; + DCHECK(placeholder.IsPlaceholder()); + box_data.rect.offset = placeholder.rect.offset; } } @@ -219,8 +223,13 @@ void NGInlineLayoutStateStack::EndBoxState( NGInlineBoxState* box, NGLineBoxFragmentBuilder::ChildList* line_box, FontBaseline baseline_type) { - if (box->needs_box_fragment) - AddBoxFragmentPlaceholder(box, line_box, baseline_type); + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (box->needs_box_fragment) + AddBoxData(box, line_box); + } else { + if (box->has_box_placeholder) + AddBoxData(box, line_box); + } PositionPending position_pending = ApplyBaselineShift(box, line_box, baseline_type); @@ -237,12 +246,6 @@ void NGInlineLayoutStateStack::EndBoxState( parent_box.metrics.Unite(box->metrics); } -void NGInlineBoxState::SetNeedsBoxFragment() { - DCHECK(item); - DCHECK(!needs_box_fragment); - needs_box_fragment = true; -} - // Crete a placeholder for a box fragment. // We keep a flat list of fragments because it is more suitable for operations // such as ApplyBaselineShift. Later, CreateBoxFragments() creates box fragments @@ -251,12 +254,14 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( NGInlineBoxState* box, NGLineBoxFragmentBuilder::ChildList* line_box, FontBaseline baseline_type) { - DCHECK(box->needs_box_fragment); + DCHECK(box != stack_.begin() && + box->item->Type() != NGInlineItem::kAtomicInline); + box->has_box_placeholder = true; DCHECK(box->style); const ComputedStyle& style = *box->style; - LogicalOffset offset; - LogicalSize size; + LayoutUnit block_offset; + LayoutUnit block_size; if (!is_empty_line_) { // The inline box should have the height of the font metrics without the // line-height property. Compute from style because |box->metrics| includes @@ -265,63 +270,78 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( // Extend the block direction of the box by borders and paddings. Inline // direction is already included into positions in NGLineBreaker. - offset.block_offset = + block_offset = -metrics.ascent - (box->borders.line_over + box->padding.line_over); - size.block_size = metrics.LineHeight() + box->borders.BlockSum() + - box->padding.BlockSum(); + block_size = metrics.LineHeight() + box->borders.BlockSum() + + box->padding.BlockSum(); } + line_box->AddChild(block_offset, block_size); + DCHECK((*line_box)[line_box->size() - 1].IsPlaceholder()); +} - unsigned fragment_end = line_box->size(); +// Add a |BoxData|, for each close-tag that needs a box fragment. +void NGInlineLayoutStateStack::AddBoxData( + NGInlineBoxState* box, + NGLineBoxFragmentBuilder::ChildList* line_box) { + DCHECK(box->needs_box_fragment || + (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled() && + box->has_box_placeholder && box != stack_.begin() && + box->item->Type() != NGInlineItem::kAtomicInline)); + DCHECK(box->style); + const ComputedStyle& style = *box->style; + NGLineBoxFragmentBuilder::Child& placeholder = + (*line_box)[box->fragment_start]; + DCHECK(placeholder.IsPlaceholder()); + const unsigned fragment_end = line_box->size(); DCHECK(box->item); BoxData& box_data = box_data_list_.emplace_back( - box->fragment_start, fragment_end, box->item, size); - box_data.padding = box->padding; - if (box->has_start_edge) { - box_data.has_line_left_edge = true; - box_data.margin_line_left = box->margin_inline_start; - box_data.margin_border_padding_line_left = box->margin_inline_start + - box->borders.inline_start + - box->padding.inline_start; - } - if (box->has_end_edge) { - box_data.has_line_right_edge = true; - box_data.margin_line_right = box->margin_inline_end; - box_data.margin_border_padding_line_right = box->margin_inline_end + - box->borders.inline_end + - box->padding.inline_end; - } - if (IsRtl(style.Direction())) { - std::swap(box_data.has_line_left_edge, box_data.has_line_right_edge); - std::swap(box_data.margin_line_left, box_data.margin_line_right); - std::swap(box_data.margin_border_padding_line_left, - box_data.margin_border_padding_line_right); - } - - if (fragment_end > box->fragment_start) { - // The start is marked only in BoxData, while end is marked - // in both BoxData and the list itself. - // With a list of 4 text fragments: - // | 0 | 1 | 2 | 3 | - // |text0|text1|text2|text3| - // By adding a BoxData(2,4) (end is exclusive), it becomes: - // | 0 | 1 | 2 | 3 | 4 | - // |text0|text1|text2|text3|null | - // The "null" is added to the list to compute baseline shift of the box - // separately from text fragments. - line_box->AddChild(offset); + box->fragment_start, fragment_end, box->item, placeholder.Size()); + if (box->needs_box_fragment) { + box_data.padding = box->padding; + if (box->has_start_edge) { + box_data.has_line_left_edge = true; + box_data.margin_line_left = box->margin_inline_start; + box_data.margin_border_padding_line_left = box->margin_inline_start + + box->borders.inline_start + + box->padding.inline_start; + } + if (box->has_end_edge) { + box_data.has_line_right_edge = true; + box_data.margin_line_right = box->margin_inline_end; + box_data.margin_border_padding_line_right = box->margin_inline_end + + box->borders.inline_end + + box->padding.inline_end; + } + if (IsRtl(style.Direction())) { + std::swap(box_data.has_line_left_edge, box_data.has_line_right_edge); + std::swap(box_data.margin_line_left, box_data.margin_line_right); + std::swap(box_data.margin_border_padding_line_left, + box_data.margin_border_padding_line_right); + } } else { - // Do not defer creating a box fragment if this is an empty inline box. - // An empty box fragment is still flat that we do not have to defer. - // Also, placeholders cannot be reordred if empty. - offset.inline_offset += box_data.margin_line_left; - LayoutUnit advance = box_data.margin_border_padding_line_left + - box_data.margin_border_padding_line_right; - box_data.size.inline_size = - advance - box_data.margin_line_left - box_data.margin_line_right; - line_box->AddChild(box_data.CreateBoxFragment(line_box), offset, advance, - /* bidi_level */ 0); - box_data_list_.pop_back(); + DCHECK_EQ(box->margin_inline_start, 0); + DCHECK_EQ(box->margin_inline_end, 0); + DCHECK(box->padding.IsEmpty()); + DCHECK(box->borders.IsEmpty()); } + + DCHECK((*line_box)[box->fragment_start].IsPlaceholder()); + DCHECK_GT(fragment_end, box->fragment_start); + if (fragment_end > box->fragment_start + 1) + return; + + // Do not defer creating a box fragment if this is an empty inline box. + // An empty box fragment is still flat that we do not have to defer. + // Also, placeholders cannot be reordred if empty. + placeholder.rect.offset.inline_offset += box_data.margin_line_left; + LayoutUnit advance = box_data.margin_border_padding_line_left + + box_data.margin_border_padding_line_right; + box_data.rect.size.inline_size = + advance - box_data.margin_line_left - box_data.margin_line_right; + placeholder.layout_result = box_data.CreateBoxFragment(line_box); + placeholder.inline_size = advance; + DCHECK(!placeholder.children_count); + box_data_list_.pop_back(); } void NGInlineLayoutStateStack::ChildInserted(unsigned index) { @@ -348,20 +368,26 @@ void NGInlineLayoutStateStack::PrepareForReorder( unsigned box_data_index = 0; for (const BoxData& box_data : box_data_list_) { box_data_index++; + DCHECK((*line_box)[box_data.fragment_start].IsPlaceholder()); for (unsigned i = box_data.fragment_start; i < box_data.fragment_end; i++) { NGLineBoxFragmentBuilder::Child& child = (*line_box)[i]; - if (!child.box_data_index) + unsigned child_box_data_index = child.box_data_index; + if (!child_box_data_index) { child.box_data_index = box_data_index; - } - } + continue; + } - // When boxes are nested, placeholders have indexes to which box it should be - // added. Copy them to BoxData. - for (BoxData& box_data : box_data_list_) { - const NGLineBoxFragmentBuilder::Child& placeholder = - (*line_box)[box_data.fragment_end]; - DCHECK(!placeholder.HasFragment()); - box_data.parent_box_data_index = placeholder.box_data_index; + // This |box_data| has child boxes. Set up |parent_box_data_index| to + // represent the box nesting structure. + while (child_box_data_index != box_data_index) { + BoxData* child_box_data = &box_data_list_[child_box_data_index - 1]; + child_box_data_index = child_box_data->parent_box_data_index; + if (!child_box_data_index) { + child_box_data->parent_box_data_index = box_data_index; + break; + } + } + } } } @@ -491,8 +517,8 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions( // origins at (0, 0). Accumulate inline offset from left to right. LayoutUnit position; for (NGLineBoxFragmentBuilder::Child& child : *line_box) { - child.margin_line_left = child.offset.inline_offset; - child.offset.inline_offset += position; + child.margin_line_left = child.rect.offset.inline_offset; + child.rect.offset.inline_offset += position; // Box margins/boders/paddings will be processed later. // TODO(kojii): we could optimize this if the reordering did not occur. if (!child.HasFragment()) @@ -538,7 +564,7 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions( unsigned start = box_data.fragment_start; NGLineBoxFragmentBuilder::Child& start_child = (*line_box)[start]; LayoutUnit line_left_offset = - start_child.offset.inline_offset - start_child.margin_line_left; + start_child.rect.offset.inline_offset - start_child.margin_line_left; LinePadding& start_padding = accumulated_padding[start]; start_padding.line_left += box_data.margin_border_padding_line_left; line_left_offset -= start_padding.line_left - box_data.margin_line_left; @@ -546,15 +572,15 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions( DCHECK_GT(box_data.fragment_end, start); unsigned last = box_data.fragment_end - 1; NGLineBoxFragmentBuilder::Child& last_child = (*line_box)[last]; - LayoutUnit line_right_offset = last_child.offset.inline_offset - + LayoutUnit line_right_offset = last_child.rect.offset.inline_offset - last_child.margin_line_left + last_child.inline_size; LinePadding& last_padding = accumulated_padding[last]; last_padding.line_right += box_data.margin_border_padding_line_right; line_right_offset += last_padding.line_right - box_data.margin_line_right; - box_data.offset.inline_offset = line_left_offset; - box_data.size.inline_size = line_right_offset - line_left_offset; + box_data.rect.offset.inline_offset = line_left_offset; + box_data.rect.size.inline_size = line_right_offset - line_left_offset; } return position; @@ -569,24 +595,38 @@ void NGInlineLayoutStateStack::CreateBoxFragments( unsigned end = box_data.fragment_end; DCHECK_GT(end, start); NGLineBoxFragmentBuilder::Child* child = &(*line_box)[start]; + if (box_data.item->ShouldCreateBoxFragment()) { + scoped_refptr<const NGLayoutResult> box_fragment = + box_data.CreateBoxFragment(line_box); + if (child->IsPlaceholder()) { + child->layout_result = std::move(box_fragment); + child->rect = box_data.rect; + child->children_count = end - start; + continue; + } - scoped_refptr<const NGLayoutResult> box_fragment = - box_data.CreateBoxFragment(line_box); - if (!child->HasFragment()) { - child->layout_result = std::move(box_fragment); - child->offset = box_data.offset; - child->children_count = end - start; - } else { - // In most cases, |start_child| is moved to the children of the box, and - // is empty. It's not empty when it's out-of-flow. Insert in such case. - // TODO(kojii): With |NGFragmentItem|, all cases hit this code. Consider - // creating an empty item beforehand to avoid inserting. - line_box->InsertChild(start, std::move(box_fragment), box_data.offset, - LayoutUnit(), 0); + // |AddBoxFragmentPlaceholder| adds a placeholder at |fragment_start|, but + // bidi reordering may move it. Insert in such case. + line_box->InsertChild(start, std::move(box_fragment), box_data.rect, + end - start + 1); ChildInserted(start + 1); - child = &(*line_box)[start]; - child->children_count = end - start + 1; + continue; + } + + DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DCHECK(box_data.item); + if (child->IsPlaceholder()) { + child->inline_item = box_data.item; + child->rect = box_data.rect; + child->children_count = end - start; + continue; } + + // |AddBoxFragmentPlaceholder| adds a placeholder at |fragment_start|, but + // bidi reordering may move it. Insert in such case. + line_box->InsertChild(start, *box_data.item, box_data.rect, + end - start + 1); + ChildInserted(start + 1); } box_data_list_.clear(); @@ -600,8 +640,8 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment( const ComputedStyle& style = *item->Style(); NGFragmentGeometry fragment_geometry; - fragment_geometry.border_box_size = {size.inline_size.ClampNegativeToZero(), - size.block_size}; + fragment_geometry.border_box_size = { + rect.size.inline_size.ClampNegativeToZero(), rect.size.block_size}; fragment_geometry.padding = NGBoxStrut(padding, IsFlippedLinesWritingMode(style.GetWritingMode())); @@ -618,6 +658,8 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment( // supported today. box.SetBorderEdges({true, has_line_right_edge, true, has_line_left_edge}); + box.SetIsFirstForNode(has_line_left_edge); + for (unsigned i = fragment_start; i < fragment_end; i++) { NGLineBoxFragmentBuilder::Child& child = (*line_box)[i]; if (child.out_of_flow_positioned_box) { @@ -627,7 +669,7 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment( // child.offset is the static position wrt. the linebox. As we are adding // this as a child of an inline level fragment, we adjust the static // position to be relative to this fragment. - LogicalOffset static_offset = child.offset - offset; + LogicalOffset static_offset = child.rect.offset - rect.offset; box.AddOutOfFlowInlineChildCandidate(oof_box, static_offset, child.container_direction); @@ -636,24 +678,33 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment( } if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { - // |NGFragmentItems| has a flat list of all descendants, except OOF - // objects. Still creates |NGPhysicalBoxFragment|, but don't add children - // to it and keep them in the flat list. + // Propagate any OOF-positioned descendants from any atomic-inlines, etc. + if (child.layout_result) { + box.PropagateChildData(child.layout_result->PhysicalFragment(), + child.rect.offset - rect.offset); + } + + // |NGFragmentItems| has a flat list of all descendants, except + // OOF-positioned descendants. + // We still create a |NGPhysicalBoxFragment|, but don't add children to + // it and keep them in the flat list. continue; } if (child.layout_result) { box.AddChild(child.layout_result->PhysicalFragment(), - child.offset - offset); + child.rect.offset - rect.offset); child.layout_result.reset(); } else if (child.fragment) { - box.AddChild(std::move(child.fragment), child.offset - offset); + box.AddChild(std::move(child.fragment), child.rect.offset - rect.offset); } } - // Inline boxes that produce DisplayItemClient should do full paint - // invalidations. - item->GetLayoutObject()->SetShouldDoFullPaintInvalidation(); + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + // Inline boxes that produce DisplayItemClient should do full paint + // invalidations. + item->GetLayoutObject()->SetShouldDoFullPaintInvalidation(); + } box.MoveOutOfFlowDescendantCandidatesToDescendants(); return box.ToInlineBoxFragment(); @@ -826,10 +877,12 @@ NGLineHeightMetrics NGInlineLayoutStateStack::MetricsForTopAndBottomAlign( continue; // |block_offset| is the top position when the baseline is at 0. - LayoutUnit box_ascent = - -line_box[box_data.fragment_end].offset.block_offset; + const NGLineBoxFragmentBuilder::Child& placeholder = + line_box[box_data.fragment_start]; + DCHECK(placeholder.IsPlaceholder()); + LayoutUnit box_ascent = -placeholder.rect.offset.block_offset; NGLineHeightMetrics box_metrics(box_ascent, - box_data.size.block_size - box_ascent); + box_data.rect.size.block_size - box_ascent); // The top/bottom of inline boxes should not include their paddings. box_metrics.ascent -= box_data.padding.line_over; box_metrics.descent -= box_data.padding.line_under; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h index 066225e80ec..2431f010067 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h @@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_INLINE_BOX_STATE_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_INLINE_BOX_STATE_H_ -#include "third_party/blink/renderer/core/layout/geometry/logical_size.h" +#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" @@ -67,6 +67,7 @@ struct NGInlineBoxState { Vector<NGPendingPositions> pending_descendants; bool include_used_fonts = false; + bool has_box_placeholder = false; bool needs_box_fragment = false; // True if this box has a metrics, including pending ones. Pending metrics @@ -88,9 +89,6 @@ struct NGInlineBoxState { // 'text-top' offset for 'vertical-align'. LayoutUnit TextTop(FontBaseline baseline_type) const; - // Create a box fragment for this box. - void SetNeedsBoxFragment(); - // Returns if the text style can be added without open-tag. // Text with different font or vertical-align needs to be wrapped with an // inline box. @@ -116,14 +114,22 @@ class CORE_EXPORT NGInlineLayoutStateStack { // Initialize the box state stack for a new line. // @return The initial box state for the line. - NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle&, FontBaseline, bool); + NGInlineBoxState* OnBeginPlaceItems( + const ComputedStyle&, + FontBaseline, + bool line_height_quirk, + NGLineBoxFragmentBuilder::ChildList* line_box); // Push a box state stack. NGInlineBoxState* OnOpenTag(const NGInlineItem&, const NGInlineItemResult&, + FontBaseline baseline_type, const NGLineBoxFragmentBuilder::ChildList&); - NGInlineBoxState* OnOpenTag(const ComputedStyle&, - const NGLineBoxFragmentBuilder::ChildList&); + // This variation adds a box placeholder to |line_box|. + NGInlineBoxState* OnOpenTag(const NGInlineItem&, + const NGInlineItemResult&, + FontBaseline baseline_type, + NGLineBoxFragmentBuilder::ChildList* line_box); // Pop a box state stack. NGInlineBoxState* OnCloseTag(NGLineBoxFragmentBuilder::ChildList*, @@ -182,6 +188,7 @@ class CORE_EXPORT NGInlineLayoutStateStack { void AddBoxFragmentPlaceholder(NGInlineBoxState*, NGLineBoxFragmentBuilder::ChildList*, FontBaseline); + void AddBoxData(NGInlineBoxState*, NGLineBoxFragmentBuilder::ChildList*); enum PositionPending { kPositionNotPending, kPositionPending }; @@ -208,14 +215,16 @@ class CORE_EXPORT NGInlineLayoutStateStack { unsigned end, const NGInlineItem* item, LogicalSize size) - : fragment_start(start), fragment_end(end), item(item), size(size) {} + : fragment_start(start), + fragment_end(end), + item(item), + rect(LogicalOffset(), size) {} BoxData(const BoxData& other, unsigned start, unsigned end) : fragment_start(start), fragment_end(end), item(other.item), - size(other.size), - offset(other.offset) {} + rect(other.rect) {} void SetFragmentRange(unsigned start_index, unsigned end_index) { fragment_start = start_index; @@ -227,7 +236,7 @@ class CORE_EXPORT NGInlineLayoutStateStack { unsigned fragment_end; const NGInlineItem* item; - LogicalSize size; + LogicalRect rect; bool has_line_left_edge = false; bool has_line_right_edge = false; @@ -238,7 +247,6 @@ class CORE_EXPORT NGInlineLayoutStateStack { LayoutUnit margin_border_padding_line_left; LayoutUnit margin_border_padding_line_right; - LogicalOffset offset; unsigned parent_box_data_index = 0; unsigned fragmented_box_data_index = 0; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc index 3a9b6f7814d..0485ed7dbb4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc @@ -22,6 +22,7 @@ static_assert(sizeof(NGInlineBreakToken) == } // namespace NGInlineBreakToken::NGInlineBreakToken( + PassKey key, NGInlineNode node, const ComputedStyle* style, unsigned item_index, @@ -34,7 +35,7 @@ NGInlineBreakToken::NGInlineBreakToken( flags_ = flags; } -NGInlineBreakToken::NGInlineBreakToken(NGLayoutInputNode node) +NGInlineBreakToken::NGInlineBreakToken(PassKey key, NGLayoutInputNode node) : NGBreakToken(kInlineBreakToken, kFinished, node), item_index_(0), text_offset_(0) {} diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h index fdf2ebdccde..6558270e9cd 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h @@ -31,13 +31,13 @@ class CORE_EXPORT NGInlineBreakToken final : public NGBreakToken { unsigned item_index, unsigned text_offset, unsigned flags /* NGInlineBreakTokenFlags */) { - return base::AdoptRef( - new NGInlineBreakToken(node, style, item_index, text_offset, flags)); + return base::AdoptRef(new NGInlineBreakToken( + PassKey(), node, style, item_index, text_offset, flags)); } // Creates a break token for a node which cannot produce any more fragments. static scoped_refptr<NGInlineBreakToken> Create(NGLayoutInputNode node) { - return base::AdoptRef(new NGInlineBreakToken(node)); + return base::AdoptRef(new NGInlineBreakToken(PassKey(), node)); } ~NGInlineBreakToken() override; @@ -69,19 +69,21 @@ class CORE_EXPORT NGInlineBreakToken final : public NGBreakToken { return flags_ & kIsForcedBreak; } -#if DCHECK_IS_ON() - String ToString() const override; -#endif - - private: - NGInlineBreakToken(NGInlineNode node, + using PassKey = util::PassKey<NGInlineBreakToken>; + NGInlineBreakToken(PassKey, + NGInlineNode node, const ComputedStyle*, unsigned item_index, unsigned text_offset, unsigned flags /* NGInlineBreakTokenFlags */); - explicit NGInlineBreakToken(NGLayoutInputNode node); + explicit NGInlineBreakToken(PassKey, NGLayoutInputNode node); +#if DCHECK_IS_ON() + String ToString() const override; +#endif + + private: scoped_refptr<const ComputedStyle> style_; unsigned item_index_; unsigned text_offset_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc index df7664e661e..bb39e1b44f1 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc @@ -18,8 +18,8 @@ namespace blink { void NGInlineCursor::MoveToItem(const ItemsSpan::iterator& iter) { DCHECK(IsItemCursor()); DCHECK(iter >= items_.begin() && iter <= items_.end()); - item_iter_ = iter; - current_item_ = iter == items_.end() ? nullptr : iter->get(); + current_.item_iter_ = iter; + current_.item_ = iter == items_.end() ? nullptr : iter->get(); } void NGInlineCursor::SetRoot(const NGFragmentItems& fragment_items, @@ -28,6 +28,8 @@ void NGInlineCursor::SetRoot(const NGFragmentItems& fragment_items, DCHECK(!HasRoot()); fragment_items_ = &fragment_items; items_ = items; + DCHECK(items_.empty() || (items_.data() >= fragment_items_->Items().data() && + items_.data() < fragment_items_->Items().end())); MoveToItem(items_.begin()); } @@ -39,7 +41,7 @@ void NGInlineCursor::SetRoot(const NGPaintFragment& root_paint_fragment) { DCHECK(&root_paint_fragment); DCHECK(!HasRoot()); root_paint_fragment_ = &root_paint_fragment; - current_paint_fragment_ = root_paint_fragment.FirstChild(); + current_.paint_fragment_ = root_paint_fragment.FirstChild(); } void NGInlineCursor::SetRoot(const LayoutBlockFlow& block_flow) { @@ -80,28 +82,22 @@ NGInlineCursor::NGInlineCursor(const NGPaintFragment& root_paint_fragment) { SetRoot(root_paint_fragment); } -NGInlineCursor::NGInlineCursor(const NGInlineCursor& other) - : items_(other.items_), - item_iter_(other.item_iter_), - current_item_(other.current_item_), - fragment_items_(other.fragment_items_), - root_paint_fragment_(other.root_paint_fragment_), - current_paint_fragment_(other.current_paint_fragment_), - layout_inline_(other.layout_inline_) {} - -NGInlineCursor::NGInlineCursor() = default; +NGInlineCursor::NGInlineCursor(const NGInlineBackwardCursor& backward_cursor) + : NGInlineCursor(backward_cursor.cursor_) { + MoveTo(backward_cursor.Current()); +} bool NGInlineCursor::operator==(const NGInlineCursor& other) const { if (root_paint_fragment_) { return root_paint_fragment_ == other.root_paint_fragment_ && - current_paint_fragment_ == other.current_paint_fragment_; + current_.paint_fragment_ == other.current_.paint_fragment_; } - if (current_item_ != other.current_item_) + if (current_.item_ != other.current_.item_) return false; DCHECK_EQ(items_.data(), other.items_.data()); DCHECK_EQ(items_.size(), other.items_.size()); DCHECK_EQ(fragment_items_, other.fragment_items_); - DCHECK(item_iter_ == other.item_iter_); + DCHECK(current_.item_iter_ == other.current_.item_iter_); return true; } @@ -122,34 +118,35 @@ const LayoutBlockFlow* NGInlineCursor::GetLayoutBlockFlow() const { return layout_object->RootInlineFormattingContext(); } if (IsItemCursor()) { - for (const auto& item : items_) { - const LayoutObject* layout_object = item->GetLayoutObject(); - if (layout_object && layout_object->IsInline()) - return layout_object->RootInlineFormattingContext(); - } + const NGFragmentItem& item = *fragment_items_->Items().front(); + const LayoutObject* layout_object = item.GetLayoutObject(); + if (item.Type() == NGFragmentItem::kLine) + return To<LayoutBlockFlow>(layout_object); + return layout_object->RootInlineFormattingContext(); } NOTREACHED(); return nullptr; } bool NGInlineCursor::HasChildren() const { - if (current_paint_fragment_) - return current_paint_fragment_->FirstChild(); - if (current_item_) - return current_item_->HasChildren(); + if (current_.paint_fragment_) + return current_.paint_fragment_->FirstChild(); + if (current_.item_) + return current_.item_->HasChildren(); NOTREACHED(); return false; } NGInlineCursor NGInlineCursor::CursorForDescendants() const { - if (current_paint_fragment_) - return NGInlineCursor(*current_paint_fragment_); - if (current_item_) { - unsigned descendants_count = current_item_->DescendantsCount(); + if (current_.paint_fragment_) + return NGInlineCursor(*current_.paint_fragment_); + if (current_.item_) { + unsigned descendants_count = current_.item_->DescendantsCount(); if (descendants_count > 1) { DCHECK(fragment_items_); - return NGInlineCursor(*fragment_items_, ItemsSpan(&*(item_iter_ + 1), - descendants_count - 1)); + return NGInlineCursor( + *fragment_items_, + ItemsSpan(&*(current_.item_iter_ + 1), descendants_count - 1)); } return NGInlineCursor(); } @@ -157,96 +154,119 @@ NGInlineCursor NGInlineCursor::CursorForDescendants() const { return NGInlineCursor(); } +void NGInlineCursor::ExpandRootToContainingBlock() { + if (root_paint_fragment_) { + root_paint_fragment_ = root_paint_fragment_->Root(); + return; + } + if (fragment_items_) { + const unsigned index_diff = items_.data() - fragment_items_->Items().data(); + DCHECK_LT(index_diff, fragment_items_->Items().size()); + const unsigned item_index = current_.item_iter_ - items_.begin(); + items_ = fragment_items_->Items(); + // Update the iterator to the one for the new span. + MoveToItem(items_.begin() + item_index + index_diff); + return; + } + NOTREACHED(); +} + bool NGInlineCursor::HasSoftWrapToNextLine() const { - DCHECK(IsLineBox()); - const NGInlineBreakToken& break_token = CurrentInlineBreakToken(); - return !break_token.IsFinished() && !break_token.IsForcedBreak(); + DCHECK(Current().IsLineBox()); + const NGInlineBreakToken* break_token = Current().InlineBreakToken(); + DCHECK(break_token); + return !break_token->IsFinished() && !break_token->IsForcedBreak(); } -bool NGInlineCursor::IsAtomicInline() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsAtomicInline(); - if (current_item_) - return current_item_->IsAtomicInline(); +bool NGInlineCursorPosition::IsInlineBox() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsInlineBox(); + if (item_) + return item_->IsInlineBox(); NOTREACHED(); return false; } -bool NGInlineCursor::IsEllipsis() const { - if (current_paint_fragment_) - return current_paint_fragment_->IsEllipsis(); - if (current_item_) - return current_item_->IsEllipsis(); +bool NGInlineCursorPosition::IsAtomicInline() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsAtomicInline(); + if (item_) + return item_->IsAtomicInline(); NOTREACHED(); return false; } -bool NGInlineCursor::IsGeneratedText() const { - if (current_paint_fragment_) { +bool NGInlineCursorPosition::IsEllipsis() const { + if (paint_fragment_) + return paint_fragment_->IsEllipsis(); + if (item_) + return item_->IsEllipsis(); + NOTREACHED(); + return false; +} + +bool NGInlineCursorPosition::IsGeneratedText() const { + if (paint_fragment_) { if (auto* text_fragment = DynamicTo<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment())) + paint_fragment_->PhysicalFragment())) return text_fragment->IsGeneratedText(); return false; } - if (current_item_) - return current_item_->IsGeneratedText(); + if (item_) + return item_->IsGeneratedText(); NOTREACHED(); return false; } -bool NGInlineCursor::IsGeneratedTextType() const { - if (current_paint_fragment_) { +bool NGInlineCursorPosition::IsGeneratedTextType() const { + if (paint_fragment_) { if (auto* text_fragment = DynamicTo<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment())) { + paint_fragment_->PhysicalFragment())) { return text_fragment->TextType() == NGPhysicalTextFragment::kGeneratedText; } return false; } - if (current_item_) - return current_item_->Type() == NGFragmentItem::kGeneratedText; + if (item_) + return item_->Type() == NGFragmentItem::kGeneratedText; NOTREACHED(); return false; } -bool NGInlineCursor::IsHiddenForPaint() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsHiddenForPaint(); - if (current_item_) - return current_item_->IsHiddenForPaint(); +bool NGInlineCursorPosition::IsHiddenForPaint() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsHiddenForPaint(); + if (item_) + return item_->IsHiddenForPaint(); NOTREACHED(); return false; } -bool NGInlineCursor::IsHorizontal() const { - return CurrentStyle().GetWritingMode() == WritingMode::kHorizontalTb; -} - bool NGInlineCursor::IsInlineLeaf() const { - if (IsHiddenForPaint()) + if (Current().IsHiddenForPaint()) return false; - if (IsText()) - return !IsGeneratedTextType(); - if (!IsAtomicInline()) + if (Current().IsText()) + return !Current().IsGeneratedTextType(); + if (!Current().IsAtomicInline()) return false; - return !IsListMarker(); + return !Current().IsListMarker(); } bool NGInlineCursor::IsPartOfCulledInlineBox( const LayoutInline& layout_inline) const { - const LayoutObject* const layout_object = CurrentLayoutObject(); + const LayoutObject* const layout_object = Current().GetLayoutObject(); // We use |IsInline()| to exclude floating and out-of-flow objects. if (!layout_object || !layout_object->IsInline() || layout_object->IsAtomicInlineLevel()) return false; DCHECK(!layout_object->IsFloatingOrOutOfFlowPositioned()); - DCHECK(!CurrentBoxFragment() || - !CurrentBoxFragment()->IsBlockFormattingContextRoot()); + DCHECK(!Current().BoxFragment() || + !Current().BoxFragment()->IsFormattingContextRoot()); return layout_object->IsDescendantOf(&layout_inline); } bool NGInlineCursor::IsLastLineInInlineBlock() const { - DCHECK(IsLineBox()); + DCHECK(Current().IsLineBox()); if (!GetLayoutBlockFlow()->IsAtomicInlineLevel()) return false; NGInlineCursor next_sibling(*this); @@ -254,45 +274,45 @@ bool NGInlineCursor::IsLastLineInInlineBlock() const { next_sibling.MoveToNextSibling(); if (!next_sibling) return true; - if (next_sibling.IsLineBox()) + if (next_sibling.Current().IsLineBox()) return false; // There maybe other top-level objects such as floats, OOF, or list-markers. } } -bool NGInlineCursor::IsLineBreak() const { - if (current_paint_fragment_) { +bool NGInlineCursorPosition::IsLineBreak() const { + if (paint_fragment_) { if (auto* text_fragment = DynamicTo<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment())) + paint_fragment_->PhysicalFragment())) return text_fragment->IsLineBreak(); return false; } - if (current_item_) - return IsText() && current_item_->IsLineBreak(); + if (item_) + return IsText() && item_->IsLineBreak(); NOTREACHED(); return false; } -bool NGInlineCursor::IsListMarker() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsListMarker(); - if (current_item_) - return current_item_->IsListMarker(); +bool NGInlineCursorPosition::IsListMarker() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsListMarker(); + if (item_) + return item_->IsListMarker(); NOTREACHED(); return false; } -bool NGInlineCursor::IsText() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsText(); - if (current_item_) - return current_item_->IsText(); +bool NGInlineCursorPosition::IsText() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsText(); + if (item_) + return item_->IsText(); NOTREACHED(); return false; } bool NGInlineCursor::IsBeforeSoftLineBreak() const { - if (IsLineBreak()) + if (Current().IsLineBreak()) return false; // Inline block is not be container line box. // See paint/selection/text-selection-inline-block.html. @@ -315,57 +335,55 @@ bool NGInlineCursor::IsBeforeSoftLineBreak() const { // Even If |fragment| is before linebreak, if its direction differs to line // direction, we don't paint line break. See // paint/selection/text-selection-newline-mixed-ltr-rtl.html. - return line.CurrentBaseDirection() == CurrentResolvedDirection(); + return line.Current().BaseDirection() == Current().ResolvedDirection(); } bool NGInlineCursor::CanHaveChildren() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsContainer(); - if (current_item_) { - return current_item_->Type() == NGFragmentItem::kLine || - (current_item_->Type() == NGFragmentItem::kBox && - !current_item_->IsAtomicInline()); + if (current_.paint_fragment_) + return current_.paint_fragment_->PhysicalFragment().IsContainer(); + if (current_.item_) { + return current_.item_->Type() == NGFragmentItem::kLine || + (current_.item_->Type() == NGFragmentItem::kBox && + !current_.item_->IsAtomicInline()); } NOTREACHED(); return false; } -bool NGInlineCursor::IsEmptyLineBox() const { +bool NGInlineCursorPosition::IsEmptyLineBox() const { DCHECK(IsLineBox()); - if (current_paint_fragment_) { - return To<NGPhysicalLineBoxFragment>( - current_paint_fragment_->PhysicalFragment()) + if (paint_fragment_) { + return To<NGPhysicalLineBoxFragment>(paint_fragment_->PhysicalFragment()) .IsEmptyLineBox(); } - if (current_item_) - return current_item_->IsEmptyLineBox(); + if (item_) + return item_->IsEmptyLineBox(); NOTREACHED(); return false; } -bool NGInlineCursor::IsLineBox() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().IsLineBox(); - if (current_item_) - return current_item_->Type() == NGFragmentItem::kLine; +bool NGInlineCursorPosition::IsLineBox() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().IsLineBox(); + if (item_) + return item_->Type() == NGFragmentItem::kLine; NOTREACHED(); return false; } -TextDirection NGInlineCursor::CurrentBaseDirection() const { +TextDirection NGInlineCursorPosition::BaseDirection() const { DCHECK(IsLineBox()); - if (current_paint_fragment_) { - return To<NGPhysicalLineBoxFragment>( - current_paint_fragment_->PhysicalFragment()) + if (paint_fragment_) { + return To<NGPhysicalLineBoxFragment>(paint_fragment_->PhysicalFragment()) .BaseDirection(); } - if (current_item_) - return current_item_->BaseDirection(); + if (item_) + return item_->BaseDirection(); NOTREACHED(); return TextDirection::kLtr; } -UBiDiLevel NGInlineCursor::CurrentBidiLevel() const { +UBiDiLevel NGInlineCursorPosition::BidiLevel() const { if (IsText()) { if (IsGeneratedTextType()) { // TODO(yosin): Until we have clients, we don't support bidi-level for @@ -373,17 +391,18 @@ UBiDiLevel NGInlineCursor::CurrentBidiLevel() const { NOTREACHED() << this; return 0; } - const LayoutText& layout_text = *ToLayoutText(CurrentLayoutObject()); + const LayoutText& layout_text = *ToLayoutText(GetLayoutObject()); DCHECK(!layout_text.NeedsLayout()) << this; const auto* const items = layout_text.GetNGInlineItems(); if (!items || items->size() == 0) { // In case of <br>, <wbr>, text-combine-upright, etc. return 0; } + const NGTextOffset offset = TextOffset(); const auto& item = std::find_if( - items->begin(), items->end(), [this](const NGInlineItem& item) { - return item.StartOffset() <= CurrentTextStartOffset() && - item.EndOffset() >= CurrentTextEndOffset(); + items->begin(), items->end(), [offset](const NGInlineItem& item) { + return item.StartOffset() <= offset.start && + item.EndOffset() >= offset.end; }); DCHECK(item != items->end()) << this; return item->BidiLevel(); @@ -391,13 +410,13 @@ UBiDiLevel NGInlineCursor::CurrentBidiLevel() const { if (IsAtomicInline()) { const NGPhysicalBoxFragment* fragmentainer = - CurrentLayoutObject()->ContainingBlockFlowFragment(); + GetLayoutObject()->ContainingBlockFlowFragment(); DCHECK(fragmentainer); const LayoutBlockFlow& block_flow = *To<LayoutBlockFlow>(fragmentainer->GetLayoutObject()); const Vector<NGInlineItem> items = block_flow.GetNGInlineNodeData()->ItemsData(UsesFirstLineStyle()).items; - const LayoutObject* const layout_object = CurrentLayoutObject(); + const LayoutObject* const layout_object = GetLayoutObject(); const auto* const item = std::find_if( items.begin(), items.end(), [layout_object](const NGInlineItem& item) { return item.GetLayoutObject() == layout_object; @@ -410,237 +429,469 @@ UBiDiLevel NGInlineCursor::CurrentBidiLevel() const { return 0; } -const NGPhysicalBoxFragment* NGInlineCursor::CurrentBoxFragment() const { - if (current_paint_fragment_) { +const NGPhysicalBoxFragment* NGInlineCursorPosition::BoxFragment() const { + if (paint_fragment_) { return DynamicTo<NGPhysicalBoxFragment>( - ¤t_paint_fragment_->PhysicalFragment()); + &paint_fragment_->PhysicalFragment()); } - if (current_item_) - return current_item_->BoxFragment(); + if (item_) + return item_->BoxFragment(); NOTREACHED(); return nullptr; } -const DisplayItemClient* NGInlineCursor::CurrentDisplayItemClient() const { - if (current_paint_fragment_) - return current_paint_fragment_; - if (current_item_) - return current_item_; +const DisplayItemClient* NGInlineCursorPosition::GetDisplayItemClient() const { + if (paint_fragment_) + return paint_fragment_; + if (item_) + return item_; NOTREACHED(); return nullptr; } -const NGInlineBreakToken& NGInlineCursor::CurrentInlineBreakToken() const { +const NGInlineBreakToken* NGInlineCursorPosition::InlineBreakToken() const { DCHECK(IsLineBox()); - if (current_paint_fragment_) { + if (paint_fragment_) { return To<NGInlineBreakToken>( - *To<NGPhysicalLineBoxFragment>( - current_paint_fragment_->PhysicalFragment()) - .BreakToken()); + To<NGPhysicalLineBoxFragment>(paint_fragment_->PhysicalFragment()) + .BreakToken()); } - DCHECK(current_item_); - return *current_item_->InlineBreakToken(); + DCHECK(item_); + return item_->InlineBreakToken(); } -const LayoutObject* NGInlineCursor::CurrentLayoutObject() const { - if (current_paint_fragment_) - return current_paint_fragment_->GetLayoutObject(); - if (current_item_) - return current_item_->GetLayoutObject(); +const LayoutObject* NGInlineCursorPosition::GetLayoutObject() const { + if (paint_fragment_) + return paint_fragment_->GetLayoutObject(); + if (item_) + return item_->GetLayoutObject(); NOTREACHED(); return nullptr; } -LayoutObject* NGInlineCursor::CurrentMutableLayoutObject() const { - if (current_paint_fragment_) - return current_paint_fragment_->GetMutableLayoutObject(); - if (current_item_) - return current_item_->GetMutableLayoutObject(); +LayoutObject* NGInlineCursorPosition::GetMutableLayoutObject() const { + if (paint_fragment_) + return paint_fragment_->GetMutableLayoutObject(); + if (item_) + return item_->GetMutableLayoutObject(); NOTREACHED(); return nullptr; } -Node* NGInlineCursor::CurrentNode() const { - if (const LayoutObject* layout_object = CurrentLayoutObject()) +const Node* NGInlineCursorPosition::GetNode() const { + if (const LayoutObject* layout_object = GetLayoutObject()) return layout_object->GetNode(); return nullptr; } -const PhysicalRect NGInlineCursor::CurrentInkOverflow() const { - if (current_paint_fragment_) - return current_paint_fragment_->InkOverflow(); - if (current_item_) - return current_item_->InkOverflow(); +const PhysicalRect NGInlineCursorPosition::InkOverflow() const { + if (paint_fragment_) + return paint_fragment_->InkOverflow(); + if (item_) + return item_->InkOverflow(); NOTREACHED(); return PhysicalRect(); } -const PhysicalOffset NGInlineCursor::CurrentOffset() const { - if (current_paint_fragment_) - return current_paint_fragment_->InlineOffsetToContainerBox(); - if (current_item_) - return current_item_->Offset(); +const PhysicalOffset NGInlineCursorPosition::OffsetInContainerBlock() const { + if (paint_fragment_) + return paint_fragment_->OffsetInContainerBlock(); + if (item_) + return item_->OffsetInContainerBlock(); NOTREACHED(); return PhysicalOffset(); } -const PhysicalRect NGInlineCursor::CurrentRect() const { - return PhysicalRect(CurrentOffset(), CurrentSize()); +const PhysicalSize NGInlineCursorPosition::Size() const { + if (paint_fragment_) + return paint_fragment_->Size(); + if (item_) + return item_->Size(); + NOTREACHED(); + return PhysicalSize(); } -TextDirection NGInlineCursor::CurrentResolvedDirection() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().ResolvedDirection(); - if (current_item_) - return current_item_->ResolvedDirection(); +const PhysicalRect NGInlineCursorPosition::RectInContainerBlock() const { + if (paint_fragment_) { + return {paint_fragment_->OffsetInContainerBlock(), paint_fragment_->Size()}; + } + if (item_) + return item_->RectInContainerBlock(); NOTREACHED(); - return TextDirection::kLtr; + return PhysicalRect(); } -const PhysicalSize NGInlineCursor::CurrentSize() const { - if (current_paint_fragment_) - return current_paint_fragment_->Size(); - if (current_item_) - return current_item_->Size(); +const PhysicalRect NGInlineCursorPosition::SelfInkOverflow() const { + if (paint_fragment_) + return paint_fragment_->SelfInkOverflow(); + if (item_) + return item_->SelfInkOverflow(); NOTREACHED(); - return PhysicalSize(); + return PhysicalRect(); +} + +TextDirection NGInlineCursorPosition::ResolvedDirection() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().ResolvedDirection(); + if (item_) + return item_->ResolvedDirection(); + NOTREACHED(); + return TextDirection::kLtr; } -const ComputedStyle& NGInlineCursor::CurrentStyle() const { - if (current_paint_fragment_) - return current_paint_fragment_->Style(); - return current_item_->Style(); +const ComputedStyle& NGInlineCursorPosition::Style() const { + if (paint_fragment_) + return paint_fragment_->Style(); + return item_->Style(); } -NGStyleVariant NGInlineCursor::CurrentStyleVariant() const { - if (current_paint_fragment_) - return current_paint_fragment_->PhysicalFragment().StyleVariant(); - return current_item_->StyleVariant(); +NGStyleVariant NGInlineCursorPosition::StyleVariant() const { + if (paint_fragment_) + return paint_fragment_->PhysicalFragment().StyleVariant(); + return item_->StyleVariant(); } -bool NGInlineCursor::UsesFirstLineStyle() const { - return CurrentStyleVariant() == NGStyleVariant::kFirstLine; +bool NGInlineCursorPosition::UsesFirstLineStyle() const { + return StyleVariant() == NGStyleVariant::kFirstLine; } -NGTextOffset NGInlineCursor::CurrentTextOffset() const { - if (current_paint_fragment_) { +NGTextOffset NGInlineCursorPosition::TextOffset() const { + if (paint_fragment_) { const auto& text_fragment = - To<NGPhysicalTextFragment>(current_paint_fragment_->PhysicalFragment()); - return {text_fragment.StartOffset(), text_fragment.EndOffset()}; + To<NGPhysicalTextFragment>(paint_fragment_->PhysicalFragment()); + return text_fragment.TextOffset(); } - if (current_item_) - return {current_item_->StartOffset(), current_item_->EndOffset()}; + if (item_) + return item_->TextOffset(); NOTREACHED(); return {}; } -StringView NGInlineCursor::CurrentText() const { +StringView NGInlineCursorPosition::Text(const NGInlineCursor& cursor) const { DCHECK(IsText()); - if (current_paint_fragment_) { - return To<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment()) + cursor.CheckValid(*this); + if (paint_fragment_) { + return To<NGPhysicalTextFragment>(paint_fragment_->PhysicalFragment()) .Text(); } - if (current_item_) - return current_item_->Text(*fragment_items_); + if (item_) + return item_->Text(cursor.Items()); NOTREACHED(); return ""; } -const ShapeResultView* NGInlineCursor::CurrentTextShapeResult() const { +const ShapeResultView* NGInlineCursorPosition::TextShapeResult() const { DCHECK(IsText()); - if (current_paint_fragment_) { - return To<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment()) + if (paint_fragment_) { + return To<NGPhysicalTextFragment>(paint_fragment_->PhysicalFragment()) .TextShapeResult(); } - if (current_item_) - return current_item_->TextShapeResult(); + if (item_) + return item_->TextShapeResult(); NOTREACHED(); return nullptr; } PhysicalRect NGInlineCursor::CurrentLocalRect(unsigned start_offset, unsigned end_offset) const { - DCHECK(IsText()); - if (current_paint_fragment_) { + DCHECK(Current().IsText()); + if (current_.paint_fragment_) { return To<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment()) + current_.paint_fragment_->PhysicalFragment()) .LocalRect(start_offset, end_offset); } - if (current_item_) { - return current_item_->LocalRect(current_item_->Text(*fragment_items_), - start_offset, end_offset); + if (current_.item_) { + return current_.item_->LocalRect(current_.item_->Text(*fragment_items_), + start_offset, end_offset); } NOTREACHED(); return PhysicalRect(); } LayoutUnit NGInlineCursor::InlinePositionForOffset(unsigned offset) const { - DCHECK(IsText()); - if (current_paint_fragment_) { + DCHECK(Current().IsText()); + if (current_.paint_fragment_) { return To<NGPhysicalTextFragment>( - current_paint_fragment_->PhysicalFragment()) + current_.paint_fragment_->PhysicalFragment()) .InlinePositionForOffset(offset); } - if (current_item_) { - return current_item_->InlinePositionForOffset( - current_item_->Text(*fragment_items_), offset); + if (current_.item_) { + return current_.item_->InlinePositionForOffset( + current_.item_->Text(*fragment_items_), offset); } NOTREACHED(); return LayoutUnit(); } PhysicalOffset NGInlineCursor::LineStartPoint() const { - DCHECK(IsLineBox()) << this; + DCHECK(Current().IsLineBox()) << this; const LogicalOffset logical_start; // (0, 0) const PhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1)); - return logical_start.ConvertToPhysical(CurrentStyle().GetWritingMode(), - CurrentBaseDirection(), CurrentSize(), - pixel_size); + return logical_start.ConvertToPhysical(Current().Style().GetWritingMode(), + Current().BaseDirection(), + Current().Size(), pixel_size); } PhysicalOffset NGInlineCursor::LineEndPoint() const { - DCHECK(IsLineBox()) << this; - const LayoutUnit inline_size = - IsHorizontal() ? CurrentSize().width : CurrentSize().height; + DCHECK(Current().IsLineBox()) << this; + const WritingMode writing_mode = Current().Style().GetWritingMode(); + const LayoutUnit inline_size = IsHorizontalWritingMode(writing_mode) + ? Current().Size().width + : Current().Size().height; const LogicalOffset logical_end(inline_size, LayoutUnit()); const PhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1)); - return logical_end.ConvertToPhysical(CurrentStyle().GetWritingMode(), - CurrentBaseDirection(), CurrentSize(), - pixel_size); + return logical_end.ConvertToPhysical(writing_mode, Current().BaseDirection(), + Current().Size(), pixel_size); } -PositionWithAffinity NGInlineCursor::PositionForPoint( - const PhysicalOffset& point) { - if (root_paint_fragment_) - return root_paint_fragment_->PositionForPoint(point); +PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext( + const PhysicalOffset& point, + const NGPhysicalBoxFragment& container) { DCHECK(IsItemCursor()); + const ComputedStyle& container_style = container.Style(); + const WritingMode writing_mode = container_style.GetWritingMode(); + const TextDirection direction = container_style.Direction(); + const PhysicalSize& container_size = container.Size(); + const LayoutUnit point_block_offset = + point + .ConvertToLogical(writing_mode, direction, container_size, + // |point| is actually a pixel with size 1x1. + PhysicalSize(LayoutUnit(1), LayoutUnit(1))) + .block_offset; + + // Stores the closest line box child above |point| in the block direction. + // Used if we can't find any child |point| falls in to resolve the position. + NGInlineCursorPosition closest_line_before; + LayoutUnit closest_line_before_block_offset = LayoutUnit::Min(); + + // Stores the closest line box child below |point| in the block direction. + // Used if we can't find any child |point| falls in to resolve the position. + NGInlineCursorPosition closest_line_after; + LayoutUnit closest_line_after_block_offset = LayoutUnit::Max(); + while (*this) { - const NGFragmentItem* item = CurrentItem(); - DCHECK(item); - // TODO(kojii): Do more staff, when the point is not on any item but within - // line box, etc., see |NGPaintFragment::PositionForPoint|. - if (!item->Rect().Contains(point)) { + const NGFragmentItem* child_item = CurrentItem(); + DCHECK(child_item); + if (child_item->Type() == NGFragmentItem::kLine) { + // Try to resolve if |point| falls in a line box in block direction. + const LayoutUnit child_block_offset = + child_item->OffsetInContainerBlock() + .ConvertToLogical(writing_mode, direction, container_size, + child_item->Size()) + .block_offset; + if (point_block_offset < child_block_offset) { + if (child_block_offset < closest_line_after_block_offset) { + closest_line_after_block_offset = child_block_offset; + closest_line_after = Current(); + } + MoveToNextItemSkippingChildren(); + continue; + } + + // Hitting on line bottom doesn't count, to match legacy behavior. + const LayoutUnit child_block_end_offset = + child_block_offset + + child_item->Size().ConvertToLogical(writing_mode).block_size; + if (point_block_offset >= child_block_end_offset) { + if (child_block_end_offset > closest_line_before_block_offset) { + closest_line_before_block_offset = child_block_end_offset; + closest_line_before = Current(); + } + MoveToNextItemSkippingChildren(); + continue; + } + + if (const PositionWithAffinity child_position = + PositionForPointInInlineBox(point)) + return child_position; MoveToNextItemSkippingChildren(); continue; } - if (item->Type() == NGFragmentItem::kText) - return item->PositionForPointInText(point, *this); - MoveToNext(); + DCHECK_NE(child_item->Type(), NGFragmentItem::kText); + MoveToNextItem(); + } + + if (closest_line_after) { + MoveTo(closest_line_after); + if (const PositionWithAffinity child_position = + PositionForPointInInlineBox(point)) + return child_position; + } + + if (closest_line_before) { + MoveTo(closest_line_before); + if (const PositionWithAffinity child_position = + PositionForPointInInlineBox(point)) + return child_position; + } + + return PositionWithAffinity(); +} + +PositionWithAffinity NGInlineCursor::PositionForPointInInlineBox( + const PhysicalOffset& point) const { + if (const NGPaintFragment* paint_fragment = CurrentPaintFragment()) { + DCHECK(paint_fragment->PhysicalFragment().IsLineBox()); + return paint_fragment->PositionForPoint(point); + } + const NGFragmentItem* container = CurrentItem(); + DCHECK(container); + DCHECK(container->Type() == NGFragmentItem::kLine || + container->Type() == NGFragmentItem::kBox); + const ComputedStyle& container_style = container->Style(); + const WritingMode writing_mode = container_style.GetWritingMode(); + const TextDirection direction = container_style.Direction(); + const PhysicalSize& container_size = container->Size(); + const LayoutUnit point_inline_offset = + point + .ConvertToLogical(writing_mode, direction, container_size, + // |point| is actually a pixel with size 1x1. + PhysicalSize(LayoutUnit(1), LayoutUnit(1))) + .inline_offset; + + // Stores the closest child before |point| in the inline direction. Used if we + // can't find any child |point| falls in to resolve the position. + NGInlineCursorPosition closest_child_before; + LayoutUnit closest_child_before_inline_offset = LayoutUnit::Min(); + + // Stores the closest child after |point| in the inline direction. Used if we + // can't find any child |point| falls in to resolve the position. + NGInlineCursorPosition closest_child_after; + LayoutUnit closest_child_after_inline_offset = LayoutUnit::Max(); + + NGInlineCursor descendants = CursorForDescendants(); + for (; descendants; descendants.MoveToNext()) { + const NGFragmentItem* child_item = descendants.CurrentItem(); + DCHECK(child_item); + if (child_item->Type() == NGFragmentItem::kBox && + !child_item->BoxFragment()) { + // Skip virtually "culled" inline box, e.g. <span>foo</span> + // "editing/selection/shift-click.html" reaches here. + DCHECK(child_item->GetLayoutObject()->IsLayoutInline()) << child_item; + continue; + } + const LayoutUnit child_inline_offset = + child_item->OffsetInContainerBlock() + .ConvertToLogical(writing_mode, direction, container_size, + child_item->Size()) + .inline_offset; + if (point_inline_offset < child_inline_offset) { + if (child_inline_offset < closest_child_after_inline_offset) { + closest_child_after_inline_offset = child_inline_offset; + closest_child_after = descendants.Current(); + } + continue; + } + const LayoutUnit child_inline_end_offset = + child_inline_offset + + child_item->Size().ConvertToLogical(writing_mode).inline_size; + if (point_inline_offset > child_inline_end_offset) { + if (child_inline_end_offset > closest_child_before_inline_offset) { + closest_child_before_inline_offset = child_inline_end_offset; + closest_child_before = descendants.Current(); + } + continue; + } + + if (const PositionWithAffinity child_position = + descendants.PositionForPointInChild(point, *child_item)) + return child_position; + } + + if (closest_child_after) { + descendants.MoveTo(closest_child_after); + if (const PositionWithAffinity child_position = + descendants.PositionForPointInChild(point, *closest_child_after)) + return child_position; + // TODO(yosin): we should do like "closest_child_before" once we have a + // case. + } + + if (closest_child_before) { + descendants.MoveTo(closest_child_before); + if (const PositionWithAffinity child_position = + descendants.PositionForPointInChild(point, *closest_child_before)) + return child_position; + if (closest_child_before->BoxFragment()) { + // LayoutViewHitTest.HitTestHorizontal "Top-right corner (outside) of div" + // reach here. + return descendants.PositionForPointInInlineBox(point); + } + } + + return PositionWithAffinity(); +} + +PositionWithAffinity NGInlineCursor::PositionForPointInChild( + const PhysicalOffset& point, + const NGFragmentItem& child_item) const { + DCHECK_EQ(&child_item, CurrentItem()); + switch (child_item.Type()) { + case NGFragmentItem::kText: + return child_item.PositionForPointInText( + point - child_item.OffsetInContainerBlock(), *this); + case NGFragmentItem::kGeneratedText: + break; + case NGFragmentItem::kBox: + if (const NGPhysicalBoxFragment* box_fragment = + child_item.BoxFragment()) { + // We must fallback to legacy for old layout roots. We also fallback (to + // LayoutNGMixin::PositionForPoint()) for NG block layout, so that we + // can utilize LayoutBlock::PositionForPoint() that resolves the + // position in block layout. + // TODO(xiaochengh): Don't fallback to legacy for NG block layout. + if (box_fragment->IsBlockFlow() || box_fragment->IsLegacyLayoutRoot()) { + return child_item.GetLayoutObject()->PositionForPoint( + point - child_item.OffsetInContainerBlock()); + } + } else { + // |LayoutInline| used to be culled. + } + DCHECK(child_item.GetLayoutObject()->IsLayoutInline()) << child_item; + break; + case NGFragmentItem::kLine: + NOTREACHED(); + break; } return PositionWithAffinity(); } void NGInlineCursor::MakeNull() { if (root_paint_fragment_) { - current_paint_fragment_ = nullptr; + current_.paint_fragment_ = nullptr; return; } if (fragment_items_) return MoveToItem(items_.end()); } +void NGInlineCursor::MoveTo(const NGInlineCursorPosition& position) { + CheckValid(position); + current_ = position; +} + +inline unsigned NGInlineCursor::SpanIndexFromItemIndex(unsigned index) const { + DCHECK(IsItemCursor()); + DCHECK_GE(items_.data(), fragment_items_->Items().data()); + DCHECK_LT(items_.data(), fragment_items_->Items().end()); + if (items_.data() == fragment_items_->Items().data()) + return index; + unsigned span_index = fragment_items_->Items().data() - items_.data() + index; + DCHECK_LT(span_index, items_.size()); + return span_index; +} + +NGInlineCursor::ItemsSpan::iterator NGInlineCursor::SlowFirstItemIteratorFor( + const LayoutObject& layout_object) const { + DCHECK(IsItemCursor()); + for (ItemsSpan::iterator iter = items_.begin(); iter != items_.end(); + ++iter) { + if ((*iter)->GetLayoutObject() == &layout_object) + return iter; + } + return items_.end(); +} + void NGInlineCursor::InternalMoveTo(const LayoutObject& layout_object) { DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()); // If this cursor is rootless, find the root of the inline formatting context. @@ -662,30 +913,18 @@ void NGInlineCursor::InternalMoveTo(const LayoutObject& layout_object) { } } if (fragment_items_) { - const wtf_size_t index = layout_object.FirstInlineFragmentItemIndex(); - if (!index) { + const wtf_size_t item_index = layout_object.FirstInlineFragmentItemIndex(); + if (!item_index) { // TODO(yosin): Once we update all |LayoutObject::FirstInlineFragment()| // clients, we should replace to |return MakeNull()| - item_iter_ = items_.begin(); - while (current_item_ && CurrentLayoutObject() != &layout_object) - MoveToNextItem(); + MoveToItem(SlowFirstItemIteratorFor(layout_object)); return; } - DCHECK_LT(index, items_.size()); - if (!had_root) - return MoveToItem(items_.begin() + index); - // Map |index| in |NGFragmentItems| to index of |items_|. - const LayoutBlockFlow& block_flow = - *layout_object.RootInlineFormattingContext(); - const auto items = - ItemsSpan(block_flow.CurrentFragment()->Items()->Items()); - // Note: We use address instead of iterator because we can't compare - // iterators in different span. See |base::CheckedContiguousIterator<T>|. - const ptrdiff_t adjusted_index = - &*(items.begin() + index) - &*items_.begin(); - DCHECK_GE(adjusted_index, 0); - DCHECK_LT(static_cast<size_t>(adjusted_index), items_.size()); - return MoveToItem(items_.begin() + adjusted_index); + const unsigned span_index = SpanIndexFromItemIndex(item_index); + DCHECK_EQ(span_index, + static_cast<unsigned>(SlowFirstItemIteratorFor(layout_object) - + items_.begin())); + return MoveToItem(items_.begin() + span_index); } if (root_paint_fragment_) { const auto fragments = NGPaintFragment::InlineFragmentsFor(&layout_object); @@ -698,13 +937,16 @@ void NGInlineCursor::InternalMoveTo(const LayoutObject& layout_object) { void NGInlineCursor::MoveTo(const LayoutObject& layout_object) { DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()) << layout_object; InternalMoveTo(layout_object); - if (*this || !HasRoot()) { + if (*this || !HasRoot() || + RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { layout_inline_ = nullptr; return; } // This |layout_object| did not produce any fragments. - // + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; + // Try to find ancestors if this is a culled inline. layout_inline_ = ToLayoutInlineOrNull(&layout_object); if (!layout_inline_) @@ -715,17 +957,28 @@ void NGInlineCursor::MoveTo(const LayoutObject& layout_object) { MoveToNext(); } +void NGInlineCursor::MoveTo(const NGFragmentItem& fragment_item) { + DCHECK(!root_paint_fragment_ && !current_.paint_fragment_); + MoveTo(*fragment_item.GetLayoutObject()); + while (IsNotNull()) { + if (CurrentItem() == &fragment_item) + return; + MoveToNext(); + } + NOTREACHED(); +} + void NGInlineCursor::MoveTo(const NGInlineCursor& cursor) { if (const NGPaintFragment* paint_fragment = cursor.CurrentPaintFragment()) { MoveTo(*paint_fragment); return; } - if (cursor.current_item_) { + if (cursor.current_.item_) { if (!fragment_items_) SetRoot(*cursor.fragment_items_); // Note: We use address instead of iterato because we can't compare // iterators in different span. See |base::CheckedContiguousIterator<T>|. - const ptrdiff_t index = &*cursor.item_iter_ - &*items_.begin(); + const ptrdiff_t index = &*cursor.current_.item_iter_ - &*items_.begin(); DCHECK_GE(index, 0); DCHECK_LT(static_cast<size_t>(index), items_.size()); MoveToItem(items_.begin() + index); @@ -741,19 +994,26 @@ void NGInlineCursor::MoveTo(const NGPaintFragment& paint_fragment) { DCHECK(root_paint_fragment_); DCHECK(paint_fragment.IsDescendantOfNotSelf(*root_paint_fragment_)) << paint_fragment << " " << root_paint_fragment_; - current_paint_fragment_ = &paint_fragment; + current_.paint_fragment_ = &paint_fragment; +} + +void NGInlineCursor::MoveTo(const NGPaintFragment* paint_fragment) { + if (paint_fragment) { + MoveTo(*paint_fragment); + return; + } + MakeNull(); } void NGInlineCursor::MoveToContainingLine() { - DCHECK(!IsLineBox()); - if (current_paint_fragment_) { - current_paint_fragment_ = current_paint_fragment_->ContainerLineBox(); + DCHECK(!Current().IsLineBox()); + if (current_.paint_fragment_) { + current_.paint_fragment_ = current_.paint_fragment_->ContainerLineBox(); return; } - if (current_item_) { - do { + if (current_.item_) { + while (current_.item_ && !Current().IsLineBox()) MoveToPreviousItem(); - } while (current_item_ && !IsLineBox()); return; } NOTREACHED(); @@ -761,7 +1021,7 @@ void NGInlineCursor::MoveToContainingLine() { void NGInlineCursor::MoveToFirst() { if (root_paint_fragment_) { - current_paint_fragment_ = root_paint_fragment_->FirstChild(); + current_.paint_fragment_ = root_paint_fragment_->FirstChild(); return; } if (IsItemCursor()) { @@ -777,13 +1037,32 @@ void NGInlineCursor::MoveToFirstChild() { MakeNull(); } +void NGInlineCursor::MoveToFirstLine() { + if (root_paint_fragment_) { + MoveTo(root_paint_fragment_->FirstLineBox()); + return; + } + if (IsItemCursor()) { + auto iter = std::find_if( + items_.begin(), items_.end(), + [](const auto& item) { return item->Type() == NGFragmentItem::kLine; }); + if (iter != items_.end()) { + MoveToItem(iter); + return; + } + MakeNull(); + return; + } + NOTREACHED(); +} + void NGInlineCursor::MoveToFirstLogicalLeaf() { - DCHECK(IsLineBox()); + DCHECK(Current().IsLineBox()); // TODO(yosin): This isn't correct for mixed Bidi. Fix it. Besides, we // should compute and store it during layout. // TODO(yosin): We should check direction of each container instead of line // box. - if (IsLtr(CurrentStyle().Direction())) { + if (IsLtr(Current().Style().Direction())) { while (TryToMoveToFirstChild()) continue; return; @@ -799,21 +1078,23 @@ void NGInlineCursor::MoveToLastChild() { } void NGInlineCursor::MoveToLastForSameLayoutObject() { - NGInlineCursor last; - while (IsNotNull()) { - last = *this; + if (!Current()) + return; + NGInlineCursorPosition last; + do { + last = Current(); MoveToNextForSameLayoutObject(); - } - *this = last; + } while (Current()); + MoveTo(last); } void NGInlineCursor::MoveToLastLogicalLeaf() { - DCHECK(IsLineBox()); + DCHECK(Current().IsLineBox()); // TODO(yosin): This isn't correct for mixed Bidi. Fix it. Besides, we // should compute and store it during layout. // TODO(yosin): We should check direction of each container instead of line // box. - if (IsLtr(CurrentStyle().Direction())) { + if (IsLtr(Current().Style().Direction())) { while (TryToMoveToLastChild()) continue; return; @@ -830,15 +1111,16 @@ void NGInlineCursor::MoveToNext() { void NGInlineCursor::MoveToNextForSameLayoutObject() { if (layout_inline_) { + DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); // Move to next fragment in culled inline box undef |layout_inline_|. do { MoveToNext(); } while (IsNotNull() && !IsPartOfCulledInlineBox(*layout_inline_)); return; } - if (current_paint_fragment_) { + if (current_.paint_fragment_) { if (auto* paint_fragment = - current_paint_fragment_->NextForSameLayoutObject()) { + current_.paint_fragment_->NextForSameLayoutObject()) { // |paint_fragment| can be in another fragment tree rooted by // |root_paint_fragment_|, e.g. "multicol-span-all-restyle-002.html" root_paint_fragment_ = paint_fragment->Root(); @@ -846,11 +1128,11 @@ void NGInlineCursor::MoveToNextForSameLayoutObject() { } return MakeNull(); } - if (current_item_) { - const wtf_size_t delta = current_item_->DeltaToNextForSameLayoutObject(); + if (current_.item_) { + const wtf_size_t delta = current_.item_->DeltaToNextForSameLayoutObject(); if (delta == 0u) return MakeNull(); - return MoveToItem(item_iter_ + delta); + return MoveToItem(current_.item_iter_ + delta); } } @@ -864,7 +1146,7 @@ void NGInlineCursor::MoveToNextInlineLeaf() { void NGInlineCursor::MoveToNextInlineLeafIgnoringLineBreak() { do { MoveToNextInlineLeaf(); - } while (IsNotNull() && IsLineBreak()); + } while (Current() && Current().IsLineBreak()); } void NGInlineCursor::MoveToNextInlineLeafOnLine() { @@ -883,23 +1165,23 @@ void NGInlineCursor::MoveToNextInlineLeafOnLine() { } void NGInlineCursor::MoveToNextLine() { - DCHECK(IsLineBox()); - if (current_paint_fragment_) { - if (auto* paint_fragment = current_paint_fragment_->NextSibling()) + DCHECK(Current().IsLineBox()); + if (current_.paint_fragment_) { + if (auto* paint_fragment = current_.paint_fragment_->NextSibling()) return MoveTo(*paint_fragment); return MakeNull(); } - if (current_item_) { + if (current_.item_) { do { MoveToNextItem(); - } while (IsNotNull() && !IsLineBox()); + } while (Current() && !Current().IsLineBox()); return; } NOTREACHED(); } void NGInlineCursor::MoveToNextSibling() { - if (current_paint_fragment_) + if (current_.paint_fragment_) return MoveToNextSiblingPaintFragment(); return MoveToNextSiblingItem(); } @@ -926,7 +1208,7 @@ void NGInlineCursor::MoveToPreviousInlineLeaf() { void NGInlineCursor::MoveToPreviousInlineLeafIgnoringLineBreak() { do { MoveToPreviousInlineLeaf(); - } while (IsNotNull() && IsLineBreak()); + } while (Current() && Current().IsLineBreak()); } void NGInlineCursor::MoveToPreviousInlineLeafOnLine() { @@ -945,17 +1227,17 @@ void NGInlineCursor::MoveToPreviousInlineLeafOnLine() { void NGInlineCursor::MoveToPreviousLine() { // Note: List marker is sibling of line box. - DCHECK(IsLineBox()); - if (current_paint_fragment_) { + DCHECK(Current().IsLineBox()); + if (current_.paint_fragment_) { do { MoveToPreviousSiblingPaintFragment(); - } while (IsNotNull() && !IsLineBox()); + } while (Current() && !Current().IsLineBox()); return; } - if (current_item_) { + if (current_.item_) { do { MoveToPreviousItem(); - } while (IsNotNull() && !IsLineBox()); + } while (Current() && !Current().IsLineBox()); return; } NOTREACHED(); @@ -965,10 +1247,10 @@ bool NGInlineCursor::TryToMoveToFirstChild() { if (!HasChildren()) return false; if (root_paint_fragment_) { - MoveTo(*current_paint_fragment_->FirstChild()); + MoveTo(*current_.paint_fragment_->FirstChild()); return true; } - MoveToItem(item_iter_ + 1); + MoveToItem(current_.item_iter_ + 1); return true; } @@ -976,14 +1258,14 @@ bool NGInlineCursor::TryToMoveToLastChild() { if (!HasChildren()) return false; if (root_paint_fragment_) { - MoveTo(current_paint_fragment_->Children().back()); + MoveTo(current_.paint_fragment_->Children().back()); return true; } - const auto end = item_iter_ + CurrentItem()->DescendantsCount(); + const auto end = current_.item_iter_ + CurrentItem()->DescendantsCount(); MoveToNextItem(); DCHECK(!IsNull()); - for (auto it = item_iter_ + 1; it != end; ++it) { - if (CurrentItem()->HasSameParent(**it)) + for (auto it = current_.item_iter_ + 1; it != end; ++it) { + if (CurrentItem()->IsSiblingOf(**it)) MoveToItem(it); } return true; @@ -991,78 +1273,78 @@ bool NGInlineCursor::TryToMoveToLastChild() { void NGInlineCursor::MoveToNextItem() { DCHECK(IsItemCursor()); - if (UNLIKELY(!current_item_)) + if (UNLIKELY(!current_.item_)) return; - DCHECK(item_iter_ != items_.end()); - ++item_iter_; - MoveToItem(item_iter_); + DCHECK(current_.item_iter_ != items_.end()); + ++current_.item_iter_; + MoveToItem(current_.item_iter_); } void NGInlineCursor::MoveToNextItemSkippingChildren() { DCHECK(IsItemCursor()); - if (UNLIKELY(!current_item_)) + if (UNLIKELY(!current_.item_)) return; // If the current item has |DescendantsCount|, add it to move to the next // sibling, skipping all children and their descendants. - if (wtf_size_t descendants_count = current_item_->DescendantsCount()) - return MoveToItem(item_iter_ + descendants_count); + if (wtf_size_t descendants_count = current_.item_->DescendantsCount()) + return MoveToItem(current_.item_iter_ + descendants_count); return MoveToNextItem(); } void NGInlineCursor::MoveToNextSiblingItem() { DCHECK(IsItemCursor()); - if (UNLIKELY(!current_item_)) + if (UNLIKELY(!current_.item_)) return; const NGFragmentItem& item = *CurrentItem(); MoveToNextItemSkippingChildren(); - if (IsNull() || item.HasSameParent(*CurrentItem())) + if (IsNull() || item.IsSiblingOf(*CurrentItem())) return; MakeNull(); } void NGInlineCursor::MoveToPreviousItem() { DCHECK(IsItemCursor()); - if (UNLIKELY(!current_item_)) + if (UNLIKELY(!current_.item_)) return; - if (item_iter_ == items_.begin()) + if (current_.item_iter_ == items_.begin()) return MakeNull(); - --item_iter_; - current_item_ = item_iter_->get(); + --current_.item_iter_; + current_.item_ = current_.item_iter_->get(); } void NGInlineCursor::MoveToParentPaintFragment() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - const NGPaintFragment* parent = current_paint_fragment_->Parent(); + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + const NGPaintFragment* parent = current_.paint_fragment_->Parent(); if (parent && parent != root_paint_fragment_) { - current_paint_fragment_ = parent; + current_.paint_fragment_ = parent; return; } - current_paint_fragment_ = nullptr; + current_.paint_fragment_ = nullptr; } void NGInlineCursor::MoveToNextPaintFragment() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - if (const NGPaintFragment* child = current_paint_fragment_->FirstChild()) { - current_paint_fragment_ = child; + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + if (const NGPaintFragment* child = current_.paint_fragment_->FirstChild()) { + current_.paint_fragment_ = child; return; } MoveToNextPaintFragmentSkippingChildren(); } void NGInlineCursor::MoveToNextSiblingPaintFragment() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - if (const NGPaintFragment* next = current_paint_fragment_->NextSibling()) { - current_paint_fragment_ = next; + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + if (const NGPaintFragment* next = current_.paint_fragment_->NextSibling()) { + current_.paint_fragment_ = next; return; } - current_paint_fragment_ = nullptr; + current_.paint_fragment_ = nullptr; } void NGInlineCursor::MoveToNextPaintFragmentSkippingChildren() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - while (current_paint_fragment_) { - if (const NGPaintFragment* next = current_paint_fragment_->NextSibling()) { - current_paint_fragment_ = next; + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + while (current_.paint_fragment_) { + if (const NGPaintFragment* next = current_.paint_fragment_->NextSibling()) { + current_.paint_fragment_ = next; return; } MoveToParentPaintFragment(); @@ -1070,25 +1352,25 @@ void NGInlineCursor::MoveToNextPaintFragmentSkippingChildren() { } void NGInlineCursor::MoveToPreviousPaintFragment() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - const NGPaintFragment* const parent = current_paint_fragment_->Parent(); + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + const NGPaintFragment* const parent = current_.paint_fragment_->Parent(); MoveToPreviousSiblingPaintFragment(); - if (current_paint_fragment_) { + if (current_.paint_fragment_) { while (TryToMoveToLastChild()) continue; return; } - current_paint_fragment_ = parent == root_paint_fragment_ ? nullptr : parent; + current_.paint_fragment_ = parent == root_paint_fragment_ ? nullptr : parent; } void NGInlineCursor::MoveToPreviousSiblingPaintFragment() { - DCHECK(IsPaintFragmentCursor() && current_paint_fragment_); - const NGPaintFragment* const current = current_paint_fragment_; - current_paint_fragment_ = nullptr; + DCHECK(IsPaintFragmentCursor() && current_.paint_fragment_); + const NGPaintFragment* const current = current_.paint_fragment_; + current_.paint_fragment_ = nullptr; for (auto* sibling : current->Parent()->Children()) { if (sibling == current) return; - current_paint_fragment_ = sibling; + current_.paint_fragment_ = sibling; } NOTREACHED(); } @@ -1096,28 +1378,35 @@ void NGInlineCursor::MoveToPreviousSiblingPaintFragment() { NGInlineBackwardCursor::NGInlineBackwardCursor(const NGInlineCursor& cursor) : cursor_(cursor) { if (cursor.root_paint_fragment_) { + DCHECK(!cursor.CurrentPaintFragment() || + cursor.CurrentPaintFragment()->Parent()->FirstChild() == + cursor.CurrentPaintFragment()); for (NGInlineCursor sibling(cursor); sibling; sibling.MoveToNextSibling()) sibling_paint_fragments_.push_back(sibling.CurrentPaintFragment()); current_index_ = sibling_paint_fragments_.size(); if (current_index_) - current_paint_fragment_ = sibling_paint_fragments_[--current_index_]; + current_.paint_fragment_ = sibling_paint_fragments_[--current_index_]; return; } if (cursor.IsItemCursor()) { - for (NGInlineCursor sibling(cursor); sibling; sibling.MoveToNextSibling()) - sibling_item_iterators_.push_back(sibling.item_iter_); + DCHECK(!cursor || cursor.items_.begin() == cursor.Current().item_iter_); + for (NGInlineCursor sibling(cursor); sibling; + sibling.MoveToNextSkippingChildren()) + sibling_item_iterators_.push_back(sibling.Current().item_iter_); current_index_ = sibling_item_iterators_.size(); - if (current_index_) - current_item_ = sibling_item_iterators_[--current_index_]->get(); + if (current_index_) { + current_.item_iter_ = sibling_item_iterators_[--current_index_]; + current_.item_ = current_.item_iter_->get(); + } return; } - NOTREACHED(); + DCHECK(!cursor); } NGInlineCursor NGInlineBackwardCursor::CursorForDescendants() const { - if (const NGPaintFragment* current_paint_fragment = CurrentPaintFragment()) + if (const NGPaintFragment* current_paint_fragment = Current().PaintFragment()) return NGInlineCursor(*current_paint_fragment); - if (current_item_) { + if (current_.item_) { NGInlineCursor cursor(cursor_); cursor.MoveToItem(sibling_item_iterators_[current_index_]); return cursor.CursorForDescendants(); @@ -1126,38 +1415,21 @@ NGInlineCursor NGInlineBackwardCursor::CursorForDescendants() const { return NGInlineCursor(); } -const PhysicalOffset NGInlineBackwardCursor::CurrentOffset() const { - if (current_paint_fragment_) - return current_paint_fragment_->InlineOffsetToContainerBox(); - if (current_item_) - return current_item_->Offset(); - NOTREACHED(); - return PhysicalOffset(); -} - -const PhysicalRect NGInlineBackwardCursor::CurrentSelfInkOverflow() const { - if (current_paint_fragment_) - return current_paint_fragment_->SelfInkOverflow(); - if (current_item_) - return current_item_->SelfInkOverflow(); - NOTREACHED(); - return PhysicalRect(); -} - void NGInlineBackwardCursor::MoveToPreviousSibling() { if (current_index_) { - if (current_paint_fragment_) { - current_paint_fragment_ = sibling_paint_fragments_[--current_index_]; + if (current_.paint_fragment_) { + current_.paint_fragment_ = sibling_paint_fragments_[--current_index_]; return; } - if (current_item_) { - current_item_ = sibling_item_iterators_[--current_index_]->get(); + if (current_.item_) { + current_.item_iter_ = sibling_item_iterators_[--current_index_]; + current_.item_ = current_.item_iter_->get(); return; } NOTREACHED(); } - current_paint_fragment_ = nullptr; - current_item_ = nullptr; + current_.paint_fragment_ = nullptr; + current_.item_ = nullptr; } std::ostream& operator<<(std::ostream& ostream, const NGInlineCursor& cursor) { @@ -1177,4 +1449,18 @@ std::ostream& operator<<(std::ostream& ostream, const NGInlineCursor* cursor) { return ostream << *cursor; } +#if DCHECK_IS_ON() +void NGInlineCursor::CheckValid(const NGInlineCursorPosition& position) const { + if (position.PaintFragment()) { + DCHECK(root_paint_fragment_); + DCHECK( + position.PaintFragment()->IsDescendantOfNotSelf(*root_paint_fragment_)); + } else if (position.Item()) { + DCHECK(IsItemCursor()); + const unsigned index = position.item_iter_ - items_.begin(); + DCHECK_LT(index, items_.size()); + } +} +#endif + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h index 9c23ab2daef..c9626d87b5e 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h @@ -25,7 +25,9 @@ class LayoutObject; class LayoutUnit; class NGFragmentItem; class NGFragmentItems; +class NGInlineBackwardCursor; class NGInlineBreakToken; +class NGInlineCursor; class NGPaintFragment; class NGPhysicalBoxFragment; class Node; @@ -35,6 +37,128 @@ struct PhysicalOffset; struct PhysicalRect; struct PhysicalSize; +// Represents a position of |NGInlineCursor|. This class: +// 1. Provides properties for the current position. +// 2. Allows to save |Current()|, and can move back later. Moving to |Position| +// is faster than moving to |NGFragmentItem|. +class CORE_EXPORT NGInlineCursorPosition { + STACK_ALLOCATED(); + + public: + using ItemsSpan = base::span<const std::unique_ptr<NGFragmentItem>>; + + const NGPaintFragment* PaintFragment() const { return paint_fragment_; } + const NGFragmentItem* Item() const { return item_; } + const NGFragmentItem* operator->() const { return item_; } + const NGFragmentItem& operator*() const { return *item_; } + + operator bool() const { return paint_fragment_ || item_; } + + bool operator==(const NGInlineCursorPosition& other) const { + return paint_fragment_ == other.paint_fragment_ && item_ == other.item_; + } + bool operator!=(const NGInlineCursorPosition& other) const { + return !operator==(other); + } + + // True if the current position is a text. It is error to call at end. + bool IsText() const; + + // True if the current position is a generatd text. It is error to call at + // end. + bool IsGeneratedText() const; + + // True if fragment is |NGFragmentItem::kGeneratedText| or + // |NGPhysicalTextFragment::kGeneratedText|. + // TODO(yosin): We should rename |IsGeneratedTextType()| to another name. + bool IsGeneratedTextType() const; + + // True if the current position is a line break. It is error to call at end. + bool IsLineBreak() const; + + // True if the current position is an ellipsis. It is error to call at end. + bool IsEllipsis() const; + + // True if the current position is a line box. It is error to call at end. + bool IsLineBox() const; + + // True if the current position is an empty line box. It is error to call + // other then line box. + bool IsEmptyLineBox() const; + + // True if the current position is an inline box. It is error to call at end. + bool IsInlineBox() const; + + // True if the current position is an atomic inline. It is error to call at + // end. + bool IsAtomicInline() const; + + // True if the current position is a list marker. + bool IsListMarker() const; + + // True if the current position is hidden for paint. It is error to call at + // end. + bool IsHiddenForPaint() const; + + // |ComputedStyle| and related functions. + NGStyleVariant StyleVariant() const; + bool UsesFirstLineStyle() const; + const ComputedStyle& Style() const; + + // Functions to get corresponding objects for this position. + const NGPhysicalBoxFragment* BoxFragment() const; + const LayoutObject* GetLayoutObject() const; + LayoutObject* GetMutableLayoutObject() const; + const Node* GetNode() const; + const DisplayItemClient* GetDisplayItemClient() const; + + // Returns break token for line box. It is error to call other than line box. + const NGInlineBreakToken* InlineBreakToken() const; + + // The offset relative to the root of the inline formatting context. + const PhysicalRect RectInContainerBlock() const; + const PhysicalOffset OffsetInContainerBlock() const; + const PhysicalSize Size() const; + + // InkOverflow of itself, including contents if they contribute to the ink + // overflow of this object (e.g. when not clipped,) in the local coordinate. + const PhysicalRect InkOverflow() const; + const PhysicalRect SelfInkOverflow() const; + + // Returns start/end of offset in text content of current text fragment. + // It is error when this cursor doesn't point to text fragment. + NGTextOffset TextOffset() const; + unsigned TextStartOffset() const { return TextOffset().start; } + unsigned TextEndOffset() const { return TextOffset().end; } + + // Returns text of the current position. It is error to call other than + // text. + StringView Text(const NGInlineCursor& cursor) const; + + // Returns |ShapeResultView| of the current position. It is error to call + // other than text. + const ShapeResultView* TextShapeResult() const; + + // Returns bidi level of current position. It is error to call other than + // text and atomic inline. It is also error to call |IsGeneratedTextType()|. + UBiDiLevel BidiLevel() const; + // Returns text direction of current text or atomic inline. It is error to + // call at other than text or atomic inline. Note: <span> doesn't have + // reserved direction. + TextDirection ResolvedDirection() const; + // Returns text direction of current line. It is error to call at other than + // line. + TextDirection BaseDirection() const; + + private: + const NGPaintFragment* paint_fragment_ = nullptr; + const NGFragmentItem* item_ = nullptr; + ItemsSpan::iterator item_iter_; + + friend class NGInlineBackwardCursor; + friend class NGInlineCursor; +}; + // This class traverses fragments in an inline formatting context. // // When constructed, the initial position is empty. Call |MoveToNext()| to move @@ -53,12 +177,13 @@ class CORE_EXPORT NGInlineCursor { explicit NGInlineCursor(const NGFragmentItems& fragment_items, ItemsSpan items); explicit NGInlineCursor(const NGPaintFragment& root_paint_fragment); - NGInlineCursor(const NGInlineCursor& other); + NGInlineCursor(const NGInlineCursor& other) = default; + NGInlineCursor(const NGInlineBackwardCursor& backward_cursor); // Creates an |NGInlineCursor| without the root. Even when callers don't know // the root of the inline formatting context, this cursor can |MoveTo()| // specific |LayoutObject|. - NGInlineCursor(); + NGInlineCursor() = default; bool operator==(const NGInlineCursor& other) const; bool operator!=(const NGInlineCursor& other) const { @@ -83,12 +208,13 @@ class CORE_EXPORT NGInlineCursor { // // Functions to query the current position. // + const NGInlineCursorPosition& Current() const { return current_; } // Returns true if cursor is out of fragment tree, e.g. before first fragment // or after last fragment in tree. - bool IsNull() const { return !current_item_ && !current_paint_fragment_; } - bool IsNotNull() const { return !IsNull(); } - explicit operator bool() const { return !IsNull(); } + bool IsNull() const { return !Current(); } + bool IsNotNull() const { return Current(); } + operator bool() const { return Current(); } // True if fragment at the current position can have children. bool CanHaveChildren() const; @@ -101,104 +227,38 @@ class CORE_EXPORT NGInlineCursor { // has no children, returns an empty cursor. NGInlineCursor CursorForDescendants() const; + // If |this| is created by |CursorForDescendants()| to traverse parts of an + // inline formatting context, expand the traversable range to the containing + // |LayoutBlockFlow|. Does nothing if |this| is for an inline formatting + // context. + void ExpandRootToContainingBlock(); + // True if current position has soft wrap to next line. It is error to call // other than line. bool HasSoftWrapToNextLine() const; - // True if the current position is a atomic inline. It is error to call at - // end. - bool IsAtomicInline() const; - // True if the current position is before soft line break. It is error to call // at end. bool IsBeforeSoftLineBreak() const; - // True if the current position is an ellipsis. It is error to call at end. - bool IsEllipsis() const; - - // True if the current position is an empty line box. It is error to call - // other then line box. - bool IsEmptyLineBox() const; - - // True if the current position is a generatd text. It is error to call at - // end. - bool IsGeneratedText() const; - - // True if fragment is |NGFragmentItem::kGeneratedText| or - // |NGPhysicalTextFragment::kGeneratedText|. - // TODO(yosin): We should rename |IsGeneratedTextType()| to another name. - bool IsGeneratedTextType() const; - - // True if the current position is hidden for paint. It is error to call at - // end. - bool IsHiddenForPaint() const; - - // True if the current position's writing mode in style is horizontal. - bool IsHorizontal() const; - // True if the current position is text or atomic inline box. // Note: Because of this function is used for caret rect, hit testing, etc, // this function returns false for hidden for paint, text overflow ellipsis, // and line break hyphen. bool IsInlineLeaf() const; - // True if the current position is a line box. It is error to call at end. - bool IsLineBox() const; - - // True if the current position is a line break. It is error to call at end. - bool IsLineBreak() const; - - // True if the current position is a list marker. - bool IsListMarker() const; - - // True if the current position is a text. It is error to call at end. - bool IsText() const; - // |Current*| functions return an object for the current position. - const NGFragmentItem* CurrentItem() const { return current_item_; } + const NGFragmentItem* CurrentItem() const { return Current().Item(); } const NGPaintFragment* CurrentPaintFragment() const { - return current_paint_fragment_; + return Current().PaintFragment(); + } + LayoutObject* CurrentMutableLayoutObject() const { + return Current().GetMutableLayoutObject(); } - // Returns text direction of current line. It is error to call at other than - // line. - TextDirection CurrentBaseDirection() const; - const NGPhysicalBoxFragment* CurrentBoxFragment() const; - const DisplayItemClient* CurrentDisplayItemClient() const; - const LayoutObject* CurrentLayoutObject() const; - LayoutObject* CurrentMutableLayoutObject() const; - Node* CurrentNode() const; - - // Returns bidi level of current position. It is error to call other than - // text and atomic inline. It is also error to call |IsGeneratedTextType()|. - UBiDiLevel CurrentBidiLevel() const; - - // Returns text direction of current text or atomic inline. It is error to - // call at other than text or atomic inline. Note: <span> doesn't have - // reserved direction. - TextDirection CurrentResolvedDirection() const; - const ComputedStyle& CurrentStyle() const; - - // InkOverflow of itself, including contents if they contribute to the ink - // overflow of this object (e.g. when not clipped,) in the local coordinate. - const PhysicalRect CurrentInkOverflow() const; - // The offset relative to the root of the inline formatting context. - const PhysicalOffset CurrentOffset() const; - const PhysicalRect CurrentRect() const; - const PhysicalSize CurrentSize() const; - - // Returns start/end of offset in text content of current text fragment. - // It is error when this cursor doesn't point to text fragment. - NGTextOffset CurrentTextOffset() const; - unsigned CurrentTextStartOffset() const { return CurrentTextOffset().start; } - unsigned CurrentTextEndOffset() const { return CurrentTextOffset().end; } // Returns text of the current position. It is error to call other than // text. - StringView CurrentText() const; - - // Returns |ShapeResultView| of the current position. It is error to call - // other than text. - const ShapeResultView* CurrentTextShapeResult() const; + StringView CurrentText() const { return Current().Text(*this); } // The layout box of text in (start, end) range in local coordinate. // Start and end offsets must be between |CurrentTextStartOffset()| and @@ -216,12 +276,24 @@ class CORE_EXPORT NGInlineCursor { PhysicalOffset LineEndPoint() const; // Converts the given point, relative to the fragment itself, into a position - // in DOM tree within the range of |this|. - PositionWithAffinity PositionForPoint(const PhysicalOffset&); + // in DOM tree within the range of |this|. This variation ignores the inline + // offset, and snaps to the nearest line in the block direction. + PositionWithAffinity PositionForPointInInlineFormattingContext( + const PhysicalOffset& point, + const NGPhysicalBoxFragment& container); + // Find the |Position| in the line box |Current()| points to. This variation + // ignores the block offset, and snaps to the nearest item in inline + // direction. + PositionWithAffinity PositionForPointInInlineBox( + const PhysicalOffset& point) const; // // Functions to move the current position. // + void MoveTo(const NGInlineCursorPosition& position); + + // Move the current position at |fragment_item|. + void MoveTo(const NGFragmentItem& fragment_item); // Move the current position at |cursor|. Unlinke copy constrcutr, this // function doesn't copy root. Note: The current position in |cursor| @@ -230,6 +302,7 @@ class CORE_EXPORT NGInlineCursor { // Move the current posint at |paint_fragment|. void MoveTo(const NGPaintFragment& paint_fragment); + void MoveTo(const NGPaintFragment* paint_fragment); // Move to first |NGFragmentItem| or |NGPaintFragment| associated to // |layout_object|. When |layout_object| has no associated fragments, this @@ -244,6 +317,9 @@ class CORE_EXPORT NGInlineCursor { // See also |TryToMoveToFirstChild()|. void MoveToFirstChild(); + // Move to the first line. + void MoveToFirstLine(); + // Move to first logical leaf of current line box. If current line box has // no children, curosr becomes null. void MoveToFirstLogicalLeaf(); @@ -253,6 +329,9 @@ class CORE_EXPORT NGInlineCursor { // See also |TryToMoveToFirstChild()|. void MoveToLastChild(); + // Move the current position to the last fragment on same layout object. + void MoveToLastForSameLayoutObject(); + // Move to last logical leaf of current line box. If current line box has // no children, curosr becomes null. void MoveToLastLogicalLeaf(); @@ -269,6 +348,7 @@ class CORE_EXPORT NGInlineCursor { void MoveToNextLine(); // Move the current position to next sibling fragment. + // |MoveToNextSibling()| is deprecated. New code should not be used. void MoveToNextSibling(); // Same as |MoveToNext| except that this skips children even if they exist. @@ -304,14 +384,13 @@ class CORE_EXPORT NGInlineCursor { // TODO(kojii): Add more variations as needed, NextSibling, // NextSkippingChildren, Previous, etc. - private: - // Returns break token for line box. It is error to call other than line box. - const NGInlineBreakToken& CurrentInlineBreakToken() const; - - // Returns style variant of the current position. - NGStyleVariant CurrentStyleVariant() const; - bool UsesFirstLineStyle() const; +#if DCHECK_IS_ON() + void CheckValid(const NGInlineCursorPosition& position) const; +#else + void CheckValid(const NGInlineCursorPosition&) const {} +#endif + private: // True if current position is part of culled inline box |layout_inline|. bool IsPartOfCulledInlineBox(const LayoutInline& layout_inline) const; @@ -326,9 +405,6 @@ class CORE_EXPORT NGInlineCursor { // Move the cursor position to the first fragment in tree. void MoveToFirst(); - // Move the current position to the last fragment on same layout object. - void MoveToLastForSameLayoutObject(); - // Same as |MoveTo()| but not support culled inline. void InternalMoveTo(const LayoutObject& layout_object); @@ -350,13 +426,20 @@ class CORE_EXPORT NGInlineCursor { void MoveToPreviousPaintFragment(); void MoveToPreviousSiblingPaintFragment(); + ItemsSpan::iterator SlowFirstItemIteratorFor( + const LayoutObject& layout_object) const; + unsigned SpanIndexFromItemIndex(unsigned index) const; + + PositionWithAffinity PositionForPointInChild( + const PhysicalOffset& point, + const NGFragmentItem& child_item) const; + + NGInlineCursorPosition current_; + ItemsSpan items_; - ItemsSpan::iterator item_iter_; - const NGFragmentItem* current_item_ = nullptr; const NGFragmentItems* fragment_items_ = nullptr; const NGPaintFragment* root_paint_fragment_ = nullptr; - const NGPaintFragment* current_paint_fragment_ = nullptr; // Used in |MoveToNextForSameLayoutObject()| to support culled inline. const LayoutInline* layout_inline_ = nullptr; @@ -370,31 +453,25 @@ class CORE_EXPORT NGInlineBackwardCursor { STACK_ALLOCATED(); public: + // |cursor| should be the first child of root or descendants, e.g. the first + // item in |NGInlineCursor::items_|. NGInlineBackwardCursor(const NGInlineCursor& cursor); - NGInlineCursor CursorForDescendants() const; - - explicit operator bool() const { - return current_paint_fragment_ || current_item_; - } - - const NGFragmentItem* CurrentItem() const { return current_item_; } - const NGPaintFragment* CurrentPaintFragment() const { - return current_paint_fragment_; - } + const NGInlineCursorPosition& Current() const { return current_; } + operator bool() const { return Current(); } - const PhysicalOffset CurrentOffset() const; - const PhysicalRect CurrentSelfInkOverflow() const; + NGInlineCursor CursorForDescendants() const; void MoveToPreviousSibling(); private: + NGInlineCursorPosition current_; const NGInlineCursor& cursor_; Vector<const NGPaintFragment*, 16> sibling_paint_fragments_; Vector<NGInlineCursor::ItemsSpan::iterator, 16> sibling_item_iterators_; - const NGPaintFragment* current_paint_fragment_ = nullptr; - const NGFragmentItem* current_item_ = nullptr; wtf_size_t current_index_; + + friend class NGInlineCursor; }; CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGInlineCursor&); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc index 8ded96613d1..8398da81ecb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc @@ -53,7 +53,7 @@ class NGInlineCursorTest : public NGLayoutTest, Vector<const NGPaintFragment*> backwards; for (NGInlineBackwardCursor cursor(start); cursor; cursor.MoveToPreviousSibling()) - backwards.push_back(cursor.CurrentPaintFragment()); + backwards.push_back(cursor.Current().PaintFragment()); backwards.Reverse(); EXPECT_THAT(backwards, forwards); return; @@ -65,16 +65,16 @@ class NGInlineCursorTest : public NGLayoutTest, Vector<const NGFragmentItem*> backwards; for (NGInlineBackwardCursor cursor(start); cursor; cursor.MoveToPreviousSibling()) - backwards.push_back(cursor.CurrentItem()); + backwards.push_back(cursor.Current().Item()); backwards.Reverse(); EXPECT_THAT(backwards, forwards); } String ToDebugString(const NGInlineCursor& cursor) { - if (cursor.IsLineBox()) + if (cursor.Current().IsLineBox()) return "#linebox"; - if (cursor.IsGeneratedTextType()) { + if (cursor.Current().IsGeneratedTextType()) { StringBuilder result; result.Append("#'"); result.Append(cursor.CurrentText()); @@ -82,10 +82,11 @@ class NGInlineCursorTest : public NGLayoutTest, return result.ToString(); } - if (cursor.IsText()) + if (cursor.Current().IsText()) return cursor.CurrentText().ToString().StripWhiteSpace(); - if (const LayoutObject* layout_object = cursor.CurrentLayoutObject()) { + if (const LayoutObject* layout_object = + cursor.Current().GetLayoutObject()) { if (const Element* element = DynamicTo<Element>(layout_object->GetNode())) { if (const AtomicString& id = element->GetIdAttribute()) @@ -100,18 +101,22 @@ class NGInlineCursorTest : public NGLayoutTest, Vector<String> ToDebugStringListWithBidiLevel(const NGInlineCursor& start) { Vector<String> list; - for (NGInlineCursor cursor(start); cursor; cursor.MoveToNext()) + for (NGInlineCursor cursor(start); cursor; cursor.MoveToNext()) { + // Inline boxes do not have bidi level. + if (cursor.Current().IsInlineBox()) + continue; list.push_back(ToDebugStringWithBidiLevel(cursor)); + } return list; } String ToDebugStringWithBidiLevel(const NGInlineCursor& cursor) { - if (!cursor.IsText() && !cursor.IsAtomicInline()) + if (!cursor.Current().IsText() && !cursor.Current().IsAtomicInline()) return ToDebugString(cursor); StringBuilder result; result.Append(ToDebugString(cursor)); result.Append(':'); - result.AppendNumber(cursor.CurrentBidiLevel()); + result.AppendNumber(cursor.Current().BidiLevel()); return result.ToString(); } }; @@ -126,8 +131,8 @@ TEST_P(NGInlineCursorTest, BidiLevelInlineBoxLTR) { "<div id=root dir=ltr>" "abc<b id=def>def</b><bdo dir=rtl><b id=ghi>GHI</b></bdo>jkl</div>"); Vector<String> list = ToDebugStringListWithBidiLevel(cursor); - EXPECT_THAT(list, ElementsAre("#linebox", "abc:0", "#def:0", - "LayoutInline BDO", "#ghi:1", "jkl:0")); + EXPECT_THAT(list, + ElementsAre("#linebox", "abc:0", "#def:0", "#ghi:1", "jkl:0")); } TEST_P(NGInlineCursorTest, BidiLevelInlineBoxRTL) { @@ -136,8 +141,8 @@ TEST_P(NGInlineCursorTest, BidiLevelInlineBoxRTL) { "<div id=root dir=rtl>" "abc<b id=def>def</b><bdo dir=rtl><b id=ghi>GHI</b></bdo>jkl</div>"); Vector<String> list = ToDebugStringListWithBidiLevel(cursor); - EXPECT_THAT(list, ElementsAre("#linebox", "LayoutInline BDO", "#ghi:3", - "jkl:2", "#def:1", "abc:2")); + EXPECT_THAT(list, + ElementsAre("#linebox", "#ghi:3", "jkl:2", "#def:1", "abc:2")); } TEST_P(NGInlineCursorTest, BidiLevelSimpleLTR) { @@ -163,7 +168,7 @@ TEST_P(NGInlineCursorTest, BidiLevelSimpleRTL) { TEST_P(NGInlineCursorTest, GetLayoutBlockFlowWithScopedCursor) { NGInlineCursor line = SetupCursor("<div id=root>line1<br>line2</div>"); - ASSERT_TRUE(line.IsLineBox()) << line; + ASSERT_TRUE(line.Current().IsLineBox()) << line; NGInlineCursor cursor = line.CursorForDescendants(); EXPECT_EQ(line.GetLayoutBlockFlow(), cursor.GetLayoutBlockFlow()); } @@ -175,11 +180,11 @@ TEST_P(NGInlineCursorTest, ContainingLine) { SetupCursor("<div id=root>abc<a id=target>def</a>ghi<br>xyz</div>"); const LayoutBlockFlow& block_flow = *cursor.GetLayoutBlockFlow(); NGInlineCursor line1(cursor); - ASSERT_TRUE(line1.IsLineBox()); + ASSERT_TRUE(line1.Current().IsLineBox()); NGInlineCursor line2(line1); line2.MoveToNextSibling(); - ASSERT_TRUE(line2.IsLineBox()); + ASSERT_TRUE(line2.Current().IsLineBox()); cursor.MoveTo(*block_flow.FirstChild()); cursor.MoveToContainingLine(); @@ -212,9 +217,14 @@ TEST_P(NGInlineCursorTest, CulledInlineWithAtomicInline) { list.push_back(ToDebugString(cursor)); cursor.MoveToNextForSameLayoutObject(); } - EXPECT_THAT(list, ElementsAre("abc", "ABC", "", "XYZ", "xyz")); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + EXPECT_THAT(list, ElementsAre("#culled", "#culled")); + else + EXPECT_THAT(list, ElementsAre("abc", "ABC", "", "XYZ", "xyz")); } +// We should not have float:right fragment, because it isn't in-flow in +// an inline formatting context. // For https://crbug.com/1026022 TEST_P(NGInlineCursorTest, CulledInlineWithFloat) { SetBodyInnerHTML( @@ -228,23 +238,27 @@ TEST_P(NGInlineCursorTest, CulledInlineWithFloat) { list.push_back(ToDebugString(cursor)); cursor.MoveToNextForSameLayoutObject(); } - EXPECT_THAT(list, ElementsAre("abc", "xyz")) - << "We should not have float:right fragment, because it isn't in-flow in " - "an inline formatting context."; + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + EXPECT_THAT(list, ElementsAre("#culled")); + else + EXPECT_THAT(list, ElementsAre("abc", "xyz")); } TEST_P(NGInlineCursorTest, CulledInlineWithRoot) { - NGInlineCursor cursor = - SetupCursor("<div id=root><a><b>abc</b><br><i>xyz</i></a></div>"); - const LayoutInline& layout_inline = - ToLayoutInline(*cursor.GetLayoutBlockFlow()->FirstChild()); - cursor.MoveTo(layout_inline); + NGInlineCursor cursor = SetupCursor(R"HTML( + <div id="root"><a id="a"><b>abc</b><br><i>xyz</i></a></div> + )HTML"); + const LayoutObject* layout_inline_a = GetLayoutObjectByElementId("a"); + cursor.MoveTo(*layout_inline_a); Vector<String> list; while (cursor) { list.push_back(ToDebugString(cursor)); cursor.MoveToNextForSameLayoutObject(); } - EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + EXPECT_THAT(list, ElementsAre("#a", "#a")); + else + EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); } TEST_P(NGInlineCursorTest, CulledInlineWithoutRoot) { @@ -259,7 +273,10 @@ TEST_P(NGInlineCursorTest, CulledInlineWithoutRoot) { list.push_back(ToDebugString(cursor)); cursor.MoveToNextForSameLayoutObject(); } - EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + EXPECT_THAT(list, ElementsAre("#a", "#a")); + else + EXPECT_THAT(list, ElementsAre("abc", "", "xyz")); } TEST_P(NGInlineCursorTest, FirstChild) { @@ -367,6 +384,17 @@ TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithImages) { EXPECT_EQ("#last", ToDebugString(last_logical_leaf)); } +TEST_P(NGInlineCursorTest, IsEmptyLineBox) { + InsertStyleElement("b { margin-bottom: 1px; }"); + NGInlineCursor cursor = SetupCursor("<div id=root>abc<br><b></b></div>"); + + EXPECT_FALSE(cursor.Current().IsEmptyLineBox()) + << "'abc\\n' is in non-empty line box."; + cursor.MoveToNextLine(); + EXPECT_TRUE(cursor.Current().IsEmptyLineBox()) + << "<b></b> with margin produces empty line box."; +} + TEST_P(NGInlineCursorTest, LastChild) { // TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline. InsertStyleElement("a, b { background: gray; }"); @@ -431,11 +459,27 @@ TEST_P(NGInlineCursorTest, NextWithEllipsis) { EXPECT_THAT(list, ElementsAre("#linebox", "abcdefghi", "abcd", u"#'\u2026'")); } +TEST_P(NGInlineCursorTest, NextWithEllipsisInlineBoxOnly) { + LoadAhem(); + InsertStyleElement( + "#root {" + "font: 10px/1 Ahem;" + "width: 5ch;" + "overflow: hidden;" + "text-overflow: ellipsis;" + "}" + "span { border: solid 10ch blue; }"); + NGInlineCursor cursor = SetupCursor("<div id=root><span></span></div>"); + Vector<String> list = ToDebugStringList(cursor); + EXPECT_THAT(list, ElementsAre("#linebox", "LayoutInline SPAN")); +} + TEST_P(NGInlineCursorTest, NextWithListItem) { NGInlineCursor cursor = SetupCursor("<ul><li id=root>abc</li></ul>"); Vector<String> list = ToDebugStringList(cursor); - EXPECT_THAT(list, - ElementsAre("LayoutNGListMarker (anonymous)", "#linebox", "abc")); + EXPECT_THAT(list, ElementsAre("LayoutNGOutsideListMarker ::marker", + "#linebox", "abc")); + EXPECT_EQ(GetLayoutObjectByElementId("root"), cursor.GetLayoutBlockFlow()); } TEST_P(NGInlineCursorTest, NextWithSoftHyphens) { @@ -546,12 +590,12 @@ TEST_P(NGInlineCursorTest, NextInlineLeafIgnoringLineBreak) { TEST_P(NGInlineCursorTest, NextLine) { NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>"); NGInlineCursor line1(cursor); - while (line1 && !line1.IsLineBox()) + while (line1 && !line1.Current().IsLineBox()) line1.MoveToNext(); ASSERT_TRUE(line1.IsNotNull()); NGInlineCursor line2(line1); line2.MoveToNext(); - while (line2 && !line2.IsLineBox()) + while (line2 && !line2.Current().IsLineBox()) line2.MoveToNext(); ASSERT_NE(line1, line2); @@ -576,6 +620,10 @@ TEST_P(NGInlineCursorTest, NextWithInlineBox) { SetupCursor("<div id=root>abc<b id=ib>def</b>xyz</div>"); Vector<String> list = ToDebugStringList(cursor); EXPECT_THAT(list, ElementsAre("#linebox", "abc", "#ib", "xyz")); + + NGInlineCursor cursor2; + cursor2.MoveTo(*GetElementById("ib")->firstChild()->GetLayoutObject()); + EXPECT_EQ(GetLayoutObjectByElementId("ib"), cursor2.GetLayoutBlockFlow()); } TEST_P(NGInlineCursorTest, NextForSameLayoutObject) { @@ -594,10 +642,10 @@ TEST_P(NGInlineCursorTest, Sibling) { InsertStyleElement("a, b { background: gray; }"); NGInlineCursor cursor = SetupCursor("<div id=root>abc<a>DEF<b>GHI</b></a>xyz</div>"); + TestPrevoiusSibling(cursor.CursorForDescendants()); cursor.MoveToFirstChild(); // go to "abc" Vector<String> list = SiblingsToDebugStringList(cursor); EXPECT_THAT(list, ElementsAre("abc", "LayoutInline A", "xyz")); - TestPrevoiusSibling(cursor); } TEST_P(NGInlineCursorTest, Sibling2) { @@ -606,10 +654,10 @@ TEST_P(NGInlineCursorTest, Sibling2) { NGInlineCursor cursor = SetupCursor("<div id=root><a>abc<b>def</b>xyz</a></div>"); cursor.MoveToFirstChild(); // go to <a>abc</a> + TestPrevoiusSibling(cursor.CursorForDescendants()); cursor.MoveToFirstChild(); // go to "abc" Vector<String> list = SiblingsToDebugStringList(cursor); EXPECT_THAT(list, ElementsAre("abc", "LayoutInline B", "xyz")); - TestPrevoiusSibling(cursor); } TEST_P(NGInlineCursorTest, NextSkippingChildren) { @@ -741,12 +789,12 @@ TEST_P(NGInlineCursorTest, PreviousInlineLeafOnLineFromLayoutText) { TEST_P(NGInlineCursorTest, PreviousLine) { NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>"); NGInlineCursor line1(cursor); - while (line1 && !line1.IsLineBox()) + while (line1 && !line1.Current().IsLineBox()) line1.MoveToNext(); ASSERT_TRUE(line1.IsNotNull()); NGInlineCursor line2(line1); line2.MoveToNext(); - while (line2 && !line2.IsLineBox()) + while (line2 && !line2.Current().IsLineBox()) line2.MoveToNext(); ASSERT_NE(line1, line2); @@ -784,9 +832,9 @@ TEST_P(NGInlineCursorTest, CursorForDescendants) { LayoutBlockFlow* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("root")); NGInlineCursor cursor(*block_flow); - EXPECT_TRUE(cursor.IsLineBox()); + EXPECT_TRUE(cursor.Current().IsLineBox()); cursor.MoveToNext(); - EXPECT_TRUE(cursor.IsText()); + EXPECT_TRUE(cursor.Current().IsText()); EXPECT_THAT(ToDebugStringList(cursor.CursorForDescendants()), ElementsAre()); cursor.MoveToNext(); EXPECT_EQ(ToDebugString(cursor), "#span1"); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc index d9aa540d902..443c9070088 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc @@ -62,13 +62,13 @@ class NGPhysicalFragmentCollectorBase { // Traverse descendants unless the fragment is laid out separately from the // inline layout algorithm. - if (&fragment != root_fragment_ && fragment.IsBlockFormattingContextRoot()) + if (&fragment != root_fragment_ && fragment.IsFormattingContextRoot()) return; DCHECK(fragment.IsContainer()); DCHECK(fragment.IsInline() || fragment.IsLineBox() || (fragment.IsBlockFlow() && - To<NGPhysicalBoxFragment>(fragment).ChildrenInline())); + To<NGPhysicalBoxFragment>(fragment).IsInlineFormattingContext())); for (const auto& child : To<NGPhysicalContainerFragment>(fragment).Children()) { @@ -179,7 +179,7 @@ Vector<Result> NGInlineFragmentTraversal::SelfFragmentsOf( for (const NGPaintFragment* fragment : NGPaintFragment::InlineFragmentsFor(layout_object)) { result.push_back(Result{&fragment->PhysicalFragment(), - fragment->InlineOffsetToContainerBox()}); + fragment->OffsetInContainerBlock()}); } return result; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc index e9ab83060fe..d356f218bbf 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc @@ -50,6 +50,10 @@ class NGInlineFragmentTraversalTest : public NGLayoutTest { } TEST_F(NGInlineFragmentTraversalTest, DescendantsOf) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + // NGFragmentItem doesn't use |NGInlineFragmentTraversal|. + return; + } SetBodyInnerHTML( "<style>* { border: 1px solid}</style>" "<div id=t>foo<b id=b>bar</b><br>baz</div>"); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc index 1e17b432948..bb809f51b2b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc @@ -60,7 +60,8 @@ bool IsInlineBoxEndEmpty(const ComputedStyle& style, NGInlineItem::NGInlineItem(NGInlineItemType type, unsigned start, unsigned end, - LayoutObject* layout_object) + LayoutObject* layout_object, + bool is_first_for_node) : start_offset_(start), end_offset_(end), layout_object_(layout_object), @@ -74,7 +75,8 @@ NGInlineItem::NGInlineItem(NGInlineItemType type, end_collapse_type_(kNotCollapsible), is_end_collapsible_newline_(false), is_symbol_marker_(false), - is_generated_for_line_break_(false) { + is_generated_for_line_break_(false), + is_first_for_node_(is_first_for_node) { DCHECK_GE(end, start); ComputeBoxProperties(); } @@ -97,7 +99,8 @@ NGInlineItem::NGInlineItem(const NGInlineItem& other, end_collapse_type_(other.end_collapse_type_), is_end_collapsible_newline_(other.is_end_collapsible_newline_), is_symbol_marker_(other.is_symbol_marker_), - is_generated_for_line_break_(other.is_generated_for_line_break_) { + is_generated_for_line_break_(other.is_generated_for_line_break_), + is_first_for_node_(other.is_first_for_node_) { DCHECK_GE(end, start); } @@ -197,6 +200,7 @@ void NGInlineItem::Split(Vector<NGInlineItem>& items, items.insert(index + 1, items[index]); items[index].end_offset_ = offset; items[index + 1].start_offset_ = offset; + items[index + 1].is_first_for_node_ = false; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h index 018e02db8d5..5a8575d3a51 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h @@ -66,7 +66,8 @@ class CORE_EXPORT NGInlineItem { NGInlineItem(NGInlineItemType type, unsigned start, unsigned end, - LayoutObject* layout_object = nullptr); + LayoutObject* layout_object, + bool is_first_for_node); ~NGInlineItem(); // Copy constructor adjusting start/end and shape results. @@ -199,6 +200,11 @@ class CORE_EXPORT NGInlineItem { static void Split(Vector<NGInlineItem>&, unsigned index, unsigned offset); + // Return true if this is the first item created for the node. A node may be + // split into multiple inline items due e.g. hard line breaks or bidi + // segments. + bool IsFirstForNode() const { return is_first_for_node_; } + // RunSegmenter properties. unsigned SegmentData() const { return segment_data_; } static void SetSegmentData(const RunSegmenter::RunSegmenterRange& range, @@ -253,6 +259,7 @@ class CORE_EXPORT NGInlineItem { unsigned is_end_collapsible_newline_ : 1; unsigned is_symbol_marker_ : 1; unsigned is_generated_for_line_break_ : 1; + unsigned is_first_for_node_ : 1; friend class NGInlineNode; friend class NGInlineNodeDataEditor; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc index 801aaf305c2..b6e4388cefb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc @@ -220,18 +220,7 @@ LayoutUnit NGLineInfo::ComputeWidth() const { for (const NGInlineItemResult& item_result : Results()) inline_size += item_result.inline_size; - if (UNLIKELY(line_end_fragment_)) { - inline_size += line_end_fragment_->Size() - .ConvertToLogical(LineStyle().GetWritingMode()) - .inline_size; - } - return inline_size; } -void NGLineInfo::SetLineEndFragment( - scoped_refptr<const NGPhysicalTextFragment> fragment) { - line_end_fragment_ = std::move(fragment); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h index c55dfae46de..2015830ed2c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h @@ -7,7 +7,6 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" @@ -37,6 +36,15 @@ struct CORE_EXPORT NGInlineItemResult { return end_offset - start_offset; } + LayoutUnit HyphenInlineSize() const { + return hyphen_shape_result->SnappedWidth().ClampNegativeToZero(); + } + + void ClearHyphen() { + hyphen_string = String(); + hyphen_shape_result = nullptr; + } + // The NGInlineItem and its index. const NGInlineItem* item; unsigned item_index; @@ -52,6 +60,10 @@ struct CORE_EXPORT NGInlineItemResult { // is needed in the line breaker. scoped_refptr<const ShapeResultView> shape_result; + // Hyphen character and its |ShapeResult| if this text is hyphenated. + String hyphen_string; + scoped_refptr<const ShapeResult> hyphen_shape_result; + // NGLayoutResult for atomic inline items. scoped_refptr<const NGLayoutResult> layout_result; @@ -112,10 +124,6 @@ struct CORE_EXPORT NGInlineItemResult { // position) any unpositioned floats. bool has_unpositioned_floats = false; - // End effects for text items. - // The effects are included in |shape_result|, but not in text content. - NGTextEndEffect text_end_effect = NGTextEndEffect::kNone; - NGInlineItemResult(); NGInlineItemResult(const NGInlineItem*, unsigned index, @@ -139,7 +147,7 @@ using NGInlineItemResults = Vector<NGInlineItemResult, 32>; // // NGLineBreaker produces, and NGInlineLayoutAlgorithm consumes. class CORE_EXPORT NGLineInfo { - DISALLOW_NEW(); + STACK_ALLOCATED(); public: const NGInlineItemsData& ItemsData() const { @@ -208,7 +216,7 @@ class CORE_EXPORT NGLineInfo { // True if this line has overflow, excluding preserved trailing spaces. bool HasOverflow() const { return has_overflow_; } - void SetHasOverflow() { has_overflow_ = true; } + void SetHasOverflow(bool value = true) { has_overflow_ = value; } void SetBfcOffset(const NGBfcOffset& bfc_offset) { bfc_offset_ = bfc_offset; } void SetWidth(LayoutUnit available_width, LayoutUnit width) { @@ -242,12 +250,6 @@ class CORE_EXPORT NGLineInfo { // justify alignment. bool NeedsAccurateEndPosition() const { return needs_accurate_end_position_; } - // Fragment to append to the line end. Used by 'text-overflow: ellipsis'. - scoped_refptr<const NGPhysicalTextFragment>& LineEndFragment() { - return line_end_fragment_; - } - void SetLineEndFragment(scoped_refptr<const NGPhysicalTextFragment>); - private: bool ComputeNeedsAccurateEndPosition() const; @@ -258,7 +260,6 @@ class CORE_EXPORT NGLineInfo { const NGInlineItemsData* items_data_ = nullptr; const ComputedStyle* line_style_ = nullptr; NGInlineItemResults results_; - scoped_refptr<const NGPhysicalTextFragment> line_end_fragment_; NGBfcOffset bfc_offset_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc index 744cff0d35a..84d422cbc20 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc @@ -128,8 +128,10 @@ void AppendItem(Vector<NGInlineItem>* items, NGInlineItem::NGInlineItemType type, unsigned start, unsigned end, - LayoutObject* layout_object = nullptr) { - items->push_back(NGInlineItem(type, start, end, layout_object)); + LayoutObject* layout_object, + bool is_first_for_node = true) { + items->push_back( + NGInlineItem(type, start, end, layout_object, is_first_for_node)); } inline bool ShouldIgnore(UChar c) { @@ -197,8 +199,8 @@ NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::BoxInfo::BoxInfo( const NGInlineItem& item) : item_index(item_index), should_create_box_fragment(item.ShouldCreateBoxFragment()), - style(*item.Style()), - text_metrics(NGLineHeightMetrics(style)) { + may_have_margin_(item.Style()->MayHaveMargin()), + text_metrics(NGLineHeightMetrics(*item.Style())) { DCHECK(item.Style()); } @@ -208,7 +210,7 @@ bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::BoxInfo:: ShouldCreateBoxFragmentForChild(const BoxInfo& child) const { // When a child inline box has margins, the parent has different width/height // from the union of children. - if (child.style.MayHaveMargin()) + if (child.may_have_margin_) return true; // Returns true when parent and child boxes have different font metrics, since @@ -231,21 +233,24 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::BoxInfo:: template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextItem( const StringView string, - LayoutText* layout_object) { + LayoutText* layout_object, + bool is_first_for_node) { DCHECK(layout_object); - AppendTextItem(NGInlineItem::kText, string, layout_object); + AppendTextItem(NGInlineItem::kText, string, layout_object, is_first_for_node); } template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextItem( NGInlineItem::NGInlineItemType type, const StringView string, - LayoutText* layout_object) { + LayoutText* layout_object, + bool is_first_for_node) { DCHECK(layout_object); unsigned start_offset = text_.length(); text_.Append(string); mapping_builder_.AppendIdentityMapping(string.length()); - AppendItem(items_, type, start_offset, text_.length(), layout_object); + AppendItem(items_, type, start_offset, text_.length(), layout_object, + is_first_for_node); DCHECK(!items_->back().IsEmptyItem()); // text item is not empty. is_empty_inline_ = false; @@ -501,7 +506,8 @@ template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate< OffsetMappingBuilder>::AppendCollapseWhitespace(const StringView string, const ComputedStyle* style, - LayoutText* layout_object) { + LayoutText* layout_object, + bool is_first_for_node) { DCHECK(!string.IsEmpty()); // This algorithm segments the input string at the collapsible space, and @@ -528,7 +534,7 @@ void NGInlineItemsBuilderTemplate< // LayoutBR does not set preserve_newline, but should be preserved. if (UNLIKELY(space_run_has_newline && string.length() == 1 && layout_object && layout_object->IsBR())) { - AppendForcedBreakCollapseWhitespace(layout_object); + AppendForcedBreakCollapseWhitespace(layout_object, is_first_for_node); return; } @@ -685,7 +691,7 @@ void NGInlineItemsBuilderTemplate< } AppendItem(items_, NGInlineItem::kText, start_offset, text_.length(), - layout_object); + layout_object, is_first_for_node); NGInlineItem& item = items_->back(); item.SetEndCollapseType(end_collapse, space_run_has_newline); DCHECK(!item.IsEmptyItem()); @@ -727,7 +733,8 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>:: do { ++end; } while (end < string.length() && string[end] == kSpaceCharacter); - AppendTextItem(StringView(string, *start, end - *start), layout_object); + AppendTextItem(StringView(string, *start, end - *start), layout_object, + /* is_first_for_node */ false); AppendGeneratedBreakOpportunity(layout_object); *start = end; } @@ -752,11 +759,12 @@ void NGInlineItemsBuilderTemplate< unsigned start = 0; InsertBreakOpportunityAfterLeadingPreservedSpaces(string, *style, layout_object, &start); - for (; start < string.length();) { + bool is_first_for_node = true; + for (; start < string.length(); is_first_for_node = false) { UChar c = string[start]; if (IsControlItemCharacter(c)) { if (c == kNewlineCharacter) { - AppendForcedBreak(layout_object); + AppendForcedBreak(layout_object, is_first_for_node); start++; // A forced break is not a collapsible space, but following collapsible // spaces are leading spaces and they need a special code in the line @@ -771,13 +779,14 @@ void NGInlineItemsBuilderTemplate< if (end == kNotFound) end = string.length(); AppendTextItem(NGInlineItem::kControl, - StringView(string, start, end - start), layout_object); + StringView(string, start, end - start), layout_object, + is_first_for_node); start = end; continue; } // ZWNJ splits item, but it should be text. if (c != kZeroWidthNonJoinerCharacter) { - Append(NGInlineItem::kControl, c, layout_object); + Append(NGInlineItem::kControl, c, layout_object, is_first_for_node); start++; continue; } @@ -786,7 +795,8 @@ void NGInlineItemsBuilderTemplate< wtf_size_t end = string.Find(IsControlItemCharacter, start + 1); if (end == kNotFound) end = string.length(); - AppendTextItem(StringView(string, start, end - start), layout_object); + AppendTextItem(StringView(string, start, end - start), layout_object, + is_first_for_node); start = end; } } @@ -796,9 +806,10 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendPreserveNewline( const String& string, const ComputedStyle* style, LayoutText* layout_object) { - for (unsigned start = 0; start < string.length();) { + bool is_first_for_node = true; + for (unsigned start = 0; start < string.length(); is_first_for_node = false) { if (string[start] == kNewlineCharacter) { - AppendForcedBreakCollapseWhitespace(layout_object); + AppendForcedBreakCollapseWhitespace(layout_object, is_first_for_node); start++; continue; } @@ -808,14 +819,15 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendPreserveNewline( end = string.length(); DCHECK_GE(end, start); AppendCollapseWhitespace(StringView(string, start, end - start), style, - layout_object); + layout_object, is_first_for_node); start = end; } } template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendForcedBreak( - LayoutObject* layout_object) { + LayoutObject* layout_object, + bool is_first_for_node) { DCHECK(layout_object); // At the forced break, add bidi controls to pop all contexts. // https://drafts.csswg.org/css-writing-modes-3/#bidi-embedding-breaks @@ -829,7 +841,8 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendForcedBreak( } } - Append(NGInlineItem::kControl, kNewlineCharacter, layout_object); + Append(NGInlineItem::kControl, kNewlineCharacter, layout_object, + is_first_for_node); // A forced break is not a collapsible space, but following collapsible spaces // are leading spaces and that they should be collapsed. @@ -849,11 +862,12 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendForcedBreak( template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>:: - AppendForcedBreakCollapseWhitespace(LayoutObject* layout_object) { + AppendForcedBreakCollapseWhitespace(LayoutObject* layout_object, + bool is_first_for_node) { // Remove collapsible spaces immediately before a preserved newline. RemoveTrailingCollapsibleSpaceIfExists(); - AppendForcedBreak(layout_object); + AppendForcedBreak(layout_object, is_first_for_node); } template <typename OffsetMappingBuilder> @@ -867,13 +881,15 @@ template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Append( NGInlineItem::NGInlineItemType type, UChar character, - LayoutObject* layout_object) { + LayoutObject* layout_object, + bool is_first_for_node) { DCHECK_NE(character, kSpaceCharacter); text_.Append(character); mapping_builder_.AppendIdentityMapping(1); unsigned end_offset = text_.length(); - AppendItem(items_, type, end_offset - 1, end_offset, layout_object); + AppendItem(items_, type, end_offset - 1, end_offset, layout_object, + is_first_for_node); is_empty_inline_ &= items_->back().IsEmptyItem(); is_block_level_ &= items_->back().IsBlockLevel(); @@ -887,7 +903,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendAtomicInline( layout_object); RestoreTrailingCollapsibleSpaceIfRemoved(); Append(NGInlineItem::kAtomicInline, kObjectReplacementCharacter, - layout_object); + layout_object, /* is_first_for_node */ true); // Mark dirty lines. Clear if marked, only the first dirty line is relevant. if (dirty_lines_ && @@ -1231,6 +1247,27 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::Exit( } template <typename OffsetMappingBuilder> +bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::MayBeBidiEnabled() + const { + return !text_.Is8Bit() || HasBidiControls(); +} + +template <typename OffsetMappingBuilder> +void NGInlineItemsBuilderTemplate< + OffsetMappingBuilder>::DidFinishCollectInlines(NGInlineNodeData* data) { + data->text_content = ToString(); + + // Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this + // point the string may or may not contain RTL characters. + // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it + // doesn't contain any RTL characters. + data->is_bidi_enabled_ = MayBeBidiEnabled(); + data->is_empty_inline_ = IsEmptyInline(); + data->is_block_level_ = IsBlockLevel(); + data->changes_may_affect_earlier_lines_ = ChangesMayAffectEarlierLines(); +} + +template <typename OffsetMappingBuilder> void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::SetIsSymbolMarker( bool b) { DCHECK(!items_->IsEmpty()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h index 8b0939aa096..eb58909de1d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h @@ -134,6 +134,9 @@ class NGInlineItemsBuilderTemplate { void EnterInline(LayoutInline*); void ExitInline(LayoutObject*); + // Set collected inline items data to |data|. + void DidFinishCollectInlines(NGInlineNodeData* data); + OffsetMappingBuilder& GetOffsetMappingBuilder() { return mapping_builder_; } void SetIsSymbolMarker(bool b); @@ -160,9 +163,11 @@ class NGInlineItemsBuilderTemplate { // Keep track of inline boxes to compute ShouldCreateBoxFragment. struct BoxInfo { + DISALLOW_NEW(); + unsigned item_index; bool should_create_box_fragment; - const ComputedStyle& style; + bool may_have_margin_; NGLineHeightMetrics text_metrics; BoxInfo(unsigned item_index, const NGInlineItem& item); @@ -191,18 +196,21 @@ class NGInlineItemsBuilderTemplate { // LayoutObject. void Append(NGInlineItem::NGInlineItemType, UChar, - LayoutObject*); + LayoutObject*, + bool is_first_for_node); void AppendCollapseWhitespace(const StringView, const ComputedStyle*, - LayoutText*); + LayoutText*, + bool is_first_for_node = true); void AppendPreserveWhitespace(const String&, const ComputedStyle*, LayoutText*); void AppendPreserveNewline(const String&, const ComputedStyle*, LayoutText*); - void AppendForcedBreakCollapseWhitespace(LayoutObject*); - void AppendForcedBreak(LayoutObject*); + void AppendForcedBreakCollapseWhitespace(LayoutObject*, + bool is_first_for_node); + void AppendForcedBreak(LayoutObject*, bool is_first_for_node); void RemoveTrailingCollapsibleSpaceIfExists(); void RemoveTrailingCollapsibleSpace(NGInlineItem*); @@ -211,16 +219,20 @@ class NGInlineItemsBuilderTemplate { void RestoreTrailingCollapsibleSpace(NGInlineItem*); void AppendTextItem(const StringView, - LayoutText* layout_object); + LayoutText* layout_object, + bool is_first_for_node); void AppendTextItem(NGInlineItem::NGInlineItemType type, const StringView, - LayoutText* layout_object); + LayoutText* layout_object, + bool is_first_for_node); void AppendEmptyTextItem(LayoutText* layout_object); void AppendGeneratedBreakOpportunity(LayoutObject*); void Exit(LayoutObject*); + bool MayBeBidiEnabled() const; + bool ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces( const String&, const ComputedStyle&, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc index df95d2fd3f6..77aff76eecc 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc @@ -28,7 +28,6 @@ class NGInlineItemsBuilderTest : public NGLayoutTest { void SetUp() override { NGLayoutTest::SetUp(); style_ = ComputedStyle::Create(); - style_->GetFont().Update(nullptr); } void TearDown() override { @@ -448,11 +447,12 @@ TEST_F(NGInlineItemsBuilderTest, BidiBlockOverride) { builder.ToString()); } -static std::unique_ptr<LayoutInline> CreateLayoutInline( +static LayoutInline* CreateLayoutInline( + Document* document, void (*initialize_style)(ComputedStyle*)) { scoped_refptr<ComputedStyle> style(ComputedStyle::Create()); initialize_style(style.get()); - std::unique_ptr<LayoutInline> node = std::make_unique<LayoutInline>(nullptr); + LayoutInline* const node = LayoutInline::CreateAnonymous(document); node->SetModifiedStyleOutsideStyleRecalc( std::move(style), LayoutObject::ApplyStyleChanges::kNo); node->SetIsInLayoutNGInlineFormattingContext(true); @@ -463,14 +463,14 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolate) { Vector<NGInlineItem> items; NGInlineItemsBuilder builder(&items); AppendText("Hello ", &builder); - std::unique_ptr<LayoutInline> isolate_rtl( - CreateLayoutInline([](ComputedStyle* style) { + LayoutInline* const isolate_rtl = + CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) { style->SetUnicodeBidi(UnicodeBidi::kIsolate); style->SetDirection(TextDirection::kRtl); - })); - builder.EnterInline(isolate_rtl.get()); + }); + builder.EnterInline(isolate_rtl); AppendText(u"\u05E2\u05D1\u05E8\u05D9\u05EA", &builder); - builder.ExitInline(isolate_rtl.get()); + builder.ExitInline(isolate_rtl); AppendText(" World", &builder); // Expected control characters as defined in: @@ -481,20 +481,21 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolate) { u"\u2069" u" World"), builder.ToString()); + isolate_rtl->Destroy(); } TEST_F(NGInlineItemsBuilderTest, BidiIsolateOverride) { Vector<NGInlineItem> items; NGInlineItemsBuilder builder(&items); AppendText("Hello ", &builder); - std::unique_ptr<LayoutInline> isolate_override_rtl( - CreateLayoutInline([](ComputedStyle* style) { + LayoutInline* const isolate_override_rtl = + CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) { style->SetUnicodeBidi(UnicodeBidi::kIsolateOverride); style->SetDirection(TextDirection::kRtl); - })); - builder.EnterInline(isolate_override_rtl.get()); + }); + builder.EnterInline(isolate_override_rtl); AppendText(u"\u05E2\u05D1\u05E8\u05D9\u05EA", &builder); - builder.ExitInline(isolate_override_rtl.get()); + builder.ExitInline(isolate_override_rtl); AppendText(" World", &builder); // Expected control characters as defined in: @@ -505,6 +506,7 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolateOverride) { u"\u202C\u2069" u" World"), builder.ToString()); + isolate_override_rtl->Destroy(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc index 732b84c0ba2..76a67f7e9eb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc @@ -6,8 +6,9 @@ #include <memory> +#include "base/compiler_specific.h" #include "base/containers/adapters.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" +#include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" @@ -19,12 +20,13 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" @@ -65,7 +67,9 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( context_(context), baseline_type_(container_builder_.Style().GetFontBaseline()), is_horizontal_writing_mode_( - blink::IsHorizontalWritingMode(space.GetWritingMode())) { + blink::IsHorizontalWritingMode(space.GetWritingMode())), + truncate_type_( + static_cast<unsigned>(TruncateTypeFromConstraintSpace(space))) { DCHECK(context); quirks_mode_ = inline_node.InLineHeightQuirksMode(); } @@ -77,8 +81,10 @@ NGInlineLayoutAlgorithm::~NGInlineLayoutAlgorithm() = default; NGInlineBoxState* NGInlineLayoutAlgorithm::HandleOpenTag( const NGInlineItem& item, const NGInlineItemResult& item_result, + NGLineBoxFragmentBuilder::ChildList* line_box, NGInlineLayoutStateStack* box_states) const { - NGInlineBoxState* box = box_states->OnOpenTag(item, item_result, line_box_); + NGInlineBoxState* box = + box_states->OnOpenTag(item, item_result, baseline_type_, line_box); // Compute text metrics for all inline boxes since even empty inlines // influence the line height, except when quirks mode and the box is empty // for the purpose of empty block calculation. @@ -103,10 +109,14 @@ NGInlineBoxState* NGInlineLayoutAlgorithm::HandleCloseTag( box->EnsureTextMetrics(*item.Style(), baseline_type_); box = box_states_->OnCloseTag(&line_box_, box, baseline_type_, item.HasEndEdge()); - // Just clear |NeedsLayout| flags. Culled inline boxes do not need paint - // invalidations. If this object produces box fragments, - // |NGInlineBoxStateStack| takes care of invalidations. - item.GetLayoutObject()->ClearNeedsLayoutWithoutPaintInvalidation(); + if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + // Just clear |NeedsLayout| flags. Culled inline boxes do not need paint + // invalidations. If this object produces box fragments, + // |NGInlineBoxStateStack| takes care of invalidations. + item.GetLayoutObject()->ClearNeedsLayoutWithoutPaintInvalidation(); + } else { + item.GetLayoutObject()->ClearNeedsLayout(); + } return box; } @@ -160,12 +170,13 @@ void NGInlineLayoutAlgorithm::RebuildBoxStates( } // Create box states for tags that are not closed yet. + NGLineBoxFragmentBuilder::ChildList line_box; box_states->OnBeginPlaceItems(line_info.LineStyle(), baseline_type_, - quirks_mode_); + quirks_mode_, &line_box); for (const NGInlineItem* item : open_items) { NGInlineItemResult item_result; NGLineBreaker::ComputeOpenTagResult(*item, ConstraintSpace(), &item_result); - HandleOpenTag(*item, item_result, box_states); + HandleOpenTag(*item, item_result, &line_box, box_states); } } @@ -175,9 +186,9 @@ void NGInlineLayoutAlgorithm::CheckBoxStates( const NGInlineBreakToken* break_token) const { NGInlineLayoutStateStack rebuilt; RebuildBoxStates(line_info, break_token, &rebuilt); - rebuilt.OnBeginPlaceItems(line_info.LineStyle(), baseline_type_, - quirks_mode_); - + NGLineBoxFragmentBuilder::ChildList line_box; + rebuilt.OnBeginPlaceItems(line_info.LineStyle(), baseline_type_, quirks_mode_, + &line_box); DCHECK(box_states_); box_states_->CheckSame(rebuilt); } @@ -201,8 +212,8 @@ void NGInlineLayoutAlgorithm::CreateLine( // The baseline is adjusted after the height of the line box is computed. const ComputedStyle& line_style = line_info->LineStyle(); box_states_->SetIsEmptyLine(line_info->IsEmptyLine()); - NGInlineBoxState* box = - box_states_->OnBeginPlaceItems(line_style, baseline_type_, quirks_mode_); + NGInlineBoxState* box = box_states_->OnBeginPlaceItems( + line_style, baseline_type_, quirks_mode_, &line_box_); #if DCHECK_IS_ON() if (is_box_states_from_context_) CheckBoxStates(*line_info, BreakToken()); @@ -227,6 +238,8 @@ void NGInlineLayoutAlgorithm::CreateLine( item.GetLayoutObject()->IsLayoutNGListItem()); DCHECK(item_result.shape_result); + text_builder.SetIsFirstForNode(IsFirstForNode(item, BreakToken())); + if (UNLIKELY(quirks_mode_)) box->EnsureTextMetrics(*item.Style(), baseline_type_); @@ -236,7 +249,7 @@ void NGInlineLayoutAlgorithm::CreateLine( baseline_type_); } - if (item.IsSymbolMarker()) { + if (UNLIKELY(item.IsSymbolMarker())) { text_builder.SetItem(NGPhysicalTextFragment::kSymbolMarker, line_info->ItemsData(), &item_result, box->text_height); @@ -245,14 +258,23 @@ void NGInlineLayoutAlgorithm::CreateLine( line_info->ItemsData(), &item_result, box->text_height); } - line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, - item_result.inline_size, item.BidiLevel()); + if (UNLIKELY(item_result.hyphen_shape_result)) { + LayoutUnit hyphen_inline_size = item_result.HyphenInlineSize(); + line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, + item_result.inline_size - hyphen_inline_size, + item.BidiLevel()); + PlaceHyphen(item_result, hyphen_inline_size, box); + } else { + line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, + item_result.inline_size, item.BidiLevel()); + } // Text boxes always need full paint invalidations. item.GetLayoutObject()->ClearNeedsLayoutWithFullPaintInvalidation(); + } else if (item.Type() == NGInlineItem::kControl) { PlaceControlItem(item, *line_info, &item_result, box); } else if (item.Type() == NGInlineItem::kOpenTag) { - box = HandleOpenTag(item, item_result, box_states_); + box = HandleOpenTag(item, item_result, &line_box_, box_states_); } else if (item.Type() == NGInlineItem::kCloseTag) { box = HandleCloseTag(item, item_result, box); } else if (item.Type() == NGInlineItem::kAtomicInline) { @@ -284,13 +306,6 @@ void NGInlineLayoutAlgorithm::CreateLine( } } - if (line_info->LineEndFragment()) { - // Add a generated text fragment, hyphen or ellipsis, at the logical end. - // By using the paragraph bidi_level, it will appear at the visual end. - PlaceGeneratedContent(std::move(line_info->LineEndFragment()), - IsLtr(line_info->BaseDirection()) ? 0 : 1, box); - } - box_states_->OnEndPlaceItems(&line_box_, baseline_type_); if (UNLIKELY(Node().IsBidiEnabled())) { @@ -308,11 +323,21 @@ void NGInlineLayoutAlgorithm::CreateLine( } } - // Truncate the line if 'text-overflow: ellipsis' is set. - if (UNLIKELY(inline_size > line_info->AvailableWidth() && - node_.GetLayoutBlockFlow()->ShouldTruncateOverflowingText())) { - inline_size = NGLineTruncator(*line_info) - .TruncateLine(inline_size, &line_box_, box_states_); + // Truncate the line if 'text-overflow: ellipsis' is set, or for line-clamp. + if (UNLIKELY((inline_size > + line_info->AvailableWidth() - line_info->TextIndent() && + node_.GetLayoutBlockFlow()->ShouldTruncateOverflowingText()) || + ShouldTruncateForLineClamp(*line_info))) { + NGLineTruncator truncator(*line_info); + auto* input = + DynamicTo<HTMLInputElement>(node_.GetLayoutBlockFlow()->GetNode()); + if (input && input->ShouldApplyMiddleEllipsis()) { + inline_size = truncator.TruncateLineInTheMiddle(inline_size, &line_box_, + box_states_); + } else { + inline_size = + truncator.TruncateLine(inline_size, &line_box_, box_states_); + } } // Negative margins can make the position negative, but the inline size is @@ -421,36 +446,28 @@ void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item, NGTextFragmentBuilder text_builder(ConstraintSpace().GetWritingMode()); text_builder.SetItem(type, line_info.ItemsData(), item_result, box->text_height); + text_builder.SetIsFirstForNode(IsFirstForNode(item, BreakToken())); line_box_.AddChild(text_builder.ToTextFragment(), box->text_top, item_result->inline_size, item.BidiLevel()); } -// Place a generated content that does not exist in DOM nor in LayoutObject -// tree. -void NGInlineLayoutAlgorithm::PlaceGeneratedContent( - scoped_refptr<const NGPhysicalTextFragment> fragment, - UBiDiLevel bidi_level, - NGInlineBoxState* box) { - LayoutUnit inline_size = IsHorizontalWritingMode() ? fragment->Size().width - : fragment->Size().height; - const ComputedStyle& style = fragment->Style(); - if (box->CanAddTextOfStyle(style)) { - if (UNLIKELY(quirks_mode_)) - box->EnsureTextMetrics(style, baseline_type_); - DCHECK(!box->text_metrics.IsEmpty()); - line_box_.AddChild(std::move(fragment), box->text_top, inline_size, - bidi_level); - } else { - scoped_refptr<ComputedStyle> text_style = - ComputedStyle::CreateAnonymousStyleWithDisplay(style, - EDisplay::kInline); - NGInlineBoxState* box = box_states_->OnOpenTag(*text_style, line_box_); - box->ComputeTextMetrics(*text_style, baseline_type_); - DCHECK(!box->text_metrics.IsEmpty()); - line_box_.AddChild(std::move(fragment), box->text_top, inline_size, - bidi_level); - box_states_->OnCloseTag(&line_box_, box, baseline_type_); - } +void NGInlineLayoutAlgorithm::PlaceHyphen(const NGInlineItemResult& item_result, + LayoutUnit hyphen_inline_size, + NGInlineBoxState* box) { + DCHECK(item_result.item); + DCHECK(item_result.hyphen_string); + DCHECK(item_result.hyphen_shape_result); + DCHECK_EQ(hyphen_inline_size, item_result.HyphenInlineSize()); + const NGInlineItem& item = *item_result.item; + const WritingMode writing_mode = ConstraintSpace().GetWritingMode(); + NGTextFragmentBuilder builder(writing_mode); + builder.SetText( + item.GetLayoutObject(), item_result.hyphen_string, item.Style(), + /* is_ellipsis_style */ false, + ShapeResultView::Create(item_result.hyphen_shape_result.get())); + DCHECK(!box->text_metrics.IsEmpty()); + line_box_.AddChild(builder.ToTextFragment(), box->text_top, + hyphen_inline_size, item.BidiLevel()); } NGInlineBoxState* NGInlineLayoutAlgorithm::PlaceAtomicInline( @@ -465,7 +482,8 @@ NGInlineBoxState* NGInlineLayoutAlgorithm::PlaceAtomicInline( // position += item_result->margins.LineLeft(style.Direction()); item_result->has_edge = true; - NGInlineBoxState* box = box_states_->OnOpenTag(item, *item_result, line_box_); + NGInlineBoxState* box = + box_states_->OnOpenTag(item, *item_result, baseline_type_, line_box_); PlaceLayoutResult(item_result, box, box->margin_inline_start); return box_states_->OnCloseTag(&line_box_, box, baseline_type_); } @@ -478,20 +496,20 @@ void NGInlineLayoutAlgorithm::PlaceLayoutResult(NGInlineItemResult* item_result, DCHECK(item_result->item); const NGInlineItem& item = *item_result->item; DCHECK(item.Style()); - NGBoxFragment fragment(ConstraintSpace().GetWritingMode(), - ConstraintSpace().Direction(), - To<NGPhysicalBoxFragment>( - item_result->layout_result->PhysicalFragment())); - NGLineHeightMetrics metrics = fragment.BaselineMetrics( - {NGBaselineAlgorithmType::kAtomicInline, baseline_type_}, - ConstraintSpace()); + NGLineHeightMetrics metrics = + NGBoxFragment(ConstraintSpace().GetWritingMode(), + ConstraintSpace().Direction(), + To<NGPhysicalBoxFragment>( + item_result->layout_result->PhysicalFragment())) + .BaselineMetrics(item_result->margins, baseline_type_); if (box) box->metrics.Unite(metrics); LayoutUnit line_top = item_result->margins.line_over - metrics.ascent; line_box_.AddChild(std::move(item_result->layout_result), LogicalOffset{inline_offset, line_top}, - item_result->inline_size, item.BidiLevel()); + item_result->inline_size, /* children_count */ 0, + item.BidiLevel()); } // Place all out-of-flow objects in |line_box_|. @@ -548,7 +566,7 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects( if (box->StyleRef().IsOriginalDisplayInlineType()) { // An inline-level OOF element positions itself within the line, at the // position it would have been if it was in-flow. - static_offset.inline_offset = child.offset.inline_offset; + static_offset.inline_offset = child.rect.offset.inline_offset; // The static-position of inline-level OOF-positioned nodes depends on // previous floats (if any). @@ -572,7 +590,7 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects( } } - child.offset = static_offset; + child.rect.offset = static_offset; } if (UNLIKELY(has_rtl_block_level_out_of_flow_objects)) { @@ -585,7 +603,7 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects( } if (has_preceding_inline_level_content && !box->StyleRef().IsOriginalDisplayInlineType()) { - child.offset.block_offset += line_height; + child.rect.offset.block_offset += line_height; } } } @@ -646,8 +664,8 @@ void NGInlineLayoutAlgorithm::PlaceFloatingObjects( block_offset = -fragment.BlockSize() - block_offset; } - child.offset = {child.bfc_offset.line_offset - bfc_line_offset, - block_offset}; + child.rect.offset = {child.bfc_offset.line_offset - bfc_line_offset, + block_offset}; } } @@ -660,8 +678,8 @@ void NGInlineLayoutAlgorithm::PlaceListMarker(const NGInlineItem& item, baseline_type_); } - container_builder_.SetUnpositionedListMarker( - NGUnpositionedListMarker(ToLayoutNGListMarker(item.GetLayoutObject()))); + container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker( + ToLayoutNGOutsideListMarker(item.GetLayoutObject()))); } // Justify the line. This changes the size of items by adding spacing. @@ -694,8 +712,8 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space, // matches to the |ShapeResult|. DCHECK(!line_info->Results().IsEmpty()); const NGInlineItemResult& last_item_result = line_info->Results().back(); - if (last_item_result.text_end_effect == NGTextEndEffect::kHyphen) - line_text_builder.Append(last_item_result.item->Style()->HyphenString()); + if (last_item_result.hyphen_string) + line_text_builder.Append(last_item_result.hyphen_string); // Compute the spacing to justify. String line_text = line_text_builder.ToString(); @@ -714,14 +732,14 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space, scoped_refptr<ShapeResult> shape_result = item_result.shape_result->CreateShapeResult(); DCHECK_GE(item_result.start_offset, line_info->StartOffset()); - // |shape_result| has more characters if it's hyphenated. - DCHECK(item_result.text_end_effect != NGTextEndEffect::kNone || - shape_result->NumCharacters() == - item_result.end_offset - item_result.start_offset); + DCHECK_EQ(shape_result->NumCharacters(), + item_result.end_offset - item_result.start_offset); shape_result->ApplySpacing(spacing, item_result.start_offset - line_info->StartOffset() - shape_result->StartIndex()); item_result.inline_size = shape_result->SnappedWidth(); + if (UNLIKELY(item_result.hyphen_shape_result)) + item_result.inline_size += item_result.HyphenInlineSize(); item_result.shape_result = ShapeResultView::Create(shape_result.get()); } else if (item_result.item->Type() == NGInlineItem::kAtomicInline) { float offset = 0.f; @@ -823,7 +841,7 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // In order to get the correct list of layout opportunities, we need to // position any "leading" floats within the exclusion space first. - NGPositionedFloatVector leading_floats; + STACK_UNINITIALIZED NGPositionedFloatVector leading_floats; unsigned handled_leading_floats_index = PositionLeadingFloats(&initial_exclusion_space, &leading_floats); @@ -832,7 +850,7 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { // We query all the layout opportunities on the initial exclusion space up // front, as if the line breaker may add floats and change the opportunities. - const LayoutOpportunityVector opportunities = + const LayoutOpportunityVector& opportunities = initial_exclusion_space.AllLayoutOpportunities( {ConstraintSpace().BfcOffset().line_offset, is_empty_inline ? ConstraintSpace().ExpectedBfcBlockOffset() @@ -876,7 +894,7 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { opportunity.ComputeLineLayoutOpportunity(ConstraintSpace(), line_block_size, block_delta); - NGLineInfo line_info; + STACK_UNINITIALIZED NGLineInfo line_info; NGLineBreaker line_breaker(Node(), NGLineBreakerMode::kContent, ConstraintSpace(), line_opportunity, leading_floats, handled_leading_floats_index, @@ -1092,22 +1110,9 @@ void NGInlineLayoutAlgorithm::BidiReorder(TextDirection base_direction) { // For opaque items, copy bidi levels from adjacent items. if (has_opaque_items) { - UBiDiLevel last_level = levels.front(); - if (last_level == kOpaqueBidiLevel) { - for (const UBiDiLevel level : levels) { - if (level != kOpaqueBidiLevel) { - last_level = level; - break; - } - } - } - // If all items are opaque, use the base direction. - if (last_level == kOpaqueBidiLevel) { - if (IsLtr(base_direction)) - return; - last_level = 1; - } - for (UBiDiLevel& level : levels) { + // Use the paragraph level for trailing opaque items. + UBiDiLevel last_level = IsLtr(base_direction) ? 0 : 1; + for (UBiDiLevel& level : base::Reversed(levels)) { if (level == kOpaqueBidiLevel) level = last_level; else @@ -1130,4 +1135,22 @@ void NGInlineLayoutAlgorithm::BidiReorder(TextDirection base_direction) { line_box_ = std::move(visual_items); } +// static +NGInlineLayoutAlgorithm::TruncateType +NGInlineLayoutAlgorithm::TruncateTypeFromConstraintSpace( + const NGConstraintSpace& space) { + if (space.LinesUntilClamp() != 1) + return TruncateType::kDefault; + return space.ForceTruncateAtLineClamp() ? TruncateType::kAlways + : TruncateType::kIfNotLastLine; +} + +bool NGInlineLayoutAlgorithm::ShouldTruncateForLineClamp( + const NGLineInfo& line_info) const { + const TruncateType truncate_type = static_cast<TruncateType>(truncate_type_); + return truncate_type == TruncateType::kAlways || + (truncate_type == TruncateType::kIfNotLastLine && + !line_info.IsLastLine()); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h index 6fe33fe9220..604eefb4041 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h @@ -52,6 +52,19 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final scoped_refptr<const NGLayoutResult> Layout() override; private: + enum class TruncateType { + // Indicates default behavior. The default truncates if the text doesn't + // fit and ShouldTruncateOverflowingText() returns true. + kDefault, + + // Truncate if NGLineInfo has more lines. + kIfNotLastLine, + + // Forces truncation. This is used when line-clamp is set and there are + // blocks after this. + kAlways, + }; + unsigned PositionLeadingFloats(NGExclusionSpace*, NGPositionedFloatVector*); NGPositionedFloat PositionFloat(LayoutUnit origin_block_bfc_offset, LayoutObject* floating_object, @@ -69,6 +82,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final NGInlineBoxState* HandleOpenTag(const NGInlineItem&, const NGInlineItemResult&, + NGLineBoxFragmentBuilder::ChildList*, NGInlineLayoutStateStack*) const; NGInlineBoxState* HandleCloseTag(const NGInlineItem&, const NGInlineItemResult&, @@ -80,9 +94,9 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final const NGLineInfo&, NGInlineItemResult*, NGInlineBoxState*); - void PlaceGeneratedContent(scoped_refptr<const NGPhysicalTextFragment>, - UBiDiLevel, - NGInlineBoxState*); + void PlaceHyphen(const NGInlineItemResult&, + LayoutUnit hyphen_inline_size, + NGInlineBoxState*); NGInlineBoxState* PlaceAtomicInline(const NGInlineItem&, const NGLineInfo&, NGInlineItemResult*); @@ -105,6 +119,13 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final const NGExclusionSpace&, LayoutUnit line_height); + static TruncateType TruncateTypeFromConstraintSpace( + const NGConstraintSpace& space); + + // Returns true if truncuation should happen as a result of line-clamp for + // |line_info|. + bool ShouldTruncateForLineClamp(const NGLineInfo& line_info) const; + NGLineBoxFragmentBuilder::ChildList line_box_; NGInlineLayoutStateStack* box_states_; NGInlineChildLayoutContext* context_; @@ -113,6 +134,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final unsigned is_horizontal_writing_mode_ : 1; unsigned quirks_mode_ : 1; + unsigned truncate_type_ : 2; #if DCHECK_IS_ON() // True if |box_states_| is taken from |context_|, to check the |box_states_| diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc index 3c1aa36a1bc..fbb4e323541 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc @@ -9,9 +9,11 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.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/inline/ng_physical_text_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -50,6 +52,14 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) { NGConstraintSpace constraint_space = builder.ToConstraintSpace(); NGInlineChildLayoutContext context; + NGBoxFragmentBuilder container_builder(block_flow, block_flow->Style(), + block_flow->Style()->GetWritingMode(), + block_flow->Style()->Direction()); + NGFragmentItemsBuilder items_builder(&container_builder); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + container_builder.SetItemsBuilder(&items_builder); + context.SetItemsBuilder(&items_builder); + } scoped_refptr<const NGLayoutResult> layout_result = inline_node.Layout(constraint_space, nullptr, &context); const auto& line1 = layout_result->PhysicalFragment(); @@ -68,95 +78,6 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) { EXPECT_TRUE(line3.BreakToken()->IsFinished()); } -TEST_F(NGInlineLayoutAlgorithmTest, GenerateHyphen) { - LoadAhem(); - SetBodyInnerHTML(R"HTML( - <!DOCTYPE html> - <style> - html, body { margin: 0; } - #container { - font: 10px/1 Ahem; - width: 5ch; - } - </style> - <div id=container>abc­def</div> - )HTML"); - scoped_refptr<const NGPhysicalBoxFragment> block = - GetBoxFragmentByElementId("container"); - EXPECT_EQ(2u, block->Children().size()); - const NGPhysicalLineBoxFragment& line1 = - To<NGPhysicalLineBoxFragment>(*block->Children()[0].get()); - - // The hyphen is in its own NGPhysicalTextFragment. - EXPECT_EQ(2u, line1.Children().size()); - EXPECT_EQ(NGPhysicalFragment::kFragmentText, line1.Children()[1]->Type()); - const auto& hyphen = To<NGPhysicalTextFragment>(*line1.Children()[1].get()); - EXPECT_EQ(String(u"\u2010"), hyphen.Text().ToString()); - // It should have the same LayoutObject as the hyphened word. - EXPECT_EQ(line1.Children()[0]->GetLayoutObject(), hyphen.GetLayoutObject()); -} - -TEST_F(NGInlineLayoutAlgorithmTest, GenerateEllipsis) { - LoadAhem(); - SetBodyInnerHTML(R"HTML( - <!DOCTYPE html> - <style> - html, body { margin: 0; } - #container { - font: 10px/1 Ahem; - width: 5ch; - overflow: hidden; - text-overflow: ellipsis; - } - </style> - <div id=container>123456</div> - )HTML"); - scoped_refptr<const NGPhysicalBoxFragment> block = - GetBoxFragmentByElementId("container"); - EXPECT_EQ(1u, block->Children().size()); - const auto& line1 = - To<NGPhysicalLineBoxFragment>(*block->Children()[0].get()); - - // The ellipsis is in its own NGPhysicalTextFragment. - EXPECT_EQ(3u, line1.Children().size()); - const auto& ellipsis = To<NGPhysicalTextFragment>(*line1.Children().back()); - EXPECT_EQ(String(u"\u2026"), ellipsis.Text().ToString()); - // It should have the same LayoutObject as the clipped word. - EXPECT_EQ(line1.Children()[0]->GetLayoutObject(), ellipsis.GetLayoutObject()); -} - -TEST_F(NGInlineLayoutAlgorithmTest, EllipsisInlineBoxOnly) { - LoadAhem(); - SetBodyInnerHTML(R"HTML( - <!DOCTYPE html> - <style> - html, body { margin: 0; } - #container { - font: 10px/1 Ahem; - width: 5ch; - overflow: hidden; - text-overflow: ellipsis; - } - span { - border: solid 10ch blue; - } - </style> - <div id=container><span></span></div> - )HTML"); - scoped_refptr<const NGPhysicalBoxFragment> block = - GetBoxFragmentByElementId("container"); - EXPECT_EQ(1u, block->Children().size()); - const auto& line1 = - To<NGPhysicalLineBoxFragment>(*block->Children()[0].get()); - - // There should not be ellipsis in this line. - for (const auto& child : line1.Children()) { - if (const auto* text = DynamicTo<NGPhysicalTextFragment>(child.get())) { - EXPECT_FALSE(text->IsEllipsis()); - } - } -} - // This test ensures box fragments are generated when necessary, even when the // line is empty. One such case is when the line contains a containing box of an // out-of-flow object. @@ -179,7 +100,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, } </style> <div id=container> - <oof-container> + <oof-container id=target> <oof></oof> </oof-container> </div> @@ -190,15 +111,17 @@ TEST_F(NGInlineLayoutAlgorithmTest, ASSERT_TRUE(container); EXPECT_EQ(LayoutUnit(), container->Size().height); - EXPECT_EQ(2u, container->Children().size()); - const auto& linebox = - To<NGPhysicalLineBoxFragment>(*container->Children()[0]); - - EXPECT_EQ(1u, linebox.Children().size()); - EXPECT_EQ(PhysicalSize(), linebox.Size()); - - const auto& oof_container = To<NGPhysicalBoxFragment>(*linebox.Children()[0]); - EXPECT_EQ(PhysicalSize(), oof_container.Size()); + NGInlineCursor line_box(*block_flow); + ASSERT_TRUE(line_box); + ASSERT_TRUE(line_box.Current().IsLineBox()); + EXPECT_EQ(PhysicalSize(), line_box.Current().Size()); + + NGInlineCursor off_container(line_box); + off_container.MoveToNext(); + ASSERT_TRUE(off_container); + ASSERT_EQ(GetLayoutObjectByElementId("target"), + off_container.Current().GetLayoutObject()); + EXPECT_EQ(PhysicalSize(), off_container.Current().Size()); } // This test ensures that if an inline box generates (or does not generate) box @@ -219,21 +142,31 @@ TEST_F(NGInlineLayoutAlgorithmTest, BoxForEndMargin) { } </style> <!-- This line wraps, and only 2nd line has a border. --> - <div id=container>12 <span>3 45</span> 6</div> + <div id=container>12 <span id=span>3 45</span> 6</div> )HTML"); auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")); - const NGPhysicalBoxFragment* block_box = block_flow->CurrentFragment(); - ASSERT_TRUE(block_box); - EXPECT_EQ(2u, block_box->Children().size()); - const auto& line_box1 = - To<NGPhysicalLineBoxFragment>(*block_box->Children()[0].get()); - EXPECT_EQ(2u, line_box1.Children().size()); + NGInlineCursor line_box(*block_flow); + ASSERT_TRUE(line_box) << "line_box is at start of first line."; + ASSERT_TRUE(line_box.Current().IsLineBox()); + line_box.MoveToNextLine(); + ASSERT_TRUE(line_box) << "line_box is at start of second line."; + NGInlineCursor cursor(line_box); + ASSERT_TRUE(line_box.Current().IsLineBox()); + cursor.MoveToNext(); + ASSERT_TRUE(cursor); + EXPECT_EQ(GetLayoutObjectByElementId("span"), + cursor.Current().GetLayoutObject()); // The <span> generates a box fragment for the 2nd line because it has a // right border. It should also generate a box fragment for the 1st line even // though there's no borders on the 1st line. - EXPECT_EQ(NGPhysicalFragment::kFragmentBox, line_box1.Children()[1]->Type()); + const NGPhysicalBoxFragment* box_fragment = cursor.Current().BoxFragment(); + ASSERT_TRUE(box_fragment); + EXPECT_EQ(NGPhysicalFragment::kFragmentBox, box_fragment->Type()); + + line_box.MoveToNextLine(); + ASSERT_FALSE(line_box) << "block_flow has two lines."; } // A block with inline children generates fragment tree as follows: @@ -257,8 +190,8 @@ TEST_F(NGInlineLayoutAlgorithmTest, ContainerBorderPadding) { auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")); NGBlockNode block_node(block_flow); - NGConstraintSpace space = NGConstraintSpace::CreateFromLayoutObject( - *block_flow, false /* is_layout_root */); + NGConstraintSpace space = + NGConstraintSpace::CreateFromLayoutObject(*block_flow); scoped_refptr<const NGLayoutResult> layout_result = block_node.Layout(space); EXPECT_TRUE(layout_result->BfcBlockOffset().has_value()); @@ -289,17 +222,13 @@ TEST_F(NGInlineLayoutAlgorithmTest, MAYBE_VerticalAlignBottomReplaced) { )HTML"); auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")); - NGInlineNode inline_node(block_flow); - NGInlineChildLayoutContext context; - NGConstraintSpace space = NGConstraintSpace::CreateFromLayoutObject( - *block_flow, false /* is_layout_root */); - scoped_refptr<const NGLayoutResult> layout_result = - inline_node.Layout(space, nullptr, &context); - - const auto& line = layout_result->PhysicalFragment(); - EXPECT_EQ(LayoutUnit(96), line.Size().height); - PhysicalOffset img_offset = line.Children()[0].Offset(); - EXPECT_EQ(LayoutUnit(0), img_offset.top); + NGInlineCursor cursor(*block_flow); + ASSERT_TRUE(cursor); + EXPECT_EQ(LayoutUnit(96), cursor.Current().Size().height); + cursor.MoveToNext(); + ASSERT_TRUE(cursor); + EXPECT_EQ(LayoutUnit(0), cursor.Current().OffsetInContainerBlock().top) + << "Offset top of <img> should be zero."; } // Verifies that text can flow correctly around floats that were positioned @@ -393,7 +322,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, TextFloatsAroundInlineFloatThatFitsOnLine) { ASSERT_TRUE(block_box); // Two lines. - EXPECT_EQ(2u, block_box->Children().size()); + ASSERT_EQ(2u, block_box->Children().size()); PhysicalOffset first_line_offset = block_box->Children()[1].Offset(); // 30 == narrow-float's width. @@ -521,20 +450,24 @@ TEST_F(NGInlineLayoutAlgorithmTest, InkOverflow) { auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")); const NGPaintFragment* paint_fragment = block_flow->PaintFragment(); - ASSERT_TRUE(paint_fragment); - const NGPhysicalFragment& box_fragment = paint_fragment->PhysicalFragment(); - + const NGPhysicalBoxFragment& box_fragment = *block_flow->CurrentFragment(); + if (paint_fragment) + ASSERT_EQ(&paint_fragment->PhysicalFragment(), &box_fragment); EXPECT_EQ(LayoutUnit(10), box_fragment.Size().height); - PhysicalRect ink_overflow = paint_fragment->InkOverflow(); + NGInlineCursor cursor(*block_flow); + PhysicalRect ink_overflow = cursor.Current().InkOverflow(); EXPECT_EQ(LayoutUnit(-5), ink_overflow.offset.top); EXPECT_EQ(LayoutUnit(20), ink_overflow.size.height); - // |ContentsInkOverflow| should match to |InkOverflow|, except the width - // because |<div id=container>| might be wider than the content. - EXPECT_EQ(ink_overflow.offset, paint_fragment->ContentsInkOverflow().offset); - EXPECT_EQ(ink_overflow.size.height, - paint_fragment->ContentsInkOverflow().size.height); + if (paint_fragment) { + // |ContentsInkOverflow| should match to |InkOverflow|, except the width + // because |<div id=container>| might be wider than the content. + const PhysicalRect contents_ink_overflow = + paint_fragment->ContentsInkOverflow(); + EXPECT_EQ(ink_overflow.offset, contents_ink_overflow.offset); + EXPECT_EQ(ink_overflow.size.height, contents_ink_overflow.size.height); + } } #undef MAYBE_VerticalAlignBottomReplaced diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc index 6f83ab768f0..740ab5a243f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc @@ -24,7 +24,6 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" @@ -285,7 +284,7 @@ void CollectInlinesInternal(LayoutBlockFlow* block, builder->ClearInlineFragment(node); } else if (node->IsAtomicInlineLevel()) { - if (node->IsListMarkerIncludingNG()) { + if (node->IsListMarkerIncludingNGOutside()) { // LayoutNGListItem produces the 'outside' list marker as an inline // block. This is an out-of-flow item whose position is computed // automatically. @@ -427,13 +426,6 @@ void TruncateOrPadText(String* text, unsigned length) { } } -template <typename OffsetMappingBuilder> -bool MayBeBidiEnabled( - const String& text_content, - const NGInlineItemsBuilderTemplate<OffsetMappingBuilder>& builder) { - return !text_content.Is8Bit() || builder.HasBidiControls(); -} - } // namespace NGInlineNode::NGInlineNode(LayoutBlockFlow* block) @@ -528,6 +520,11 @@ class NGInlineNodeDataEditor final { if (layout_text_.StyleRef().TextSecurity() != ETextSecurity::kNone) return nullptr; + // It is hard to figure differences of bidi control codes before/after + // editing. See http://crbug.com/1039143 + if (layout_text_.HasBidiControlInlineItems()) + return nullptr; + // Note: We should compute offset mapping before calling // |LayoutBlockFlow::TakeNGInlineNodeData()| const NGOffsetMapping* const offset_mapping = @@ -756,6 +753,10 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text, if (!previous_data) return false; + // This function runs outside of the layout phase. Prevent purging font cache + // while shaping. + FontCachePurgePreventer fontCachePurgePreventer; + String new_text(std::move(new_text_in)); layout_text->StyleRef().ApplyTextTransform(&new_text, layout_text->PreviousCharacter()); @@ -769,7 +770,7 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text, // inline items. layout_text->ClearInlineItems(); CollectInlinesInternal(node.GetLayoutBlockFlow(), &builder, previous_data); - data->text_content = builder.ToString(); + builder.DidFinishCollectInlines(data); // Relocates |ShapeResult| in |previous_data| after |offset|+|length| editor.Run(); node.SegmentText(data); @@ -877,17 +878,7 @@ void NGInlineNode::CollectInlines(NGInlineNodeData* data, data->items.ReserveCapacity(EstimateInlineItemsCount(*block)); NGInlineItemsBuilder builder(&data->items, dirty_lines); CollectInlinesInternal(block, &builder, previous_data); - data->text_content = builder.ToString(); - - // Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this - // point the string may or may not contain RTL characters. - // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it - // doesn't contain any RTL characters. - data->is_bidi_enabled_ = MayBeBidiEnabled(data->text_content, builder); - data->is_empty_inline_ = builder.IsEmptyInline(); - data->is_block_level_ = builder.IsBlockLevel(); - data->changes_may_affect_earlier_lines_ = - builder.ChangesMayAffectEarlierLines(); + builder.DidFinishCollectInlines(data); } void NGInlineNode::SegmentText(NGInlineNodeData* data) { @@ -1476,7 +1467,7 @@ String NGInlineNode::TextContentForStickyImagesQuirk( static LayoutUnit ComputeContentSize( NGInlineNode node, WritingMode container_writing_mode, - const MinMaxSizeInput& input, + const MinMaxSizesInput& input, NGLineBreakerMode mode, NGLineBreaker::MaxSizeCache* max_size_cache, base::Optional<LayoutUnit>* max_size_out) { @@ -1510,7 +1501,7 @@ static LayoutUnit ComputeContentSize( STACK_ALLOCATED(); public: - explicit FloatsMaxSize(const MinMaxSizeInput& input) + explicit FloatsMaxSize(const MinMaxSizesInput& input) : floats_inline_size_(input.float_left_inline_size + input.float_right_inline_size) { DCHECK_GE(floats_inline_size_, 0); @@ -1519,7 +1510,7 @@ static LayoutUnit ComputeContentSize( void AddFloat(const ComputedStyle& float_style, const ComputedStyle& style, LayoutUnit float_inline_max_size_with_margin) { - floating_objects_.push_back(FloatingObject{ + floating_objects_.push_back(NGInlineNode::FloatingObject{ float_style, style, float_inline_max_size_with_margin}); } @@ -1561,12 +1552,7 @@ static LayoutUnit ComputeContentSize( private: LayoutUnit floats_inline_size_; - struct FloatingObject { - const ComputedStyle& float_style; - const ComputedStyle& style; - LayoutUnit float_inline_max_size_with_margin; - }; - Vector<FloatingObject, 4> floating_objects_; + HeapVector<NGInlineNode::FloatingObject, 4> floating_objects_; }; // This struct computes the max size from the line break results for the min @@ -1711,8 +1697,8 @@ static LayoutUnit ComputeContentSize( const ComputedStyle& float_style = float_node.Style(); // Floats don't intrude into floats. - MinMaxSizeInput float_input(input.percentage_resolution_block_size); - MinMaxSize child_sizes = + MinMaxSizesInput float_input(input.percentage_resolution_block_size); + MinMaxSizes child_sizes = ComputeMinAndMaxContentContribution(style, float_node, float_input); LayoutUnit child_inline_margins = ComputeMinMaxMargins(style, float_node).InlineSum(); @@ -1751,9 +1737,9 @@ static LayoutUnit ComputeContentSize( return result; } -MinMaxSize NGInlineNode::ComputeMinMaxSize( +MinMaxSizes NGInlineNode::ComputeMinMaxSizes( WritingMode container_writing_mode, - const MinMaxSizeInput& input, + const MinMaxSizesInput& input, const NGConstraintSpace* constraint_space) { PrepareLayoutIfNeeded(); @@ -1761,7 +1747,7 @@ MinMaxSize NGInlineNode::ComputeMinMaxSize( // size. This gives the min-content, the width where lines wrap at every // break opportunity. NGLineBreaker::MaxSizeCache max_size_cache; - MinMaxSize sizes; + MinMaxSizes sizes; base::Optional<LayoutUnit> max_size; sizes.min_size = ComputeContentSize(*this, container_writing_mode, input, NGLineBreakerMode::kMinContent, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h index ddb8325722d..72a5dd5fa81 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h @@ -22,7 +22,6 @@ class NGInlineChildLayoutContext; class NGInlineNodeLegacy; class NGLayoutResult; class NGOffsetMapping; -struct MinMaxSize; struct NGInlineItemsData; // Represents an anonymous block box to be laid out, that contains consecutive @@ -55,9 +54,9 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { // Computes the value of min-content and max-content for this anonymous block // box. min-content is the inline size when lines wrap at every break // opportunity, and max-content is when lines do not wrap at all. - MinMaxSize ComputeMinMaxSize(WritingMode container_writing_mode, - const MinMaxSizeInput&, - const NGConstraintSpace* = nullptr); + MinMaxSizes ComputeMinMaxSizes(WritingMode container_writing_mode, + const MinMaxSizesInput&, + const NGConstraintSpace* = nullptr); // Instruct to re-compute |PrepareLayout| on the next layout. void InvalidatePrepareLayoutForTest() { @@ -72,10 +71,19 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { return Data().ItemsData(is_first_line); } + // There's a special intrinsic size measure quirk for images that are direct + // children of table cells that have auto inline-size: When measuring + // intrinsic min/max inline sizes, we pretend that it's not possible to break + // between images, or between text and images. Note that this only applies + // when measuring. During actual layout, on the other hand, standard breaking + // rules are to be followed. + // See https://quirks.spec.whatwg.org/#the-table-cell-width-calculation-quirk + bool IsStickyImagesQuirkForContentSize() const; + // Returns the text content to use for content sizing. This is normally the // same as |items_data.text_content|, except when sticky images quirk is // needed. - String TextContentForContentSize(const NGInlineItemsData& items_data) const; + static String TextContentForStickyImagesQuirk(const NGInlineItemsData&); // Clear associated fragments for LayoutObjects. // They are associated when NGPaintFragment is constructed, but when clearing, @@ -121,6 +129,16 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { String ToString() const; + struct FloatingObject { + DISALLOW_NEW(); + + void Trace(Visitor* visitor) {} + + const ComputedStyle& float_style; + const ComputedStyle& style; + LayoutUnit float_inline_max_size_with_margin; + }; + protected: bool IsPrepareLayoutFinished() const; @@ -160,8 +178,6 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { } const NGInlineNodeData& EnsureData(); - static String TextContentForStickyImagesQuirk(const NGInlineItemsData&); - static void ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow, NGInlineNodeData* data); @@ -169,28 +185,14 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode { friend class NGInlineNodeLegacy; }; -inline String NGInlineNode::TextContentForContentSize( - const NGInlineItemsData& items_data) const { - const String& text_content = items_data.text_content; - if (UNLIKELY(text_content.IsEmpty())) - return text_content; - - // There's a special intrinsic size measure quirk for images that are direct - // children of table cells that have auto inline-size: When measuring - // intrinsic min/max inline sizes, we pretend that it's not possible to break - // between images, or between text and images. Note that this only applies - // when measuring. During actual layout, on the other hand, standard breaking - // rules are to be followed. - // See https://quirks.spec.whatwg.org/#the-table-cell-width-calculation-quirk +inline bool NGInlineNode::IsStickyImagesQuirkForContentSize() const { if (UNLIKELY(GetDocument().InQuirksMode())) { const ComputedStyle& style = Style(); if (UNLIKELY(style.Display() == EDisplay::kTableCell && - style.LogicalWidth().IsIntrinsicOrAuto())) { - return TextContentForStickyImagesQuirk(items_data); - } + style.LogicalWidth().IsIntrinsicOrAuto())) + return true; } - - return text_content; + return false; } template <> @@ -202,4 +204,7 @@ struct DowncastTraits<NGInlineNode> { } // namespace blink +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGInlineNode::FloatingObject) + #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_INLINE_NODE_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h index e77f4b569c9..5dda66cc9f2 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h @@ -11,6 +11,9 @@ namespace blink { +template <typename OffsetMappingBuilder> +class NGInlineItemsBuilderTemplate; + // Data which is required for inline nodes. struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData { public: @@ -40,6 +43,9 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData { friend class NGInlineNodeForTest; friend class NGOffsetMappingTest; + template <typename OffsetMappingBuilder> + friend class NGInlineItemsBuilderTemplate; + // Items to use for the first line, when the node has :first-line rules. // // Items have different ComputedStyle, and may also have different diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc index 819493a0e6e..8d6cc679213 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/dom/text.h" #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.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_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" @@ -48,7 +49,8 @@ class NGInlineNodeForTest : public NGInlineNode { unsigned start = data->text_content.length(); data->text_content = data->text_content + text; data->items.push_back(NGInlineItem(NGInlineItem::kText, start, - start + text.length(), layout_object)); + start + text.length(), layout_object, + /* is_first_for_node */ true)); data->is_empty_inline_ = false; } @@ -56,8 +58,9 @@ class NGInlineNodeForTest : public NGInlineNode { NGInlineNodeData* data = MutableData(); data->text_content = data->text_content + character; unsigned end = data->text_content.length(); - data->items.push_back( - NGInlineItem(NGInlineItem::kBidiControl, end - 1, end, nullptr)); + data->items.push_back(NGInlineItem(NGInlineItem::kBidiControl, end - 1, end, + nullptr, + /* is_first_for_node */ true)); data->is_bidi_enabled_ = true; data->is_empty_inline_ = false; } @@ -90,7 +93,6 @@ class NGInlineNodeTest : public NGLayoutTest { void SetUp() override { NGLayoutTest::SetUp(); style_ = ComputedStyle::Create(); - style_->GetFont().Update(nullptr); } void SetupHtml(const char* id, String html) { @@ -115,31 +117,10 @@ class NGInlineNodeTest : public NGLayoutTest { return node; } - MinMaxSize ComputeMinMaxSize(NGInlineNode node) { - return node.ComputeMinMaxSize( + MinMaxSizes ComputeMinMaxSizes(NGInlineNode node) { + return node.ComputeMinMaxSizes( node.Style().GetWritingMode(), - MinMaxSizeInput(/* percentage_resolution_block_size */ LayoutUnit())); - } - - void CreateLine( - NGInlineNode node, - Vector<scoped_refptr<const NGPhysicalTextFragment>>* fragments_out) { - NGConstraintSpaceBuilder builder(WritingMode::kHorizontalTb, - WritingMode::kHorizontalTb, - /* is_new_fc */ false); - builder.SetAvailableSize({LayoutUnit::Max(), LayoutUnit(-1)}); - NGConstraintSpace constraint_space = builder.ToConstraintSpace(); - NGInlineChildLayoutContext context; - scoped_refptr<const NGLayoutResult> result = - NGInlineLayoutAlgorithm(node, constraint_space, - nullptr /* break_token */, &context) - .Layout(); - - const auto& line = - To<NGPhysicalLineBoxFragment>(result->PhysicalFragment()); - for (const auto& child : line.Children()) { - fragments_out->push_back(To<NGPhysicalTextFragment>(child.get())); - } + MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit())); } const String& GetText() const { @@ -177,8 +158,8 @@ class NGInlineNodeTest : public NGLayoutTest { Vector<unsigned> ToEndOffsetList( NGInlineItemSegments::const_iterator segments) { Vector<unsigned> end_offsets; - for (const NGInlineItemSegment& segment : segments) - end_offsets.push_back(segment.EndOffset()); + for (const RunSegmenter::RunSegmenterRange& segment : segments) + end_offsets.push_back(segment.end); return end_offsets; } @@ -442,49 +423,27 @@ TEST_F(NGInlineNodeTest, SegmentBidiIsolate) { TEST_ITEM_OFFSET_DIR(items[8], 22u, 28u, TextDirection::kLtr); } -#define TEST_TEXT_FRAGMENT(fragment, start_offset, end_offset) \ - EXPECT_EQ(start_offset, fragment->StartOffset()); \ - EXPECT_EQ(end_offset, fragment->EndOffset()); - -TEST_F(NGInlineNodeTest, CreateLineBidiIsolate) { - UseLayoutObjectAndAhem(); - scoped_refptr<ComputedStyle> style = ComputedStyle::Create(); - style->SetLineHeight(Length::Fixed(1)); - style->GetFont().Update(nullptr); - NGInlineNodeForTest node = CreateInlineNode(); - node = CreateBidiIsolateNode(node, layout_object_); - node.ShapeText(); - Vector<scoped_refptr<const NGPhysicalTextFragment>> fragments; - CreateLine(node, &fragments); - EXPECT_EQ(5u, fragments.size()); - TEST_TEXT_FRAGMENT(fragments[0], 0u, 6u); - TEST_TEXT_FRAGMENT(fragments[1], 16u, 21u); - TEST_TEXT_FRAGMENT(fragments[2], 14u, 15u); - TEST_TEXT_FRAGMENT(fragments[3], 7u, 13u); - TEST_TEXT_FRAGMENT(fragments[4], 22u, 28u); -} - -TEST_F(NGInlineNodeTest, MinMaxSize) { +TEST_F(NGInlineNodeTest, MinMaxSizes) { LoadAhem(); SetupHtml("t", "<div id=t style='font:10px Ahem'>AB CDEF</div>"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); EXPECT_EQ(40, sizes.min_size); EXPECT_EQ(70, sizes.max_size); } -TEST_F(NGInlineNodeTest, MinMaxSizeElementBoundary) { +TEST_F(NGInlineNodeTest, MinMaxSizesElementBoundary) { LoadAhem(); SetupHtml("t", "<div id=t style='font:10px Ahem'>A B<span>C D</span></div>"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); // |min_content| should be the width of "BC" because there is an element // boundary between "B" and "C" but no break opportunities. EXPECT_EQ(20, sizes.min_size); EXPECT_EQ(60, sizes.max_size); } -TEST_F(NGInlineNodeTest, MinMaxSizeFloats) { +TEST_F(NGInlineNodeTest, MinMaxSizesFloats) { LoadAhem(); SetupHtml("t", R"HTML( <style> @@ -496,13 +455,13 @@ TEST_F(NGInlineNodeTest, MinMaxSizeFloats) { )HTML"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); EXPECT_EQ(50, sizes.min_size); EXPECT_EQ(130, sizes.max_size); } -TEST_F(NGInlineNodeTest, MinMaxSizeCloseTagAfterForcedBreak) { +TEST_F(NGInlineNodeTest, MinMaxSizesCloseTagAfterForcedBreak) { LoadAhem(); SetupHtml("t", R"HTML( <style> @@ -514,14 +473,14 @@ TEST_F(NGInlineNodeTest, MinMaxSizeCloseTagAfterForcedBreak) { )HTML"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); // The right border of the `</span>` is included in the line even if it // appears after `<br>`. crbug.com/991320. EXPECT_EQ(80, sizes.min_size); EXPECT_EQ(80, sizes.max_size); } -TEST_F(NGInlineNodeTest, MinMaxSizeFloatsClearance) { +TEST_F(NGInlineNodeTest, MinMaxSizesFloatsClearance) { LoadAhem(); SetupHtml("t", R"HTML( <style> @@ -534,13 +493,13 @@ TEST_F(NGInlineNodeTest, MinMaxSizeFloatsClearance) { )HTML"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); EXPECT_EQ(50, sizes.min_size); EXPECT_EQ(160, sizes.max_size); } -TEST_F(NGInlineNodeTest, MinMaxSizeTabulationWithBreakWord) { +TEST_F(NGInlineNodeTest, MinMaxSizesTabulationWithBreakWord) { LoadAhem(); SetupHtml("t", R"HTML( <style> @@ -554,7 +513,7 @@ TEST_F(NGInlineNodeTest, MinMaxSizeTabulationWithBreakWord) { )HTML"); NGInlineNodeForTest node = CreateInlineNode(); - MinMaxSize sizes = ComputeMinMaxSize(node); + MinMaxSizes sizes = ComputeMinMaxSizes(node); EXPECT_EQ(160, sizes.min_size); EXPECT_EQ(170, sizes.max_size); } @@ -882,7 +841,12 @@ TEST_F(NGInlineNodeTest, CollectInlinesShouldNotClearFirstInlineFragment) { // Running |CollectInlines| should not clear |FirstInlineFragment|. LayoutObject* first_child = container->firstChild()->GetLayoutObject(); - EXPECT_NE(first_child->FirstInlineFragment(), nullptr); + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + // TODO(yosin): We should use |FirstInlineItemFragmentIndex()| once we + // implement it. + } else { + EXPECT_NE(first_child->FirstInlineFragment(), nullptr); + } } TEST_F(NGInlineNodeTest, InvalidateAddSpan) { @@ -1371,7 +1335,7 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyInInlineBlock) { // Inline block with auto-size calls |ComputeMinMaxSize|, which may call // |CollectInlines|. Emulate it to ensure it does not let tests to fail. GetDocument().UpdateStyleAndLayoutTree(); - ComputeMinMaxSize(NGInlineNode(layout_block_flow_)); + ComputeMinMaxSizes(NGInlineNode(layout_block_flow_)); auto lines = MarkLineBoxesDirty(); // TODO(kojii): Ideally, 0 should be false, or even 1 as well. @@ -1394,7 +1358,7 @@ TEST_F(NGInlineNodeTest, RemoveInlineNodeDataIfBlockBecomesEmpty2) { SetupHtml("container", "<div id=container><b><i>foo</i></b></div>"); ASSERT_TRUE(layout_block_flow_->HasNGInlineNodeData()); - GetElementById("container")->SetInnerHTMLFromString(""); + GetElementById("container")->setInnerHTML(""); UpdateAllLifecyclePhasesForTest(); EXPECT_FALSE(layout_block_flow_->HasNGInlineNodeData()); @@ -1425,9 +1389,9 @@ TEST_F(NGInlineNodeTest, ClearFirstInlineFragmentOnSplitFlow) { // Keep the text fragment to compare later. Element* inner_span = GetElementById("inner_span"); Node* text = inner_span->firstChild(); - scoped_refptr<NGPaintFragment> text_fragment_before_split = - text->GetLayoutObject()->FirstInlineFragment(); - EXPECT_NE(text_fragment_before_split.get(), nullptr); + NGInlineCursor text_fragment_before_split; + text_fragment_before_split.MoveTo(*text->GetLayoutObject()); + EXPECT_TRUE(text_fragment_before_split); // Append <div> to <span>. causing SplitFlow(). Element* outer_span = GetElementById("outer_span"); @@ -1435,30 +1399,29 @@ TEST_F(NGInlineNodeTest, ClearFirstInlineFragmentOnSplitFlow) { outer_span->appendChild(div); // Update tree but do NOT update layout. At this point, there's no guarantee, - // but there are some clients (e.g., Schroll Anchor) who try to read + // but there are some clients (e.g., Scroll Anchor) who try to read // associated fragments. // // NGPaintFragment is owned by LayoutNGBlockFlow. Because the original owner // no longer has an inline formatting context, the NGPaintFragment subtree is // destroyed, and should not be accessible. GetDocument().UpdateStyleAndLayoutTree(); - scoped_refptr<NGPaintFragment> text_fragment_before_layout = - text->GetLayoutObject()->FirstInlineFragment(); - EXPECT_EQ(text_fragment_before_layout, nullptr); + EXPECT_FALSE(text->GetLayoutObject()->IsInLayoutNGInlineFormattingContext()); // Update layout. There should be a different instance of the text fragment. UpdateAllLifecyclePhasesForTest(); - scoped_refptr<NGPaintFragment> text_fragment_after_layout = - text->GetLayoutObject()->FirstInlineFragment(); - EXPECT_NE(text_fragment_before_split, text_fragment_after_layout); + NGInlineCursor text_fragment_after_layout; + text_fragment_after_layout.MoveTo(*text->GetLayoutObject()); + EXPECT_NE(text_fragment_before_split.Current(), + text_fragment_after_layout.Current()); // Check it is the one owned by the new root inline formatting context. LayoutBlock* anonymous_block = inner_span->GetLayoutObject()->ContainingBlock(); EXPECT_TRUE(anonymous_block->IsAnonymous()); - const NGPaintFragment* block_fragment = anonymous_block->PaintFragment(); - const NGPaintFragment* line_box_fragment = block_fragment->FirstChild(); - EXPECT_EQ(line_box_fragment->FirstChild(), text_fragment_after_layout); + EXPECT_EQ(anonymous_block, text_fragment_after_layout.Current() + .GetLayoutObject() + ->ContainingBlock()); } TEST_F(NGInlineNodeTest, AddChildToSVGRoot) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc index af3b4b14df1..acddeab0eaf 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc @@ -57,14 +57,27 @@ NGLineBoxFragmentBuilder::ChildList::LastInFlowChild() { return nullptr; } +void NGLineBoxFragmentBuilder::ChildList::WillInsertChild( + unsigned insert_before) { + unsigned index = 0; + for (Child& child : children_) { + if (index >= insert_before) + break; + if (child.children_count && index + child.children_count > insert_before) + ++child.children_count; + ++index; + } +} + void NGLineBoxFragmentBuilder::ChildList::InsertChild(unsigned index) { + WillInsertChild(index); children_.insert(index, Child()); } void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection( LayoutUnit delta) { for (auto& child : children_) - child.offset.inline_offset += delta; + child.rect.offset.inline_offset += delta; } void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection( @@ -72,20 +85,20 @@ void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection( unsigned start, unsigned end) { for (unsigned index = start; index < end; index++) - children_[index].offset.inline_offset += delta; + children_[index].rect.offset.inline_offset += delta; } void NGLineBoxFragmentBuilder::ChildList::MoveInBlockDirection( LayoutUnit delta) { for (auto& child : children_) - child.offset.block_offset += delta; + child.rect.offset.block_offset += delta; } void NGLineBoxFragmentBuilder::ChildList::MoveInBlockDirection(LayoutUnit delta, unsigned start, unsigned end) { for (unsigned index = start; index < end; index++) - children_[index].offset.block_offset += delta; + children_[index].rect.offset.block_offset += delta; } void NGLineBoxFragmentBuilder::AddChildren(ChildList& children) { @@ -94,42 +107,39 @@ void NGLineBoxFragmentBuilder::AddChildren(ChildList& children) { for (auto& child : children) { if (child.layout_result) { DCHECK(!child.fragment); - AddChild(child.layout_result->PhysicalFragment(), child.offset); + AddChild(child.layout_result->PhysicalFragment(), child.Offset()); child.layout_result.reset(); } else if (child.fragment) { - AddChild(std::move(child.fragment), child.offset); + AddChild(std::move(child.fragment), child.Offset()); DCHECK(!child.fragment); } else if (child.out_of_flow_positioned_box) { AddOutOfFlowInlineChildCandidate( NGBlockNode(ToLayoutBox(child.out_of_flow_positioned_box)), - child.offset, child.container_direction); + child.Offset(), child.container_direction); child.out_of_flow_positioned_box = nullptr; } } } void NGLineBoxFragmentBuilder::PropagateChildrenData(ChildList& children) { - for (auto& child : children) { + for (unsigned index = 0; index < children.size(); ++index) { + auto& child = children[index]; if (child.layout_result) { DCHECK(!child.fragment); - const NGPhysicalContainerFragment& fragment = - child.layout_result->PhysicalFragment(); - if (fragment.IsFloating()) { - // Add positioned floating objects to the fragment tree, not to the - // fragment item list. Because they are not necessary for inline - // traversals, and leading floating objects are still in the fragment - // tree, this helps simplifying painting floats. - AddChild(fragment, child.offset); - child.layout_result.reset(); - continue; - } - PropagateChildData(child.layout_result->PhysicalFragment(), child.offset); + PropagateChildData(child.layout_result->PhysicalFragment(), + child.Offset()); + + // Skip over any children, the information should have already been + // propagated into this layout result. + if (child.children_count) + index += child.children_count - 1; + continue; } if (child.out_of_flow_positioned_box) { AddOutOfFlowInlineChildCandidate( NGBlockNode(ToLayoutBox(child.out_of_flow_positioned_box)), - child.offset, child.container_direction); + child.Offset(), child.container_direction); child.out_of_flow_positioned_box = nullptr; } } @@ -148,7 +158,9 @@ NGLineBoxFragmentBuilder::ToLineBoxFragment() { scoped_refptr<const NGPhysicalLineBoxFragment> fragment = NGPhysicalLineBoxFragment::Create(this); - return base::AdoptRef(new NGLayoutResult(std::move(fragment), this)); + return base::AdoptRef( + new NGLayoutResult(NGLayoutResult::NGLineBoxFragmentBuilderPassKey(), + std::move(fragment), this)); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h index 00807812e98..93550d8b80f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h @@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BOX_FRAGMENT_BUILDER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BOX_FRAGMENT_BUILDER_H_ -#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h" +#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h" @@ -77,11 +77,12 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final scoped_refptr<const NGLayoutResult> layout_result; scoped_refptr<const NGPhysicalTextFragment> fragment; + const NGInlineItem* inline_item = nullptr; LayoutObject* out_of_flow_positioned_box = nullptr; LayoutObject* unpositioned_float = nullptr; // The offset of the border box, initially in this child coordinate system. // |ComputeInlinePositions()| converts it to the offset within the line box. - LogicalOffset offset; + LogicalRect rect; // The offset of a positioned float wrt. the root BFC. This should only be // set for positioned floats. NGBfcOffset bfc_offset; @@ -103,18 +104,35 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final Child() = default; // Create a placeholder. A placeholder does not have a fragment nor a bidi // level. - Child(LogicalOffset offset) : offset(offset) {} + Child(LayoutUnit block_offset, LayoutUnit block_size) + : rect(LayoutUnit(), block_offset, LayoutUnit(), block_size) {} + Child(const NGInlineItem& inline_item, + const LogicalRect& rect, + unsigned children_count) + : inline_item(&inline_item), + rect(rect), + children_count(children_count) {} // Crete a bidi control. A bidi control does not have a fragment, but has // bidi level and affects bidi reordering. Child(UBiDiLevel bidi_level) : bidi_level(bidi_level) {} // Create an in-flow |NGLayoutResult|. Child(scoped_refptr<const NGLayoutResult> layout_result, + const LogicalRect& rect, + unsigned children_count, + UBiDiLevel bidi_level) + : layout_result(std::move(layout_result)), + rect(rect), + children_count(children_count), + bidi_level(bidi_level) {} + Child(scoped_refptr<const NGLayoutResult> layout_result, LogicalOffset offset, LayoutUnit inline_size, + unsigned children_count, UBiDiLevel bidi_level) : layout_result(std::move(layout_result)), - offset(offset), + rect(offset, LogicalSize()), inline_size(inline_size), + children_count(children_count), bidi_level(bidi_level) {} // Create an in-flow |NGPhysicalTextFragment|. Child(scoped_refptr<const NGPhysicalTextFragment> fragment, @@ -122,7 +140,7 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final LayoutUnit inline_size, UBiDiLevel bidi_level) : fragment(std::move(fragment)), - offset(offset), + rect(offset, LogicalSize()), inline_size(inline_size), bidi_level(bidi_level) {} Child(scoped_refptr<const NGPhysicalTextFragment> fragment, @@ -130,7 +148,7 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final LayoutUnit inline_size, UBiDiLevel bidi_level) : fragment(std::move(fragment)), - offset({LayoutUnit(), block_offset}), + rect(LayoutUnit(), block_offset, LayoutUnit(), LayoutUnit()), inline_size(inline_size), bidi_level(bidi_level) {} // Create an out-of-flow positioned object. @@ -180,11 +198,20 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final } return false; } + const LogicalOffset& Offset() const { return rect.offset; } + LayoutUnit InlineOffset() const { return rect.offset.inline_offset; } + const LogicalSize& Size() const { return rect.size; } const NGPhysicalFragment* PhysicalFragment() const { if (layout_result) return &layout_result->PhysicalFragment(); return fragment.get(); } + TextDirection ResolvedDirection() const { + // Inline boxes are not leaves that they don't have directions. + DCHECK(HasBidiLevel() || layout_result->PhysicalFragment().IsInlineBox()); + return HasBidiLevel() ? DirectionFromLevel(bidi_level) + : TextDirection::kLtr; + } }; // A vector of Child. @@ -234,11 +261,18 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final void InsertChild(unsigned index); void InsertChild(unsigned index, scoped_refptr<const NGLayoutResult> layout_result, - const LogicalOffset& offset, - LayoutUnit inline_size, - UBiDiLevel bidi_level) { - children_.insert(index, Child{std::move(layout_result), offset, - inline_size, bidi_level}); + const LogicalRect& rect, + unsigned children_count) { + WillInsertChild(index); + children_.insert(index, Child(std::move(layout_result), rect, + children_count, /* bidi_level */ 0)); + } + void InsertChild(unsigned index, + const NGInlineItem& inline_item, + const LogicalRect& rect, + unsigned children_count) { + WillInsertChild(index); + children_.insert(index, Child(inline_item, rect, children_count)); } void MoveInInlineDirection(LayoutUnit); @@ -247,6 +281,8 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end); private: + void WillInsertChild(unsigned index); + Vector<Child, 16> children_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc index 31c45dd7290..494d7ae3167 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc @@ -132,22 +132,16 @@ LayoutUnit ComputeFloatAncestorInlineEndSize(const NGConstraintSpace& space, return inline_end_size; } -scoped_refptr<const NGPhysicalTextFragment> CreateHyphenFragment( - NGInlineNode node, - WritingMode writing_mode, - const NGInlineItem& item) { +void CreateHyphen(NGInlineNode node, + WritingMode writing_mode, + const NGInlineItem& item, + NGInlineItemResult* item_result) { DCHECK(item.Style()); const ComputedStyle& style = *item.Style(); TextDirection direction = style.Direction(); - String hyphen_string = style.HyphenString(); - HarfBuzzShaper shaper(hyphen_string); - scoped_refptr<ShapeResult> hyphen_result = - shaper.Shape(&style.GetFont(), direction); - NGTextFragmentBuilder builder(writing_mode); - builder.SetText(item.GetLayoutObject(), hyphen_string, &style, - /* is_ellipsis_style */ false, - ShapeResultView::Create(hyphen_result.get())); - return builder.ToTextFragment(); + item_result->hyphen_string = style.HyphenString(); + HarfBuzzShaper shaper(item_result->hyphen_string); + item_result->hyphen_shape_result = shaper.Shape(&style.GetFont(), direction); } inline void ClearNeedsLayout(const NGInlineItem& item) { @@ -183,10 +177,13 @@ NGLineBreaker::NGLineBreaker(NGInlineNode node, use_first_line_style_(is_first_formatted_line_ && node.UseFirstLineStyle()), in_line_height_quirks_mode_(node.InLineHeightQuirksMode()), + sticky_images_quirk_(mode != NGLineBreakerMode::kContent && + node.IsStickyImagesQuirkForContentSize()), items_data_(node.ItemsData(use_first_line_style_)), - text_content_(mode == NGLineBreakerMode::kContent - ? items_data_.text_content - : node.TextContentForContentSize(items_data_)), + text_content_( + !sticky_images_quirk_ + ? items_data_.text_content + : NGInlineNode::TextContentForStickyImagesQuirk(items_data_)), constraint_space_(space), exclusion_space_(exclusion_space), break_token_(break_token), @@ -262,25 +259,6 @@ void NGLineBreaker::SetMaxSizeCache(MaxSizeCache* max_size_cache) { max_size_cache_ = max_size_cache; } -LayoutUnit NGLineBreaker::SetLineEndFragment( - scoped_refptr<const NGPhysicalTextFragment> fragment, - NGLineInfo* line_info) { - LayoutUnit inline_size; - bool is_horizontal = - IsHorizontalWritingMode(constraint_space_.GetWritingMode()); - if (line_info->LineEndFragment()) { - const PhysicalSize& size = line_info->LineEndFragment()->Size(); - inline_size = is_horizontal ? -size.width : -size.height; - } - if (fragment) { - const PhysicalSize& size = fragment->Size(); - inline_size = is_horizontal ? size.width : size.height; - } - line_info->SetLineEndFragment(std::move(fragment)); - position_ += inline_size; - return inline_size; -} - // Compute the base direction for bidi algorithm for this line. void NGLineBreaker::ComputeBaseDirection() { // If 'unicode-bidi' is not 'plaintext', use the base direction of the block. @@ -492,29 +470,15 @@ void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const { line_info->UpdateTextAlign(); } -// For Web-compatibility, allow break between an atomic inline and any adjacent -// U+00A0 NO-BREAK SPACE character. -// https://www.w3.org/TR/css-text-3/#line-break-details -bool NGLineBreaker::IsAtomicInlineBeforeNoBreakSpace( - const NGInlineItemResult& item_result) const { - DCHECK(auto_wrap_); - DCHECK_EQ(item_result.item->Type(), NGInlineItem::kAtomicInline); - const String& text = Text(); - DCHECK_GE(text.length(), item_result.end_offset); - return text.length() > item_result.end_offset && - text[item_result.end_offset] == kNoBreakSpaceCharacter && - // Except when sticky images quirk was applied. - text[item_result.start_offset] != kNoBreakSpaceCharacter; -} - -bool NGLineBreaker::IsAtomicInlineAfterNoBreakSpace( +// Atomic inlines have break opportunities before and after, even when the +// adjacent character is U+00A0 NO-BREAK SPACE character. +bool NGLineBreaker::ShouldForceCanBreakAfter( const NGInlineItemResult& item_result) const { DCHECK(auto_wrap_); DCHECK_EQ(item_result.item->Type(), NGInlineItem::kText); const String& text = Text(); DCHECK_GE(text.length(), item_result.end_offset); - if (text[item_result.end_offset - 1] != kNoBreakSpaceCharacter || - text.length() <= item_result.end_offset || + if (text.length() <= item_result.end_offset || text[item_result.end_offset] != kObjectReplacementCharacter) return false; // This kObjectReplacementCharacter can be any objects, such as a floating or @@ -598,8 +562,10 @@ void NGLineBreaker::HandleText(const NGInlineItem& item, } // Try to break inside of this text item. - BreakResult break_result = BreakText(item_result, item, shape_result, - RemainingAvailableWidth(), line_info); + const LayoutUnit available_width = RemainingAvailableWidth(); + BreakResult break_result = + BreakText(item_result, item, shape_result, available_width, + available_width, line_info); DCHECK(item_result->shape_result || (break_result == kOverflow && break_anywhere_if_overflow_ && !override_break_anywhere_)); @@ -677,6 +643,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText( const NGInlineItem& item, const ShapeResult& item_shape_result, LayoutUnit available_width, + LayoutUnit available_width_with_hyphens, NGLineInfo* line_info) { DCHECK(item.Type() == NGInlineItem::kText || (item.Type() == NGInlineItem::kControl && @@ -739,7 +706,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText( // rewinded. Making this item long enough to overflow is enough. if (!shape_result) { DCHECK(options & ShapingLineBreaker::kNoResultIfOverflow); - item_result->inline_size = available_width + 1; + item_result->inline_size = available_width_with_hyphens + 1; item_result->end_offset = item.EndOffset(); return kOverflow; } @@ -750,25 +717,27 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText( CHECK_GT(result.break_offset, item_result->start_offset); inline_size = shape_result->SnappedWidth().ClampNegativeToZero(); - item_result->inline_size = inline_size; if (UNLIKELY(result.is_hyphenated)) { const WritingMode writing_mode = constraint_space_.GetWritingMode(); - scoped_refptr<const NGPhysicalTextFragment> hyphen_fragment = - CreateHyphenFragment(node_, writing_mode, item); - LayoutUnit space_for_hyphen = available_width - inline_size; - LayoutUnit hyphen_inline_size = IsHorizontalWritingMode(writing_mode) - ? hyphen_fragment->Size().width - : hyphen_fragment->Size().height; + CreateHyphen(node_, writing_mode, item, item_result); + DCHECK(item_result->hyphen_shape_result); + DCHECK(item_result->hyphen_string); + LayoutUnit hyphen_inline_size = item_result->HyphenInlineSize(); // If the hyphen overflows, retry with the reduced available width. - if (space_for_hyphen >= 0 && hyphen_inline_size > space_for_hyphen) { - available_width -= hyphen_inline_size; - continue; + if (!result.is_overflow && inline_size <= available_width) { + LayoutUnit space_for_hyphen = + available_width_with_hyphens - inline_size; + if (space_for_hyphen >= 0 && hyphen_inline_size > space_for_hyphen) { + available_width -= hyphen_inline_size; + continue; + } } - inline_size += SetLineEndFragment(std::move(hyphen_fragment), line_info); - item_result->text_end_effect = NGTextEndEffect::kHyphen; + inline_size += hyphen_inline_size; + } else if (UNLIKELY(item_result->hyphen_shape_result)) { + item_result->hyphen_shape_result = nullptr; + item_result->hyphen_string = String(); } - item_result->inline_size = - shape_result->SnappedWidth().ClampNegativeToZero(); + item_result->inline_size = inline_size; item_result->end_offset = result.break_offset; item_result->shape_result = std::move(shape_result); break; @@ -796,7 +765,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText( item_result->can_break_after = break_iterator_.IsBreakable(item_result->end_offset); if (!item_result->can_break_after && item.Type() == NGInlineItem::kText && - IsAtomicInlineAfterNoBreakSpace(*item_result)) + ShouldForceCanBreakAfter(*item_result)) item_result->can_break_after = true; trailing_whitespace_ = WhitespaceState::kUnknown; } @@ -1344,6 +1313,21 @@ void NGLineBreaker::HandleAtomicInline( DCHECK(item.Style()); const ComputedStyle& style = *item.Style(); + const LayoutUnit remaining_width = RemainingAvailableWidth(); + bool ignore_overflow_if_negative_margin = false; + if (state_ == LineBreakState::kContinue && remaining_width < 0) { + const unsigned item_index = item_index_; + DCHECK_EQ(item_index, static_cast<unsigned>(&item - Items().begin())); + DCHECK(!line_info->HasOverflow()); + HandleOverflow(line_info); + if (!line_info->HasOverflow() || item_index != item_index_) + return; + // Compute margins if this line overflows. Negative margins can put the + // position back. + DCHECK_NE(state_, LineBreakState::kContinue); + ignore_overflow_if_negative_margin = true; + } + // Compute margins before computing overflow, because even when the current // position is beyond the end, negative margins can bring this item back to on // the current line. @@ -1351,13 +1335,18 @@ void NGLineBreaker::HandleAtomicInline( item_result->margins = ComputeLineMarginsForVisualContainer(constraint_space_, style); LayoutUnit inline_margins = item_result->margins.InlineSum(); - LayoutUnit remaining_width = RemainingAvailableWidth(); - bool is_overflow_before = - state_ == LineBreakState::kContinue && remaining_width < 0; - if (UNLIKELY(is_overflow_before && inline_margins > remaining_width)) { - RemoveLastItem(line_info); - HandleOverflow(line_info); - return; + if (UNLIKELY(ignore_overflow_if_negative_margin)) { + DCHECK_LT(remaining_width, 0); + // The margin isn't negative, or the negative margin isn't large enough to + // put the position back. Break this line before this item. + if (inline_margins >= remaining_width) { + RemoveLastItem(line_info); + return; + } + // This line once overflowed, but the negative margin puts the position + // back. + state_ = LineBreakState::kContinue; + line_info->SetHasOverflow(false); } // When we're just computing min/max content sizes, we can skip the full @@ -1369,7 +1358,6 @@ void NGLineBreaker::HandleAtomicInline( item_result->layout_result = NGBlockNode(ToLayoutBox(item.GetLayoutObject())) .LayoutAtomicInline(constraint_space_, node_.Style(), - line_info->LineStyle().GetFontBaseline(), line_info->UseFirstLineStyle()); item_result->inline_size = NGFragment(constraint_space_.GetWritingMode(), @@ -1382,8 +1370,8 @@ void NGLineBreaker::HandleAtomicInline( } else { DCHECK(mode_ == NGLineBreakerMode::kMinContent || !max_size_cache_); NGBlockNode child(ToLayoutBox(item.GetLayoutObject())); - MinMaxSizeInput input(percentage_resolution_block_size_for_min_max); - MinMaxSize sizes = + MinMaxSizesInput input(percentage_resolution_block_size_for_min_max); + MinMaxSizes sizes = ComputeMinAndMaxContentContribution(node_.Style(), child, input); if (mode_ == NGLineBreakerMode::kMinContent) { item_result->inline_size = sizes.min_size + inline_margins; @@ -1399,10 +1387,11 @@ void NGLineBreaker::HandleAtomicInline( } item_result->should_create_line_box = true; - ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_); - if (!item_result->can_break_after && auto_wrap_ && - IsAtomicInlineBeforeNoBreakSpace(*item_result)) - item_result->can_break_after = true; + // Atomic inlines have break opportunities before and after, even when the + // adjacent character is U+00A0 NO-BREAK SPACE character, except when sticky + // images quirk is applied. + item_result->can_break_after = + auto_wrap_ && !(sticky_images_quirk_ && item.IsImage()); position_ += item_result->inline_size; trailing_whitespace_ = WhitespaceState::kNone; @@ -1448,6 +1437,10 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item, !leading_floats_.IsEmpty()) { DCHECK_LT(leading_floats_index_, leading_floats_.size()); item_result->positioned_float = leading_floats_[leading_floats_index_++]; + + // Don't break after leading floats if indented. + if (position_ != 0) + item_result->can_break_after = false; return; } @@ -1654,9 +1647,6 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) { LayoutUnit width_to_rewind = position_ - available_width; DCHECK_GT(width_to_rewind, 0); - // Indicates positions of items may be changed and need to UpdatePosition(). - bool position_maybe_changed = false; - // Keep track of the shortest break opportunity. unsigned break_before = 0; @@ -1699,40 +1689,36 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) { // If space is available, and if this text is breakable, part of the text // may fit. Try to break this item. if (width_to_rewind < 0 && item_result->may_break_inside) { - LayoutUnit item_available_width = -width_to_rewind; + const LayoutUnit item_available_width = -width_to_rewind; // Make sure the available width is smaller than the current width. The // break point must not be at the end when e.g., the text fits but its // right margin does not or following items do not. const LayoutUnit min_available_width = item_result->inline_size - 1; - if (item_available_width > min_available_width) { - item_available_width = min_available_width; - // If |inline_size| is zero (e.g., `font-size: 0`), |BreakText| cannot - // make it shorter. Take the previous break opportunity. - if (UNLIKELY(item_available_width <= 0)) { - if (BreakTextAtPreviousBreakOpportunity(item_result)) { - RewindOverflow(i + 1, line_info); - return; - } - continue; + // If |inline_size| is zero (e.g., `font-size: 0`), |BreakText| cannot + // make it shorter. Take the previous break opportunity. + if (UNLIKELY(min_available_width <= 0)) { + if (BreakTextAtPreviousBreakOpportunity(item_result)) { + RewindOverflow(i + 1, line_info); + return; } + continue; } - auto was_current_style = current_style_; + scoped_refptr<const ComputedStyle> was_current_style = current_style_; SetCurrentStyle(*item.Style()); - const unsigned end_offset_before = item_result->end_offset; - BreakResult break_result = - BreakText(item_result, item, *item.TextShapeResult(), - item_available_width, line_info); - DCHECK_LE(item_result->end_offset, end_offset_before); + const NGInlineItemResult item_result_before = *item_result; + BreakText(item_result, item, *item.TextShapeResult(), + std::min(item_available_width, min_available_width), + item_available_width, line_info); + DCHECK_LE(item_result->end_offset, item_result_before.end_offset); #if DCHECK_IS_ON() item_result->CheckConsistency(true); #endif + // If BreakText() changed this item small enough to fit, break here. - DCHECK_EQ(break_result == kSuccess, - item_result->inline_size <= item_available_width); - if (break_result == kSuccess) { - DCHECK_LE(item_result->inline_size, item_available_width); + if (item_result->can_break_after && + item_result->inline_size <= item_available_width && + item_result->end_offset < item_result_before.end_offset) { DCHECK_LT(item_result->end_offset, item.EndOffset()); - DCHECK(item_result->can_break_after); // If this is the last item, adjust it to accommodate the change. const unsigned new_end = i + 1; @@ -1740,8 +1726,6 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) { if (new_end == item_results->size()) { position_ = available_width + width_to_rewind + item_result->inline_size; - if (line_info->LineEndFragment()) - SetLineEndFragment(nullptr, line_info); DCHECK_EQ(position_, line_info->ComputeWidth()); item_index_ = item_result->item_index; offset_ = item_result->end_offset; @@ -1757,8 +1741,10 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) { HandleTrailingSpaces(item, line_info); return; } + + // Failed to break to fit. Restore to the original state. + *item_result = std::move(item_result_before); SetCurrentStyle(*was_current_style); - position_maybe_changed = true; } } } @@ -1786,11 +1772,6 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) { return; } - if (position_maybe_changed) { - trailing_whitespace_ = WhitespaceState::kUnknown; - position_ = line_info->ComputeWidth(); - } - if (CanBreakAfterLast(*item_results)) { state_ = LineBreakState::kTrailing; return; @@ -1810,6 +1791,7 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) { const Vector<NGInlineItem>& items = Items(); const NGInlineItemResults& item_results = line_info->Results(); DCHECK_LT(new_end, item_results.size()); + unsigned open_tag_count = 0; const String& text = Text(); for (unsigned index = new_end; index < item_results.size(); index++) { @@ -1819,16 +1801,20 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) { if (item.Type() == NGInlineItem::kText) { // Text items are trailable if they start with trailable spaces. DCHECK_GT(item_result.Length(), 0u); - if (item_result.shape_result) { + if (item_result.shape_result || // kNoResultIfOverflow if 'break-word' + (break_anywhere_if_overflow_ && !override_break_anywhere_)) { DCHECK(item.Style()); const EWhiteSpace white_space = item.Style()->WhiteSpace(); if (ComputedStyle::AutoWrap(white_space) && white_space != EWhiteSpace::kBreakSpaces && IsBreakableSpace(text[item_result.start_offset])) { - // If all characters are trailable spaces, check the next item. - if (IsAllBreakableSpaces(text, item_result.start_offset + 1, - item_result.end_offset)) + // If more items left and all characters are trailable spaces, check + // the next item. + if (item_result.shape_result && index < item_results.size() - 1 && + IsAllBreakableSpaces(text, item_result.start_offset + 1, + item_result.end_offset)) { continue; + } // If this item starts with spaces followed by non-space characters, // rewind to before this item. |HandleText()| will include the spaces // and break there. @@ -1836,6 +1822,9 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) { Rewind(index, line_info); DCHECK_EQ(static_cast<unsigned>(&item - items.begin()), item_index_); HandleTrailingSpaces(item, line_info); +#if DCHECK_IS_ON() + item_results.back().CheckConsistency(false); +#endif return; } } @@ -1962,7 +1951,6 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) { item_results.Shrink(new_end); trailing_collapsible_space_.reset(); - SetLineEndFragment(nullptr, line_info); position_ = line_info->ComputeWidth(); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h index d19c268bcae..ac8685caad5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h @@ -99,8 +99,6 @@ class CORE_EXPORT NGLineBreaker { unsigned end_offset, NGLineInfo*); NGInlineItemResult* AddItem(const NGInlineItem&, NGLineInfo*); - LayoutUnit SetLineEndFragment(scoped_refptr<const NGPhysicalTextFragment>, - NGLineInfo*); void BreakLine(LayoutUnit percentage_resolution_block_size_for_min_max, NGLineInfo*); @@ -135,6 +133,7 @@ class CORE_EXPORT NGLineBreaker { const NGInlineItem&, const ShapeResult&, LayoutUnit available_width, + LayoutUnit available_width_with_hyphens, NGLineInfo*); bool BreakTextAtPreviousBreakOpportunity(NGInlineItemResult* item_result); bool HandleTextForFastMinContent(NGInlineItemResult*, @@ -166,10 +165,7 @@ class CORE_EXPORT NGLineBreaker { const NGInlineItem&, LayoutUnit percentage_resolution_block_size_for_min_max, NGLineInfo*); - bool IsAtomicInlineAfterNoBreakSpace( - const NGInlineItemResult& item_result) const; - bool IsAtomicInlineBeforeNoBreakSpace( - const NGInlineItemResult& item_result) const; + bool ShouldForceCanBreakAfter(const NGInlineItemResult& item_result) const; void HandleFloat(const NGInlineItem&, NGLineInfo*); void HandleOutOfFlowPositioned(const NGInlineItem&, NGLineInfo*); @@ -260,6 +256,10 @@ class CORE_EXPORT NGLineBreaker { // the next line. bool is_after_forced_break_ = false; + // Set in quirks mode when we're not supposed to break inside table cells + // between images, and between text and images. + bool sticky_images_quirk_ = false; + const NGInlineItemsData& items_data_; // The text content of this node. This is same as |items_data_.text_content| diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc index 34eba2935cd..4e53d79afa3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc @@ -17,6 +17,17 @@ namespace blink { +String ToString(NGInlineItemResults line, NGInlineNode node) { + StringBuilder builder; + const String& text = node.ItemsData(false).text_content; + for (const auto& item_result : line) { + builder.Append( + StringView(text, item_result.start_offset, + item_result.end_offset - item_result.start_offset)); + } + return builder.ToString(); +} + class NGLineBreakerTest : public NGLayoutTest { protected: NGInlineNode CreateInlineNode(const String& html_content) { @@ -28,8 +39,10 @@ class NGLineBreakerTest : public NGLayoutTest { } // Break lines using the specified available width. - Vector<NGLineInfo> BreakToLineInfo(NGInlineNode node, - LayoutUnit available_width) { + Vector<std::pair<String, unsigned>> BreakLines( + NGInlineNode node, + LayoutUnit available_width, + bool fill_first_space_ = false) { DCHECK(node); node.PrepareLayoutIfNeeded(); @@ -42,13 +55,13 @@ class NGLineBreakerTest : public NGLayoutTest { scoped_refptr<NGInlineBreakToken> break_token; - Vector<NGLineInfo> line_infos; + Vector<std::pair<String, unsigned>> lines; trailing_whitespaces_.resize(0); NGExclusionSpace exclusion_space; NGPositionedFloatVector leading_floats; NGLineLayoutOpportunity line_opportunity(available_width); while (!break_token || !break_token->IsFinished()) { - NGLineInfo& line_info = line_infos.emplace_back(); + NGLineInfo line_info; NGLineBreaker line_breaker(node, NGLineBreakerMode::kContent, space, line_opportunity, leading_floats, 0u, break_token.get(), &exclusion_space); @@ -60,36 +73,25 @@ class NGLineBreakerTest : public NGLayoutTest { break; break_token = line_breaker.CreateBreakToken(line_info); + if (fill_first_space_ && lines.IsEmpty()) { + first_should_hang_trailing_space_ = + line_info.ShouldHangTrailingSpaces(); + first_hang_width_ = line_info.HangWidth(); + } + lines.push_back(std::make_pair(ToString(line_info.Results(), node), + line_info.Results().back().item_index)); } - return line_infos; - } - - Vector<NGInlineItemResults> BreakLines(NGInlineNode node, - LayoutUnit available_width) { - Vector<NGLineInfo> line_infos = BreakToLineInfo(node, available_width); - Vector<NGInlineItemResults> lines; - for (NGLineInfo& line_info : line_infos) - lines.push_back(std::move(line_info.Results())); return lines; } Vector<NGLineBreaker::WhitespaceState> trailing_whitespaces_; + bool first_should_hang_trailing_space_; + LayoutUnit first_hang_width_; }; namespace { -String ToString(NGInlineItemResults line, NGInlineNode node) { - StringBuilder builder; - const String& text = node.ItemsData(false).text_content; - for (const auto& item_result : line) { - builder.Append( - StringView(text, item_result.start_offset, - item_result.end_offset - item_result.start_offset)); - } - return builder.ToString(); -} - TEST_F(NGLineBreakerTest, SingleNode) { LoadAhem(); NGInlineNode node = CreateInlineNode(R"HTML( @@ -102,17 +104,17 @@ TEST_F(NGLineBreakerTest, SingleNode) { <div id=container>123 456 789</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(80)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("123 456", ToString(lines[0], node)); - EXPECT_EQ("789", ToString(lines[1], node)); + EXPECT_EQ("123 456", lines[0].first); + EXPECT_EQ("789", lines[1].first); lines = BreakLines(node, LayoutUnit(60)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("123", ToString(lines[0], node)); - EXPECT_EQ("456", ToString(lines[1], node)); - EXPECT_EQ("789", ToString(lines[2], node)); + EXPECT_EQ("123", lines[0].first); + EXPECT_EQ("456", lines[1].first); + EXPECT_EQ("789", lines[2].first); } TEST_F(NGLineBreakerTest, OverflowWord) { @@ -128,17 +130,17 @@ TEST_F(NGLineBreakerTest, OverflowWord) { )HTML"); // The first line overflows, but the last line does not. - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(40)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("12345", ToString(lines[0], node)); - EXPECT_EQ("678", ToString(lines[1], node)); + EXPECT_EQ("12345", lines[0].first); + EXPECT_EQ("678", lines[1].first); // Both lines overflow. lines = BreakLines(node, LayoutUnit(20)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("12345", ToString(lines[0], node)); - EXPECT_EQ("678", ToString(lines[1], node)); + EXPECT_EQ("12345", lines[0].first); + EXPECT_EQ("678", lines[1].first); } TEST_F(NGLineBreakerTest, OverflowTab) { @@ -156,11 +158,11 @@ TEST_F(NGLineBreakerTest, OverflowTab) { <div id=container>12345		678</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(100)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("12345\t\t", ToString(lines[0], node)); - EXPECT_EQ("678", ToString(lines[1], node)); + EXPECT_EQ("12345\t\t", lines[0].first); + EXPECT_EQ("678", lines[1].first); } TEST_F(NGLineBreakerTest, OverflowTabBreakWord) { @@ -179,11 +181,11 @@ TEST_F(NGLineBreakerTest, OverflowTabBreakWord) { <div id=container>12345		678</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(100)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("12345\t\t", ToString(lines[0], node)); - EXPECT_EQ("678", ToString(lines[1], node)); + EXPECT_EQ("12345\t\t", lines[0].first); + EXPECT_EQ("678", lines[1].first); } TEST_F(NGLineBreakerTest, OverflowAtomicInline) { @@ -203,28 +205,28 @@ TEST_F(NGLineBreakerTest, OverflowAtomicInline) { <div id=container>12345<span></span>678</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(80)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ(String(u"12345\uFFFC"), ToString(lines[0], node)); - EXPECT_EQ("678", ToString(lines[1], node)); + EXPECT_EQ(String(u"12345\uFFFC"), lines[0].first); + EXPECT_EQ("678", lines[1].first); lines = BreakLines(node, LayoutUnit(70)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("12345", ToString(lines[0], node)); - EXPECT_EQ(String(u"\uFFFC678"), ToString(lines[1], node)); + EXPECT_EQ("12345", lines[0].first); + EXPECT_EQ(String(u"\uFFFC678"), lines[1].first); lines = BreakLines(node, LayoutUnit(40)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("12345", ToString(lines[0], node)); - EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node)); - EXPECT_EQ("678", ToString(lines[2], node)); + EXPECT_EQ("12345", lines[0].first); + EXPECT_EQ(String(u"\uFFFC"), lines[1].first); + EXPECT_EQ("678", lines[2].first); lines = BreakLines(node, LayoutUnit(20)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("12345", ToString(lines[0], node)); - EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node)); - EXPECT_EQ("678", ToString(lines[2], node)); + EXPECT_EQ("12345", lines[0].first); + EXPECT_EQ(String(u"\uFFFC"), lines[1].first); + EXPECT_EQ("678", lines[2].first); } TEST_F(NGLineBreakerTest, OverflowMargin) { @@ -246,21 +248,21 @@ TEST_F(NGLineBreakerTest, OverflowMargin) { // While "123 456" can fit in a line, "456" has a right margin that cannot // fit. Since "456" and its right margin is not breakable, "456" should be on // the next line. - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(80)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("123", ToString(lines[0], node)); - EXPECT_EQ("456", ToString(lines[1], node)); - DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type()); - EXPECT_EQ("789", ToString(lines[2], node)); + EXPECT_EQ("123", lines[0].first); + EXPECT_EQ("456", lines[1].first); + DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].second].Type()); + EXPECT_EQ("789", lines[2].first); // Same as above, but this time "456" overflows the line because it is 70px. lines = BreakLines(node, LayoutUnit(60)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("123", ToString(lines[0], node)); - EXPECT_EQ("456", ToString(lines[1], node)); - DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type()); - EXPECT_EQ("789", ToString(lines[2], node)); + EXPECT_EQ("123", lines[0].first); + EXPECT_EQ("456", lines[1].first); + DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].second].Type()); + EXPECT_EQ("789", lines[2].first); } TEST_F(NGLineBreakerTest, OverflowAfterSpacesAcrossElements) { @@ -278,12 +280,12 @@ TEST_F(NGLineBreakerTest, OverflowAfterSpacesAcrossElements) { <div id=container><span>12345 </span> 1234567890123</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(100)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("12345 ", ToString(lines[0], node)); - EXPECT_EQ("1234567890", ToString(lines[1], node)); - EXPECT_EQ("123", ToString(lines[2], node)); + EXPECT_EQ("12345 ", lines[0].first); + EXPECT_EQ("1234567890", lines[1].first); + EXPECT_EQ("123", lines[2].first); } // Tests when the last word in a node wraps, and another node continues. @@ -299,11 +301,11 @@ TEST_F(NGLineBreakerTest, WrapLastWord) { <div id=container>AAA AAA AAA <span>BB</span> CC</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(100)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("AAA AAA", ToString(lines[0], node)); - EXPECT_EQ("AAA BB CC", ToString(lines[1], node)); + EXPECT_EQ("AAA AAA", lines[0].first); + EXPECT_EQ("AAA BB CC", lines[1].first); } TEST_F(NGLineBreakerTest, WrapLetterSpacing) { @@ -319,11 +321,11 @@ TEST_F(NGLineBreakerTest, WrapLetterSpacing) { <div id=container>Star Wars</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(100)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("Star", ToString(lines[0], node)); - EXPECT_EQ("Wars", ToString(lines[1], node)); + EXPECT_EQ("Star", lines[0].first); + EXPECT_EQ("Wars", lines[1].first); } TEST_F(NGLineBreakerTest, BoundaryInWord) { @@ -340,20 +342,20 @@ TEST_F(NGLineBreakerTest, BoundaryInWord) { // The element boundary within "456789" should not cause a break. // Since "789" does not fit, it should go to the next line along with "456". - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(80)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("123", ToString(lines[0], node)); - EXPECT_EQ("456789", ToString(lines[1], node)); - EXPECT_EQ("abc", ToString(lines[2], node)); + EXPECT_EQ("123", lines[0].first); + EXPECT_EQ("456789", lines[1].first); + EXPECT_EQ("abc", lines[2].first); // Same as above, but this time "456789" overflows the line because it is // 60px. lines = BreakLines(node, LayoutUnit(50)); EXPECT_EQ(3u, lines.size()); - EXPECT_EQ("123", ToString(lines[0], node)); - EXPECT_EQ("456789", ToString(lines[1], node)); - EXPECT_EQ("abc", ToString(lines[2], node)); + EXPECT_EQ("123", lines[0].first); + EXPECT_EQ("456789", lines[1].first); + EXPECT_EQ("abc", lines[2].first); } TEST_F(NGLineBreakerTest, BoundaryInFirstWord) { @@ -368,21 +370,21 @@ TEST_F(NGLineBreakerTest, BoundaryInFirstWord) { <div id=container><span>123</span>456 789</div> )HTML"); - Vector<NGInlineItemResults> lines; + Vector<std::pair<String, unsigned>> lines; lines = BreakLines(node, LayoutUnit(80)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("123456", ToString(lines[0], node)); - EXPECT_EQ("789", ToString(lines[1], node)); + EXPECT_EQ("123456", lines[0].first); + EXPECT_EQ("789", lines[1].first); lines = BreakLines(node, LayoutUnit(50)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("123456", ToString(lines[0], node)); - EXPECT_EQ("789", ToString(lines[1], node)); + EXPECT_EQ("123456", lines[0].first); + EXPECT_EQ("789", lines[1].first); lines = BreakLines(node, LayoutUnit(20)); EXPECT_EQ(2u, lines.size()); - EXPECT_EQ("123456", ToString(lines[0], node)); - EXPECT_EQ("789", ToString(lines[1], node)); + EXPECT_EQ("123456", lines[0].first); + EXPECT_EQ("789", lines[1].first); } struct WhitespaceStateTestData { @@ -451,7 +453,7 @@ TEST_P(NGWhitespaceStateTest, WhitespaceState) { R"HTML(</div> )HTML"); - Vector<NGLineInfo> line_infos = BreakToLineInfo(node, LayoutUnit(50)); + BreakLines(node, LayoutUnit(50)); EXPECT_EQ(trailing_whitespaces_[0], data.expected); } @@ -512,13 +514,11 @@ TEST_P(NGTrailingSpaceWidthTest, TrailingSpaceWidth) { R"HTML(</div> )HTML"); - Vector<NGLineInfo> line_infos = BreakToLineInfo(node, LayoutUnit(50)); - const NGLineInfo& line_info = line_infos[0]; - if (line_info.ShouldHangTrailingSpaces()) { - EXPECT_EQ(line_info.HangWidth(), - LayoutUnit(10) * data.trailing_space_width); + BreakLines(node, LayoutUnit(50), true); + if (first_should_hang_trailing_space_) { + EXPECT_EQ(first_hang_width_, LayoutUnit(10) * data.trailing_space_width); } else { - EXPECT_EQ(line_info.HangWidth(), LayoutUnit()); + EXPECT_EQ(first_hang_width_, LayoutUnit()); } } @@ -535,9 +535,9 @@ TEST_F(NGLineBreakerTest, MinMaxWithTrailingSpaces) { <div id=container>12345 6789 </div> )HTML"); - auto size = node.ComputeMinMaxSize( + auto size = node.ComputeMinMaxSizes( WritingMode::kHorizontalTb, - MinMaxSizeInput(/* percentage_resolution_block_size */ (LayoutUnit()))); + MinMaxSizesInput(/* percentage_resolution_block_size */ (LayoutUnit()))); EXPECT_EQ(size.min_size, LayoutUnit(60)); EXPECT_EQ(size.max_size, LayoutUnit(110)); } @@ -559,9 +559,9 @@ TEST_F(NGLineBreakerTest, TableCellWidthCalculationQuirkOutOfFlow) { GetDocument().SetCompatibilityMode(Document::kQuirksMode); EXPECT_TRUE(node.GetDocument().InQuirksMode()); - node.ComputeMinMaxSize( + node.ComputeMinMaxSizes( WritingMode::kHorizontalTb, - MinMaxSizeInput(/* percentage_resolution_block_size */ LayoutUnit())); + MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit())); // Pass if |ComputeMinMaxSize| doesn't hit DCHECK failures. } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc index 0014fe382c4..0306094c1f4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc @@ -14,112 +14,366 @@ namespace blink { +namespace { + +bool IsLeftMostOffset(const ShapeResult& shape_result, unsigned offset) { + if (shape_result.Rtl()) + return offset == shape_result.NumCharacters(); + return offset == 0; +} + +bool IsRightMostOffset(const ShapeResult& shape_result, unsigned offset) { + if (shape_result.Rtl()) + return offset == 0; + return offset == shape_result.NumCharacters(); +} + +} // namespace + NGLineTruncator::NGLineTruncator(const NGLineInfo& line_info) : line_style_(&line_info.LineStyle()), - available_width_(line_info.AvailableWidth()), + available_width_(line_info.AvailableWidth() - line_info.TextIndent()), line_direction_(line_info.BaseDirection()) {} -LayoutUnit NGLineTruncator::TruncateLine( - LayoutUnit line_width, - NGLineBoxFragmentBuilder::ChildList* line_box, - NGInlineLayoutStateStack* box_states) { - // Shape the ellipsis and compute its inline size. +const ComputedStyle& NGLineTruncator::EllipsisStyle() const { // The ellipsis is styled according to the line style. // https://drafts.csswg.org/css-ui/#ellipsing-details - const ComputedStyle* ellipsis_style = line_style_.get(); - const Font& font = ellipsis_style->GetFont(); - const SimpleFontData* font_data = font.PrimaryFont(); - DCHECK(font_data); - String ellipsis_text = - font_data && font_data->GlyphForCharacter(kHorizontalEllipsisCharacter) + return *line_style_; +} + +void NGLineTruncator::SetupEllipsis() { + const Font& font = EllipsisStyle().GetFont(); + ellipsis_font_data_ = font.PrimaryFont(); + DCHECK(ellipsis_font_data_); + ellipsis_text_ = + ellipsis_font_data_ && ellipsis_font_data_->GlyphForCharacter( + kHorizontalEllipsisCharacter) ? String(&kHorizontalEllipsisCharacter, 1) : String(u"..."); - HarfBuzzShaper shaper(ellipsis_text); - scoped_refptr<ShapeResultView> ellipsis_shape_result = + HarfBuzzShaper shaper(ellipsis_text_); + ellipsis_shape_result_ = ShapeResultView::Create(shaper.Shape(&font, line_direction_).get()); - LayoutUnit ellipsis_width = ellipsis_shape_result->SnappedWidth(); + ellipsis_width_ = ellipsis_shape_result_->SnappedWidth(); +} + +LayoutUnit NGLineTruncator::PlaceEllipsisNextTo( + NGLineBoxFragmentBuilder::ChildList* line_box, + NGLineBoxFragmentBuilder::Child* ellipsized_child) { + // Create the ellipsis, associating it with the ellipsized child. + DCHECK(ellipsized_child->HasInFlowFragment()); + LayoutObject* ellipsized_layout_object = + ellipsized_child->PhysicalFragment()->GetMutableLayoutObject(); + DCHECK(ellipsized_layout_object); + DCHECK(ellipsized_layout_object->IsInline()); + DCHECK(ellipsized_layout_object->IsText() || + ellipsized_layout_object->IsAtomicInlineLevel()); + NGTextFragmentBuilder builder(line_style_->GetWritingMode()); + builder.SetText(ellipsized_layout_object, ellipsis_text_, &EllipsisStyle(), + true /* is_ellipsis_style */, + std::move(ellipsis_shape_result_)); + + // Now the offset of the ellpisis is determined. Place the ellpisis into the + // line box. + LayoutUnit ellipsis_inline_offset = + IsLtr(line_direction_) + ? ellipsized_child->InlineOffset() + ellipsized_child->inline_size + : ellipsized_child->InlineOffset() - ellipsis_width_; + LayoutUnit ellpisis_ascent; + DCHECK(ellipsis_font_data_); + if (ellipsis_font_data_) { + FontBaseline baseline_type = line_style_->GetFontBaseline(); + NGLineHeightMetrics ellipsis_metrics(ellipsis_font_data_->GetFontMetrics(), + baseline_type); + ellpisis_ascent = ellipsis_metrics.ascent; + } + line_box->AddChild(builder.ToTextFragment(), + LogicalOffset{ellipsis_inline_offset, -ellpisis_ascent}, + ellipsis_width_, 0); + return ellipsis_inline_offset; +} + +wtf_size_t NGLineTruncator::AddTruncatedChild( + wtf_size_t source_index, + bool leave_one_character, + LayoutUnit position, + TextDirection edge, + NGLineBoxFragmentBuilder::ChildList* line_box, + NGInlineLayoutStateStack* box_states) { + NGLineBoxFragmentBuilder::ChildList& line = *line_box; + + scoped_refptr<ShapeResult> shape_result = + line[source_index].fragment->TextShapeResult()->CreateShapeResult(); + unsigned text_offset = shape_result->OffsetToFit(position, edge); + if (IsLtr(edge) ? IsLeftMostOffset(*shape_result, text_offset) + : IsRightMostOffset(*shape_result, text_offset)) { + if (!leave_one_character) + return kDidNotAddChild; + text_offset = + shape_result->OffsetToFit(shape_result->PositionForOffset( + IsRtl(edge) == shape_result->Rtl() + ? 1 + : shape_result->NumCharacters() - 1), + edge); + } + + const auto& fragment = line[source_index].fragment; + const bool keep_start = edge == fragment->ResolvedDirection(); + scoped_refptr<const NGPhysicalTextFragment> truncated_fragment = + keep_start ? fragment->TrimText(fragment->StartOffset(), + fragment->StartOffset() + text_offset) + : fragment->TrimText(fragment->StartOffset() + text_offset, + fragment->EndOffset()); + wtf_size_t new_index = line.size(); + line.AddChild(); + box_states->ChildInserted(new_index); + line[new_index] = line[source_index]; + line[new_index].inline_size = line_style_->IsHorizontalWritingMode() + ? truncated_fragment->Size().width + : truncated_fragment->Size().height; + line[new_index].fragment = std::move(truncated_fragment); + return new_index; +} + +LayoutUnit NGLineTruncator::TruncateLine( + LayoutUnit line_width, + NGLineBoxFragmentBuilder::ChildList* line_box, + NGInlineLayoutStateStack* box_states) { + // Shape the ellipsis and compute its inline size. + SetupEllipsis(); // Loop children from the logical last to the logical first to determine where // to place the ellipsis. Children maybe truncated or moved as part of the // process. - NGLineBoxFragmentBuilder::Child* ellpisized_child = nullptr; + NGLineBoxFragmentBuilder::Child* ellipsized_child = nullptr; scoped_refptr<const NGPhysicalTextFragment> truncated_fragment; if (IsLtr(line_direction_)) { NGLineBoxFragmentBuilder::Child* first_child = line_box->FirstInFlowChild(); for (auto it = line_box->rbegin(); it != line_box->rend(); it++) { auto& child = *it; - if (EllipsizeChild(line_width, ellipsis_width, &child == first_child, + if (EllipsizeChild(line_width, ellipsis_width_, &child == first_child, &child, &truncated_fragment)) { - ellpisized_child = &child; + ellipsized_child = &child; break; } } } else { NGLineBoxFragmentBuilder::Child* first_child = line_box->LastInFlowChild(); for (auto& child : *line_box) { - if (EllipsizeChild(line_width, ellipsis_width, &child == first_child, + if (EllipsizeChild(line_width, ellipsis_width_, &child == first_child, &child, &truncated_fragment)) { - ellpisized_child = &child; + ellipsized_child = &child; break; } } } // Abort if ellipsis could not be placed. - if (!ellpisized_child) + if (!ellipsized_child) return line_width; // Truncate the text fragment if needed. if (truncated_fragment) { - DCHECK(ellpisized_child->fragment); + DCHECK(ellipsized_child->fragment); // In order to preserve layout information before truncated, hide the // original fragment and insert a truncated one. - size_t child_index_to_truncate = ellpisized_child - line_box->begin(); + size_t child_index_to_truncate = ellipsized_child - line_box->begin(); line_box->InsertChild(child_index_to_truncate + 1); box_states->ChildInserted(child_index_to_truncate + 1); NGLineBoxFragmentBuilder::Child* child_to_truncate = &(*line_box)[child_index_to_truncate]; - ellpisized_child = std::next(child_to_truncate); - *ellpisized_child = *child_to_truncate; + ellipsized_child = std::next(child_to_truncate); + *ellipsized_child = *child_to_truncate; HideChild(child_to_truncate); LayoutUnit new_inline_size = line_style_->IsHorizontalWritingMode() ? truncated_fragment->Size().width : truncated_fragment->Size().height; - DCHECK_LE(new_inline_size, ellpisized_child->inline_size); + DCHECK_LE(new_inline_size, ellipsized_child->inline_size); if (UNLIKELY(IsRtl(line_direction_))) { - ellpisized_child->offset.inline_offset += - ellpisized_child->inline_size - new_inline_size; + ellipsized_child->rect.offset.inline_offset += + ellipsized_child->inline_size - new_inline_size; } - ellpisized_child->inline_size = new_inline_size; - ellpisized_child->fragment = std::move(truncated_fragment); + ellipsized_child->inline_size = new_inline_size; + ellipsized_child->fragment = std::move(truncated_fragment); } // Create the ellipsis, associating it with the ellipsized child. - LayoutObject* ellipsized_layout_object = - ellpisized_child->PhysicalFragment()->GetMutableLayoutObject(); - DCHECK(ellipsized_layout_object && ellipsized_layout_object->IsInline() && - (ellipsized_layout_object->IsText() || - ellipsized_layout_object->IsAtomicInlineLevel())); - NGTextFragmentBuilder builder(line_style_->GetWritingMode()); - builder.SetText(ellipsized_layout_object, ellipsis_text, ellipsis_style, - true /* is_ellipsis_style */, - std::move(ellipsis_shape_result)); - - // Now the offset of the ellpisis is determined. Place the ellpisis into the - // line box. LayoutUnit ellipsis_inline_offset = - IsLtr(line_direction_) - ? ellpisized_child->offset.inline_offset + - ellpisized_child->inline_size - : ellpisized_child->offset.inline_offset - ellipsis_width; - FontBaseline baseline_type = line_style_->GetFontBaseline(); - NGLineHeightMetrics ellipsis_metrics(font_data->GetFontMetrics(), - baseline_type); - line_box->AddChild( - builder.ToTextFragment(), - LogicalOffset{ellipsis_inline_offset, -ellipsis_metrics.ascent}, - ellipsis_width, 0); - return std::max(ellipsis_inline_offset + ellipsis_width, line_width); + PlaceEllipsisNextTo(line_box, ellipsized_child); + return std::max(ellipsis_inline_offset + ellipsis_width_, line_width); +} + +// This function was designed to work only with <input type=file>. +// We assume the line box contains: +// (Optional) children without in-flow fragments +// Children with in-flow fragments, and +// (Optional) children without in-flow fragments +// in this order, and the children with in-flow fragments have no padding, +// no border, and no margin. +// Children with IsPlaceholder() can appear anywhere. +LayoutUnit NGLineTruncator::TruncateLineInTheMiddle( + LayoutUnit line_width, + NGLineBoxFragmentBuilder::ChildList* line_box, + NGInlineLayoutStateStack* box_states) { + // Shape the ellipsis and compute its inline size. + SetupEllipsis(); + + NGLineBoxFragmentBuilder::ChildList& line = *line_box; + wtf_size_t initial_index_left = kNotFound; + wtf_size_t initial_index_right = kNotFound; + for (wtf_size_t i = 0; i < line_box->size(); ++i) { + auto& child = line[i]; + if (!child.fragment && child.IsPlaceholder()) + continue; + if (child.HasOutOfFlowFragment() || !child.fragment || + !child.fragment->TextShapeResult()) { + if (initial_index_right != kNotFound) + break; + continue; + } + if (initial_index_left == kNotFound) + initial_index_left = i; + initial_index_right = i; + } + // There are no truncatable children. + if (initial_index_left == kNotFound) + return line_width; + DCHECK_NE(initial_index_right, kNotFound); + DCHECK(line[initial_index_left].HasInFlowFragment()); + DCHECK(line[initial_index_right].HasInFlowFragment()); + + // line[]: + // s s s p f f p f f s s + // ^ ^ + // initial_index_left | + // initial_index_right + // s: child without in-flow fragment + // p: placeholder child + // f: child with in-flow fragment + + const LayoutUnit static_width_left = line[initial_index_left].InlineOffset(); + LayoutUnit static_width_right = LayoutUnit(0); + for (wtf_size_t i = initial_index_right + 1; i < line.size(); ++i) + static_width_right += line[i].inline_size; + const LayoutUnit available_width = + available_width_ - static_width_left - static_width_right; + if (available_width <= ellipsis_width_) + return line_width; + LayoutUnit available_width_left = (available_width - ellipsis_width_) / 2; + LayoutUnit available_width_right = available_width_left; + + // Children for ellipsis and truncated fragments will have index which + // is >= new_child_start. + const wtf_size_t new_child_start = line.size(); + + wtf_size_t index_left = initial_index_left; + wtf_size_t index_right = initial_index_right; + + if (IsLtr(line_direction_)) { + // Find truncation point at the left, truncate, and add an ellipsis. + while (available_width_left >= line[index_left].inline_size) + available_width_left -= line[index_left++].inline_size; + DCHECK_LE(index_left, index_right); + DCHECK(!line[index_left].IsPlaceholder()); + wtf_size_t new_index = AddTruncatedChild( + index_left, index_left == initial_index_left, available_width_left, + TextDirection::kLtr, line_box, box_states); + if (new_index == kDidNotAddChild) { + DCHECK_GT(index_left, initial_index_left); + DCHECK_GT(index_left, 0u); + wtf_size_t i = index_left; + while (!line[--i].HasInFlowFragment()) + DCHECK(line[i].IsPlaceholder()); + PlaceEllipsisNextTo(line_box, &line[i]); + available_width_right += available_width_left; + } else { + PlaceEllipsisNextTo(line_box, &line[new_index]); + available_width_right += + available_width_left - line[new_index].inline_size; + } + + // Find truncation point at the right. + while (available_width_right >= line[index_right].inline_size) + available_width_right -= line[index_right--].inline_size; + LayoutUnit new_modified_right_offset = + line[line.size() - 1].InlineOffset() + ellipsis_width_; + DCHECK_LE(index_left, index_right); + DCHECK(!line[index_right].IsPlaceholder()); + if (available_width_right > 0) { + new_index = AddTruncatedChild( + index_right, false, + line[index_right].inline_size - available_width_right, + TextDirection::kRtl, line_box, box_states); + if (new_index != kDidNotAddChild) { + line[new_index].rect.offset.inline_offset = new_modified_right_offset; + new_modified_right_offset += line[new_index].inline_size; + } + } + // Shift unchanged children at the right of the truncated child. + // It's ok to modify existing children's offsets because they are not + // web-exposed. + LayoutUnit offset_diff = line[index_right].InlineOffset() + + line[index_right].inline_size - + new_modified_right_offset; + for (wtf_size_t i = index_right + 1; i < new_child_start; ++i) + line[i].rect.offset.inline_offset -= offset_diff; + line_width -= offset_diff; + + } else { + // Find truncation point at the right, truncate, and add an ellipsis. + while (available_width_right >= line[index_right].inline_size) + available_width_right -= line[index_right--].inline_size; + DCHECK_LE(index_left, index_right); + DCHECK(!line[index_right].IsPlaceholder()); + wtf_size_t new_index = + AddTruncatedChild(index_right, index_right == initial_index_right, + line[index_right].inline_size - available_width_right, + TextDirection::kRtl, line_box, box_states); + if (new_index == kDidNotAddChild) { + DCHECK_LT(index_right, initial_index_right); + wtf_size_t i = index_right; + while (!line[++i].HasInFlowFragment()) + DCHECK(line[i].IsPlaceholder()); + PlaceEllipsisNextTo(line_box, &line[i]); + available_width_left += available_width_right; + } else { + line[new_index].rect.offset.inline_offset += + line[index_right].inline_size - line[new_index].inline_size; + PlaceEllipsisNextTo(line_box, &line[new_index]); + available_width_left += + available_width_right - line[new_index].inline_size; + } + LayoutUnit ellipsis_offset = line[line.size() - 1].InlineOffset(); + + // Find truncation point at the left. + while (available_width_left >= line[index_left].inline_size) + available_width_left -= line[index_left++].inline_size; + DCHECK_LE(index_left, index_right); + DCHECK(!line[index_left].IsPlaceholder()); + if (available_width_left > 0) { + new_index = AddTruncatedChild(index_left, false, available_width_left, + TextDirection::kLtr, line_box, box_states); + if (new_index != kDidNotAddChild) { + line[new_index].rect.offset.inline_offset = + ellipsis_offset - line[new_index].inline_size; + } + } + + // Shift unchanged children at the left of the truncated child. + // It's ok to modify existing children's offsets because they are not + // web-exposed. + LayoutUnit offset_diff = + line[line.size() - 1].InlineOffset() - line[index_left].InlineOffset(); + for (wtf_size_t i = index_left; i > 0; --i) + line[i - 1].rect.offset.inline_offset += offset_diff; + line_width -= offset_diff; + } + // Hide left/right truncated children and children between them. + for (wtf_size_t i = index_left; i <= index_right; ++i) { + if (line[i].HasInFlowFragment()) + HideChild(&line[i]); + } + + return line_width; } // Hide this child from being painted. Leaves a hidden fragment so that layout @@ -147,7 +401,7 @@ void NGLineTruncator::HideChild(NGLineBoxFragmentBuilder::Child* child) { // paddings, because clipping is at the content box but ellipsizing is at // the padding box. Just move to the max because we don't know paddings, // and max should do what we need. - child->offset.inline_offset = LayoutUnit::NearlyMax(); + child->rect.offset.inline_offset = LayoutUnit::NearlyMax(); return; } @@ -182,8 +436,8 @@ bool NGLineTruncator::EllipsizeChild( // Can't place ellipsis if this child is completely outside of the box. LayoutUnit child_inline_offset = IsLtr(line_direction_) - ? child->offset.inline_offset - : line_width - (child->offset.inline_offset + child->inline_size); + ? child->InlineOffset() + : line_width - (child->InlineOffset() + child->inline_size); LayoutUnit space_for_child = available_width_ - child_inline_offset; if (space_for_child <= 0) { // This child is outside of the content box, but we still need to hide it. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h index 0c507997ad2..c65fa3ce5a4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h @@ -33,7 +33,40 @@ class CORE_EXPORT NGLineTruncator final { NGLineBoxFragmentBuilder::ChildList* line_box, NGInlineLayoutStateStack* box_states); + LayoutUnit TruncateLineInTheMiddle( + LayoutUnit line_width, + NGLineBoxFragmentBuilder::ChildList* line_box, + NGInlineLayoutStateStack* box_states); + private: + const ComputedStyle& EllipsisStyle() const; + + // Initialize four ellipsis_*_ data members. + void SetupEllipsis(); + + // Add a child for ellipsis next to |ellipsized_child|. + LayoutUnit PlaceEllipsisNextTo( + NGLineBoxFragmentBuilder::ChildList* line_box, + NGLineBoxFragmentBuilder::Child* ellipsized_child); + + static constexpr wtf_size_t kDidNotAddChild = WTF::kNotFound; + // Add a child with truncated text of (*line_box)[source_index]. + // This function returns the index of the new child. + // If the truncated text is empty, kDidNotAddChild is returned. + // + // |leave_one_character| - Force to leave at least one character regardless of + // |position|. + // |position| and |edge| - Indicate truncation point and direction. + // If |edge| is TextDirection::kLtr, the left side of + // |position| will be copied to the new child. + // Otherwise, the right side of |position| will be + // copied. + wtf_size_t AddTruncatedChild(wtf_size_t source_index, + bool leave_one_character, + LayoutUnit position, + TextDirection edge, + NGLineBoxFragmentBuilder::ChildList* line_box, + NGInlineLayoutStateStack* box_states); bool EllipsizeChild( LayoutUnit line_width, LayoutUnit ellipsis_width, @@ -50,6 +83,15 @@ class CORE_EXPORT NGLineTruncator final { scoped_refptr<const ComputedStyle> line_style_; LayoutUnit available_width_; TextDirection line_direction_; + + // The following 3 data members are available after SetupEllipsis(). + const SimpleFontData* ellipsis_font_data_; + String ellipsis_text_; + LayoutUnit ellipsis_width_; + + // This data member is available between SetupEllipsis() and + // PlaceEllipsisNextTo(). + scoped_refptr<ShapeResultView> ellipsis_shape_result_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc index 9e5ef9dc4aa..a17617b0d31 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc @@ -123,8 +123,6 @@ void NGOffsetMappingUnit::AssertValid() const { #endif } -NGOffsetMappingUnit::~NGOffsetMappingUnit() = default; - const Node* NGOffsetMappingUnit::AssociatedNode() const { if (const auto* text_fragment = ToLayoutTextFragmentOrNull(layout_object_)) return text_fragment->AssociatedTextNode(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h index 40ab1e60d63..13b2eb52bbb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h @@ -47,7 +47,6 @@ class CORE_EXPORT NGOffsetMappingUnit { unsigned dom_end, unsigned text_content_start, unsigned text_content_end); - ~NGOffsetMappingUnit(); // Returns associated node for this unit or null if this unit is associated // to generated content. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc index 206226f2b87..3e2c8f22e7d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc @@ -97,7 +97,6 @@ class NGOffsetMappingTest : public NGLayoutTest { void SetUp() override { NGLayoutTest::SetUp(); style_ = ComputedStyle::Create(); - style_->GetFont().Update(nullptr); } void SetupHtml(const char* id, String html) { @@ -1496,7 +1495,8 @@ TEST_P(NGOffsetMappingGetterTest, Get) { // For the purpose of this test, ensure this is laid out by each layout // engine. - DCHECK_EQ(layout_block_flow->IsLayoutNGMixin(), GetParam()); + DCHECK_EQ(layout_block_flow->IsLayoutNGMixin(), + RuntimeEnabledFeatures::LayoutNGEnabled()); const NGOffsetMapping* mapping = NGInlineNode::GetOffsetMapping(layout_block_flow); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc index cab0866ce4c..ac726c4dea4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc @@ -6,9 +6,11 @@ #include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.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_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -37,11 +39,12 @@ NGPhysicalLineBoxFragment::Create(NGLineBoxFragmentBuilder* builder) { sizeof(NGPhysicalLineBoxFragment) + builder->children_.size() * sizeof(NGLink), ::WTF::GetStringWithTypeName<NGPhysicalLineBoxFragment>()); - new (data) NGPhysicalLineBoxFragment(builder); + new (data) NGPhysicalLineBoxFragment(PassKey(), builder); return base::AdoptRef(static_cast<NGPhysicalLineBoxFragment*>(data)); } NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment( + PassKey key, NGLineBoxFragmentBuilder* builder) : NGPhysicalContainerFragment(builder, builder->GetWritingMode(), @@ -51,48 +54,51 @@ NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment( metrics_(builder->metrics_) { // A line box must have a metrics unless it's an empty line box. DCHECK(!metrics_.IsEmpty() || IsEmptyLineBox()); - base_direction_ = static_cast<unsigned>(builder->base_direction_); + base_or_resolved_direction_ = static_cast<unsigned>(builder->base_direction_); has_hanging_ = builder->hang_inline_size_ != 0; has_propagated_descendants_ = has_floating_descendants_for_paint_ || HasOutOfFlowPositionedDescendants() || builder->unpositioned_list_marker_; } -NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics( - FontBaseline) const { +NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics() const { // TODO(kojii): Computing other baseline types than the used one is not // implemented yet. // TODO(kojii): We might need locale/script to look up OpenType BASE table. return metrics_; } +namespace { + +// Include the inline-size of the line-box in the overflow. +inline void AddInlineSizeToOverflow(const PhysicalRect& rect, + const WritingMode container_writing_mode, + PhysicalRect* overflow) { + PhysicalRect inline_rect; + inline_rect.offset = rect.offset; + if (IsHorizontalWritingMode(container_writing_mode)) + inline_rect.size.width = rect.size.width; + else + inline_rect.size.height = rect.size.height; + overflow->UniteEvenIfEmpty(inline_rect); +} + +} // namespace + PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow( - const LayoutObject* container, - const ComputedStyle* container_style, - PhysicalSize container_physical_size) const { - WritingMode container_writing_mode = container_style->GetWritingMode(); - TextDirection container_direction = container_style->Direction(); + const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style) const { + const WritingMode container_writing_mode = container_style.GetWritingMode(); + const TextDirection container_direction = container_style.Direction(); PhysicalRect overflow; for (const auto& child : Children()) { PhysicalRect child_scroll_overflow = child->ScrollableOverflowForPropagation(container); child_scroll_overflow.offset += child.Offset(); - // Chop the hanging part from scrollable overflow. Children overflow in - // inline direction should hang, which should not cause scroll. - // TODO(kojii): Should move to text fragment to make this more accurate. if (UNLIKELY(has_hanging_ && !child->IsFloatingOrOutOfFlowPositioned())) { - if (IsHorizontalWritingMode(container_writing_mode)) { - if (child_scroll_overflow.offset.left < 0) - child_scroll_overflow.offset.left = LayoutUnit(); - if (child_scroll_overflow.Right() > Size().width) - child_scroll_overflow.ShiftRightEdgeTo(Size().width); - } else { - if (child_scroll_overflow.offset.top < 0) - child_scroll_overflow.offset.top = LayoutUnit(); - if (child_scroll_overflow.Bottom() > Size().height) - child_scroll_overflow.ShiftBottomEdgeTo(Size().height); - } + AdjustScrollableOverflowForHanging(LocalRect(), container_writing_mode, + &child_scroll_overflow); } // For implementation reasons, text nodes inherit computed style from their @@ -102,18 +108,35 @@ PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow( if (!child->IsText()) { child_scroll_overflow.offset += ComputeRelativeOffset(child->Style(), container_writing_mode, - container_direction, container_physical_size); + container_direction, container.Size()); } overflow.Unite(child_scroll_overflow); } // Make sure we include the inline-size of the line-box in the overflow. - PhysicalRect rect; - if (IsHorizontalWritingMode(container_writing_mode)) - rect.size.width = Size().width; - else - rect.size.height = Size().height; - overflow.UniteEvenIfEmpty(rect); + AddInlineSizeToOverflow(LocalRect(), container_writing_mode, &overflow); + + return overflow; +} + +PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflowForLine( + const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style, + const NGFragmentItem& line, + const NGInlineCursor& cursor) const { + DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DCHECK_EQ(&line, cursor.CurrentItem()); + DCHECK_EQ(line.LineBoxFragment(), this); + + PhysicalRect overflow; + AddScrollableOverflowForInlineChild(container, container_style, line, + has_hanging_, cursor, &overflow); + + // Make sure we include the inline-size of the line-box in the overflow. + // Note, the bottom half-leading should not be included. crbug.com/996847 + const WritingMode container_writing_mode = container_style.GetWritingMode(); + AddInlineSizeToOverflow(line.RectInContainerBlock(), container_writing_mode, + &overflow); return overflow; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h index 6ee0a0a81fb..044007e1cde 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h @@ -13,6 +13,7 @@ namespace blink { +class NGFragmentItem; class NGLineBoxFragmentBuilder; class CORE_EXPORT NGPhysicalLineBoxFragment final @@ -31,6 +32,9 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final static scoped_refptr<const NGPhysicalLineBoxFragment> Create( NGLineBoxFragmentBuilder* builder); + using PassKey = util::PassKey<NGPhysicalLineBoxFragment>; + NGPhysicalLineBoxFragment(PassKey, NGLineBoxFragmentBuilder* builder); + ~NGPhysicalLineBoxFragment() { for (const NGLink& child : Children()) child.fragment->Release(); @@ -50,19 +54,22 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final // This may be different from the direction of the container box when // first-line style is used, or when 'unicode-bidi: plaintext' is used. TextDirection BaseDirection() const { - return static_cast<TextDirection>(base_direction_); + return static_cast<TextDirection>(base_or_resolved_direction_); } - // Compute baseline for the specified baseline type. - NGLineHeightMetrics BaselineMetrics(FontBaseline) const; + // Compute the baseline metrics for this linebox. + NGLineHeightMetrics BaselineMetrics() const; // Scrollable overflow. including contents, in the local coordinate. // |ScrollableOverflow| is not precomputed/cached because it cannot be // computed when LineBox is generated because it needs container dimensions // to resolve relative position of its children. - PhysicalRect ScrollableOverflow(const LayoutObject* container, - const ComputedStyle* container_style, - PhysicalSize container_physical_size) const; + PhysicalRect ScrollableOverflow(const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style) const; + PhysicalRect ScrollableOverflowForLine(const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style, + const NGFragmentItem& line, + const NGInlineCursor& cursor) const; // Whether the content soft-wraps to the next line. bool HasSoftWrapToNextLine() const; @@ -72,8 +79,6 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final const LayoutObject* ContainerLayoutObject() const { return layout_object_; } private: - NGPhysicalLineBoxFragment(NGLineBoxFragmentBuilder* builder); - NGLineHeightMetrics metrics_; NGLink children_[]; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc index 834188b7add..a15a5429402 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc @@ -32,6 +32,7 @@ static_assert(sizeof(NGPhysicalTextFragment) == } // anonymous namespace NGPhysicalTextFragment::NGPhysicalTextFragment( + PassKey key, const NGPhysicalTextFragment& source, unsigned start_offset, unsigned end_offset, @@ -45,25 +46,29 @@ NGPhysicalTextFragment::NGPhysicalTextFragment( kFragmentText, source.TextType()), text_(source.text_), - start_offset_(start_offset), - end_offset_(end_offset), + text_offset_(start_offset, end_offset), shape_result_(std::move(shape_result)) { - DCHECK_GE(start_offset_, source.StartOffset()); - DCHECK_LE(end_offset_, source.EndOffset()); + DCHECK_GE(text_offset_.start, source.StartOffset()); + DCHECK_LE(text_offset_.end, source.EndOffset()); DCHECK(shape_result_ || IsFlowControl()) << *this; - is_generated_text_ = source.is_generated_text_; + base_or_resolved_direction_ = source.base_or_resolved_direction_; + is_generated_text_or_math_fraction_ = + source.is_generated_text_or_math_fraction_; ink_overflow_computed_ = false; + is_first_for_node_ = source.is_first_for_node_; } NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder) : NGPhysicalFragment(builder, kFragmentText, builder->text_type_), text_(builder->text_), - start_offset_(builder->start_offset_), - end_offset_(builder->end_offset_), + text_offset_({builder->start_offset_, builder->end_offset_}), shape_result_(std::move(builder->shape_result_)) { DCHECK(shape_result_ || IsFlowControl()) << *this; - is_generated_text_ = builder->IsGeneratedText(); + base_or_resolved_direction_ = + static_cast<unsigned>(builder->ResolvedDirection()); + is_generated_text_or_math_fraction_ = builder->IsGeneratedText(); ink_overflow_computed_ = false; + is_first_for_node_ = builder->is_first_for_node_; } LayoutUnit NGPhysicalTextFragment::InlinePositionForOffset( @@ -214,8 +219,9 @@ scoped_refptr<const NGPhysicalTextFragment> NGPhysicalTextFragment::TrimText( DCHECK_LE(new_end_offset, EndOffset()); scoped_refptr<ShapeResultView> new_shape_result = ShapeResultView::Create( shape_result_.get(), new_start_offset, new_end_offset); - return base::AdoptRef(new NGPhysicalTextFragment( - *this, new_start_offset, new_end_offset, std::move(new_shape_result))); + return base::AdoptRef( + new NGPhysicalTextFragment(PassKey(), *this, new_start_offset, + new_end_offset, std::move(new_shape_result))); } unsigned NGPhysicalTextFragment::TextOffsetForPoint( @@ -263,10 +269,4 @@ UBiDiLevel NGPhysicalTextFragment::BidiLevel() const { return containing_item->BidiLevel(); } -TextDirection NGPhysicalTextFragment::ResolvedDirection() const { - if (TextShapeResult()) - return TextShapeResult()->Direction(); - return DirectionFromLevel(BidiLevel()); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h index 7d0adc9bfce..dbd1bae97af 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h @@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_PHYSICAL_TEXT_FRAGMENT_H_ #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h" #include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h" #include "third_party/blink/renderer/platform/fonts/ng_text_fragment_paint_info.h" @@ -45,10 +45,18 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { NGPhysicalTextFragment(NGTextFragmentBuilder*); + using PassKey = util::PassKey<NGPhysicalTextFragment>; + // For use by TrimText only + NGPhysicalTextFragment(PassKey, + const NGPhysicalTextFragment& source, + unsigned start_offset, + unsigned end_offset, + scoped_refptr<const ShapeResultView> shape_result); + NGTextType TextType() const { return static_cast<NGTextType>(sub_type_); } // Returns true if the text is generated (from, e.g., list marker, // pseudo-element, ...) instead of from a DOM text node. - bool IsGeneratedText() const { return is_generated_text_; } + bool IsGeneratedText() const { return is_generated_text_or_math_fraction_; } // True if this is a forced line break. bool IsLineBreak() const { return TextType() == kForcedLineBreak; } // True if this is not for painting; i.e., a forced line break, a tabulation, @@ -63,18 +71,19 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { bool IsSymbolMarker() const { return TextType() == kSymbolMarker; } - unsigned TextLength() const { return end_offset_ - start_offset_; } - StringView Text() const { - return StringView(text_, start_offset_, TextLength()); - } const String& TextContent() const { return text_; } // ShapeResult may be nullptr if |IsFlowControl()|. const ShapeResultView* TextShapeResult() const { return shape_result_.get(); } // Start/end offset to the text of the block container. - unsigned StartOffset() const { return start_offset_; } - unsigned EndOffset() const { return end_offset_; } + const NGTextOffset& TextOffset() const { return text_offset_; } + unsigned StartOffset() const { return text_offset_.start; } + unsigned EndOffset() const { return text_offset_.end; } + unsigned TextLength() const { return text_offset_.Length(); } + StringView Text() const { + return StringView(text_, text_offset_.start, TextLength()); + } WritingMode GetWritingMode() const { return Style().GetWritingMode(); } bool IsHorizontal() const { @@ -113,7 +122,9 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { unsigned TextOffsetForPoint(const PhysicalOffset&) const; UBiDiLevel BidiLevel() const; - TextDirection ResolvedDirection() const; + TextDirection ResolvedDirection() const { + return static_cast<TextDirection>(base_or_resolved_direction_); + } // Compute line-relative coordinates for given offsets, this is not // flow-relative: @@ -123,12 +134,6 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { unsigned end_offset) const; private: - // For use by TrimText only - NGPhysicalTextFragment(const NGPhysicalTextFragment& source, - unsigned start_offset, - unsigned end_offset, - scoped_refptr<const ShapeResultView> shape_result); - LayoutUnit InlinePositionForOffset(unsigned offset, LayoutUnit (*round)(float), AdjustMidCluster) const; @@ -140,8 +145,7 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment { const String text_; // Start and end offset of the parent block text. - const unsigned start_offset_; - const unsigned end_offset_; + const NGTextOffset text_offset_; const scoped_refptr<const ShapeResultView> shape_result_; // Fragments are immutable but allow certain expensive data, specifically ink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc index 7de65ceb791..ebb2a5abd83 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc @@ -43,6 +43,8 @@ class NGPhysicalTextFragmentTest : public NGLayoutTest { }; TEST_F(NGPhysicalTextFragmentTest, LocalRect) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -59,6 +61,8 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRect) { } TEST_F(NGPhysicalTextFragmentTest, LocalRectRTL) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -81,6 +85,8 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectRTL) { } TEST_F(NGPhysicalTextFragmentTest, LocalRectVLR) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -98,6 +104,8 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectVLR) { } TEST_F(NGPhysicalTextFragmentTest, LocalRectVRL) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -115,6 +123,8 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectVRL) { } TEST_F(NGPhysicalTextFragmentTest, NormalTextIsNotAnonymousText) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML("<div id=div>text</div>"); auto text_fragments = CollectTextFragmentsInContainer("div"); @@ -125,6 +135,8 @@ TEST_F(NGPhysicalTextFragmentTest, NormalTextIsNotAnonymousText) { } TEST_F(NGPhysicalTextFragmentTest, FirstLetterIsNotAnonymousText) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML( "<style>::first-letter {color:red}</style>" "<div id=div>text</div>"); @@ -139,6 +151,8 @@ TEST_F(NGPhysicalTextFragmentTest, FirstLetterIsNotAnonymousText) { } TEST_F(NGPhysicalTextFragmentTest, BeforeAndAfterAreAnonymousText) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML( "<style>::before{content:'x'} ::after{content:'x'}</style>" "<div id=div>text</div>"); @@ -155,6 +169,8 @@ TEST_F(NGPhysicalTextFragmentTest, BeforeAndAfterAreAnonymousText) { } TEST_F(NGPhysicalTextFragmentTest, Ellipsis) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -191,6 +207,8 @@ TEST_F(NGPhysicalTextFragmentTest, Ellipsis) { } TEST_F(NGPhysicalTextFragmentTest, ListMarkerIsGeneratedText) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML( "<ol style='list-style-position:inside'>" "<li id=list>text</li>" @@ -206,6 +224,8 @@ TEST_F(NGPhysicalTextFragmentTest, ListMarkerIsGeneratedText) { } TEST_F(NGPhysicalTextFragmentTest, SoftHyphen) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -234,6 +254,8 @@ TEST_F(NGPhysicalTextFragmentTest, SoftHyphen) { } TEST_F(NGPhysicalTextFragmentTest, QuotationMarksAreAnonymousText) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; SetBodyInnerHTML("<div id=div><q>text</q></div>"); auto text_fragments = CollectTextFragmentsInContainer("div"); @@ -248,6 +270,8 @@ TEST_F(NGPhysicalTextFragmentTest, QuotationMarksAreAnonymousText) { } TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulation) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> @@ -270,6 +294,8 @@ TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulation) { } TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulationRtl) { + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) + return; LoadAhem(); SetBodyInnerHTML(R"HTML( <style> diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h deleted file mode 100644 index 0dbc9f84a77..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_END_EFFECT_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_END_EFFECT_H_ - -namespace blink { - -// Effects at the end of text fragments. -enum class NGTextEndEffect { - kNone, - kHyphen, - - // When adding new values, ensure NGPhysicalTextFragment has enough bits. -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_END_EFFECT_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc index ea7efc7acb4..0b85af665ca 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc @@ -36,6 +36,7 @@ void NGTextFragmentBuilder::SetItem( text_ = items_data.text_content; start_offset_ = item_result->start_offset; end_offset_ = item_result->end_offset; + resolved_direction_ = item_result->item->Direction(); SetStyle(item_result->item->Style(), item_result->item->StyleVariant()); size_ = {item_result->inline_size, line_height}; shape_result_ = std::move(item_result->shape_result); @@ -56,6 +57,7 @@ void NGTextFragmentBuilder::SetText( text_ = text; start_offset_ = shape_result->StartIndex(); end_offset_ = shape_result->EndIndex(); + resolved_direction_ = shape_result->Direction(); SetStyle(style, is_ellipsis_style ? NGStyleVariant::kEllipsis : NGStyleVariant::kStandard); size_ = {shape_result->SnappedWidth(), diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h index 422fcd3aa3e..3cb38c1af32 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/layout/geometry/logical_size.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_text_fragment.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -27,6 +26,8 @@ class CORE_EXPORT NGTextFragmentBuilder final : public NGFragmentBuilder { NGTextFragmentBuilder(const NGPhysicalTextFragment& fragment); + TextDirection ResolvedDirection() const { return resolved_direction_; } + // NOTE: Takes ownership of the shape result within the item result. void SetItem(NGPhysicalTextFragment::NGTextType, const NGInlineItemsData&, @@ -56,6 +57,9 @@ class CORE_EXPORT NGTextFragmentBuilder final : public NGFragmentBuilder { NGPhysicalTextFragment::NGTextType text_type_ = NGPhysicalTextFragment::kNormalText; + // Set from |NGInlineItem| by |SetItem()|. + TextDirection resolved_direction_ = TextDirection::kLtr; + friend class NGPhysicalTextFragment; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h index e8132013463..0d6c7251095 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_OFFSET_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_OFFSET_H_ +#include "base/logging.h" #include "third_party/blink/renderer/core/core_export.h" namespace blink { @@ -13,10 +14,13 @@ namespace blink { struct CORE_EXPORT NGTextOffset { NGTextOffset() = default; NGTextOffset(unsigned start, unsigned end) : start(start), end(end) { - DCHECK_GE(end, start); + AssertValid(); } - unsigned Length() const { return end - start; } + unsigned Length() const { + AssertValid(); + return end - start; + } void AssertValid() const { DCHECK_GE(end, start); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc index eac09620636..ef990b7ce3f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc @@ -72,21 +72,6 @@ void LayoutNGBlockFlowMixin<Base>::ClearNGInlineNodeData() { ng_inline_node_data_.reset(); } -// The current fragment from the last layout cycle for this box. -// When pre-NG layout calls functions of this block flow, fragment and/or -// LayoutResult are required to compute the result. -// TODO(kojii): Use the cached result for now, we may need to reconsider as the -// cache evolves. -template <typename Base> -const NGPhysicalBoxFragment* LayoutNGBlockFlowMixin<Base>::CurrentFragment() - const { - const NGLayoutResult* cached_layout_result = Base::GetCachedLayoutResult(); - if (!cached_layout_result) - return nullptr; - - return &To<NGPhysicalBoxFragment>(cached_layout_result->PhysicalFragment()); -} - template <typename Base> void LayoutNGBlockFlowMixin<Base>::AddLayoutOverflowFromChildren() { if (Base::LayoutBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren)) @@ -104,83 +89,15 @@ void LayoutNGBlockFlowMixin<Base>::AddLayoutOverflowFromChildren() { template <typename Base> void LayoutNGBlockFlowMixin<Base>::AddScrollingOverflowFromChildren() { - const NGPhysicalBoxFragment* physical_fragment = CurrentFragment(); DCHECK(physical_fragment); - if (physical_fragment->Children().empty()) - return; - - const ComputedStyle& style = Base::StyleRef(); - const WritingMode writing_mode = style.GetWritingMode(); - const TextDirection direction = style.Direction(); - const LayoutUnit border_inline_start = LayoutUnit(style.BorderStartWidth()); - const LayoutUnit border_block_start = LayoutUnit(style.BorderBeforeWidth()); - const PhysicalSize& size = physical_fragment->Size(); - - // End and under padding are added to scroll overflow of inline children. - // https://github.com/w3c/csswg-drafts/issues/129 - base::Optional<NGPhysicalBoxStrut> padding_strut; - if (Base::HasOverflowClip()) { - padding_strut = NGBoxStrut(LayoutUnit(), Base::PaddingEnd(), LayoutUnit(), - Base::PaddingUnder()) - .ConvertToPhysical(writing_mode, direction); - } - - // Rectangles not reachable by scroll should not be added to overflow. - auto IsRectReachableByScroll = [&border_inline_start, &border_block_start, - &writing_mode, &direction, - &size](const PhysicalRect& rect) { - LogicalOffset rect_logical_end = - rect.offset.ConvertToLogical(writing_mode, direction, size, rect.size) + - rect.size.ConvertToLogical(writing_mode); - return (rect_logical_end.inline_offset > border_inline_start && - rect_logical_end.block_offset > border_block_start); - }; - - bool children_inline = Base::ChildrenInline(); - PhysicalRect children_overflow; - base::Optional<PhysicalRect> lineboxes_enclosing_rect; - // Only add overflow for fragments NG has not reflected into Legacy. - // These fragments are: - // - inline fragments, - // - out of flow fragments whose css container is inline box. - // TODO(layout-dev) Transforms also need to be applied to compute overflow - // correctly. NG is not yet transform-aware. crbug.com/855965 - for (const auto& child : physical_fragment->Children()) { - PhysicalRect child_scrollable_overflow; - if (child->IsFloatingOrOutOfFlowPositioned()) { - child_scrollable_overflow = child->ScrollableOverflowForPropagation(this); - child_scrollable_overflow.offset += - ComputeRelativeOffset(child->Style(), writing_mode, direction, size); - } else if (children_inline && child->IsLineBox()) { - DCHECK(child->IsLineBox()); - child_scrollable_overflow = - To<NGPhysicalLineBoxFragment>(*child).ScrollableOverflow(this, &style, - size); - if (padding_strut) { - PhysicalRect linebox_rect(child.Offset(), child->Size()); - if (lineboxes_enclosing_rect) - lineboxes_enclosing_rect->Unite(linebox_rect); - else - lineboxes_enclosing_rect = linebox_rect; - } - } else { - continue; - } - child_scrollable_overflow.offset += child.Offset(); - // Do not add overflow if fragment is not reachable by scrolling. - if (IsRectReachableByScroll(child_scrollable_overflow)) - children_overflow.Unite(child_scrollable_overflow); - } - if (lineboxes_enclosing_rect) { - lineboxes_enclosing_rect->Expand(*padding_strut); - if (IsRectReachableByScroll(*lineboxes_enclosing_rect)) - children_overflow.Unite(*lineboxes_enclosing_rect); - } + PhysicalRect children_overflow = + physical_fragment->ScrollableOverflowFromChildren(); // LayoutOverflow takes flipped blocks coordinates, adjust as needed. + const ComputedStyle& style = physical_fragment->Style(); LayoutRect children_flipped_overflow = - children_overflow.ToLayoutFlippedRect(style, size); + children_overflow.ToLayoutFlippedRect(style, physical_fragment->Size()); Base::AddLayoutOverflow(children_flipped_overflow); } @@ -193,60 +110,44 @@ void LayoutNGBlockFlowMixin<Base>::AddOutlineRects( To<NGPhysicalBoxFragment>(PaintFragment()->PhysicalFragment()) .AddSelfOutlineRects(additional_offset, include_block_overflows, &rects); - } else { - Base::AddOutlineRects(rects, additional_offset, include_block_overflows); + return; } -} - -template <typename Base> -bool LayoutNGBlockFlowMixin< - Base>::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const { - // LayoutNGBlockFlowMixin is in charge of paint invalidation of the first - // line. - if (PaintFragment()) - return false; - if (Base::StyleRef().HasColumnRule()) - return false; + if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { + if (fragment->HasItems()) { + fragment->AddSelfOutlineRects(additional_offset, include_block_overflows, + &rects); + return; + } + } - return Base::PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); + Base::AddOutlineRects(rects, additional_offset, include_block_overflows); } // Retrieve NGBaseline from the current fragment. template <typename Base> -base::Optional<LayoutUnit> LayoutNGBlockFlowMixin<Base>::FragmentBaseline( - NGBaselineAlgorithmType type) const { +base::Optional<LayoutUnit> LayoutNGBlockFlowMixin<Base>::FragmentBaseline() + const { if (Base::ShouldApplyLayoutContainment()) return base::nullopt; - if (const NGPhysicalFragment* physical_fragment = CurrentFragment()) { - FontBaseline baseline_type = Base::StyleRef().GetFontBaseline(); - return To<NGPhysicalBoxFragment>(physical_fragment) - ->Baseline({type, baseline_type}); - } + if (const NGPhysicalFragment* physical_fragment = CurrentFragment()) + return To<NGPhysicalBoxFragment>(physical_fragment)->Baseline(); return base::nullopt; } template <typename Base> LayoutUnit LayoutNGBlockFlowMixin<Base>::FirstLineBoxBaseline() const { - if (Base::ChildrenInline()) { - if (base::Optional<LayoutUnit> offset = - FragmentBaseline(NGBaselineAlgorithmType::kFirstLine)) { - return *offset; - } - } + if (base::Optional<LayoutUnit> offset = FragmentBaseline()) + return *offset; return Base::FirstLineBoxBaseline(); } template <typename Base> LayoutUnit LayoutNGBlockFlowMixin<Base>::InlineBlockBaseline( LineDirectionMode line_direction) const { - if (Base::ChildrenInline()) { - if (base::Optional<LayoutUnit> offset = - FragmentBaseline(NGBaselineAlgorithmType::kAtomicInline)) { - return *offset; - } - } + if (base::Optional<LayoutUnit> offset = FragmentBaseline()) + return *offset; return Base::InlineBlockBaseline(line_direction); } @@ -300,11 +201,9 @@ void LayoutNGBlockFlowMixin<Base>::Paint(const PaintInfo& paint_info) const { return; } - if (RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled()) { - if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { - NGBoxFragmentPainter(*fragment).Paint(paint_info); - return; - } + if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { + NGBoxFragmentPainter(*fragment).Paint(paint_info); + return; } Base::Paint(paint_info); @@ -317,7 +216,7 @@ bool LayoutNGBlockFlowMixin<Base>::NodeAtPoint( const PhysicalOffset& accumulated_offset, HitTestAction action) { if (const NGPaintFragment* paint_fragment = PaintFragment()) { - if (!this->IsEffectiveRootScroller()) { + if (!Base::IsEffectiveRootScroller()) { // Check if we need to do anything at all. // If we have clipping, then we can't have any spillout. PhysicalRect overflow_box = Base::HasOverflowClip() @@ -338,7 +237,11 @@ bool LayoutNGBlockFlowMixin<Base>::NodeAtPoint( if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())) { if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { - if (fragment->HasItems()) { + if (fragment->HasItems() || + // Check descendants of this fragment because floats may be in the + // |NGFragmentItems| of the descendants. + (action == kHitTestFloat && + fragment->HasFloatingDescendantsForPaint())) { return NGBoxFragmentPainter(*fragment).NodeAtPoint( result, hit_test_location, accumulated_offset, action); } @@ -370,15 +273,18 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint( if (const PositionWithAffinity position = paint_fragment->PositionForPoint(point_in_contents)) return position; - } else if (const NGFragmentItems* items = Base::FragmentItems()) { - // The given offset is relative to this |LayoutBlockFlow|. Convert to the - // contents offset. - PhysicalOffset point_in_contents = point; - Base::OffsetForContents(point_in_contents); - NGInlineCursor cursor(*items); - if (const PositionWithAffinity position = - cursor.PositionForPoint(point_in_contents)) - return position; + } else if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { + if (const NGFragmentItems* items = fragment->Items()) { + // The given offset is relative to this |LayoutBlockFlow|. Convert to the + // contents offset. + PhysicalOffset point_in_contents = point; + Base::OffsetForContents(point_in_contents); + NGInlineCursor cursor(*items); + if (const PositionWithAffinity position = + cursor.PositionForPointInInlineFormattingContext( + point_in_contents, *fragment)) + return position; + } } return Base::CreatePositionWithAffinity(0); @@ -402,26 +308,16 @@ void LayoutNGBlockFlowMixin<Base>::UpdateNGBlockLayout() { LayoutAnalyzer::BlockScope analyzer(*this); if (Base::IsOutOfFlowPositioned()) { - this->UpdateOutOfFlowBlockLayout(); + LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout(); return; } - NGConstraintSpace constraint_space = - NGConstraintSpace::CreateFromLayoutObject( - *this, !Base::View()->GetLayoutState()->Next() /* is_layout_root */); - - scoped_refptr<const NGLayoutResult> result = - NGBlockNode(this).Layout(constraint_space); - - for (const auto& descendant : - result->PhysicalFragment().OutOfFlowPositionedDescendants()) - descendant.node.UseLegacyOutOfFlowPositioning(); - this->UpdateMargins(constraint_space); + LayoutNGMixin<Base>::UpdateInFlowBlockLayout(); + UpdateMargins(); } template <typename Base> -void LayoutNGBlockFlowMixin<Base>::UpdateMargins( - const NGConstraintSpace& space) { +void LayoutNGBlockFlowMixin<Base>::UpdateMargins() { const LayoutBlock* containing_block = Base::ContainingBlock(); if (!containing_block || !containing_block->IsLayoutBlockFlow()) return; @@ -434,13 +330,13 @@ void LayoutNGBlockFlowMixin<Base>::UpdateMargins( const ComputedStyle& cb_style = containing_block->StyleRef(); const auto writing_mode = cb_style.GetWritingMode(); const auto direction = cb_style.Direction(); - LayoutUnit percentage_resolution_size = - space.PercentageResolutionInlineSizeForParentWritingMode(); - NGBoxStrut margins = ComputePhysicalMargins(style, percentage_resolution_size) + LayoutUnit available_logical_width = + LayoutBoxUtils::AvailableLogicalWidth(*this, containing_block); + NGBoxStrut margins = ComputePhysicalMargins(style, available_logical_width) .ConvertToLogical(writing_mode, direction); - ResolveInlineMargins(style, cb_style, space.AvailableSize().inline_size, + ResolveInlineMargins(style, cb_style, available_logical_width, Base::LogicalWidth(), &margins); - this->SetMargin(margins.ConvertToPhysical(writing_mode, direction)); + Base::SetMargin(margins.ConvertToPhysical(writing_mode, direction)); } template class CORE_TEMPLATE_EXPORT LayoutNGBlockFlowMixin<LayoutBlockFlow>; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h index 6b5d874ce5d..2e808c039bb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h @@ -60,20 +60,18 @@ class LayoutNGBlockFlowMixin : public LayoutNGMixin<Base> { void SetPaintFragment(const NGBlockBreakToken*, scoped_refptr<const NGPhysicalFragment>) final; + using LayoutNGMixin<Base>::CurrentFragment; + protected: void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; - const NGPhysicalBoxFragment* CurrentFragment() const final; - void AddLayoutOverflowFromChildren() final; void AddOutlineRects(Vector<PhysicalRect>&, const PhysicalOffset& additional_offset, NGOutlineType) const final; - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final; - - base::Optional<LayoutUnit> FragmentBaseline(NGBaselineAlgorithmType) const; + base::Optional<LayoutUnit> FragmentBaseline() const; void DirtyLinesFromChangedChild(LayoutObject* child, MarkingBehavior marking_behavior) final; @@ -89,7 +87,7 @@ class LayoutNGBlockFlowMixin : public LayoutNGMixin<Base> { private: void AddScrollingOverflowFromChildren(); - void UpdateMargins(const NGConstraintSpace& space); + void UpdateMargins(); }; // If you edit these export templates, also update templates in diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc index a39b2b4dee0..907d45bb278 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc @@ -73,17 +73,4 @@ bool LayoutNGFieldset::IsOfType(LayoutObjectType type) const { return type == kLayoutObjectNGFieldset || LayoutNGBlockFlow::IsOfType(type); } -void LayoutNGFieldset::Paint(const PaintInfo& paint_info) const { - // TODO(crbug.com/988015): This override should not be needed when painting - // fragment is enabled in parent classes. - if (!RuntimeEnabledFeatures::LayoutNGFragmentPaintEnabled()) { - if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { - NGBoxFragmentPainter(*fragment, PaintFragment()).Paint(paint_info); - return; - } - } - - LayoutNGBlockFlow::Paint(paint_info); -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h index 4076cdbc70f..5178d12f26d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h @@ -21,14 +21,10 @@ class CORE_EXPORT LayoutNGFieldset final : public LayoutNGBlockFlow { bool CreatesNewFormattingContext() const final { return true; } - void Paint(const PaintInfo&) const final; - protected: bool IsOfType(LayoutObjectType) const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGFieldset, IsLayoutNGFieldset()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_FIELDSET_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc index dddadee04b5..a74fdfe086d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc @@ -17,6 +17,21 @@ namespace blink { LayoutNGFlexibleBox::LayoutNGFlexibleBox(Element* element) : LayoutNGMixin<LayoutBlock>(element) {} +bool LayoutNGFlexibleBox::HasTopOverflow() const { + if (IsHorizontalWritingMode()) + return StyleRef().ResolvedIsColumnReverseFlexDirection(); + return StyleRef().IsLeftToRightDirection() == + StyleRef().ResolvedIsRowReverseFlexDirection(); +} + +bool LayoutNGFlexibleBox::HasLeftOverflow() const { + if (IsHorizontalWritingMode()) { + return StyleRef().IsLeftToRightDirection() == + StyleRef().ResolvedIsRowReverseFlexDirection(); + } + return StyleRef().ResolvedIsColumnReverseFlexDirection(); +} + void LayoutNGFlexibleBox::UpdateBlockLayout(bool relayout_children) { LayoutAnalyzer::BlockScope analyzer(*this); @@ -25,16 +40,34 @@ void LayoutNGFlexibleBox::UpdateBlockLayout(bool relayout_children) { return; } - NGConstraintSpace constraint_space = - NGConstraintSpace::CreateFromLayoutObject( - *this, !View()->GetLayoutState()->Next() /* is_layout_root */); + UpdateInFlowBlockLayout(); +} + +namespace { + +void MergeAnonymousFlexItems(LayoutObject* remove_child) { + // When we remove a flex item, and the previous and next siblings of the item + // are text nodes wrapped in anonymous flex items, the adjacent text nodes + // need to be merged into the same flex item. + LayoutObject* prev = remove_child->PreviousSibling(); + if (!prev || !prev->IsAnonymousBlock()) + return; + LayoutObject* next = remove_child->NextSibling(); + if (!next || !next->IsAnonymousBlock()) + return; + ToLayoutBoxModelObject(next)->MoveAllChildrenTo(ToLayoutBoxModelObject(prev)); + To<LayoutBlockFlow>(next)->DeleteLineBoxTree(); + next->Destroy(); +} + +} // namespace - scoped_refptr<const NGLayoutResult> result = - NGBlockNode(this).Layout(constraint_space); +void LayoutNGFlexibleBox::RemoveChild(LayoutObject* child) { + if (!DocumentBeingDestroyed() && + !StyleRef().IsDeprecatedFlexboxUsingFlexLayout()) + MergeAnonymousFlexItems(child); - for (const auto& descendant : - result->PhysicalFragment().OutOfFlowPositionedDescendants()) - descendant.node.UseLegacyOutOfFlowPositioning(); + LayoutBlock::RemoveChild(child); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h index c4c0fcfd227..f1a361e1b56 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h @@ -15,6 +15,9 @@ class CORE_EXPORT LayoutNGFlexibleBox : public LayoutNGMixin<LayoutBlock> { public: explicit LayoutNGFlexibleBox(Element*); + bool HasTopOverflow() const override; + bool HasLeftOverflow() const override; + void UpdateBlockLayout(bool relayout_children) override; bool IsFlexibleBoxIncludingDeprecatedAndNG() const final { return true; } @@ -22,6 +25,8 @@ class CORE_EXPORT LayoutNGFlexibleBox : public LayoutNGMixin<LayoutBlock> { const char* GetName() const override { return "LayoutNGFlexibleBox"; } protected: + void RemoveChild(LayoutObject*) override; + bool IsOfType(LayoutObjectType type) const override { return type == kLayoutObjectNGFlexibleBox || LayoutNGMixin<LayoutBlock>::IsOfType(type); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc index bc31a16375c..89ad70133c1 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc @@ -11,9 +11,11 @@ #include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" +#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" namespace blink { @@ -30,27 +32,65 @@ template <typename Base> LayoutNGMixin<Base>::~LayoutNGMixin() = default; template <typename Base> +void LayoutNGMixin<Base>::Paint(const PaintInfo& paint_info) const { + // Avoid painting dirty objects because descendants maybe already destroyed. + if (UNLIKELY(Base::NeedsLayout() && + !Base::LayoutBlockedByDisplayLock( + DisplayLockLifecycleTarget::kChildren))) { + NOTREACHED(); + return; + } + + if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) + NGBoxFragmentPainter(*fragment).Paint(paint_info); +} + +template <typename Base> +bool LayoutNGMixin<Base>::NodeAtPoint(HitTestResult& result, + const HitTestLocation& hit_test_location, + const PhysicalOffset& accumulated_offset, + HitTestAction action) { + if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) { + DCHECK_EQ(Base::PhysicalFragmentCount(), 1u); + return NGBoxFragmentPainter(*fragment).NodeAtPoint( + result, hit_test_location, accumulated_offset, action); + } + + return false; +} + +// The current fragment from the last layout cycle for this box. +// When pre-NG layout calls functions of this block flow, fragment and/or +// LayoutResult are required to compute the result. +// TODO(kojii): Use the cached result for now, we may need to reconsider as the +// cache evolves. +template <typename Base> +const NGPhysicalBoxFragment* LayoutNGMixin<Base>::CurrentFragment() const { + const NGLayoutResult* cached_layout_result = Base::GetCachedLayoutResult(); + if (!cached_layout_result) + return nullptr; + + return &To<NGPhysicalBoxFragment>(cached_layout_result->PhysicalFragment()); +} + +template <typename Base> bool LayoutNGMixin<Base>::IsOfType(LayoutObject::LayoutObjectType type) const { return type == LayoutObject::kLayoutObjectNGMixin || Base::IsOfType(type); } template <typename Base> -void LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const { +MinMaxSizes LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths() const { NGBlockNode node(const_cast<LayoutNGMixin<Base>*>(this)); - if (!node.CanUseNewLayout()) { - Base::ComputeIntrinsicLogicalWidths(min_logical_width, max_logical_width); - return; - } + if (!node.CanUseNewLayout()) + return Base::ComputeIntrinsicLogicalWidths(); LayoutUnit available_logical_height = LayoutBoxUtils::AvailableLogicalHeight(*this, Base::ContainingBlock()); - MinMaxSizeInput input(available_logical_height); - // This function returns content-box plus scrollbar. - input.size_type = NGMinMaxSizeType::kContentBoxSize; - MinMaxSize sizes = - node.ComputeMinMaxSize(node.Style().GetWritingMode(), input); + + NGConstraintSpace space = ConstraintSpaceForMinMaxSizes(); + MinMaxSizes sizes = node.ComputeMinMaxSizes( + node.Style().GetWritingMode(), MinMaxSizesInput(available_logical_height), + &space); if (Base::IsTableCell()) { // If a table cell, or the column that it belongs to, has a specified fixed @@ -61,14 +101,34 @@ void LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths( Length table_cell_width = cell->StyleOrColLogicalWidth(); if (table_cell_width.IsFixed() && table_cell_width.Value() > 0) { sizes.max_size = std::max(sizes.min_size, - Base::AdjustContentBoxLogicalWidthForBoxSizing( + Base::AdjustBorderBoxLogicalWidthForBoxSizing( LayoutUnit(table_cell_width.Value()))); } } - sizes += LayoutUnit(Base::ScrollbarLogicalWidth()); - min_logical_width = sizes.min_size; - max_logical_width = sizes.max_size; + return sizes; +} + +template <typename Base> +NGConstraintSpace LayoutNGMixin<Base>::ConstraintSpaceForMinMaxSizes() const { + const ComputedStyle& style = Base::StyleRef(); + const WritingMode writing_mode = style.GetWritingMode(); + + NGConstraintSpaceBuilder builder(writing_mode, writing_mode, + /* is_new_fc */ true); + builder.SetTextDirection(style.Direction()); + builder.SetAvailableSize( + {Base::ContainingBlockLogicalWidthForContent(), kIndefiniteSize}); + + // Table cells borders may be collapsed, we can't calculate these directly + // from the style. + if (Base::IsTableCell()) { + builder.SetIsTableCell(true); + builder.SetTableCellBorders({Base::BorderStart(), Base::BorderEnd(), + Base::BorderBefore(), Base::BorderAfter()}); + } + + return builder.ToConstraintSpace(); } template <typename Base> @@ -79,8 +139,7 @@ void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() { : Base::ContainingBlock(); const ComputedStyle* container_style = container->Style(); NGConstraintSpace constraint_space = - NGConstraintSpace::CreateFromLayoutObject(*this, - false /* is_layout_root */); + NGConstraintSpace::CreateFromLayoutObject(*this); // As this is part of the Legacy->NG bridge, the container_builder is used // for indicating the resolved size of the OOF-positioned containing-block @@ -97,7 +156,7 @@ void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() { container_node.CreatesNewFormattingContext()); NGFragmentGeometry fragment_geometry; - fragment_geometry.border = ComputeBorders(constraint_space, container_node); + fragment_geometry.border = ComputeBorders(constraint_space, *container_style); fragment_geometry.scrollbar = ComputeScrollbars(constraint_space, container_node); fragment_geometry.padding = @@ -141,8 +200,9 @@ void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() { NGBlockNode(this), static_position, ToLayoutInlineOrNull(css_container)); base::Optional<LogicalSize> initial_containing_block_fixed_size; - if (container->IsLayoutView() && !Base::GetDocument().Printing()) { - if (LocalFrameView* frame_view = ToLayoutView(container)->GetFrameView()) { + auto* layout_view = DynamicTo<LayoutView>(container); + if (layout_view && !Base::GetDocument().Printing()) { + if (LocalFrameView* frame_view = layout_view->GetFrameView()) { IntSize size = frame_view->LayoutViewport()->ExcludeScrollbars(frame_view->Size()); PhysicalSize physical_size(size); @@ -190,6 +250,29 @@ void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() { Base::SetIsLegacyInitiatedOutOfFlowLayout(true); } +template <typename Base> +scoped_refptr<const NGLayoutResult> +LayoutNGMixin<Base>::UpdateInFlowBlockLayout() { + const auto* previous_result = Base::GetCachedLayoutResult(); + bool is_layout_root = !Base::View()->GetLayoutState()->Next(); + + // If we are a layout root, use the previous space if available. This will + // include any stretched sizes if applicable. + NGConstraintSpace constraint_space = + is_layout_root && previous_result + ? previous_result->GetConstraintSpaceForCaching() + : NGConstraintSpace::CreateFromLayoutObject(*this); + + scoped_refptr<const NGLayoutResult> result = + NGBlockNode(this).Layout(constraint_space); + + for (const auto& descendant : + result->PhysicalFragment().OutOfFlowPositionedDescendants()) + descendant.node.UseLegacyOutOfFlowPositioning(); + + return result; +} + template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlock>; template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlockFlow>; template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutProgress>; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h index e75f321437e..a6194724268 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h @@ -23,16 +23,25 @@ class LayoutNGMixin : public Base { explicit LayoutNGMixin(Element* element); ~LayoutNGMixin() override; + void Paint(const PaintInfo&) const override; + + bool NodeAtPoint(HitTestResult&, + const HitTestLocation&, + const PhysicalOffset& accumulated_offset, + HitTestAction) override; + bool IsLayoutNGObject() const final { return true; } + const NGPhysicalBoxFragment* CurrentFragment() const final; + protected: bool IsOfType(LayoutObject::LayoutObjectType) const override; - void ComputeIntrinsicLogicalWidths( - LayoutUnit& min_logical_width, - LayoutUnit& max_logical_width) const override; + MinMaxSizes ComputeIntrinsicLogicalWidths() const override; + NGConstraintSpace ConstraintSpaceForMinMaxSizes() const; void UpdateOutOfFlowBlockLayout(); + scoped_refptr<const NGLayoutResult> UpdateInFlowBlockLayout(); }; extern template class CORE_EXTERN_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlock>; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_progress.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_progress.h index 7ad4c0c18d7..86d6850fdd5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_progress.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_progress.h @@ -25,8 +25,6 @@ class CORE_EXPORT LayoutNGProgress bool IsOfType(LayoutObjectType type) const override; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGProgress, IsLayoutNGProgress()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_PROGRESS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc index 5d5418d2916..2a611c31d3a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc @@ -57,23 +57,9 @@ void LayoutNGTableCaption::UpdateBlockLayout(bool relayout_children) { DCHECK(!IsOutOfFlowPositioned()) << "Out of flow captions are blockified."; - NGConstraintSpace constraint_space = - NGConstraintSpace::CreateFromLayoutObject( - *this, !View()->GetLayoutState()->Next() /* is_layout_root */); - - scoped_refptr<const NGLayoutResult> result = - NGBlockNode(this).Layout(constraint_space); - - CalculateAndSetMargins(constraint_space, result->PhysicalFragment()); - - // Tell legacy layout there were abspos descendents we couldn't place. We know - // we have to pass up to legacy here because this method is legacy's entry - // point to LayoutNG. If our parent were LayoutNG, it wouldn't have called - // UpdateBlockLayout, it would have packaged this LayoutObject into - // NGBlockNode and called Layout on that. - for (const auto& descendant : - result->PhysicalFragment().OutOfFlowPositionedDescendants()) - descendant.node.UseLegacyOutOfFlowPositioning(); + scoped_refptr<const NGLayoutResult> result = UpdateInFlowBlockLayout(); + CalculateAndSetMargins(result->GetConstraintSpaceForCaching(), + result->PhysicalFragment()); // The parent table sometimes changes the caption's position after laying it // out. So there's no point in setting the fragment's offset here; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc index da93f774221..87eec6dff43 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc @@ -22,17 +22,7 @@ void LayoutNGTableCell::UpdateBlockLayout(bool relayout_children) { LayoutAnalyzer::BlockScope analyzer(*this); SetOverrideLogicalWidth(LogicalWidth()); - - NGConstraintSpace constraint_space = - NGConstraintSpace::CreateFromLayoutObject( - *this, !View()->GetLayoutState()->Next() /* is_layout_root */); - - scoped_refptr<const NGLayoutResult> result = - NGBlockNode(this).Layout(constraint_space); - - for (const auto& descendant : - result->PhysicalFragment().OutOfFlowPositionedDescendants()) - descendant.node.UseLegacyOutOfFlowPositioning(); + UpdateInFlowBlockLayout(); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/README.md b/chromium/third_party/blink/renderer/core/layout/ng/list/README.md index 9dbe472eaac..b8380c16919 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/README.md +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/README.md @@ -41,7 +41,7 @@ When the content is inline level and therefore generates line boxes: generates a box tree of: - LayoutNGListItem - - LayoutNGListMarker + - LayoutNGOutsideListMarker - LayoutText (1.) - LayoutText (sample text) @@ -56,7 +56,7 @@ When the content is block level: ``` - LayoutNGListItem - - LayoutNGListMarker + - LayoutNGOutsideListMarker - LayoutText (1.) - LayoutNGBlockFlow (div) - LayoutText (sample text) @@ -74,7 +74,7 @@ When the content is mixed: ``` - LayoutNGListItem - - LayoutNGListMarker + - LayoutNGOutsideListMarker - LayoutText (1.) - LayoutNGBlockFlow (anonymous) - LayoutText (inline text) @@ -134,7 +134,8 @@ and still easy to implement across implementations. [marker positioning]: https://drafts.csswg.org/css-lists-3/#positioning [LayoutNGListItem]: layout_ng_list_item.h -[LayoutNGListMarker]: layout_ng_list_marker.h +[LayoutNGInsideListMarker]: layout_ng_inside_list_marker.h +[LayoutNGOutsideListMarker]: layout_ng_outside_list_marker.h [NGBlockLayoutAlgorithm]: ../ng_block_layout_algorithm.h [NGInlineItem]: ../inline/ng_inline_item.h [NGInlineLayoutAlgorithm]: ../inline/ng_inline_layout_algorithm.h diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc index 5cdeb8d9124..626ba03e22b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc @@ -5,20 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h" #include "third_party/blink/renderer/core/layout/layout_text.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" namespace blink { LayoutNGInsideListMarker::LayoutNGInsideListMarker(Element* element) : LayoutInline(element) {} -LayoutNGInsideListMarker* LayoutNGInsideListMarker::CreateAnonymous( - Document* document) { - LayoutNGInsideListMarker* object = new LayoutNGInsideListMarker(nullptr); - object->SetDocumentForAnonymous(document); - return object; -} - bool LayoutNGInsideListMarker::IsOfType(LayoutObjectType type) const { return type == kLayoutObjectNGInsideListMarker || LayoutInline::IsOfType(type); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h index 0ce5f756adb..5702a72422c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h @@ -7,23 +7,24 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" +#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h" namespace blink { -class Document; - // A LayoutObject subclass for inside-positioned list markers in LayoutNG. class CORE_EXPORT LayoutNGInsideListMarker final : public LayoutInline { public: explicit LayoutNGInsideListMarker(Element*); - static LayoutNGInsideListMarker* CreateAnonymous(Document*); const char* GetName() const override { return "LayoutNGInsideListMarker"; } + const ListMarker& Marker() const { return list_marker_; } + ListMarker& Marker() { return list_marker_; } + #if DCHECK_IS_ON() void AddChild(LayoutObject* new_child, LayoutObject* before_child) override { - // Anonymous list marker should have at most one child. - DCHECK(GetNode() || !FirstChild()); + // List markers with 'content: normal' should have at most one child. + DCHECK(!StyleRef().ContentBehavesAsNormal() || !FirstChild()); LayoutInline::AddChild(new_child, before_child); } #endif @@ -31,6 +32,8 @@ class CORE_EXPORT LayoutNGInsideListMarker final : public LayoutInline { private: bool IsOfType(LayoutObjectType) const override; PositionWithAffinity PositionForPoint(const PhysicalOffset&) const override; + + ListMarker list_marker_; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGInsideListMarker, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc index 826c41c3da0..c3a6d3f0b9f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc @@ -4,21 +4,12 @@ #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/layout_image_resource_style_image.h" -#include "third_party/blink/renderer/core/layout/layout_inline.h" -#include "third_party/blink/renderer/core/layout/layout_list_marker.h" -#include "third_party/blink/renderer/core/layout/list_marker_text.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h" -#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" +#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h" namespace blink { LayoutNGListItem::LayoutNGListItem(Element* element) - : LayoutNGBlockFlow(element), - marker_type_(kStatic), - is_marker_text_updated_(false) { + : LayoutNGBlockFlow(element) { SetInline(false); SetConsumesSubtreeChangeNotification(); @@ -29,12 +20,6 @@ bool LayoutNGListItem::IsOfType(LayoutObjectType type) const { return type == kLayoutObjectNGListItem || LayoutNGBlockFlow::IsOfType(type); } -void LayoutNGListItem::WillBeDestroyed() { - DestroyMarker(); - - LayoutNGBlockFlow::WillBeDestroyed(); -} - void LayoutNGListItem::InsertedIntoTree() { LayoutNGBlockFlow::InsertedIntoTree(); @@ -51,200 +36,52 @@ void LayoutNGListItem::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { LayoutNGBlockFlow::StyleDidChange(diff, old_style); - UpdateMarker(); + LayoutObject* marker = Marker(); + ListMarker* list_marker = ListMarker::Get(marker); + if (!list_marker) + return; + + list_marker->UpdateMarkerContentIfNeeded(*marker); if (old_style && (old_style->ListStyleType() != StyleRef().ListStyleType() || (StyleRef().ListStyleType() == EListStyleType::kString && old_style->ListStyleStringValue() != StyleRef().ListStyleStringValue()))) - ListStyleTypeChanged(); -} - -// If the value of ListStyleType changed, we need to the marker text has been -// updated. -void LayoutNGListItem::ListStyleTypeChanged() { - if (!is_marker_text_updated_) - return; - - is_marker_text_updated_ = false; - if (marker_) { - marker_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - layout_invalidation_reason::kListStyleTypeChange); - } + list_marker->ListStyleTypeChanged(*marker); } void LayoutNGListItem::OrdinalValueChanged() { - if (marker_type_ == kOrdinalValue && is_marker_text_updated_) { - is_marker_text_updated_ = false; - - // |marker_| can be a nullptr, for example, in the case of :after list item - // elements. - if (marker_) { - marker_->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( - layout_invalidation_reason::kListValueChange); - } - } + LayoutObject* marker = Marker(); + if (ListMarker* list_marker = ListMarker::Get(marker)) + list_marker->OrdinalValueChanged(*marker); } void LayoutNGListItem::SubtreeDidChange() { - if (!marker_) - return; - - if (ordinal_.NotInListChanged()) { - UpdateMarker(); - ordinal_.SetNotInListChanged(false); + LayoutObject* marker = Marker(); + ListMarker* list_marker = ListMarker::Get(marker); + if (!list_marker) return; - } - // Make sure outside marker is the direct child of ListItem. - if (!IsInside() && marker_->Parent() != this) { - marker_->Remove(); - AddChild(marker_, FirstChild()); + // Make sure an outside marker is a direct child of the list item (not nested + // inside an anonymous box), and that a marker originated by a ::before or + // ::after precedes the generated contents. + if ((marker->IsLayoutNGOutsideListMarker() && marker->Parent() != this) || + (IsPseudoElement() && marker != FirstChild())) { + marker->Remove(); + AddChild(marker, FirstChild()); } - UpdateMarkerContentIfNeeded(); + list_marker->UpdateMarkerContentIfNeeded(*marker); } void LayoutNGListItem::WillCollectInlines() { UpdateMarkerTextIfNeeded(); } -// Returns true if this is 'list-style-position: inside', or should be laid out -// as 'inside'. -bool LayoutNGListItem::IsInside() const { - return ordinal_.NotInList() || - StyleRef().ListStylePosition() == EListStylePosition::kInside; -} - -// Destroy the list marker objects if exists. -void LayoutNGListItem::DestroyMarker() { - if (marker_) { - marker_->Destroy(); - marker_ = nullptr; - } -} - -void LayoutNGListItem::UpdateMarkerText(LayoutText* text) { - DCHECK(text); - StringBuilder marker_text_builder; - marker_type_ = MarkerText(&marker_text_builder, kWithSuffix); - text->SetTextIfNeeded(marker_text_builder.ToString().ReleaseImpl()); - is_marker_text_updated_ = true; -} - -void LayoutNGListItem::UpdateMarkerText() { - DCHECK(marker_); - UpdateMarkerText(ToLayoutText(marker_->SlowFirstChild())); -} - -void LayoutNGListItem::UpdateMarker() { - const ComputedStyle& style = StyleRef(); - if (style.ListStyleType() == EListStyleType::kNone && !IsMarkerImage()) { - DestroyMarker(); - marker_type_ = kStatic; - is_marker_text_updated_ = true; - return; - } - - // Create a marker box if it does not exist yet. - Node* list_item = GetNode(); - const ComputedStyle* cached_marker_style = - list_item->IsPseudoElement() - ? nullptr - : ToElement(list_item)->CachedStyleForPseudoElement(kPseudoIdMarker); - if (cached_marker_style && cached_marker_style->GetContentData()) { - // Don't create an anonymous layout for the marker, it will be generated - // by the ::marker pseudo-element. - DestroyMarker(); - marker_type_ = kStatic; - is_marker_text_updated_ = true; - return; - } - scoped_refptr<ComputedStyle> marker_style; - if (cached_marker_style) { - marker_style = ComputedStyle::Clone(*cached_marker_style); - } else { - marker_style = ComputedStyle::Create(); - marker_style->InheritFrom(style); - marker_style->SetStyleType(kPseudoIdMarker); - marker_style->SetUnicodeBidi(UnicodeBidi::kIsolate); - marker_style->SetFontVariantNumericSpacing( - FontVariantNumeric::kTabularNums); - } - if (IsInside()) { - if (marker_ && !marker_->IsLayoutInline()) - DestroyMarker(); - if (!marker_) - marker_ = LayoutNGInsideListMarker::CreateAnonymous(&GetDocument()); - marker_style->SetDisplay(EDisplay::kInline); - auto margins = - LayoutListMarker::InlineMarginsForInside(style, IsMarkerImage()); - marker_style->SetMarginStart(Length::Fixed(margins.first)); - marker_style->SetMarginEnd(Length::Fixed(margins.second)); - } else { - if (marker_ && !marker_->IsLayoutBlockFlow()) - DestroyMarker(); - if (!marker_) - marker_ = LayoutNGListMarker::CreateAnonymous(&GetDocument()); - marker_style->SetDisplay(EDisplay::kInlineBlock); - // Do not break inside the marker, and honor the trailing spaces. - marker_style->SetWhiteSpace(EWhiteSpace::kPre); - // Compute margins for 'outside' during layout, because it requires the - // layout size of the marker. - // TODO(kojii): absolute position looks more reasonable, and maybe required - // in some cases, but this is currently blocked by crbug.com/734554 - // marker_style->SetPosition(EPosition::kAbsolute); - // marker_->SetPositionState(EPosition::kAbsolute); - } - marker_->SetStyle(std::move(marker_style)); - - UpdateMarkerContentIfNeeded(); - - LayoutObject* first_child = FirstChild(); - if (first_child != marker_) { - marker_->Remove(); - AddChild(marker_, FirstChild()); - } -} - -LayoutNGListItem* LayoutNGListItem::FromMarker(const LayoutObject& marker) { - DCHECK(marker.IsLayoutNGListMarkerIncludingInside()); - for (LayoutObject* parent = marker.Parent(); parent; - parent = parent->Parent()) { - if (parent->IsLayoutNGListItem()) { -#if DCHECK_IS_ON() - LayoutObject* parent_marker = ToLayoutNGListItem(parent)->Marker(); - if (parent_marker) { - DCHECK(!marker.GetNode()); - DCHECK_EQ(ToLayoutNGListItem(parent)->Marker(), &marker); - } else { - DCHECK(marker.GetNode()->IsMarkerPseudoElement()); - DCHECK_EQ(marker.GetNode()->parentElement()->GetLayoutBox(), parent); - } -#endif - return ToLayoutNGListItem(parent); - } - // These DCHECKs are not critical but to ensure we cover all cases we know. - DCHECK(parent->IsAnonymous()); - DCHECK(parent->IsLayoutBlockFlow() || parent->IsLayoutFlowThread()); - } - return nullptr; -} - -LayoutNGListItem* LayoutNGListItem::FromMarkerOrMarkerContent( - const LayoutObject& object) { - DCHECK(object.IsAnonymous()); - - if (object.IsLayoutNGListMarkerIncludingInside()) - return FromMarker(object); - - // Check if this is a marker content. - if (const LayoutObject* parent = object.Parent()) { - if (parent->IsLayoutNGListMarkerIncludingInside()) - return FromMarker(*parent); - } - - return nullptr; +void LayoutNGListItem::UpdateMarkerTextIfNeeded() { + LayoutObject* marker = Marker(); + if (ListMarker* list_marker = ListMarker::Get(marker)) + list_marker->UpdateMarkerTextIfNeeded(*marker); } int LayoutNGListItem::Value() const { @@ -252,190 +89,16 @@ int LayoutNGListItem::Value() const { return ordinal_.Value(*GetNode()); } -LayoutNGListItem::MarkerType LayoutNGListItem::MarkerText( - StringBuilder* text, - MarkerTextFormat format) const { - if (IsMarkerImage()) { - if (format == kWithSuffix) - text->Append(' '); - return kStatic; - } - - const ComputedStyle& style = StyleRef(); - switch (style.ListStyleType()) { - case EListStyleType::kNone: - return kStatic; - case EListStyleType::kString: { - text->Append(style.ListStyleStringValue()); - return kStatic; - } - case EListStyleType::kDisc: - case EListStyleType::kCircle: - case EListStyleType::kSquare: - // value is ignored for these types - text->Append(list_marker_text::GetText(style.ListStyleType(), 0)); - if (format == kWithSuffix) - text->Append(' '); - return kSymbolValue; - case EListStyleType::kArabicIndic: - case EListStyleType::kArmenian: - case EListStyleType::kBengali: - case EListStyleType::kCambodian: - case EListStyleType::kCjkIdeographic: - case EListStyleType::kCjkEarthlyBranch: - case EListStyleType::kCjkHeavenlyStem: - case EListStyleType::kDecimalLeadingZero: - case EListStyleType::kDecimal: - case EListStyleType::kDevanagari: - case EListStyleType::kEthiopicHalehame: - case EListStyleType::kEthiopicHalehameAm: - case EListStyleType::kEthiopicHalehameTiEr: - case EListStyleType::kEthiopicHalehameTiEt: - case EListStyleType::kGeorgian: - case EListStyleType::kGujarati: - case EListStyleType::kGurmukhi: - case EListStyleType::kHangul: - case EListStyleType::kHangulConsonant: - case EListStyleType::kHebrew: - case EListStyleType::kHiragana: - case EListStyleType::kHiraganaIroha: - case EListStyleType::kKannada: - case EListStyleType::kKatakana: - case EListStyleType::kKatakanaIroha: - case EListStyleType::kKhmer: - case EListStyleType::kKoreanHangulFormal: - case EListStyleType::kKoreanHanjaFormal: - case EListStyleType::kKoreanHanjaInformal: - case EListStyleType::kLao: - case EListStyleType::kLowerAlpha: - case EListStyleType::kLowerArmenian: - case EListStyleType::kLowerGreek: - case EListStyleType::kLowerLatin: - case EListStyleType::kLowerRoman: - case EListStyleType::kMalayalam: - case EListStyleType::kMongolian: - case EListStyleType::kMyanmar: - case EListStyleType::kOriya: - case EListStyleType::kPersian: - case EListStyleType::kSimpChineseFormal: - case EListStyleType::kSimpChineseInformal: - case EListStyleType::kTelugu: - case EListStyleType::kThai: - case EListStyleType::kTibetan: - case EListStyleType::kTradChineseFormal: - case EListStyleType::kTradChineseInformal: - case EListStyleType::kUpperAlpha: - case EListStyleType::kUpperArmenian: - case EListStyleType::kUpperLatin: - case EListStyleType::kUpperRoman: - case EListStyleType::kUrdu: { - int value = Value(); - text->Append(list_marker_text::GetText(style.ListStyleType(), value)); - if (format == kWithSuffix) { - text->Append(list_marker_text::Suffix(style.ListStyleType(), value)); - text->Append(' '); - } - return kOrdinalValue; - } - } - NOTREACHED(); - return kStatic; -} - -String LayoutNGListItem::MarkerTextWithSuffix() const { - StringBuilder text; - MarkerText(&text, kWithSuffix); - return text.ToString(); -} - -String LayoutNGListItem::MarkerTextWithoutSuffix() const { - StringBuilder text; - MarkerText(&text, kWithoutSuffix); - return text.ToString(); -} - -String LayoutNGListItem::TextAlternative(const LayoutObject& marker) { - // For accessibility, return the marker string in the logical order even in - // RTL, reflecting speech order. - if (LayoutNGListItem* list_item = FromMarker(marker)) - return list_item->MarkerTextWithSuffix(); - return g_empty_string; -} - -void LayoutNGListItem::UpdateMarkerContentIfNeeded() { - DCHECK(marker_); - - LayoutObject* child = marker_->SlowFirstChild(); - // There should be at most one child. - DCHECK(!child || !child->SlowFirstChild()); - if (IsMarkerImage()) { - StyleImage* list_style_image = StyleRef().ListStyleImage(); - if (child) { - // If the url of `list-style-image` changed, create a new LayoutImage. - if (!child->IsLayoutImage() || - ToLayoutImage(child)->ImageResource()->ImagePtr() != - list_style_image->Data()) { - child->Destroy(); - child = nullptr; - } - } - if (!child) { - LayoutNGListMarkerImage* image = - LayoutNGListMarkerImage::CreateAnonymous(&GetDocument()); - scoped_refptr<ComputedStyle> image_style = - ComputedStyle::CreateAnonymousStyleWithDisplay(marker_->StyleRef(), - EDisplay::kInline); - image->SetStyle(image_style); - image->SetImageResource( - MakeGarbageCollected<LayoutImageResourceStyleImage>( - list_style_image)); - image->SetIsGeneratedContent(); - marker_->AddChild(image); - } - } else { - // Create a LayoutText in it. - LayoutText* text = nullptr; - // |text_style| should be as same as style propagated in - // |LayoutObject::PropagateStyleToAnonymousChildren()| to avoid unexpected - // full layout due by style difference. See http://crbug.com/980399 - scoped_refptr<ComputedStyle> text_style = - ComputedStyle::CreateAnonymousStyleWithDisplay( - marker_->StyleRef(), marker_->StyleRef().Display()); - if (child) { - if (child->IsText()) { - text = ToLayoutText(child); - text->SetStyle(text_style); - } else { - child->Destroy(); - child = nullptr; - } - } - if (!child) { - text = LayoutText::CreateEmptyAnonymous(GetDocument(), text_style, - LegacyLayout::kAuto); - marker_->AddChild(text); - is_marker_text_updated_ = false; - } - } -} - -LayoutObject* LayoutNGListItem::SymbolMarkerLayoutText() const { - if (marker_type_ != kSymbolValue) - return nullptr; - DCHECK(marker_); - return marker_->SlowFirstChild(); -} - const LayoutObject* LayoutNGListItem::FindSymbolMarkerLayoutText( const LayoutObject* object) { if (!object) return nullptr; - if (object->IsLayoutNGListItem()) - return ToLayoutNGListItem(object)->SymbolMarkerLayoutText(); + if (const ListMarker* list_marker = ListMarker::Get(object)) + return list_marker->SymbolMarkerLayoutText(*object); - if (object->IsLayoutNGListMarker()) - return ToLayoutNGListMarker(object)->SymbolMarkerLayoutText(); + if (object->IsLayoutNGListItem()) + return FindSymbolMarkerLayoutText(ToLayoutNGListItem(object)->Marker()); if (object->IsAnonymousBlock()) return FindSymbolMarkerLayoutText(object->Parent()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h index d6fd7b52ee1..c236130315a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h @@ -19,62 +19,30 @@ class CORE_EXPORT LayoutNGListItem final : public LayoutNGBlockFlow { ListItemOrdinal& Ordinal() { return ordinal_; } int Value() const; - String MarkerTextWithSuffix() const; - String MarkerTextWithoutSuffix() const; - // Marker text with suffix, e.g. "1. ", for use in accessibility. - static String TextAlternative(const LayoutObject& marker); - - LayoutObject* Marker() const { return marker_; } - bool IsMarkerImage() const { - return StyleRef().ListStyleImage() && - !StyleRef().ListStyleImage()->ErrorOccurred(); + LayoutObject* Marker() const { + Element* list_item = To<Element>(GetNode()); + return list_item->PseudoElementLayoutObject(kPseudoIdMarker); } - void UpdateMarkerTextIfNeeded() { - if (marker_ && !is_marker_text_updated_ && !IsMarkerImage()) - UpdateMarkerText(); - } - void UpdateMarkerContentIfNeeded(); + void UpdateMarkerTextIfNeeded(); void OrdinalValueChanged(); void WillCollectInlines() override; - LayoutObject* SymbolMarkerLayoutText() const; static const LayoutObject* FindSymbolMarkerLayoutText(const LayoutObject*); - // Find the LayoutNGListItem from a marker. - static LayoutNGListItem* FromMarker(const LayoutObject& marker); - static LayoutNGListItem* FromMarkerOrMarkerContent(const LayoutObject&); - const char* GetName() const override { return "LayoutNGListItem"; } private: bool IsOfType(LayoutObjectType) const override; - void WillBeDestroyed() override; void InsertedIntoTree() override; void WillBeRemovedFromTree() override; void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void SubtreeDidChange() final; - bool IsInside() const; - - enum MarkerTextFormat { kWithSuffix, kWithoutSuffix }; - enum MarkerType { kStatic, kOrdinalValue, kSymbolValue }; - MarkerType MarkerText(StringBuilder*, MarkerTextFormat) const; - void UpdateMarkerText(); - void UpdateMarkerText(LayoutText*); - void UpdateMarker(); - void DestroyMarker(); - - void ListStyleTypeChanged(); - ListItemOrdinal ordinal_; - LayoutObject* marker_ = nullptr; - - unsigned marker_type_ : 2; // MarkerType - unsigned is_marker_text_updated_ : 1; }; DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGListItem, IsLayoutNGListItem()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc deleted file mode 100644 index a477149ad5d..00000000000 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" - -#include "third_party/blink/renderer/core/layout/layout_text.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" - -namespace blink { - -LayoutNGListMarker::LayoutNGListMarker(Element* element) - : LayoutNGBlockFlowMixin<LayoutBlockFlow>(element) {} - -LayoutNGListMarker* LayoutNGListMarker::CreateAnonymous(Document* document) { - LayoutNGListMarker* object = new LayoutNGListMarker(nullptr); - object->SetDocumentForAnonymous(document); - return object; -} - -bool LayoutNGListMarker::IsOfType(LayoutObjectType type) const { - return type == kLayoutObjectNGListMarker || - LayoutNGMixin<LayoutBlockFlow>::IsOfType(type); -} - -void LayoutNGListMarker::WillCollectInlines() { - if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this)) - list_item->UpdateMarkerTextIfNeeded(); -} - -bool LayoutNGListMarker::IsContentImage() const { - if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this)) - return list_item->IsMarkerImage(); - return false; -} - -LayoutObject* LayoutNGListMarker::SymbolMarkerLayoutText() const { - if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this)) - return list_item->SymbolMarkerLayoutText(); - return nullptr; -} - -bool LayoutNGListMarker::NeedsOccupyWholeLine() const { - if (!GetDocument().InQuirksMode()) - return false; - - LayoutObject* next_sibling = NextSibling(); - if (next_sibling && next_sibling->GetNode() && - (IsA<HTMLUListElement>(*next_sibling->GetNode()) || - IsA<HTMLOListElement>(*next_sibling->GetNode()))) - return true; - - return false; -} - -PositionWithAffinity LayoutNGListMarker::PositionForPoint( - const PhysicalOffset&) const { - return CreatePositionWithAffinity(0); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc index 65a6f555531..0ff22412114 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h" + #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" #include "third_party/blink/renderer/core/svg/graphics/svg_image.h" namespace blink { @@ -22,21 +24,6 @@ bool LayoutNGListMarkerImage::IsOfType(LayoutObjectType type) const { return type == kLayoutObjectNGListMarkerImage || LayoutImage::IsOfType(type); } -Node* LayoutNGListMarkerImage::NodeForHitTest() const { - // In LayoutNG tree, image list marker is structured like this: - // <li> (LayoutListItem) - // <anonymous block> (LayoutNGListMarker or LayoutNGInsideListMarker) - // <anonymous img> (LayoutNGListMarkerImage) - // Hit testing should return the list-item node. - DCHECK(!GetNode()); - for (const LayoutObject* parent = Parent(); parent; - parent = parent->Parent()) { - if (Node* node = parent->GetNode()) - return node; - } - return nullptr; -} - // Because ImageResource() is always LayoutImageResourceStyleImage. So we could // use StyleImage::ImageSize to determine the concrete object size with // default object size(ascent/2 x ascent/2). diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h index 6ebae9a91d7..86bdb5ec592 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h @@ -19,8 +19,6 @@ class CORE_EXPORT LayoutNGListMarkerImage final : public LayoutImage { bool IsLayoutNGObject() const override { return true; } - Node* NodeForHitTest() const final; - private: bool IsOfType(LayoutObjectType) const override; @@ -28,9 +26,6 @@ class CORE_EXPORT LayoutNGListMarkerImage final : public LayoutImage { void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const final; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGListMarkerImage, - IsLayoutNGListMarkerImage()); - } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_LIST_MARKER_IMAGE_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.cc new file mode 100644 index 00000000000..4507904c341 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.cc @@ -0,0 +1,41 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" + +#include "third_party/blink/renderer/core/layout/layout_text.h" + +namespace blink { + +LayoutNGOutsideListMarker::LayoutNGOutsideListMarker(Element* element) + : LayoutNGBlockFlowMixin<LayoutBlockFlow>(element) {} + +bool LayoutNGOutsideListMarker::IsOfType(LayoutObjectType type) const { + return type == kLayoutObjectNGOutsideListMarker || + LayoutNGMixin<LayoutBlockFlow>::IsOfType(type); +} + +void LayoutNGOutsideListMarker::WillCollectInlines() { + list_marker_.UpdateMarkerTextIfNeeded(*this); +} + +bool LayoutNGOutsideListMarker::NeedsOccupyWholeLine() const { + if (!GetDocument().InQuirksMode()) + return false; + + LayoutObject* next_sibling = NextSibling(); + if (next_sibling && next_sibling->GetNode() && + (IsA<HTMLUListElement>(*next_sibling->GetNode()) || + IsA<HTMLOListElement>(*next_sibling->GetNode()))) + return true; + + return false; +} + +PositionWithAffinity LayoutNGOutsideListMarker::PositionForPoint( + const PhysicalOffset&) const { + return CreatePositionWithAffinity(0); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h index d317122a3a8..3b30f46349b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h @@ -2,41 +2,41 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_LIST_MARKER_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_LIST_MARKER_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_OUTSIDE_LIST_MARKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_OUTSIDE_LIST_MARKER_H_ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h" +#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h" namespace blink { -class Document; - // A LayoutObject subclass for outside-positioned list markers in LayoutNG. -class CORE_EXPORT LayoutNGListMarker final +class CORE_EXPORT LayoutNGOutsideListMarker final : public LayoutNGBlockFlowMixin<LayoutBlockFlow> { public: - explicit LayoutNGListMarker(Element*); - static LayoutNGListMarker* CreateAnonymous(Document*); + explicit LayoutNGOutsideListMarker(Element*); void WillCollectInlines() override; - bool IsContentImage() const; - - LayoutObject* SymbolMarkerLayoutText() const; - - const char* GetName() const override { return "LayoutNGListMarker"; } + const char* GetName() const override { return "LayoutNGOutsideListMarker"; } bool NeedsOccupyWholeLine() const; + const ListMarker& Marker() const { return list_marker_; } + ListMarker& Marker() { return list_marker_; } + private: bool IsOfType(LayoutObjectType) const override; PositionWithAffinity PositionForPoint(const PhysicalOffset&) const override; + + ListMarker list_marker_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGListMarker, IsLayoutNGListMarker()); +DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGOutsideListMarker, + IsLayoutNGOutsideListMarker()); } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_LIST_MARKER_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_OUTSIDE_LIST_MARKER_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.cc new file mode 100644 index 00000000000..b2533d42d7a --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.cc @@ -0,0 +1,256 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h" + +#include "third_party/blink/renderer/core/layout/layout_image_resource_style_image.h" +#include "third_party/blink/renderer/core/layout/list_marker_text.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" + +namespace blink { + +ListMarker::ListMarker() : marker_text_type_(kNotText) {} + +const ListMarker* ListMarker::Get(const LayoutObject* object) { + if (!object) + return nullptr; + if (object->IsLayoutNGOutsideListMarker()) + return &ToLayoutNGOutsideListMarker(object)->Marker(); + if (object->IsLayoutNGInsideListMarker()) + return &ToLayoutNGInsideListMarker(object)->Marker(); + return nullptr; +} + +ListMarker* ListMarker::Get(LayoutObject* object) { + return const_cast<ListMarker*>( + ListMarker::Get(static_cast<const LayoutObject*>(object))); +} + +// If the value of ListStyleType changed, we need to the marker text has been +// updated. +void ListMarker::ListStyleTypeChanged(LayoutObject& marker) { + if (marker_text_type_ == kNotText || marker_text_type_ == kUnresolved) + return; + + marker_text_type_ = kUnresolved; + marker.SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( + layout_invalidation_reason::kListStyleTypeChange); +} + +void ListMarker::OrdinalValueChanged(LayoutObject& marker) { + if (marker_text_type_ == kOrdinalValue) { + marker_text_type_ = kUnresolved; + marker.SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation( + layout_invalidation_reason::kListValueChange); + } +} + +void ListMarker::UpdateMarkerText(LayoutObject& marker, LayoutText* text) { + DCHECK(text); + DCHECK_EQ(marker_text_type_, kUnresolved); + StringBuilder marker_text_builder; + marker_text_type_ = MarkerText(marker, &marker_text_builder, kWithSuffix); + text->SetTextIfNeeded(marker_text_builder.ToString().ReleaseImpl()); + DCHECK_NE(marker_text_type_, kNotText); + DCHECK_NE(marker_text_type_, kUnresolved); +} + +void ListMarker::UpdateMarkerText(LayoutObject& marker) { + UpdateMarkerText(marker, ToLayoutText(marker.SlowFirstChild())); +} + +LayoutNGListItem* ListMarker::ListItem(const LayoutObject& marker) { + return ToLayoutNGListItem(marker.GetNode()->parentNode()->GetLayoutObject()); +} + +ListMarker::MarkerTextType ListMarker::MarkerText( + const LayoutObject& marker, + StringBuilder* text, + MarkerTextFormat format) const { + if (IsMarkerImage(marker)) { + if (format == kWithSuffix) + text->Append(' '); + return kNotText; + } + + LayoutNGListItem* list_item = ListItem(marker); + const ComputedStyle& style = list_item->StyleRef(); + switch (style.ListStyleType()) { + case EListStyleType::kNone: + return kNotText; + case EListStyleType::kString: { + text->Append(style.ListStyleStringValue()); + return kStatic; + } + case EListStyleType::kDisc: + case EListStyleType::kCircle: + case EListStyleType::kSquare: + // value is ignored for these types + text->Append(list_marker_text::GetText(style.ListStyleType(), 0)); + if (format == kWithSuffix) + text->Append(' '); + return kSymbolValue; + case EListStyleType::kArabicIndic: + case EListStyleType::kArmenian: + case EListStyleType::kBengali: + case EListStyleType::kCambodian: + case EListStyleType::kCjkIdeographic: + case EListStyleType::kCjkEarthlyBranch: + case EListStyleType::kCjkHeavenlyStem: + case EListStyleType::kDecimalLeadingZero: + case EListStyleType::kDecimal: + case EListStyleType::kDevanagari: + case EListStyleType::kEthiopicHalehame: + case EListStyleType::kEthiopicHalehameAm: + case EListStyleType::kEthiopicHalehameTiEr: + case EListStyleType::kEthiopicHalehameTiEt: + case EListStyleType::kGeorgian: + case EListStyleType::kGujarati: + case EListStyleType::kGurmukhi: + case EListStyleType::kHangul: + case EListStyleType::kHangulConsonant: + case EListStyleType::kHebrew: + case EListStyleType::kHiragana: + case EListStyleType::kHiraganaIroha: + case EListStyleType::kKannada: + case EListStyleType::kKatakana: + case EListStyleType::kKatakanaIroha: + case EListStyleType::kKhmer: + case EListStyleType::kKoreanHangulFormal: + case EListStyleType::kKoreanHanjaFormal: + case EListStyleType::kKoreanHanjaInformal: + case EListStyleType::kLao: + case EListStyleType::kLowerAlpha: + case EListStyleType::kLowerArmenian: + case EListStyleType::kLowerGreek: + case EListStyleType::kLowerLatin: + case EListStyleType::kLowerRoman: + case EListStyleType::kMalayalam: + case EListStyleType::kMongolian: + case EListStyleType::kMyanmar: + case EListStyleType::kOriya: + case EListStyleType::kPersian: + case EListStyleType::kSimpChineseFormal: + case EListStyleType::kSimpChineseInformal: + case EListStyleType::kTelugu: + case EListStyleType::kThai: + case EListStyleType::kTibetan: + case EListStyleType::kTradChineseFormal: + case EListStyleType::kTradChineseInformal: + case EListStyleType::kUpperAlpha: + case EListStyleType::kUpperArmenian: + case EListStyleType::kUpperLatin: + case EListStyleType::kUpperRoman: + case EListStyleType::kUrdu: { + int value = list_item->Value(); + text->Append(list_marker_text::GetText(style.ListStyleType(), value)); + if (format == kWithSuffix) { + text->Append(list_marker_text::Suffix(style.ListStyleType(), value)); + text->Append(' '); + } + return kOrdinalValue; + } + } + NOTREACHED(); + return kStatic; +} + +String ListMarker::MarkerTextWithSuffix(const LayoutObject& marker) const { + StringBuilder text; + MarkerText(marker, &text, kWithSuffix); + return text.ToString(); +} + +String ListMarker::MarkerTextWithoutSuffix(const LayoutObject& marker) const { + StringBuilder text; + MarkerText(marker, &text, kWithoutSuffix); + return text.ToString(); +} + +String ListMarker::TextAlternative(const LayoutObject& marker) const { + // For accessibility, return the marker string in the logical order even in + // RTL, reflecting speech order. + return MarkerTextWithSuffix(marker); +} + +void ListMarker::UpdateMarkerContentIfNeeded(LayoutObject& marker) { + LayoutNGListItem* list_item = ListItem(marker); + + if (!marker.StyleRef().ContentBehavesAsNormal()) { + marker_text_type_ = kNotText; + return; + } + + // There should be at most one child. + LayoutObject* child = marker.SlowFirstChild(); + DCHECK(!child || !child->NextSibling()); + + if (IsMarkerImage(marker)) { + StyleImage* list_style_image = list_item->StyleRef().ListStyleImage(); + if (child) { + // If the url of `list-style-image` changed, create a new LayoutImage. + if (!child->IsLayoutImage() || + ToLayoutImage(child)->ImageResource()->ImagePtr() != + list_style_image->Data()) { + child->Destroy(); + child = nullptr; + } + } + if (!child) { + LayoutNGListMarkerImage* image = + LayoutNGListMarkerImage::CreateAnonymous(&marker.GetDocument()); + scoped_refptr<ComputedStyle> image_style = + ComputedStyle::CreateAnonymousStyleWithDisplay(marker.StyleRef(), + EDisplay::kInline); + image->SetStyle(image_style); + image->SetImageResource( + MakeGarbageCollected<LayoutImageResourceStyleImage>( + list_style_image)); + image->SetIsGeneratedContent(); + marker.AddChild(image); + } + marker_text_type_ = kNotText; + return; + } + + if (list_item->StyleRef().ListStyleType() == EListStyleType::kNone) { + marker_text_type_ = kNotText; + return; + } + + // Create a LayoutText in it. + LayoutText* text = nullptr; + // |text_style| should be as same as style propagated in + // |LayoutObject::PropagateStyleToAnonymousChildren()| to avoid unexpected + // full layout due by style difference. See http://crbug.com/980399 + scoped_refptr<ComputedStyle> text_style = + ComputedStyle::CreateAnonymousStyleWithDisplay( + marker.StyleRef(), marker.StyleRef().Display()); + if (child) { + if (child->IsText()) { + text = ToLayoutText(child); + text->SetStyle(text_style); + } else { + child->Destroy(); + child = nullptr; + } + } + if (!child) { + text = LayoutText::CreateEmptyAnonymous(marker.GetDocument(), text_style, + LegacyLayout::kAuto); + marker.AddChild(text); + marker_text_type_ = kUnresolved; + } +} + +LayoutObject* ListMarker::SymbolMarkerLayoutText( + const LayoutObject& marker) const { + if (marker_text_type_ != kSymbolValue) + return nullptr; + return marker.SlowFirstChild(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.h new file mode 100644 index 00000000000..0ecf1844689 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.h @@ -0,0 +1,71 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LIST_MARKER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LIST_MARKER_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" + +namespace blink { + +// This class holds code shared among LayoutNG classes for list markers. +class CORE_EXPORT ListMarker { + friend class LayoutNGListItem; + + public: + explicit ListMarker(); + + static const ListMarker* Get(const LayoutObject*); + static ListMarker* Get(LayoutObject*); + + static LayoutNGListItem* ListItem(const LayoutObject&); + + String MarkerTextWithSuffix(const LayoutObject&) const; + String MarkerTextWithoutSuffix(const LayoutObject&) const; + + // Marker text with suffix, e.g. "1. ", for use in accessibility. + String TextAlternative(const LayoutObject&) const; + + static bool IsMarkerImage(const LayoutObject& marker) { + return ListItem(marker)->StyleRef().GeneratesMarkerImage(); + } + + void UpdateMarkerTextIfNeeded(LayoutObject& marker) { + if (marker_text_type_ == kUnresolved) + UpdateMarkerText(marker); + } + void UpdateMarkerContentIfNeeded(LayoutObject&); + + void OrdinalValueChanged(LayoutObject&); + + LayoutObject* SymbolMarkerLayoutText(const LayoutObject&) const; + + private: + enum MarkerTextFormat { kWithSuffix, kWithoutSuffix }; + enum MarkerTextType { + kNotText, // The marker doesn't have a LayoutText, either because it has + // not been created yet or because 'list-style-type' is 'none', + // 'list-style-image' is not 'none', or 'content' is not + // 'normal'. + kUnresolved, // The marker has a LayoutText that needs to be updated. + kOrdinalValue, // The marker text depends on the ordinal. + kStatic, // The marker text doesn't depend on the ordinal. + kSymbolValue, // Like kStatic, but the marker is painted as a symbol. + }; + MarkerTextType MarkerText(const LayoutObject&, + StringBuilder*, + MarkerTextFormat) const; + void UpdateMarkerText(LayoutObject&); + void UpdateMarkerText(LayoutObject&, LayoutText*); + + void ListStyleTypeChanged(LayoutObject&); + + unsigned marker_text_type_ : 3; // MarkerTextType +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LIST_MARKER_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc index 710824388d4..e0e08726a1f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc @@ -7,7 +7,7 @@ #include "third_party/blink/renderer/core/layout/layout_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.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/layout_ng_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" @@ -15,16 +15,18 @@ namespace blink { -NGUnpositionedListMarker::NGUnpositionedListMarker(LayoutNGListMarker* marker) +NGUnpositionedListMarker::NGUnpositionedListMarker( + LayoutNGOutsideListMarker* marker) : marker_layout_object_(marker) {} NGUnpositionedListMarker::NGUnpositionedListMarker(const NGBlockNode& node) - : NGUnpositionedListMarker(ToLayoutNGListMarker(node.GetLayoutBox())) {} + : NGUnpositionedListMarker( + ToLayoutNGOutsideListMarker(node.GetLayoutBox())) {} // Returns true if this is an image marker. bool NGUnpositionedListMarker::IsImage() const { DCHECK(marker_layout_object_); - return marker_layout_object_->IsContentImage(); + return marker_layout_object_->Marker().IsMarkerImage(*marker_layout_object_); } // Compute the inline offset of the marker, relative to the list item. @@ -45,24 +47,21 @@ scoped_refptr<const NGLayoutResult> NGUnpositionedListMarker::Layout( FontBaseline baseline_type) const { DCHECK(marker_layout_object_); NGBlockNode marker_node(marker_layout_object_); + + // We need the first-line baseline from the list-marker, instead of the + // typical atomic-inline baseline. scoped_refptr<const NGLayoutResult> marker_layout_result = - marker_node.LayoutAtomicInline(parent_space, parent_style, baseline_type, - parent_space.UseFirstLineStyle()); + marker_node.LayoutAtomicInline(parent_space, parent_style, + parent_space.UseFirstLineStyle(), + NGBaselineAlgorithmType::kFirstLine); DCHECK(marker_layout_result); return marker_layout_result; } -bool NGUnpositionedListMarker::CanAddToBox( +base::Optional<LayoutUnit> NGUnpositionedListMarker::ContentAlignmentBaseline( const NGConstraintSpace& space, FontBaseline baseline_type, - const NGPhysicalFragment& content, - NGLineHeightMetrics* content_metrics) const { - DCHECK(content_metrics); - - // Baselines from two different writing-mode cannot be aligned. - if (UNLIKELY(space.GetWritingMode() != content.Style().GetWritingMode())) - return false; - + const NGPhysicalFragment& content) const { // Compute the baseline of the child content. if (content.IsLineBox()) { const auto& line_box = To<NGPhysicalLineBoxFragment>(content); @@ -71,22 +70,17 @@ bool NGUnpositionedListMarker::CanAddToBox( // with the next non-empty line box produced. (This can occur with floats // producing empty line-boxes). if (line_box.IsEmptyLineBox() && !line_box.BreakToken()->IsFinished()) - return false; + return base::nullopt; - *content_metrics = line_box.Metrics(); - } else { - NGBoxFragment content_fragment(space.GetWritingMode(), space.Direction(), - To<NGPhysicalBoxFragment>(content)); - *content_metrics = content_fragment.BaselineMetricsWithoutSynthesize( - {NGBaselineAlgorithmType::kFirstLine, baseline_type}); - - // If this child content does not have any line boxes, the list marker - // should be aligned to the first line box of next child. - // https://github.com/w3c/csswg-drafts/issues/2417 - if (content_metrics->IsEmpty()) - return false; + return line_box.Metrics().ascent; } - return true; + + // If this child content does not have any line boxes, the list marker + // should be aligned to the first line box of next child. + // https://github.com/w3c/csswg-drafts/issues/2417 + return NGBoxFragment(space.GetWritingMode(), space.Direction(), + To<NGPhysicalBoxFragment>(content)) + .FirstBaseline(); } void NGUnpositionedListMarker::AddToBox( @@ -94,12 +88,10 @@ void NGUnpositionedListMarker::AddToBox( FontBaseline baseline_type, const NGPhysicalFragment& content, const NGBoxStrut& border_scrollbar_padding, - const NGLineHeightMetrics& content_metrics, const NGLayoutResult& marker_layout_result, + LayoutUnit content_baseline, LogicalOffset* content_offset, NGBoxFragmentBuilder* container_builder) const { - DCHECK(!content_metrics.IsEmpty()); - const NGPhysicalBoxFragment& marker_physical_fragment = To<NGPhysicalBoxFragment>(marker_layout_result.PhysicalFragment()); @@ -111,8 +103,8 @@ void NGUnpositionedListMarker::AddToBox( // Adjust the block offset to align baselines of the marker and the content. NGLineHeightMetrics marker_metrics = marker_fragment.BaselineMetrics( - {NGBaselineAlgorithmType::kAtomicInline, baseline_type}, space); - LayoutUnit baseline_adjust = content_metrics.ascent - marker_metrics.ascent; + /* margins */ NGLineBoxStrut(), baseline_type); + LayoutUnit baseline_adjust = content_baseline - marker_metrics.ascent; if (baseline_adjust >= 0) { marker_offset.block_offset += baseline_adjust; } else { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h index 8305cac2cf6..5c5e6e3091f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h @@ -14,7 +14,7 @@ namespace blink { class ComputedStyle; -class LayoutNGListMarker; +class LayoutNGOutsideListMarker; class LayoutUnit; class NGBlockNode; class NGConstraintSpace; @@ -23,7 +23,6 @@ class NGLayoutResult; class NGPhysicalFragment; struct LogicalOffset; -struct NGLineHeightMetrics; // Represents an unpositioned list marker. // @@ -37,8 +36,8 @@ struct NGLineHeightMetrics; // // In order to adjust with the other content of LI, marker will be handled // after other children. -// First, try to find the adjusted content_metrics for the marker. See -// |CanAddToBox()| for details. +// First, try to find the alignment-baseline for the marker. See +// |ContentAlignmentBaseline()| for details. // If found, layout marker, compute the content adjusted offset and float // intuded offset. See |AddToBox()| for details. // If not, layout marker and deal with it in |AddToBoxWithoutLineBoxes()|. @@ -52,25 +51,27 @@ class CORE_EXPORT NGUnpositionedListMarker final { public: NGUnpositionedListMarker() : marker_layout_object_(nullptr) {} - explicit NGUnpositionedListMarker(LayoutNGListMarker*); + explicit NGUnpositionedListMarker(LayoutNGOutsideListMarker*); explicit NGUnpositionedListMarker(const NGBlockNode&); explicit operator bool() const { return marker_layout_object_; } - // Returns true if the list marker can be added to box. False indicates - // that the child content does not have a baseline to align to, and that - // caller should try next child, or "WithoutLineBoxes" version. - bool CanAddToBox(const NGConstraintSpace&, - FontBaseline, - const NGPhysicalFragment& content, - NGLineHeightMetrics* content_metrics) const; + // Returns the baseline that the list-marker should place itself along. + // + // |base::nullopt| indicates that the child |content| does not have a baseline + // to align to, and that caller should try next child, or use the + // |AddToBoxWithoutLineBoxes()| method. + base::Optional<LayoutUnit> ContentAlignmentBaseline( + const NGConstraintSpace&, + FontBaseline, + const NGPhysicalFragment& content) const; // Add a fragment for an outside list marker. void AddToBox(const NGConstraintSpace&, FontBaseline, const NGPhysicalFragment& content, const NGBoxStrut&, - const NGLineHeightMetrics& content_metrics, const NGLayoutResult& marker_layout_result, + LayoutUnit content_baseline, LogicalOffset* content_offset, NGBoxFragmentBuilder*) const; @@ -105,7 +106,7 @@ class CORE_EXPORT NGUnpositionedListMarker final { const NGBoxStrut&, LayoutUnit) const; - LayoutNGListMarker* marker_layout_object_; + LayoutNGOutsideListMarker* marker_layout_object_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc new file mode 100644 index 00000000000..161826f1cff --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc @@ -0,0 +1,46 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h" + +#include "third_party/blink/renderer/core/layout/layout_analyzer.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" + +namespace blink { + +LayoutNGMathMLBlock::LayoutNGMathMLBlock(MathMLElement* element) + : LayoutNGMixin<LayoutBlock>(element) { + DCHECK(element); +} + +void LayoutNGMathMLBlock::UpdateBlockLayout(bool relayout_children) { + LayoutAnalyzer::BlockScope analyzer(*this); + + if (IsOutOfFlowPositioned()) { + UpdateOutOfFlowBlockLayout(); + return; + } + + UpdateInFlowBlockLayout(); +} + +bool LayoutNGMathMLBlock::IsOfType(LayoutObjectType type) const { + return type == kLayoutObjectMathML || + (type == kLayoutObjectMathMLRoot && GetNode() && + GetNode()->HasTagName(mathml_names::kMathTag)) || + LayoutNGMixin<LayoutBlock>::IsOfType(type); +} + +bool LayoutNGMathMLBlock::IsChildAllowed(LayoutObject* child, + const ComputedStyle&) const { + return child->GetNode() && child->GetNode()->IsMathMLElement(); +} + +bool LayoutNGMathMLBlock::CanHaveChildren() const { + if (GetNode() && GetNode()->HasTagName(mathml_names::kMspaceTag)) + return false; + return LayoutNGMixin<LayoutBlock>::CanHaveChildren(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h new file mode 100644 index 00000000000..bc9dc975643 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h @@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_LAYOUT_NG_MATHML_BLOCK_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_LAYOUT_NG_MATHML_BLOCK_H_ + +#include "third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h" +#include "third_party/blink/renderer/core/mathml/mathml_element.h" + +namespace blink { + +class LayoutNGMathMLBlock : public LayoutNGMixin<LayoutBlock> { + public: + explicit LayoutNGMathMLBlock(MathMLElement*); + + const char* GetName() const override { return "LayoutNGMathMLBlock"; } + + private: + void UpdateBlockLayout(bool relayout_children) final; + + bool IsOfType(LayoutObjectType) const final; + bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const final; + bool CanHaveChildren() const final; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_LAYOUT_NG_MATHML_BLOCK_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc new file mode 100644 index 00000000000..9deb2e3c49f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc @@ -0,0 +1,323 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h" + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" + +namespace blink { +namespace { + +// Describes the amount to shift the numerator/denominator of the fraction when +// a fraction bar is present. Data is populated from the OpenType MATH table. +// If the OpenType MATH table is not present fallback values are used. +// https://mathml-refresh.github.io/mathml-core/#fraction-with-nonzero-line-thickness +struct FractionParameters { + LayoutUnit numerator_gap_min; + LayoutUnit denominator_gap_min; + LayoutUnit numerator_min_shift_up; + LayoutUnit denominator_min_shift_down; +}; + +FractionParameters GetFractionParameters(const ComputedStyle& style) { + FractionParameters parameters; + + bool has_display_style = HasDisplayStyle(style); + + // We try and read constants to draw the fraction from the OpenType MATH and + // use fallback values otherwise. + // The MATH table specification suggests default rule thickness or (in + // displaystyle) 3 times default rule thickness for the gaps. + parameters.numerator_gap_min = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants:: + kFractionNumDisplayStyleGapMin + : OpenTypeMathSupport::MathConstants::kFractionNumeratorGapMin) + .value_or((has_display_style ? 3 : 1) * + RuleThicknessFallback(style))); + parameters.denominator_gap_min = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants:: + kFractionDenomDisplayStyleGapMin + : OpenTypeMathSupport::MathConstants::kFractionDenominatorGapMin) + .value_or(parameters.numerator_gap_min)); + + // TODO(crbug.com/1058369): The MATH table specification does not suggest + // any values for shifts, so we leave them at zero for now. + parameters.numerator_min_shift_up = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants:: + kFractionNumeratorDisplayStyleShiftUp + : OpenTypeMathSupport::MathConstants::kFractionNumeratorShiftUp) + .value_or(0)); + parameters.denominator_min_shift_down = LayoutUnit( + MathConstant(style, has_display_style + ? OpenTypeMathSupport::MathConstants:: + kFractionDenominatorDisplayStyleShiftDown + : OpenTypeMathSupport::MathConstants:: + kFractionDenominatorShiftDown) + .value_or(0)); + + return parameters; +} + +// Describes the amount to shift the numerator/denominator of the fraction when +// a fraction bar is not present. Data is populated from the OpenType MATH +// table. If the OpenType MATH table is not present fallback values are used. +// https://mathml-refresh.github.io/mathml-core/#fraction-with-zero-line-thickness +struct FractionStackParameters { + LayoutUnit gap_min; + LayoutUnit top_shift_up; + LayoutUnit bottom_shift_down; +}; + +FractionStackParameters GetFractionStackParameters(const ComputedStyle& style) { + FractionStackParameters parameters; + + bool has_display_style = HasDisplayStyle(style); + + // We try and read constants to draw the stack from the OpenType MATH and use + // fallback values otherwise. + // We use the fallback values suggested in the MATH table specification. + parameters.gap_min = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants::kStackDisplayStyleGapMin + : OpenTypeMathSupport::MathConstants::kStackGapMin) + .value_or((has_display_style ? 7 : 3) * + RuleThicknessFallback(style))); + // The MATH table specification does not suggest any values for shifts, so + // we leave them at zero. + parameters.top_shift_up = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants::kStackTopDisplayStyleShiftUp + : OpenTypeMathSupport::MathConstants::kStackTopShiftUp) + .value_or(0)); + parameters.bottom_shift_down = LayoutUnit( + MathConstant( + style, + has_display_style + ? OpenTypeMathSupport::MathConstants:: + kStackBottomDisplayStyleShiftDown + : OpenTypeMathSupport::MathConstants::kStackBottomShiftDown) + .value_or(0)); + + return parameters; +} + +} // namespace + +NGMathFractionLayoutAlgorithm::NGMathFractionLayoutAlgorithm( + const NGLayoutAlgorithmParams& params) + : NGLayoutAlgorithm(params), + border_scrollbar_padding_(params.fragment_geometry.border + + params.fragment_geometry.padding + + params.fragment_geometry.scrollbar) { + DCHECK(params.space.IsNewFormattingContext()); + container_builder_.SetIsNewFormattingContext( + params.space.IsNewFormattingContext()); + container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); + container_builder_.SetIsMathMLFraction(); +} + +void NGMathFractionLayoutAlgorithm::GatherChildren(NGBlockNode* numerator, + NGBlockNode* denominator) { + for (NGLayoutInputNode child = Node().FirstChild(); child; + child = child.NextSibling()) { + NGBlockNode block_child = To<NGBlockNode>(child); + if (child.IsOutOfFlowPositioned()) { + container_builder_.AddOutOfFlowChildCandidate( + block_child, {border_scrollbar_padding_.inline_start, + border_scrollbar_padding_.block_start}); + continue; + } + if (!*numerator) { + *numerator = block_child; + continue; + } + if (!*denominator) { + *denominator = block_child; + continue; + } + + NOTREACHED(); + } + + DCHECK(*numerator); + DCHECK(*denominator); +} + +scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() { + DCHECK(!BreakToken()); + + NGBlockNode numerator = nullptr; + NGBlockNode denominator = nullptr; + GatherChildren(&numerator, &denominator); + + const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize(); + auto child_available_size = + ShrinkAvailableSize(border_box_size, border_scrollbar_padding_); + auto numerator_space = CreateConstraintSpaceForMathChild( + Node(), child_available_size, ConstraintSpace(), numerator); + scoped_refptr<const NGLayoutResult> numerator_layout_result = + numerator.Layout(numerator_space); + auto numerator_margins = + ComputeMarginsFor(numerator_space, numerator.Style(), ConstraintSpace()); + auto denominator_space = CreateConstraintSpaceForMathChild( + Node(), child_available_size, ConstraintSpace(), denominator); + scoped_refptr<const NGLayoutResult> denominator_layout_result = + denominator.Layout(denominator_space); + auto denominator_margins = ComputeMarginsFor( + denominator_space, denominator.Style(), ConstraintSpace()); + + NGBoxFragment numerator_fragment( + ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction(), + To<NGPhysicalBoxFragment>(numerator_layout_result->PhysicalFragment())); + NGBoxFragment denominator_fragment( + ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction(), + To<NGPhysicalBoxFragment>(denominator_layout_result->PhysicalFragment())); + + LayoutUnit content_inline_size = std::max( + numerator_fragment.InlineSize() + numerator_margins.InlineSum(), + denominator_fragment.InlineSize() + denominator_margins.InlineSum()); + + LayoutUnit numerator_ascent = + numerator_margins.block_start + + numerator_fragment.Baseline().value_or(numerator_fragment.BlockSize()); + LayoutUnit numerator_descent = numerator_fragment.BlockSize() + + numerator_margins.BlockSum() - + numerator_ascent; + LayoutUnit denominator_ascent = denominator_margins.block_start + + denominator_fragment.Baseline().value_or( + denominator_fragment.BlockSize()); + LayoutUnit denominator_descent = denominator_fragment.BlockSize() + + denominator_margins.BlockSum() - + denominator_ascent; + + LayoutUnit numerator_shift, denominator_shift; + LayoutUnit thickness = FractionLineThickness(Style()); + if (thickness) { + LayoutUnit axis_height = MathAxisHeight(Style()); + FractionParameters parameters = GetFractionParameters(Style()); + numerator_shift = + std::max(parameters.numerator_min_shift_up, + axis_height + thickness / 2 + parameters.numerator_gap_min + + numerator_descent); + denominator_shift = + std::max(parameters.denominator_min_shift_down, + thickness / 2 + parameters.denominator_gap_min + + denominator_ascent - axis_height); + } else { + FractionStackParameters parameters = GetFractionStackParameters(Style()); + numerator_shift = parameters.top_shift_up; + denominator_shift = parameters.bottom_shift_down; + LayoutUnit gap = denominator_shift - denominator_ascent + numerator_shift - + numerator_descent; + if (gap < parameters.gap_min) { + LayoutUnit diff = parameters.gap_min - gap; + LayoutUnit delta = diff / 2; + numerator_shift += delta; + denominator_shift += diff - delta; + } + } + + LayoutUnit fraction_ascent = + std::max(numerator_shift + numerator_ascent, + -denominator_shift + denominator_ascent); + LayoutUnit fraction_descent = + std::max(-numerator_shift + numerator_descent, + denominator_shift + denominator_descent); + fraction_ascent += border_scrollbar_padding_.block_start; + fraction_descent += border_scrollbar_padding_.block_end; + LayoutUnit total_block_size = fraction_ascent + fraction_descent; + + container_builder_.SetBaseline(fraction_ascent); + + LogicalOffset numerator_offset; + LogicalOffset denominator_offset; + numerator_offset.inline_offset = + border_scrollbar_padding_.inline_start + numerator_margins.inline_start + + (content_inline_size - + (numerator_fragment.InlineSize() + numerator_margins.InlineSum())) / + 2; + denominator_offset.inline_offset = + border_scrollbar_padding_.inline_start + + denominator_margins.inline_start + + (content_inline_size - + (denominator_fragment.InlineSize() + denominator_margins.InlineSum())) / + 2; + + numerator_offset.block_offset = numerator_margins.block_start + + fraction_ascent - numerator_shift - + numerator_ascent; + denominator_offset.block_offset = denominator_margins.block_start + + fraction_ascent + denominator_shift - + denominator_ascent; + + container_builder_.AddChild(numerator_layout_result->PhysicalFragment(), + numerator_offset); + container_builder_.AddChild(denominator_layout_result->PhysicalFragment(), + denominator_offset); + + numerator.StoreMargins(ConstraintSpace(), numerator_margins); + denominator.StoreMargins(ConstraintSpace(), denominator_margins); + + LayoutUnit block_size = ComputeBlockSizeForFragment( + ConstraintSpace(), Style(), border_scrollbar_padding_, total_block_size); + + container_builder_.SetIntrinsicBlockSize(total_block_size); + container_builder_.SetBlockSize(block_size); + + NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), container_builder_.Borders(), + &container_builder_) + .Run(); + + return container_builder_.ToBoxFragment(); +} + +base::Optional<MinMaxSizes> NGMathFractionLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + base::Optional<MinMaxSizes> sizes = + CalculateMinMaxSizesIgnoringChildren(Node(), border_scrollbar_padding_); + if (sizes) + return sizes; + + sizes.emplace(); + LayoutUnit child_percentage_resolution_block_size = + CalculateChildPercentageBlockSizeForMinMax( + ConstraintSpace(), Node(), border_scrollbar_padding_, + input.percentage_resolution_block_size); + + MinMaxSizesInput child_input(child_percentage_resolution_block_size); + + for (NGLayoutInputNode child = Node().FirstChild(); child; + child = child.NextSibling()) { + if (child.IsOutOfFlowPositioned()) + continue; + auto child_sizes = + ComputeMinAndMaxContentContribution(Style(), child, child_input); + NGBoxStrut margins = ComputeMinMaxMargins(Style(), child); + child_sizes += margins.InlineSum(); + sizes->Encompass(child_sizes); + } + + *sizes += border_scrollbar_padding_.InlineSum(); + return sizes; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h new file mode 100644 index 00000000000..d645439465f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h @@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_FRACTION_LAYOUT_ALGORITHM_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_FRACTION_LAYOUT_ALGORITHM_H_ + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h" + +namespace blink { + +class CORE_EXPORT NGMathFractionLayoutAlgorithm + : public NGLayoutAlgorithm<NGBlockNode, + NGBoxFragmentBuilder, + NGBlockBreakToken> { + public: + explicit NGMathFractionLayoutAlgorithm(const NGLayoutAlgorithmParams& params); + + private: + scoped_refptr<const NGLayoutResult> Layout() final; + + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const final; + + void GatherChildren(NGBlockNode* numerator, NGBlockNode* denominator); + const NGBoxStrut border_scrollbar_padding_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_FRACTION_LAYOUT_ALGORITHM_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc new file mode 100644 index 00000000000..44f6ed8d617 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc @@ -0,0 +1,97 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h" + +#include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" +#include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h" + +namespace blink { + +NGConstraintSpace CreateConstraintSpaceForMathChild( + const NGBlockNode& parent_node, + const LogicalSize& child_available_size, + const NGConstraintSpace& parent_constraint_space, + const NGLayoutInputNode& child) { + const ComputedStyle& parent_style = parent_node.Style(); + const ComputedStyle& child_style = child.Style(); + DCHECK(child.CreatesNewFormattingContext()); + NGConstraintSpaceBuilder space_builder(parent_constraint_space, + child_style.GetWritingMode(), + true /* is_new_fc */); + SetOrthogonalFallbackInlineSizeIfNeeded(parent_style, child, &space_builder); + + space_builder.SetAvailableSize(child_available_size); + space_builder.SetPercentageResolutionSize(child_available_size); + space_builder.SetReplacedPercentageResolutionSize(child_available_size); + + space_builder.SetIsShrinkToFit(child_style.LogicalWidth().IsAuto()); + + // TODO(rbuis): add target stretch sizes. + + space_builder.SetTextDirection(child_style.Direction()); + + // TODO(rbuis): add ink baselines? + space_builder.SetNeedsBaseline(true); + return space_builder.ToConstraintSpace(); +} + +NGLayoutInputNode FirstChildInFlow(const NGBlockNode& node) { + NGLayoutInputNode child = node.FirstChild(); + while (child && child.IsOutOfFlowPositioned()) + child = child.NextSibling(); + return child; +} + +NGLayoutInputNode NextSiblingInFlow(const NGBlockNode& node) { + NGLayoutInputNode sibling = node.NextSibling(); + while (sibling && sibling.IsOutOfFlowPositioned()) + sibling = sibling.NextSibling(); + return sibling; +} + +inline bool InFlowChildCountIs(const NGBlockNode& node, unsigned count) { + DCHECK(count == 2 || count == 3); + auto child = To<NGBlockNode>(FirstChildInFlow(node)); + while (count && child) { + child = To<NGBlockNode>(NextSiblingInFlow(child)); + count--; + } + return !count && !child; +} + +bool IsValidMathMLFraction(const NGBlockNode& node) { + return InFlowChildCountIs(node, 2); +} + +namespace { + +inline LayoutUnit DefaultFractionLineThickness(const ComputedStyle& style) { + return LayoutUnit( + MathConstant(style, + OpenTypeMathSupport::MathConstants::kFractionRuleThickness) + .value_or(RuleThicknessFallback(style))); +} + +} // namespace + +LayoutUnit MathAxisHeight(const ComputedStyle& style) { + return LayoutUnit( + MathConstant(style, OpenTypeMathSupport::MathConstants::kAxisHeight) + .value_or(style.GetFont().PrimaryFont()->GetFontMetrics().XHeight() / + 2)); +} + +LayoutUnit FractionLineThickness(const ComputedStyle& style) { + return std::max<LayoutUnit>( + ValueForLength(style.GetMathFractionBarThickness(), + DefaultFractionLineThickness(style)), + LayoutUnit()); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h new file mode 100644 index 00000000000..366e1a81171 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h @@ -0,0 +1,54 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_LAYOUT_UTILS_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_LAYOUT_UTILS_H_ + +#include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/platform/fonts/opentype/open_type_math_support.h" + +namespace blink { + +struct LogicalSize; +class NGBlockNode; +class NGConstraintSpace; +class NGLayoutInputNode; + +// Creates a new constraint space for the current child. +NGConstraintSpace CreateConstraintSpaceForMathChild( + const NGBlockNode& parent_node, + const LogicalSize& child_available_size, + const NGConstraintSpace& parent_constraint_space, + const NGLayoutInputNode&); + +NGLayoutInputNode FirstChildInFlow(const NGBlockNode&); +NGLayoutInputNode NextSiblingInFlow(const NGBlockNode&); + +bool IsValidMathMLFraction(const NGBlockNode&); + +inline float RuleThicknessFallback(const ComputedStyle& style) { + // This function returns a value for the default rule thickness (TeX's + // \xi_8) to be used as a fallback when we lack a MATH table. + return 0.05f * style.FontSize(); +} + +LayoutUnit MathAxisHeight(const ComputedStyle& style); + +inline base::Optional<float> MathConstant( + const ComputedStyle& style, + OpenTypeMathSupport::MathConstants constant) { + return OpenTypeMathSupport::MathConstant( + style.GetFont().PrimaryFont()->PlatformData().GetHarfBuzzFace(), + constant); +} + +LayoutUnit FractionLineThickness(const ComputedStyle& style); + +inline bool HasDisplayStyle(const ComputedStyle& style) { + return style.MathStyle() == EMathStyle::kDisplay; +} + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_LAYOUT_UTILS_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc new file mode 100644 index 00000000000..6fe8785a1cc --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc @@ -0,0 +1,180 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h" + +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h" +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/mathml/mathml_element.h" + +namespace blink { +namespace { + +inline LayoutUnit InlineOffsetForDisplayMathCentering( + bool is_display_math, + LayoutUnit available_inline_size, + LayoutUnit max_row_inline_size) { + if (is_display_math) + return (available_inline_size - max_row_inline_size) / 2; + return LayoutUnit(); +} + +} // namespace + +NGMathRowLayoutAlgorithm::NGMathRowLayoutAlgorithm( + const NGLayoutAlgorithmParams& params) + : NGLayoutAlgorithm(params), + border_padding_(params.fragment_geometry.border + + params.fragment_geometry.padding), + border_scrollbar_padding_(border_padding_ + + params.fragment_geometry.scrollbar) { + DCHECK(params.space.IsNewFormattingContext()); + DCHECK(!ConstraintSpace().HasBlockFragmentation()); + container_builder_.SetIsNewFormattingContext( + params.space.IsNewFormattingContext()); + container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); +} + +void NGMathRowLayoutAlgorithm::LayoutRowItems( + NGContainerFragmentBuilder::ChildrenVector* children, + LayoutUnit* max_row_block_baseline, + LogicalSize* row_total_size) { + LayoutUnit inline_offset, max_row_ascent, max_row_descent; + for (NGLayoutInputNode child = Node().FirstChild(); child; + child = child.NextSibling()) { + if (child.IsOutOfFlowPositioned()) { + // TODO(rbuis): OOF should be "where child would have been if not + // absolutely positioned". + // Issue: https://github.com/mathml-refresh/mathml/issues/16 + container_builder_.AddOutOfFlowChildCandidate( + To<NGBlockNode>(child), {border_scrollbar_padding_.inline_start, + border_scrollbar_padding_.block_start}); + continue; + } + const ComputedStyle& child_style = child.Style(); + NGConstraintSpace child_space = CreateConstraintSpaceForMathChild( + Node(), child_available_size_, ConstraintSpace(), child); + scoped_refptr<const NGLayoutResult> result = + To<NGBlockNode>(child).Layout(child_space, nullptr /* break token */); + const NGPhysicalContainerFragment& physical_fragment = + result->PhysicalFragment(); + NGBoxFragment fragment(ConstraintSpace().GetWritingMode(), + ConstraintSpace().Direction(), + To<NGPhysicalBoxFragment>(physical_fragment)); + + NGBoxStrut margins = + ComputeMarginsFor(child_space, child_style, ConstraintSpace()); + inline_offset += margins.inline_start; + + LayoutUnit ascent = margins.block_start + + fragment.Baseline().value_or(fragment.BlockSize()); + *max_row_block_baseline = std::max(*max_row_block_baseline, ascent); + + // TODO(rbuis): Operators can add lspace and rspace. + + children->emplace_back( + LogicalOffset{inline_offset, margins.block_start - ascent}, + &physical_fragment); + + inline_offset += fragment.InlineSize() + margins.inline_end; + + max_row_ascent = std::max(max_row_ascent, ascent + margins.block_start); + max_row_descent = std::max( + max_row_descent, fragment.BlockSize() + margins.block_end - ascent); + row_total_size->inline_size = + std::max(row_total_size->inline_size, inline_offset); + } + row_total_size->block_size = max_row_ascent + max_row_descent; +} + +scoped_refptr<const NGLayoutResult> NGMathRowLayoutAlgorithm::Layout() { + DCHECK(!BreakToken()); + + bool is_display_math = + Node().IsMathRoot() && Style().Display() == EDisplay::kMath; + + LogicalSize max_row_size; + LayoutUnit max_row_block_baseline; + + const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize(); + child_available_size_ = + ShrinkAvailableSize(border_box_size, border_scrollbar_padding_); + + NGContainerFragmentBuilder::ChildrenVector children; + LayoutRowItems(&children, &max_row_block_baseline, &max_row_size); + + // Add children taking into account centering, baseline and + // border/scrollbar/padding. + LayoutUnit center_offset = InlineOffsetForDisplayMathCentering( + is_display_math, container_builder_.InlineSize(), + max_row_size.inline_size); + LogicalOffset adjust_offset( + border_scrollbar_padding_.inline_start + center_offset, + border_scrollbar_padding_.block_start + max_row_block_baseline); + for (auto& child : children) { + child.offset += adjust_offset; + container_builder_.AddChild( + To<NGPhysicalContainerFragment>(*child.fragment), child.offset); + } + + container_builder_.SetBaseline(border_scrollbar_padding_.block_start + + max_row_block_baseline); + + auto block_size = ComputeBlockSizeForFragment( + ConstraintSpace(), Style(), border_padding_, + max_row_size.block_size + border_scrollbar_padding_.BlockSum()); + container_builder_.SetBlockSize(block_size); + + NGOutOfFlowLayoutPart( + Node(), ConstraintSpace(), + container_builder_.Borders() + container_builder_.Scrollbar(), + &container_builder_) + .Run(); + + return container_builder_.ToBoxFragment(); +} + +base::Optional<MinMaxSizes> NGMathRowLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + base::Optional<MinMaxSizes> sizes = + CalculateMinMaxSizesIgnoringChildren(Node(), border_scrollbar_padding_); + if (sizes) + return sizes; + + sizes.emplace(); + LayoutUnit child_percentage_resolution_block_size = + CalculateChildPercentageBlockSizeForMinMax( + ConstraintSpace(), Node(), border_padding_, + input.percentage_resolution_block_size); + + MinMaxSizesInput child_input(child_percentage_resolution_block_size); + + for (NGLayoutInputNode child = Node().FirstChild(); child; + child = child.NextSibling()) { + if (child.IsOutOfFlowPositioned()) + continue; + MinMaxSizes child_min_max_sizes = + ComputeMinAndMaxContentContribution(Style(), child, child_input); + NGBoxStrut child_margins = ComputeMinMaxMargins(Style(), child); + child_min_max_sizes += child_margins.InlineSum(); + sizes->max_size += child_min_max_sizes.max_size; + sizes->min_size += child_min_max_sizes.min_size; + + // TODO(rbuis): Operators can add lspace and rspace. + } + sizes->max_size = std::max(sizes->max_size, sizes->min_size); + + // Due to negative margins, it is possible that we calculated a negative + // intrinsic width. Make sure that we never return a negative width. + sizes->Encompass(LayoutUnit()); + *sizes += border_scrollbar_padding_.InlineSum(); + return sizes; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h new file mode 100644 index 00000000000..72b8dd10b40 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h @@ -0,0 +1,42 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_ROW_LAYOUT_ALGORITHM_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_ROW_LAYOUT_ALGORITHM_H_ + +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" + +namespace blink { + +class LayoutUnit; + +class CORE_EXPORT NGMathRowLayoutAlgorithm + : public NGLayoutAlgorithm<NGBlockNode, + NGBoxFragmentBuilder, + NGBlockBreakToken> { + public: + NGMathRowLayoutAlgorithm(const NGLayoutAlgorithmParams& params); + + protected: + void LayoutRowItems(NGContainerFragmentBuilder::ChildrenVector*, + LayoutUnit* max_row_block_baseline, + LogicalSize* row_total_size); + + private: + scoped_refptr<const NGLayoutResult> Layout() final; + + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const final; + + LogicalSize child_available_size_; + const NGBoxStrut border_padding_; + const NGBoxStrut border_scrollbar_padding_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_ROW_LAYOUT_ALGORITHM_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.cc new file mode 100644 index 00000000000..4abfa765870 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.cc @@ -0,0 +1,42 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h" + +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" + +namespace blink { + +NGMathSpaceLayoutAlgorithm::NGMathSpaceLayoutAlgorithm( + const NGLayoutAlgorithmParams& params) + : NGLayoutAlgorithm(params), + border_padding_(params.fragment_geometry.border + + params.fragment_geometry.padding) { + DCHECK(params.fragment_geometry.scrollbar.IsEmpty()); + container_builder_.SetIsNewFormattingContext(true); + container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); +} + +scoped_refptr<const NGLayoutResult> NGMathSpaceLayoutAlgorithm::Layout() { + DCHECK(!BreakToken()); + + LayoutUnit block_size = ComputeBlockSizeForFragment( + ConstraintSpace(), Style(), border_padding_, border_padding_.BlockSum()); + + container_builder_.SetIntrinsicBlockSize(border_padding_.BlockSum()); + container_builder_.SetBlockSize(block_size); + + container_builder_.SetBaseline( + border_padding_.block_start + + ValueForLength(Style().GetMathBaseline(), LayoutUnit())); + return container_builder_.ToBoxFragment(); +} + +base::Optional<MinMaxSizes> NGMathSpaceLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + return CalculateMinMaxSizesIgnoringChildren(Node(), border_padding_); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h new file mode 100644 index 00000000000..7b493ef0b17 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h @@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_SPACE_LAYOUT_ALGORITHM_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_SPACE_LAYOUT_ALGORITHM_H_ + +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" + +namespace blink { + +class CORE_EXPORT NGMathSpaceLayoutAlgorithm + : public NGLayoutAlgorithm<NGBlockNode, + NGBoxFragmentBuilder, + NGBlockBreakToken> { + public: + explicit NGMathSpaceLayoutAlgorithm(const NGLayoutAlgorithmParams& params); + + private: + scoped_refptr<const NGLayoutResult> Layout() final; + + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const final; + + const NGBoxStrut border_padding_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_SPACE_LAYOUT_ALGORITHM_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc index d6c66b0b708..fd2180c4c50 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc @@ -31,7 +31,7 @@ bool IsLogicalWidthTreatedAsAuto(const ComputedStyle& style) { return IsTable(style) || style.LogicalWidth().IsAuto(); } -bool IsLogicalHeightTreatAsAuto(const ComputedStyle& style) { +bool IsLogicalHeightTreatedAsAuto(const ComputedStyle& style) { return IsTable(style) || style.LogicalHeight().IsAuto(); } @@ -111,11 +111,11 @@ inline LayoutUnit StaticPositionEndInset(StaticPositionEdge edge, } LayoutUnit ComputeShrinkToFitSize( - const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSizes>& min_max_sizes, LayoutUnit computed_available_size, LayoutUnit margin_start, LayoutUnit margin_end) { - return child_minmax->ShrinkToFit( + return min_max_sizes->ShrinkToFit( (computed_available_size - margin_start - margin_end) .ClampNegativeToZero()); } @@ -123,10 +123,10 @@ LayoutUnit ComputeShrinkToFitSize( // Implement the absolute size resolution algorithm. // https://www.w3.org/TR/css-position-3/#abs-non-replaced-width // https://www.w3.org/TR/css-position-3/#abs-non-replaced-height -// |child_minmax| can have no value if an element is replaced, and has no +// |min_max_sizes| can have no value if an element is replaced, and has no // intrinsic width or height, but has an aspect ratio. void ComputeAbsoluteSize(const LayoutUnit border_padding_size, - const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSizes>& min_max_sizes, const LayoutUnit margin_percentage_resolution_size, const LayoutUnit available_size, const Length& margin_start_length, @@ -199,7 +199,7 @@ void ComputeAbsoluteSize(const LayoutUnit border_padding_size, computed_available_size = static_position_offset; break; } - size = ComputeShrinkToFitSize(child_minmax, computed_available_size, + size = ComputeShrinkToFitSize(min_max_sizes, computed_available_size, *margin_start, *margin_end); LayoutUnit margin_size = *size + *margin_start + *margin_end; if (is_start_dominant) { @@ -259,7 +259,7 @@ void ComputeAbsoluteSize(const LayoutUnit border_padding_size, // Rule 1: left/width are unknown. DCHECK(inset_end.has_value()); LayoutUnit computed_available_size = available_size - *inset_end; - size = ComputeShrinkToFitSize(child_minmax, computed_available_size, + size = ComputeShrinkToFitSize(min_max_sizes, computed_available_size, *margin_start, *margin_end); } else if (!inset_start && !inset_end) { // Rule 2. @@ -276,7 +276,7 @@ void ComputeAbsoluteSize(const LayoutUnit border_padding_size, } else if (!size && !inset_end) { // Rule 3. LayoutUnit computed_available_size = available_size - *inset_start; - size = ComputeShrinkToFitSize(child_minmax, computed_available_size, + size = ComputeShrinkToFitSize(min_max_sizes, computed_available_size, *margin_start, *margin_end); } @@ -300,7 +300,7 @@ void ComputeAbsoluteSize(const LayoutUnit border_padding_size, // is safe to recursively call ourselves here because on the second call it // is guaranteed to be within |min_size| and |max_size|. ComputeAbsoluteSize( - border_padding_size, child_minmax, margin_percentage_resolution_size, + border_padding_size, min_max_sizes, margin_percentage_resolution_size, available_size, margin_start_length, margin_end_length, inset_start_length, inset_end_length, min_size, max_size, static_position_offset, static_position_edge, is_start_dominant, @@ -334,7 +334,7 @@ bool AbsoluteNeedsChildBlockSize(const ComputedStyle& style) { return is_logical_height_intrinsic || style.LogicalMinHeight().IsIntrinsic() || style.LogicalMaxHeight().IsIntrinsic() || - (IsLogicalHeightTreatAsAuto(style) && + (IsLogicalHeightTreatedAsAuto(style) && (style.LogicalTop().IsAuto() || style.LogicalBottom().IsAuto())); } @@ -382,30 +382,31 @@ base::Optional<LayoutUnit> ComputeAbsoluteDialogYPosition( return top; } -NGLogicalOutOfFlowPosition ComputePartialAbsoluteWithChildInlineSize( +void ComputeOutOfFlowInlineDimensions( const NGConstraintSpace& space, const ComputedStyle& style, const NGBoxStrut& border_padding, const NGLogicalStaticPosition& static_position, - const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSizes>& min_max_sizes, const base::Optional<LogicalSize>& replaced_size, const WritingMode container_writing_mode, - const TextDirection container_direction) { - NGLogicalOutOfFlowPosition position; + const TextDirection container_direction, + NGLogicalOutOfFlowDimensions* dimensions) { + DCHECK(dimensions); base::Optional<LayoutUnit> inline_size; if (!IsLogicalWidthTreatedAsAuto(style)) { inline_size = ResolveMainInlineLength(space, style, border_padding, - child_minmax, style.LogicalWidth()); + min_max_sizes, style.LogicalWidth()); } else if (replaced_size.has_value()) { inline_size = replaced_size->inline_size; } LayoutUnit min_inline_size = ResolveMinInlineLength( - space, style, border_padding, child_minmax, style.LogicalMinWidth(), + space, style, border_padding, min_max_sizes, style.LogicalMinWidth(), LengthResolvePhase::kLayout); LayoutUnit max_inline_size = ResolveMaxInlineLength( - space, style, border_padding, child_minmax, style.LogicalMaxWidth(), + space, style, border_padding, min_max_sizes, style.LogicalMaxWidth(), LengthResolvePhase::kLayout); // Tables use the inline-size as a minimum. @@ -413,7 +414,7 @@ NGLogicalOutOfFlowPosition ComputePartialAbsoluteWithChildInlineSize( min_inline_size = std::max(min_inline_size, ResolveMainInlineLength(space, style, border_padding, - child_minmax, style.LogicalWidth())); + min_max_sizes, style.LogicalWidth())); } bool is_start_dominant; @@ -428,20 +429,19 @@ NGLogicalOutOfFlowPosition ComputePartialAbsoluteWithChildInlineSize( } ComputeAbsoluteSize( - border_padding.InlineSum(), child_minmax, + border_padding.InlineSum(), min_max_sizes, space.PercentageResolutionInlineSizeForParentWritingMode(), space.AvailableSize().inline_size, style.MarginStart(), style.MarginEnd(), style.LogicalInlineStart(), style.LogicalInlineEnd(), min_inline_size, max_inline_size, static_position.offset.inline_offset, GetStaticPositionEdge(static_position.inline_edge), is_start_dominant, - false /* is_block_direction */, inline_size, &position.size.inline_size, - &position.inset.inline_start, &position.inset.inline_end, - &position.margins.inline_start, &position.margins.inline_end); - - return position; + false /* is_block_direction */, inline_size, + &dimensions->size.inline_size, &dimensions->inset.inline_start, + &dimensions->inset.inline_end, &dimensions->margins.inline_start, + &dimensions->margins.inline_end); } -void ComputeFullAbsoluteWithChildBlockSize( +void ComputeOutOfFlowBlockDimensions( const NGConstraintSpace& space, const ComputedStyle& style, const NGBoxStrut& border_padding, @@ -450,20 +450,20 @@ void ComputeFullAbsoluteWithChildBlockSize( const base::Optional<LogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction, - NGLogicalOutOfFlowPosition* position) { + NGLogicalOutOfFlowDimensions* dimensions) { // After partial size has been computed, child block size is either unknown, // or fully computed, there is no minmax. To express this, a 'fixed' minmax // is created where min and max are the same. - base::Optional<MinMaxSize> child_minmax; + base::Optional<MinMaxSizes> min_max_sizes; if (child_block_size.has_value()) { - child_minmax = MinMaxSize{*child_block_size, *child_block_size}; + min_max_sizes = MinMaxSizes{*child_block_size, *child_block_size}; } LayoutUnit child_block_size_or_indefinite = child_block_size.value_or(kIndefiniteSize); base::Optional<LayoutUnit> block_size; - if (!IsLogicalHeightTreatAsAuto(style)) { + if (!IsLogicalHeightTreatedAsAuto(style)) { block_size = ResolveMainBlockLength( space, style, border_padding, style.LogicalHeight(), child_block_size_or_indefinite, LengthResolvePhase::kLayout); @@ -473,10 +473,10 @@ void ComputeFullAbsoluteWithChildBlockSize( LayoutUnit min_block_size = ResolveMinBlockLength( space, style, border_padding, style.LogicalMinHeight(), - child_block_size_or_indefinite, LengthResolvePhase::kLayout); + LengthResolvePhase::kLayout); LayoutUnit max_block_size = ResolveMaxBlockLength( space, style, border_padding, style.LogicalMaxHeight(), - child_block_size_or_indefinite, LengthResolvePhase::kLayout); + LengthResolvePhase::kLayout); bool is_start_dominant; if (style.GetWritingMode() == WritingMode::kHorizontalTb) { @@ -490,15 +490,15 @@ void ComputeFullAbsoluteWithChildBlockSize( } ComputeAbsoluteSize( - border_padding.BlockSum(), child_minmax, + border_padding.BlockSum(), min_max_sizes, space.PercentageResolutionInlineSizeForParentWritingMode(), space.AvailableSize().block_size, style.MarginBefore(), style.MarginAfter(), style.LogicalTop(), style.LogicalBottom(), min_block_size, max_block_size, static_position.offset.block_offset, GetStaticPositionEdge(static_position.block_edge), is_start_dominant, - true /* is_block_direction */, block_size, &position->size.block_size, - &position->inset.block_start, &position->inset.block_end, - &position->margins.block_start, &position->margins.block_end); + true /* is_block_direction */, block_size, &dimensions->size.block_size, + &dimensions->inset.block_start, &dimensions->inset.block_end, + &dimensions->margins.block_start, &dimensions->margins.block_end); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h index bcfa41fc6a7..5c2f3694cb8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h @@ -9,7 +9,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" #include "third_party/blink/renderer/core/layout/geometry/physical_size.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h" namespace blink { @@ -19,7 +19,7 @@ class LayoutObject; class NGConstraintSpace; struct NGLogicalStaticPosition; -struct CORE_EXPORT NGLogicalOutOfFlowPosition { +struct CORE_EXPORT NGLogicalOutOfFlowDimensions { NGBoxStrut inset; LogicalSize size; NGBoxStrut margins; @@ -36,43 +36,43 @@ CORE_EXPORT base::Optional<LayoutUnit> ComputeAbsoluteDialogYPosition( // The following routines implement the absolute size resolution algorithm. // https://www.w3.org/TR/css-position-3/#abs-non-replaced-width // -// The size is computed as |NGLogicalOutOfFlowPosition|. +// The size is computed as |NGLogicalOutOfFlowDimensions|. // It needs to be computed in 4 stages: // 1. If |AbsoluteNeedsChildInlineSize| is true, compute estimated inline_size -// using |NGBlockNode::MinMaxSize|. -// 2. Compute part of the |NGLogicalOutOfFlowPosition| which depends on the -// child inline-size with |ComputePartialAbsoluteWithChildInlineSize|. +// using |NGBlockNode::ComputeMinMaxSize|. +// 2. Compute part of the |NGLogicalOutOfFlowDimensions| which depends on the +// child inline-size with |ComputeOutOfFlowInlineDimensions|. // 3. If |AbsoluteNeedsChildBlockSize| is true, compute estimated block_size by // performing layout with the inline_size calculated from (2). -// 4. Compute the full |NGLogicalOutOfFlowPosition| with -// |ComputeFullAbsoluteWithChildBlockSize|. +// 4. Compute the full |NGLogicalOutOfFlowDimensions| with +// |ComputeOutOfFlowBlockDimensions|. -// Returns true if |ComputePartialAbsoluteWithChildInlineSize| will need an -// estimated inline-size. +// Returns true if |ComputeOutOfFlowInlineDimensions| will need an estimated +// inline-size. CORE_EXPORT bool AbsoluteNeedsChildInlineSize(const ComputedStyle&); -// Returns true if |ComputeFullAbsoluteWithChildBlockSize| will need an -// estimated block-size. +// Returns true if |ComputeOutOfFlowBlockDimensions| will need an estimated +// block-size. CORE_EXPORT bool AbsoluteNeedsChildBlockSize(const ComputedStyle&); // Computes part of the absolute position which depends on the child's // inline-size. // |replaced_size| should be set if and only if element is replaced element. // Returns the partially filled position. -CORE_EXPORT NGLogicalOutOfFlowPosition -ComputePartialAbsoluteWithChildInlineSize( +CORE_EXPORT void ComputeOutOfFlowInlineDimensions( const NGConstraintSpace&, const ComputedStyle&, const NGBoxStrut& border_padding, const NGLogicalStaticPosition&, - const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSizes>& child_minmax, const base::Optional<LogicalSize>& replaced_size, const WritingMode container_writing_mode, - const TextDirection container_direction); + const TextDirection container_direction, + NGLogicalOutOfFlowDimensions* dimensions); // Computes the rest of the absolute position which depends on child's // block-size. -CORE_EXPORT void ComputeFullAbsoluteWithChildBlockSize( +CORE_EXPORT void ComputeOutOfFlowBlockDimensions( const NGConstraintSpace&, const ComputedStyle&, const NGBoxStrut& border_padding, @@ -81,7 +81,7 @@ CORE_EXPORT void ComputeFullAbsoluteWithChildBlockSize( const base::Optional<LogicalSize>& replaced_size, const WritingMode container_writing_mode, const TextDirection container_direction, - NGLogicalOutOfFlowPosition* position); + NGLogicalOutOfFlowDimensions* dimensions); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc index 86af90137d8..dac7b8f79c4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc @@ -118,10 +118,10 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { LayoutUnit width = container_size_.inline_size - left - margin_left - right - margin_right; - base::Optional<MinMaxSize> estimated_inline; + base::Optional<MinMaxSizes> estimated_inline; base::Optional<LayoutUnit> estimated_block; - MinMaxSize minmax_60{LayoutUnit(60) + horizontal_border_padding, - LayoutUnit(60) + horizontal_border_padding}; + MinMaxSizes min_max_60{LayoutUnit(60) + horizontal_border_padding, + LayoutUnit(60) + horizontal_border_padding}; style_->SetBorderLeftWidth(border_left.ToInt()); style_->SetBorderRightWidth(border_right.ToInt()); @@ -154,151 +154,154 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { // Tests. // - NGLogicalOutOfFlowPosition p; + NGLogicalOutOfFlowDimensions dimensions; // All auto => width is estimated_inline, left is 0. SetHorizontalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); - estimated_inline = minmax_60; - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(minmax_60.min_size, p.size.inline_size); - EXPECT_EQ(LayoutUnit(0), p.inset.inline_start); + estimated_inline = min_max_60; + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min_max_60.min_size, dimensions.size.inline_size); + EXPECT_EQ(LayoutUnit(0), dimensions.inset.inline_start); // All auto => width is estimated_inline, static_position is right SetHorizontalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); - estimated_inline = minmax_60; - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position_inline_end, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(minmax_60.min_size, p.size.inline_size); - EXPECT_EQ(container_size_.inline_size, p.inset.inline_end); + estimated_inline = min_max_60; + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position_inline_end, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min_max_60.min_size, dimensions.size.inline_size); + EXPECT_EQ(container_size_.inline_size, dimensions.inset.inline_end); // All auto + RTL. - p = ComputePartialAbsoluteWithChildInlineSize( - rtl_space_, *style_, rtl_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(minmax_60.min_size, p.size.inline_size); - EXPECT_EQ(container_size_.inline_size - minmax_60.min_size, - p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(rtl_space_, *style_, rtl_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min_max_60.min_size, dimensions.size.inline_size); + EXPECT_EQ(container_size_.inline_size - min_max_60.min_size, + dimensions.inset.inline_end); // left, right, and left are known, compute margins. SetHorizontalStyle(left, NGAuto, width, NGAuto, right); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - LayoutUnit margin_space = - (container_size_.inline_size - left - right - p.size.inline_size) / 2; - EXPECT_EQ(left + margin_space, p.inset.inline_start); - EXPECT_EQ(right + margin_space, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + LayoutUnit margin_space = (container_size_.inline_size - left - right - + dimensions.size.inline_size) / + 2; + EXPECT_EQ(left + margin_space, dimensions.inset.inline_start); + EXPECT_EQ(right + margin_space, dimensions.inset.inline_end); // left, right, and left are known, compute margins, writing mode vertical_lr. SetHorizontalStyle(left, NGAuto, width, NGAuto, right, WritingMode::kVerticalLr); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); estimated_inline.reset(); - ComputeFullAbsoluteWithChildBlockSize( - vlr_space_, *style_, vlr_border_padding, static_position, estimated_block, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(left + margin_space, p.inset.block_start); - EXPECT_EQ(right + margin_space, p.inset.block_end); + ComputeOutOfFlowBlockDimensions(vlr_space_, *style_, vlr_border_padding, + static_position, estimated_block, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(left + margin_space, dimensions.inset.block_start); + EXPECT_EQ(right + margin_space, dimensions.inset.block_end); // left, right, and left are known, compute margins, writing mode vertical_rl. SetHorizontalStyle(left, NGAuto, width, NGAuto, right, WritingMode::kVerticalRl); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); estimated_inline.reset(); - ComputeFullAbsoluteWithChildBlockSize( - vrl_space_, *style_, vrl_border_padding, static_position, estimated_block, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(left + margin_space, p.inset.block_end); - EXPECT_EQ(right + margin_space, p.inset.block_start); + ComputeOutOfFlowBlockDimensions(vrl_space_, *style_, vrl_border_padding, + static_position, estimated_block, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(left + margin_space, dimensions.inset.block_end); + EXPECT_EQ(right + margin_space, dimensions.inset.block_start); // left, right, and width are known, not enough space for margins LTR. SetHorizontalStyle(left, NGAuto, LayoutUnit(200), NGAuto, right); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(left, p.inset.inline_start); - EXPECT_EQ(-left, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(left, dimensions.inset.inline_start); + EXPECT_EQ(-left, dimensions.inset.inline_end); // left, right, and left are known, not enough space for margins RTL. SetHorizontalStyle(left, NGAuto, LayoutUnit(200), NGAuto, right, WritingMode::kHorizontalTb); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - rtl_space_, *style_, rtl_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kRtl); - EXPECT_EQ(-right, p.inset.inline_start); - EXPECT_EQ(right, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(rtl_space_, *style_, rtl_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kRtl, &dimensions); + EXPECT_EQ(-right, dimensions.inset.inline_start); + EXPECT_EQ(right, dimensions.inset.inline_end); // Rule 1 left and width are auto. SetHorizontalStyle(NGAuto, margin_left, NGAuto, margin_right, right); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); - estimated_inline = minmax_60; - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(minmax_60.min_size, p.size.inline_size); + estimated_inline = min_max_60; + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min_max_60.min_size, dimensions.size.inline_size); // Rule 2 left and right are auto LTR. SetHorizontalStyle(NGAuto, margin_left, width, margin_right, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(margin_left, p.inset.inline_start); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(margin_left, dimensions.inset.inline_start); EXPECT_EQ(container_size_.inline_size - margin_left - width, - p.inset.inline_end); + dimensions.inset.inline_end); // Rule 2 left and right are auto RTL. SetHorizontalStyle(NGAuto, margin_left, width, margin_right, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - rtl_space_, *style_, rtl_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(margin_left, p.inset.inline_start); + ComputeOutOfFlowInlineDimensions(rtl_space_, *style_, rtl_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(margin_left, dimensions.inset.inline_start); EXPECT_EQ(container_size_.inline_size - margin_left - width, - p.inset.inline_end); + dimensions.inset.inline_end); // Rule 3 width and right are auto. SetHorizontalStyle(left, margin_left, NGAuto, margin_right, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), true); - estimated_inline = minmax_60; - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); + estimated_inline = min_max_60; + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); EXPECT_EQ( - container_size_.inline_size - minmax_60.min_size - left - margin_left, - p.inset.inline_end); - EXPECT_EQ(minmax_60.min_size, p.size.inline_size); + container_size_.inline_size - min_max_60.min_size - left - margin_left, + dimensions.inset.inline_end); + EXPECT_EQ(min_max_60.min_size, dimensions.size.inline_size); // Rule 4: left is auto. SetHorizontalStyle(NGAuto, margin_left, width, margin_right, right); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(left + margin_left, p.inset.inline_start); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(left + margin_left, dimensions.inset.inline_start); // Rule 4: left is auto, EBoxSizing::kContentBox style_->SetBoxSizing(EBoxSizing::kContentBox); @@ -307,32 +310,32 @@ TEST_F(NGAbsoluteUtilsTest, Horizontal) { margin_right, right); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(left + margin_left, p.inset.inline_start); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(left + margin_left, dimensions.inset.inline_start); style_->SetBoxSizing(EBoxSizing::kBorderBox); // Rule 5: right is auto. SetHorizontalStyle(left, margin_left, width, margin_right, NGAuto); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(right + margin_right, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(right + margin_right, dimensions.inset.inline_end); // Rule 6: width is auto. SetHorizontalStyle(left, margin_left, NGAuto, margin_right, right); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); estimated_inline.reset(); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(width, p.size.inline_size); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(width, dimensions.size.inline_size); } TEST_F(NGAbsoluteUtilsTest, Vertical) { @@ -365,7 +368,7 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { style_->SetBorderRightWidth(0); base::Optional<LayoutUnit> auto_height; - MinMaxSize minmax_60{LayoutUnit(60), LayoutUnit(60)}; + MinMaxSizes min_max_60{LayoutUnit(60), LayoutUnit(60)}; NGBoxStrut ltr_border_padding = ComputeBordersForTest(*style_) + ComputePadding(ltr_space_, *style_); @@ -387,133 +390,145 @@ TEST_F(NGAbsoluteUtilsTest, Vertical) { // Tests // - NGLogicalOutOfFlowPosition p; + NGLogicalOutOfFlowDimensions dimensions; // All auto, compute margins. SetVerticalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(60); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(*auto_height, p.size.block_size); - EXPECT_EQ(LayoutUnit(0), p.inset.block_start); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(*auto_height, dimensions.size.block_size); + EXPECT_EQ(LayoutUnit(0), dimensions.inset.block_start); // All auto, static position bottom - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position_block_end, - auto_height, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr, &p); - EXPECT_EQ(container_size_.block_size, p.inset.block_end); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position_block_end, auto_height, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(container_size_.block_size, dimensions.inset.block_end); // If top, bottom, and height are known, compute margins. SetVerticalStyle(top, NGAuto, height, NGAuto, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); LayoutUnit margin_space = (container_size_.block_size - top - height - bottom) / 2; - EXPECT_EQ(top + margin_space, p.inset.block_start); - EXPECT_EQ(bottom + margin_space, p.inset.block_end); + EXPECT_EQ(top + margin_space, dimensions.inset.block_start); + EXPECT_EQ(bottom + margin_space, dimensions.inset.block_end); // If top, bottom, and height are known, writing mode vertical_lr. SetVerticalStyle(top, NGAuto, height, NGAuto, bottom, WritingMode::kVerticalLr); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); - p = ComputePartialAbsoluteWithChildInlineSize( - vlr_space_, *style_, vlr_border_padding, static_position, minmax_60, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); - EXPECT_EQ(top + margin_space, p.inset.inline_start); - EXPECT_EQ(bottom + margin_space, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(vlr_space_, *style_, vlr_border_padding, + static_position, min_max_60, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(top + margin_space, dimensions.inset.inline_start); + EXPECT_EQ(bottom + margin_space, dimensions.inset.inline_end); // If top, bottom, and height are known, writing mode vertical_rl. SetVerticalStyle(top, NGAuto, height, NGAuto, bottom, WritingMode::kVerticalRl); EXPECT_EQ(AbsoluteNeedsChildInlineSize(*style_), false); - p = ComputePartialAbsoluteWithChildInlineSize( - vrl_space_, *style_, vrl_border_padding, static_position, minmax_60, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr); - EXPECT_EQ(top + margin_space, p.inset.inline_start); - EXPECT_EQ(bottom + margin_space, p.inset.inline_end); + ComputeOutOfFlowInlineDimensions(vrl_space_, *style_, vrl_border_padding, + static_position, min_max_60, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(top + margin_space, dimensions.inset.inline_start); + EXPECT_EQ(bottom + margin_space, dimensions.inset.inline_end); // If top, bottom, and height are known, negative auto margins. LayoutUnit negative_margin_space = (container_size_.block_size - top - LayoutUnit(300) - bottom) / 2; SetVerticalStyle(top, NGAuto, LayoutUnit(300), NGAuto, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(top + negative_margin_space, p.inset.block_start); - EXPECT_EQ(bottom + negative_margin_space, p.inset.block_end); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(top + negative_margin_space, dimensions.inset.block_start); + EXPECT_EQ(bottom + negative_margin_space, dimensions.inset.block_end); // Rule 1: top and height are unknown. SetVerticalStyle(NGAuto, margin_top, NGAuto, margin_bottom, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(60); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(*auto_height, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(*auto_height, dimensions.size.block_size); // Rule 2: top and bottom are unknown. SetVerticalStyle(NGAuto, margin_top, height, margin_bottom, NGAuto); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(margin_top, p.inset.block_start); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(margin_top, dimensions.inset.block_start); EXPECT_EQ(container_size_.block_size - margin_top - height, - p.inset.block_end); + dimensions.inset.block_end); // Rule 3: height and bottom are unknown, auto_height < // horizontal_border_padding. SetVerticalStyle(top, margin_top, NGAuto, margin_bottom, NGAuto); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(20); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(horizontal_border_padding, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(horizontal_border_padding, dimensions.size.block_size); // Rule 3: height and bottom are unknown. SetVerticalStyle(top, margin_top, NGAuto, margin_bottom, NGAuto); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); auto_height = LayoutUnit(70); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(*auto_height, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(*auto_height, dimensions.size.block_size); // Rule 4: top is unknown. SetVerticalStyle(NGAuto, margin_top, height, margin_bottom, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(top + margin_top, p.inset.block_start); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(top + margin_top, dimensions.inset.block_start); // Rule 5: bottom is unknown. SetVerticalStyle(top, margin_top, height, margin_bottom, NGAuto); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(bottom + margin_bottom, p.inset.block_end); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(bottom + margin_bottom, dimensions.inset.block_end); // Rule 6: height is unknown. SetVerticalStyle(top, margin_top, NGAuto, margin_bottom, bottom); EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), false); auto_height.reset(); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(height, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(height, dimensions.size.block_size); } TEST_F(NGAbsoluteUtilsTest, CenterStaticPosition) { @@ -529,28 +544,31 @@ TEST_F(NGAbsoluteUtilsTest, CenterStaticPosition) { EXPECT_EQ(AbsoluteNeedsChildBlockSize(*style_), true); NGBoxStrut border_padding; - NGLogicalOutOfFlowPosition p = ComputePartialAbsoluteWithChildInlineSize( + NGLogicalOutOfFlowDimensions dimensions; + + ComputeOutOfFlowInlineDimensions( ltr_space_, *style_, border_padding, static_position, - MinMaxSize{LayoutUnit(), LayoutUnit(1000)}, base::nullopt, - WritingMode::kHorizontalTb, TextDirection::kLtr); - EXPECT_EQ(LayoutUnit(100), p.size.inline_size); - EXPECT_EQ(LayoutUnit(100), p.inset.inline_start); - EXPECT_EQ(LayoutUnit(), p.inset.inline_end); + MinMaxSizes{LayoutUnit(), LayoutUnit(1000)}, base::nullopt, + WritingMode::kHorizontalTb, TextDirection::kLtr, &dimensions); + EXPECT_EQ(LayoutUnit(100), dimensions.size.inline_size); + EXPECT_EQ(LayoutUnit(100), dimensions.inset.inline_start); + EXPECT_EQ(LayoutUnit(), dimensions.inset.inline_end); - p = ComputePartialAbsoluteWithChildInlineSize( + ComputeOutOfFlowInlineDimensions( ltr_space_, *style_, border_padding, static_position, - MinMaxSize{LayoutUnit(), LayoutUnit(1000)}, base::nullopt, - WritingMode::kHorizontalTb, TextDirection::kRtl); - EXPECT_EQ(LayoutUnit(100), p.size.inline_size); - EXPECT_EQ(LayoutUnit(100), p.inset.inline_start); - EXPECT_EQ(LayoutUnit(), p.inset.inline_end); - - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, border_padding, static_position, LayoutUnit(150), - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(LayoutUnit(150), p.size.block_size); - EXPECT_EQ(LayoutUnit(125), p.inset.block_start); - EXPECT_EQ(LayoutUnit(25), p.inset.block_end); + MinMaxSizes{LayoutUnit(), LayoutUnit(1000)}, base::nullopt, + WritingMode::kHorizontalTb, TextDirection::kRtl, &dimensions); + EXPECT_EQ(LayoutUnit(100), dimensions.size.inline_size); + EXPECT_EQ(LayoutUnit(100), dimensions.inset.inline_start); + EXPECT_EQ(LayoutUnit(), dimensions.inset.inline_end); + + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, border_padding, + static_position, LayoutUnit(150), + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(LayoutUnit(150), dimensions.size.block_size); + EXPECT_EQ(LayoutUnit(125), dimensions.inset.block_start); + EXPECT_EQ(LayoutUnit(25), dimensions.inset.block_end); } TEST_F(NGAbsoluteUtilsTest, MinMax) { @@ -569,34 +587,34 @@ TEST_F(NGAbsoluteUtilsTest, MinMax) { {LayoutUnit(), LayoutUnit()}, NGLogicalStaticPosition::kInlineStart, NGLogicalStaticPosition::kBlockStart}; - MinMaxSize estimated_inline{LayoutUnit(20), LayoutUnit(20)}; - NGLogicalOutOfFlowPosition p; + MinMaxSizes estimated_inline{LayoutUnit(20), LayoutUnit(20)}; + NGLogicalOutOfFlowDimensions dimensions; // WIDTH TESTS // width < min gets set to min. SetHorizontalStyle(NGAuto, NGAuto, LayoutUnit(5), NGAuto, NGAuto); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(min, p.size.inline_size); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min, dimensions.size.inline_size); // width > max gets set to max. SetHorizontalStyle(NGAuto, NGAuto, LayoutUnit(200), NGAuto, NGAuto); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(max, p.size.inline_size); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(max, dimensions.size.inline_size); - // Unspecified width becomes minmax, gets clamped to min. + // Unspecified width becomes min_max, gets clamped to min. SetHorizontalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); - p = ComputePartialAbsoluteWithChildInlineSize( - ltr_space_, *style_, ltr_border_padding, static_position, - estimated_inline, base::nullopt, WritingMode::kHorizontalTb, - TextDirection::kLtr); - EXPECT_EQ(min, p.size.inline_size); + ComputeOutOfFlowInlineDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, estimated_inline, + base::nullopt, WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min, dimensions.size.inline_size); // HEIGHT TESTS @@ -604,25 +622,28 @@ TEST_F(NGAbsoluteUtilsTest, MinMax) { // height < min gets set to min. SetVerticalStyle(NGAuto, NGAuto, LayoutUnit(5), NGAuto, NGAuto); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(min, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min, dimensions.size.block_size); // height > max gets set to max. SetVerticalStyle(NGAuto, NGAuto, LayoutUnit(200), NGAuto, NGAuto); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(max, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(max, dimensions.size.block_size); // // Unspecified height becomes estimated, gets clamped to min. SetVerticalStyle(NGAuto, NGAuto, NGAuto, NGAuto, NGAuto); auto_height = LayoutUnit(20); - ComputeFullAbsoluteWithChildBlockSize( - ltr_space_, *style_, ltr_border_padding, static_position, auto_height, - base::nullopt, WritingMode::kHorizontalTb, TextDirection::kLtr, &p); - EXPECT_EQ(min, p.size.block_size); + ComputeOutOfFlowBlockDimensions(ltr_space_, *style_, ltr_border_padding, + static_position, auto_height, base::nullopt, + WritingMode::kHorizontalTb, + TextDirection::kLtr, &dimensions); + EXPECT_EQ(min, dimensions.size.block_size); } } // namespace diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc index 7f919d2599f..1ddab49c2b9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc @@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" @@ -49,8 +50,8 @@ std::pair<scoped_refptr<const NGPhysicalBoxFragment>, NGConstraintSpace> NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithmForElement(Element* element) { auto* block_flow = To<LayoutBlockFlow>(element->GetLayoutObject()); NGBlockNode node(block_flow); - NGConstraintSpace space = NGConstraintSpace::CreateFromLayoutObject( - *block_flow, false /* is_layout_root */); + NGConstraintSpace space = + NGConstraintSpace::CreateFromLayoutObject(*block_flow); NGFragmentGeometry fragment_geometry = CalculateInitialFragmentGeometry(space, node); @@ -61,6 +62,22 @@ NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithmForElement(Element* element) { } scoped_refptr<const NGPhysicalBoxFragment> +NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + NGBlockNode node, + const NGConstraintSpace& space, + const NGBreakToken* break_token) { + NGFragmentGeometry fragment_geometry = + CalculateInitialFragmentGeometry(space, node); + + scoped_refptr<const NGLayoutResult> result = + NGFieldsetLayoutAlgorithm( + {node, fragment_geometry, space, To<NGBlockBreakToken>(break_token)}) + .Layout(); + + return To<NGPhysicalBoxFragment>(&result->PhysicalFragment()); +} + +scoped_refptr<const NGPhysicalBoxFragment> NGBaseLayoutAlgorithmTest::GetBoxFragmentByElementId(const char* id) { LayoutObject* layout_object = GetLayoutObjectByElementId(id); CHECK(layout_object && layout_object->IsLayoutNGMixin()); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h index 702637ee1bb..9f9fe176151 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h @@ -41,6 +41,11 @@ class NGBaseLayoutAlgorithmTest std::pair<scoped_refptr<const NGPhysicalBoxFragment>, NGConstraintSpace> RunBlockLayoutAlgorithmForElement(Element* element); + scoped_refptr<const NGPhysicalBoxFragment> RunFieldsetLayoutAlgorithm( + NGBlockNode node, + const NGConstraintSpace& space, + const NGBreakToken* break_token = nullptr); + scoped_refptr<const NGPhysicalBoxFragment> GetBoxFragmentByElementId( const char*); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc index 6e9781b9bdf..eea8efbb3da 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc @@ -12,7 +12,7 @@ namespace blink { namespace { struct SameSizeAsNGBlockBreakToken : NGBreakToken { - unsigned numbers[2]; + unsigned numbers[3]; }; static_assert(sizeof(NGBlockBreakToken) == sizeof(SameSizeAsNGBlockBreakToken), @@ -21,13 +21,16 @@ static_assert(sizeof(NGBlockBreakToken) == sizeof(SameSizeAsNGBlockBreakToken), } // namespace NGBlockBreakToken::NGBlockBreakToken( + PassKey key, NGLayoutInputNode node, LayoutUnit consumed_block_size, + unsigned sequence_number, const NGBreakTokenVector& child_break_tokens, NGBreakAppeal break_appeal, bool has_seen_all_children) : NGBreakToken(kBlockBreakToken, kUnfinished, node), consumed_block_size_(consumed_block_size), + sequence_number_(sequence_number), num_children_(child_break_tokens.size()) { break_appeal_ = break_appeal; has_seen_all_children_ = has_seen_all_children; @@ -37,7 +40,7 @@ NGBlockBreakToken::NGBlockBreakToken( } } -NGBlockBreakToken::NGBlockBreakToken(NGLayoutInputNode node) +NGBlockBreakToken::NGBlockBreakToken(PassKey key, NGLayoutInputNode node) : NGBreakToken(kBlockBreakToken, kUnfinished, node), num_children_(0) {} const NGInlineBreakToken* NGBlockBreakToken::InlineBreakTokenFor( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h index fc5ab4ba485..ee2bcc92fba 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h @@ -27,6 +27,7 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { static scoped_refptr<NGBlockBreakToken> Create( NGLayoutInputNode node, LayoutUnit consumed_block_size, + unsigned sequence_number, const NGBreakTokenVector& child_break_tokens, NGBreakAppeal break_appeal, bool has_seen_all_children) { @@ -37,7 +38,8 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { sizeof(NGBlockBreakToken) + child_break_tokens.size() * sizeof(NGBreakToken*), ::WTF::GetStringWithTypeName<NGBlockBreakToken>()); - new (data) NGBlockBreakToken(node, consumed_block_size, child_break_tokens, + new (data) NGBlockBreakToken(PassKey(), node, consumed_block_size, + sequence_number, child_break_tokens, break_appeal, has_seen_all_children); return base::AdoptRef(static_cast<NGBlockBreakToken*>(data)); } @@ -48,7 +50,7 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { static scoped_refptr<NGBlockBreakToken> CreateBreakBefore( NGLayoutInputNode node, bool is_forced_break) { - auto* token = new NGBlockBreakToken(node); + auto* token = new NGBlockBreakToken(PassKey(), node); token->is_break_before_ = true; token->is_forced_break_ = is_forced_break; return base::AdoptRef(token); @@ -68,6 +70,17 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { // the fragmentainer is shorter than 50px, for instance). LayoutUnit ConsumedBlockSize() const { return consumed_block_size_; } + // A unique identifier for a fragment that generates a break token. This is + // unique within the generating layout input node. The break token of the + // first fragment gets 0, then second 1, and so on. Note that we don't "count" + // break tokens that aren't associated with a fragment (this happens when we + // want a fragmentainer break before laying out the node). What the sequence + // number is for such a break token is undefined. + unsigned SequenceNumber() const { + DCHECK(!IsBreakBefore()); + return sequence_number_; + } + // Return true if this is a break token that was produced without any // "preceding" fragment. This happens when we determine that the first // fragment for a node needs to be created in a later fragmentainer than the @@ -102,18 +115,23 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken { String ToString() const override; #endif - private: + using PassKey = util::PassKey<NGBlockBreakToken>; + // Must only be called from Create(), because it assumes that enough space // has been allocated in the flexible array to store the children. - NGBlockBreakToken(NGLayoutInputNode node, + NGBlockBreakToken(PassKey, + NGLayoutInputNode node, LayoutUnit consumed_block_size, + unsigned sequence_number, const NGBreakTokenVector& child_break_tokens, NGBreakAppeal break_appeal, bool has_seen_all_children); - explicit NGBlockBreakToken(NGLayoutInputNode node); + explicit NGBlockBreakToken(PassKey, NGLayoutInputNode node); + private: LayoutUnit consumed_block_size_; + unsigned sequence_number_ = 0; wtf_size_t num_children_; // This must be the last member, because it is a flexible array. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc index 917eb7864f5..01b46282762 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc @@ -58,19 +58,19 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) { NGBreakTokenVector empty_tokens_list; scoped_refptr<NGBreakToken> child_token1 = NGBlockBreakToken::Create( - node1, LayoutUnit(), empty_tokens_list, kBreakAppealPerfect, + node1, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect, /* has_seen_all_children */ false); scoped_refptr<NGBreakToken> child_token2 = NGBlockBreakToken::Create( - node2, LayoutUnit(), empty_tokens_list, kBreakAppealPerfect, + node2, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect, /* has_seen_all_children */ false); scoped_refptr<NGBreakToken> child_token3 = NGBlockBreakToken::Create( - node3, LayoutUnit(), empty_tokens_list, kBreakAppealPerfect, + node3, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect, /* has_seen_all_children */ false); NGBreakTokenVector child_break_tokens; child_break_tokens.push_back(child_token1); scoped_refptr<NGBlockBreakToken> parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ false); NGBlockChildIterator iterator(node1, parent_token.get()); @@ -86,7 +86,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) { child_break_tokens.push_back(child_token1); child_break_tokens.push_back(child_token2); parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ false); iterator = NGBlockChildIterator(node1, parent_token.get()); @@ -103,7 +103,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) { child_break_tokens.push_back(child_token2); child_break_tokens.push_back(child_token3); parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ false); iterator = NGBlockChildIterator(node1, parent_token.get()); @@ -119,7 +119,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) { child_break_tokens.push_back(child_token1); child_break_tokens.push_back(child_token3); parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ false); iterator = NGBlockChildIterator(node1, parent_token.get()); @@ -145,13 +145,13 @@ TEST_F(NGBlockChildIteratorTest, SeenAllChildren) { NGBreakTokenVector empty_tokens_list; scoped_refptr<NGBreakToken> child_token1 = NGBlockBreakToken::Create( - node1, LayoutUnit(), empty_tokens_list, kBreakAppealPerfect, + node1, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect, /* has_seen_all_children */ false); NGBreakTokenVector child_break_tokens; child_break_tokens.push_back(child_token1); scoped_refptr<NGBlockBreakToken> parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ true); // We have a break token for #child1, but have seen all children. This happens @@ -166,7 +166,7 @@ TEST_F(NGBlockChildIteratorTest, SeenAllChildren) { child_break_tokens.clear(); parent_token = NGBlockBreakToken::Create( - container, LayoutUnit(), child_break_tokens, kBreakAppealPerfect, + container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect, /* has_seen_all_children */ true); // We have no break tokens, but have seen all children. This happens e.g. when 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 ec88e7d665b..9d0e5660942 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 @@ -16,6 +16,7 @@ #include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" @@ -30,7 +31,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" -#include "third_party/blink/renderer/core/layout/text_autosizer.h" #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -53,7 +53,8 @@ inline scoped_refptr<const NGLayoutResult> LayoutBlockChild( // child. DCHECK(early_break_in_child); } - return node->Layout(space, break_token, early_break_in_child); + return node->Layout(space, To<NGBlockBreakToken>(break_token), + early_break_in_child); } inline scoped_refptr<const NGLayoutResult> LayoutInflow( @@ -180,6 +181,8 @@ NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( params.fragment_geometry.scrollbar), 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( @@ -195,10 +198,10 @@ void NGBlockLayoutAlgorithm::SetBoxType(NGPhysicalFragment::NGBoxType type) { container_builder_.SetBoxType(type); } -base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { - base::Optional<MinMaxSize> sizes = CalculateMinMaxSizesIgnoringChildren( - node_, border_scrollbar_padding_, input.size_type); +base::Optional<MinMaxSizes> NGBlockLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + base::Optional<MinMaxSizes> sizes = + CalculateMinMaxSizesIgnoringChildren(node_, border_scrollbar_padding_); if (sizes) return sizes; @@ -242,13 +245,13 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( float_right_inline_size = LayoutUnit(); } - MinMaxSizeInput child_input(child_percentage_resolution_block_size); + MinMaxSizesInput child_input(child_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; } - MinMaxSize child_sizes; + MinMaxSizes child_sizes; if (child.IsInline()) { // From |NGBlockLayoutAlgorithm| perspective, we can handle |NGInlineNode| // almost the same as |NGBlockNode|, because an |NGInlineNode| includes @@ -257,7 +260,7 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( // |NextSibling| returns the next block sibling, or nullptr, skipping all // following inline siblings and descendants. child_sizes = - child.ComputeMinMaxSize(Style().GetWritingMode(), child_input); + child.ComputeMinMaxSizes(Style().GetWritingMode(), child_input); } else { child_sizes = ComputeMinAndMaxContentContribution(Style(), child, child_input); @@ -331,8 +334,7 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize( DCHECK_GE(sizes->min_size, LayoutUnit()); DCHECK_LE(sizes->min_size, sizes->max_size) << Node().ToString(); - if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) - *sizes += border_scrollbar_padding_.InlineSum(); + *sizes += border_scrollbar_padding_.InlineSum(); return sizes; } @@ -364,7 +366,7 @@ 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().ChildrenInline()) + if (Node().IsInlineFormattingContextRoot()) result = LayoutWithInlineChildLayoutContext(); else result = Layout(nullptr); @@ -374,6 +376,11 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { DCHECK(!early_break_); DCHECK(result->GetEarlyBreak()); return RelayoutAndBreakEarlier(*result->GetEarlyBreak()); + } else if (UNLIKELY(result->Status() == + NGLayoutResult:: + kNeedsRelayoutWithNoForcedTruncateAtLineClamp)) { + DCHECK(force_truncate_at_line_clamp_); + return RelayoutNoForcedTruncateForLineClamp(); } return result; } @@ -417,6 +424,19 @@ NGBlockLayoutAlgorithm::RelayoutAndBreakEarlier( return algorithm_with_break.Layout(); } +NOINLINE scoped_refptr<const NGLayoutResult> +NGBlockLayoutAlgorithm::RelayoutNoForcedTruncateForLineClamp() { + 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; + NGBoxFragmentBuilder& new_builder = + algorithm_with_forced_truncate.container_builder_; + new_builder.SetBoxType(container_builder_.BoxType()); + return algorithm_with_forced_truncate.Layout(); +} + inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( NGInlineChildLayoutContext* inline_child_layout_context) { const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize(); @@ -436,6 +456,10 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( ConstraintSpace(), Style(), container_builder_.Scrollbar()); } + DCHECK_EQ(!!inline_child_layout_context, + 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 @@ -463,6 +487,10 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( container_builder_.SetAdjoiningObjectTypes(adjoining_object_types); } + if (Style().IsDeprecatedWebkitBoxWithVerticalLineClamp() && + RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled()) + lines_until_clamp_ = Style().LineClamp(); + LayoutUnit content_edge = border_scrollbar_padding_.block_start; NGPreviousInflowPosition previous_inflow_position = { @@ -480,8 +508,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( // // In all those cases we can and must resolve the BFC block offset now. if (border_scrollbar_padding_.block_start || is_resuming_ || - ConstraintSpace().IsNewFormattingContext() || - Style().MarginBeforeCollapse() != EMarginCollapse::kCollapse) { + ConstraintSpace().IsNewFormattingContext()) { bool discard_subsequent_margins = previous_inflow_position.margin_strut.discard_margins && !border_scrollbar_padding_.block_start; @@ -537,12 +564,6 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( if (node_.IsQuirkyContainer()) previous_inflow_position.margin_strut.is_quirky_container_start = true; - // Before we descend into children (but after we have determined our inline - // size), give the autosizer an opportunity to adjust the font size on the - // children. - TextAutosizer::NGLayoutScope text_autosizer_layout_scope( - Node(), border_box_size.inline_size); - // Try to reuse line box fragments from cached fragments if possible. // When possible, this adds fragments to |container_builder_| and update // |previous_inflow_position| and |BreakToken()|. @@ -650,6 +671,18 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout( } } + if (UNLIKELY(ConstraintSpace().IsNewFormattingContext() && + force_truncate_at_line_clamp_ && + intrinsic_block_size_when_clamped_ && lines_until_clamp_ == 0)) { + // 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 + // the node may have subsequent lines. If there aren't, the containing + // element will relayout. + return container_builder_.Abort( + NGLayoutResult::kNeedsRelayoutWithNoForcedTruncateAtLineClamp); + } + if (child_iterator.IsAtEnd()) { // We've gone through all the children. This doesn't necessarily mean that // we're done fragmenting, as there may be parallel flows [1] (visible @@ -685,15 +718,22 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( intrinsic_block_size_, exclusion_space_.ClearanceOffset(EClear::kBoth)); } - // The end margin strut of an in-flow fragment contributes to the size of the - // current fragment if: - // - There is block-end border/scrollbar/padding. - // - There was a self-collapsing child affected by clearance. - // - We are a new formatting context. - // Additionally this fragment produces no end margin strut. - if (border_scrollbar_padding_.block_end || - previous_inflow_position->self_collapsing_child_had_clearance || - ConstraintSpace().IsNewFormattingContext()) { + // If line clamping occurred, the intrinsic block-size comes from the + // intrinsic block-size at the time of the clamp. + if (intrinsic_block_size_when_clamped_) { + DCHECK(container_builder_.BfcBlockOffset()); + intrinsic_block_size_ = *intrinsic_block_size_when_clamped_; + end_margin_strut = NGMarginStrut(); + } else if (border_scrollbar_padding_.block_end || + previous_inflow_position->self_collapsing_child_had_clearance || + ConstraintSpace().IsNewFormattingContext()) { + // The end margin strut of an in-flow fragment contributes to the size of + // the current fragment if: + // - There is block-end border/scrollbar/padding. + // - There was a self-collapsing child affected by clearance. + // - We are a new formatting context. + // Additionally this fragment produces no end margin strut. + // // If we are a quirky container, we ignore any quirky margins and // just consider normal margins to extend our size. Other UAs // perform this calculation differently, e.g. by just ignoring the @@ -741,7 +781,7 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( } // Save the unconstrained intrinsic size on the builder before clamping it. - container_builder_.SetUnconstrainedIntrinsicBlockSize(intrinsic_block_size_); + container_builder_.SetOverflowBlockSize(intrinsic_block_size_); intrinsic_block_size_ = ClampIntrinsicBlockSize( ConstraintSpace(), Node(), border_scrollbar_padding_, @@ -827,11 +867,14 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout( container_builder_.CheckNoBlockFragmentation(); #endif - PropagateBaselinesFromChildren(); + // Adjust the position of the final baseline if needed. + FinalizeBaseline(); // An exclusion space is confined to nodes within the same formatting context. - if (!ConstraintSpace().IsNewFormattingContext()) + if (!ConstraintSpace().IsNewFormattingContext()) { container_builder_.SetExclusionSpace(std::move(exclusion_space_)); + container_builder_.SetLinesUntilClamp(lines_until_clamp_); + } if (ConstraintSpace().UseFirstLineStyle()) container_builder_.SetStyleVariant(NGStyleVariant::kFirstLine); @@ -1016,6 +1059,10 @@ void NGBlockLayoutAlgorithm::HandleFloat( PositionFloat(&unpositioned_float, &exclusion_space_); const NGLayoutResult& layout_result = *positioned_float.layout_result; + + // TODO(mstensho): Handle abortions caused by block fragmentation. + DCHECK_EQ(layout_result.Status(), NGLayoutResult::kSuccess); + const auto& physical_fragment = layout_result.PhysicalFragment(); if (const NGBreakToken* token = physical_fragment.BreakToken()) { DCHECK(ConstraintSpace().HasBlockFragmentation()); @@ -1230,13 +1277,6 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleNewFormattingContext( /* abort_if_cleared */ false, &child_bfc_offset); } - NGFragment fragment(ConstraintSpace().GetWritingMode(), - layout_result->PhysicalFragment()); - - LogicalOffset logical_offset = LogicalFromBfcOffsets( - child_bfc_offset, ContainerBfcOffset(), fragment.InlineSize(), - container_builder_.Size().inline_size, ConstraintSpace().Direction()); - if (ConstraintSpace().HasBlockFragmentation()) { bool has_container_separation = has_processed_first_child_ || child_margin_got_separated || @@ -1244,20 +1284,32 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleNewFormattingContext( layout_result->IsPushedByFloats(); NGBreakStatus break_status = BreakBeforeChildIfNeeded( child, *layout_result, previous_inflow_position, - logical_offset.block_offset, has_container_separation); + child_bfc_offset.block_offset, has_container_separation); if (break_status == NGBreakStatus::kBrokeBefore) return NGLayoutResult::kSuccess; if (break_status == NGBreakStatus::kNeedsEarlierBreak) return NGLayoutResult::kNeedsEarlierBreak; + + // If the child aborted layout, we cannot continue. + DCHECK_EQ(layout_result->Status(), NGLayoutResult::kSuccess); + EBreakBetween break_after = JoinFragmentainerBreakValues( layout_result->FinalBreakAfter(), child.Style().BreakAfter()); container_builder_.SetPreviousBreakAfter(break_after); } + const auto& physical_fragment = layout_result->PhysicalFragment(); + NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment); + + LogicalOffset logical_offset = LogicalFromBfcOffsets( + child_bfc_offset, ContainerBfcOffset(), fragment.InlineSize(), + container_builder_.Size().inline_size, ConstraintSpace().Direction()); + if (!PositionOrPropagateListMarker(*layout_result, &logical_offset, previous_inflow_position)) return NGLayoutResult::kBfcBlockOffsetResolved; + PropagateBaselineFromChild(physical_fragment, logical_offset.block_offset); container_builder_.AddResult(*layout_result, logical_offset); // The margins we store will be used by e.g. getComputedStyle(). @@ -1306,7 +1358,7 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( // fit where it was laid out, and is pushed downwards, we'll lay out over // again, since a new BFC block offset could result in a new fragment size, // e.g. when inline size is auto, or if we're block-fragmented. - for (const auto opportunity : opportunities) { + for (const auto& opportunity : opportunities) { if (abort_if_cleared && origin_offset.block_offset < opportunity.rect.BlockStartOffset()) { // Abort if we got pushed downwards. We need to adjust @@ -1378,6 +1430,12 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext( // should be returned. DCHECK(layout_result->ExclusionSpace().IsEmpty()); + if (layout_result->Status() != NGLayoutResult::kSuccess) { + DCHECK_EQ(layout_result->Status(), + NGLayoutResult::kOutOfFragmentainerSpace); + return layout_result; + } + NGFragment fragment(writing_mode, layout_result->PhysicalFragment()); // Check if the fragment will fit in this layout opportunity, if not proceed @@ -1730,7 +1788,8 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow( LogicalOffset logical_offset = CalculateLogicalOffset( fragment, layout_result->BfcLineOffset(), child_bfc_block_offset); - if (ConstraintSpace().HasBlockFragmentation()) { + if (ConstraintSpace().HasBlockFragmentation() && + container_builder_.BfcBlockOffset() && child_bfc_block_offset) { // Floats only cause container separation for the outermost block child that // gets pushed down (the container and the child may have adjoining // block-start margins). @@ -1739,7 +1798,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow( !container_builder_.IsPushedByFloats()); NGBreakStatus break_status = BreakBeforeChildIfNeeded( child, *layout_result, previous_inflow_position, - logical_offset.block_offset, has_container_separation); + *child_bfc_block_offset, has_container_separation); if (break_status == NGBreakStatus::kBrokeBefore) return NGLayoutResult::kSuccess; if (break_status == NGBreakStatus::kNeedsEarlierBreak) @@ -1753,6 +1812,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow( previous_inflow_position)) return NGLayoutResult::kBfcBlockOffsetResolved; + PropagateBaselineFromChild(physical_fragment, logical_offset.block_offset); container_builder_.AddResult(*layout_result, logical_offset); if (auto* block_child = DynamicTo<NGBlockNode>(child)) { @@ -1795,6 +1855,24 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow( } } + // Update |lines_until_clamp_| from the LayoutResult. + if (lines_until_clamp_) { + if (const auto* line_box = + DynamicTo<NGPhysicalLineBoxFragment>(physical_fragment)) { + if (!line_box->IsEmptyLineBox()) + lines_until_clamp_ = *lines_until_clamp_ - 1; + } else { + lines_until_clamp_ = layout_result->LinesUntilClamp(); + } + if (lines_until_clamp_ <= 0 && + !intrinsic_block_size_when_clamped_.has_value()) { + // If line-clamping occurred save the intrinsic block-size, as this + // becomes the final intrinsic block-size. + intrinsic_block_size_when_clamped_ = + previous_inflow_position->logical_block_offset + + border_scrollbar_padding_.block_end; + } + } return NGLayoutResult::kSuccess; } @@ -1832,32 +1910,9 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData( LayoutUnit logical_block_offset = previous_inflow_position.logical_block_offset; - EMarginCollapse margin_before_collapse = child.Style().MarginBeforeCollapse(); - if (margin_before_collapse != EMarginCollapse::kCollapse) { - // Stop margin collapsing on the block-start side of the child. - StopMarginCollapsing(child.Style().MarginBeforeCollapse(), - margins.block_start, &logical_block_offset, - &margin_strut); - - if (margin_before_collapse == EMarginCollapse::kSeparate) { - UseCounter::Count(Node().GetDocument(), - WebFeature::kWebkitMarginBeforeCollapseSeparate); - if (margin_strut != previous_inflow_position.margin_strut || - logical_block_offset != - previous_inflow_position.logical_block_offset) { - UseCounter::Count( - Node().GetDocument(), - WebFeature::kWebkitMarginBeforeCollapseSeparateMaybeDoesSomething); - } - } else if (margin_before_collapse == EMarginCollapse::kDiscard) { - UseCounter::Count(Node().GetDocument(), - WebFeature::kWebkitMarginBeforeCollapseDiscard); - } - } else { - margin_strut.Append(margins.block_start, - child.Style().HasMarginBeforeQuirk()); - SetSubtreeModifiedMarginStrutIfNeeded(&child.Style().MarginBefore()); - } + margin_strut.Append(margins.block_start, + child.Style().HasMarginBeforeQuirk()); + SetSubtreeModifiedMarginStrutIfNeeded(&child.Style().MarginBefore()); NGBfcOffset child_bfc_offset = { ConstraintSpace().BfcOffset().line_offset + @@ -1945,36 +2000,14 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition( NGMarginStrut margin_strut = layout_result.EndMarginStrut(); - EMarginCollapse margin_after_collapse = child.Style().MarginAfterCollapse(); - if (margin_after_collapse != EMarginCollapse::kCollapse) { - LayoutUnit logical_block_offset_copy = logical_block_offset; - // Stop margin collapsing on the block-end side of the child. - StopMarginCollapsing(margin_after_collapse, child_data.margins.block_end, - &logical_block_offset, &margin_strut); - - if (margin_after_collapse == EMarginCollapse::kSeparate) { - UseCounter::Count(Node().GetDocument(), - WebFeature::kWebkitMarginAfterCollapseSeparate); - if (margin_strut != layout_result.EndMarginStrut() || - logical_block_offset != logical_block_offset_copy) { - UseCounter::Count( - Node().GetDocument(), - WebFeature::kWebkitMarginAfterCollapseSeparateMaybeDoesSomething); - } - } else if (margin_after_collapse == EMarginCollapse::kDiscard) { - UseCounter::Count(Node().GetDocument(), - WebFeature::kWebkitMarginAfterCollapseDiscard); - } - } else { - // Self collapsing child's end margin can "inherit" quirkiness from its - // start margin. E.g. - // <ol style="margin-bottom: 20px"></ol> - bool is_quirky = - (is_self_collapsing && child.Style().HasMarginBeforeQuirk()) || - child.Style().HasMarginAfterQuirk(); - margin_strut.Append(child_data.margins.block_end, is_quirky); - SetSubtreeModifiedMarginStrutIfNeeded(&child.Style().MarginAfter()); - } + // Self collapsing child's end margin can "inherit" quirkiness from its start + // margin. E.g. + // <ol style="margin-bottom: 20px"></ol> + bool is_quirky = + (is_self_collapsing && child.Style().HasMarginBeforeQuirk()) || + child.Style().HasMarginAfterQuirk(); + margin_strut.Append(child_data.margins.block_end, is_quirky); + SetSubtreeModifiedMarginStrutIfNeeded(&child.Style().MarginAfter()); // This flag is subtle, but in order to determine our size correctly we need // to check if our last child is self-collapsing, and it was affected by @@ -2044,7 +2077,7 @@ void NGBlockLayoutAlgorithm::SetFragmentainerOutOfSpace( } bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() { - if (Node().ChildrenInline() && !early_break_) { + if (Node().IsInlineFormattingContextRoot() && !early_break_) { if (container_builder_.DidBreak() || first_overflowing_line_) { if (first_overflowing_line_ && first_overflowing_line_ < container_builder_.LineCount()) { @@ -2112,8 +2145,8 @@ bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() { } } - FinishFragmentation(ConstraintSpace(), block_size, intrinsic_block_size_, - consumed_block_size, space_left, &container_builder_); + FinishFragmentation(ConstraintSpace(), BreakToken(), block_size, + intrinsic_block_size_, space_left, &container_builder_); return true; } @@ -2122,14 +2155,13 @@ NGBreakStatus NGBlockLayoutAlgorithm::BreakBeforeChildIfNeeded( NGLayoutInputNode child, const NGLayoutResult& layout_result, NGPreviousInflowPosition* previous_inflow_position, - LayoutUnit block_offset, + LayoutUnit bfc_block_offset, bool has_container_separation) { DCHECK(ConstraintSpace().HasBlockFragmentation()); // If the BFC offset is unknown, there's nowhere to break, since there's no // non-empty child content yet (as that would have resolved the BFC offset). - if (!container_builder_.BfcBlockOffset()) - return NGBreakStatus::kContinue; + DCHECK(container_builder_.BfcBlockOffset()); // If we already know where to insert the break, we already know that it's not // going to be here, since that's something we check before entering layout of @@ -2138,8 +2170,7 @@ NGBreakStatus NGBlockLayoutAlgorithm::BreakBeforeChildIfNeeded( return NGBreakStatus::kContinue; LayoutUnit fragmentainer_block_offset = - ConstraintSpace().FragmentainerOffsetAtBfc() + - *container_builder_.BfcBlockOffset() + block_offset; + ConstraintSpace().FragmentainerOffsetAtBfc() + bfc_block_offset; if (has_container_separation) { EBreakBetween break_between = @@ -2315,7 +2346,7 @@ NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( NGConstraintSpace space = builder.ToConstraintSpace(); NGBoxStrut child_border_padding = - ComputeBorders(space, child) + ComputePadding(space, child.Style()); + ComputeBorders(space, child_style) + ComputePadding(space, child_style); LayoutUnit child_inline_size = ComputeInlineSizeForFragment(space, child, child_border_padding); @@ -2327,29 +2358,6 @@ NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( return margins; } -// Stop margin collapsing on one side of a block when -// -webkit-margin-{after,before}-collapse is something other than 'collapse' -// (the initial value) -void NGBlockLayoutAlgorithm::StopMarginCollapsing( - EMarginCollapse collapse_value, - LayoutUnit this_margin, - LayoutUnit* logical_block_offset, - NGMarginStrut* margin_strut) { - DCHECK_NE(collapse_value, EMarginCollapse::kCollapse); - if (collapse_value == EMarginCollapse::kSeparate) { - // Separate margins between previously adjoining margins and this margin, - // AND between this margin and adjoining margins to come. - *logical_block_offset += margin_strut->Sum() + this_margin; - *margin_strut = NGMarginStrut(); - return; - } - DCHECK_EQ(collapse_value, EMarginCollapse::kDiscard); - // Discard previously adjoining margins, this margin AND all adjoining margins - // to come, so that the sum becomes 0. - margin_strut->discard_margins = true; - SetSubtreeModifiedMarginStrutIfNeeded(); -} - NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( const NGLayoutInputNode child, const NGInflowChildData& child_data, @@ -2369,6 +2377,9 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( if (!IsParallelWritingMode(ConstraintSpace().GetWritingMode(), child_writing_mode)) builder.SetIsShrinkToFit(child_style.LogicalWidth().IsAuto()); + if (child_style.LogicalWidth().IsAuto() && + child.GetLayoutBox()->AutoWidthShouldFitContent()) + builder.SetIsShrinkToFit(true); builder.SetAvailableSize(child_available_size); builder.SetPercentageResolutionSize(child_percentage_size_); @@ -2387,9 +2398,6 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( builder.SetTableCellChildLayoutMode(mode); } - if (NGBaseline::ShouldPropagateBaselines(child)) - builder.AddBaselineRequests(ConstraintSpace().BaselineRequests()); - bool has_bfc_block_offset = container_builder_.BfcBlockOffset().has_value(); // Propagate the |NGConstraintSpace::ForcedBfcBlockOffset| down to our @@ -2447,11 +2455,10 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( clearance_offset = std::max(clearance_offset, child_clearance_offset); builder.SetTextDirection(child_style.Direction()); - // PositionListMarker() requires a first line baseline. - if (container_builder_.UnpositionedListMarker()) { - builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kFirstLine, style.GetFontBaseline()}); - } + // |PositionListMarker()| requires a baseline. + builder.SetNeedsBaseline(ConstraintSpace().NeedsBaseline() || + container_builder_.UnpositionedListMarker()); + builder.SetBaselineAlgorithmType(ConstraintSpace().BaselineAlgorithmType()); } else { builder.SetTextDirection(style.Direction()); } @@ -2465,6 +2472,8 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( builder.SetAdjoiningObjectTypes( 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 @@ -2479,109 +2488,78 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild( // fragmentation line. if (is_new_fc) fragmentainer_offset_delta = *child_bfc_block_offset; - SetupFragmentation(ConstraintSpace(), fragmentainer_offset_delta, &builder, - is_new_fc); + SetupFragmentation(ConstraintSpace(), child, fragmentainer_offset_delta, + &builder, is_new_fc); builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal()); } return builder.ToConstraintSpace(); } -LayoutUnit NGBlockLayoutAlgorithm::ComputeLineBoxBaselineOffset( - const NGBaselineRequest& request, - const NGPhysicalLineBoxFragment& line_box, - LayoutUnit line_box_block_offset) const { - NGLineHeightMetrics metrics = - line_box.BaselineMetrics(request.BaselineType()); - DCHECK(!metrics.IsEmpty()); - - // NGLineHeightMetrics is line-relative, which matches to the flow-relative - // unless this box is in flipped-lines writing-mode. - if (!Style().IsFlippedLinesWritingMode()) - return metrics.ascent + line_box_block_offset; - - if (Node().IsInlineLevel()) { - // If this box is inline-level, since we're in NGBlockLayoutAlgorithm, this - // is an inline-block. - DCHECK(Node().IsAtomicInlineLevel()); - // This box will be flipped when the containing line is flipped. Compute the - // baseline offset from the block-end (right in vertical-lr) content edge. - line_box_block_offset = container_builder_.Size().block_size - - (line_box_block_offset + line_box.Size().width); - return metrics.ascent + line_box_block_offset; - } - - // Otherwise, the baseline is offset by the descent from the block-start - // content edge. - return metrics.descent + line_box_block_offset; -} +void NGBlockLayoutAlgorithm::PropagateBaselineFromChild( + const NGPhysicalContainerFragment& child, + LayoutUnit block_offset) { + // Check if we've already found an appropriate baseline. + if (container_builder_.Baseline() && + ConstraintSpace().BaselineAlgorithmType() == + NGBaselineAlgorithmType::kFirstLine) + return; -// Add a baseline from a child box fragment. -// @return false if the specified child is not a box or is OOF. -bool NGBlockLayoutAlgorithm::AddBaseline(const NGBaselineRequest& request, - const NGPhysicalFragment& child, - LayoutUnit child_offset) { if (child.IsLineBox()) { const auto& line_box = To<NGPhysicalLineBoxFragment>(child); - // Skip over a line-box which is empty. These don't have any baselines which - // should be added. + // Skip over a line-box which is empty. These don't have any baselines + // which should be added. if (line_box.IsEmptyLineBox()) - return false; + return; - LayoutUnit offset = - ComputeLineBoxBaselineOffset(request, line_box, child_offset); - container_builder_.AddBaseline(request, offset); - return true; + NGLineHeightMetrics metrics = line_box.BaselineMetrics(); + DCHECK(!metrics.IsEmpty()); + LayoutUnit baseline = + block_offset + (Style().IsFlippedLinesWritingMode() ? metrics.descent + : metrics.ascent); + + if (!container_builder_.Baseline()) + container_builder_.SetBaseline(baseline); + + // Set the last baseline only if required. + if (ConstraintSpace().BaselineAlgorithmType() != + NGBaselineAlgorithmType::kFirstLine) + container_builder_.SetLastBaseline(baseline); + + return; } - if (child.IsFloatingOrOutOfFlowPositioned()) - return false; + NGBoxFragment fragment(ConstraintSpace().GetWritingMode(), + ConstraintSpace().Direction(), + To<NGPhysicalBoxFragment>(child)); - if (const auto* box = DynamicTo<NGPhysicalBoxFragment>(child)) { - if (base::Optional<LayoutUnit> baseline = box->Baseline(request)) { - container_builder_.AddBaseline(request, *baseline + child_offset); - return true; - } + if (!container_builder_.Baseline()) { + if (auto baseline = fragment.FirstBaseline()) + container_builder_.SetBaseline(block_offset + *baseline); } - return false; + // Set the last baseline only if required. + if (ConstraintSpace().BaselineAlgorithmType() != + NGBaselineAlgorithmType::kFirstLine) { + if (auto last_baseline = fragment.Baseline()) + container_builder_.SetLastBaseline(block_offset + *last_baseline); + } } -// Propagate computed baselines from children. -// Skip children that do not produce baselines (e.g., empty blocks.) -void NGBlockLayoutAlgorithm::PropagateBaselinesFromChildren() { - const NGBaselineRequestList requests = ConstraintSpace().BaselineRequests(); - if (requests.IsEmpty()) +void NGBlockLayoutAlgorithm::FinalizeBaseline() { + if (ConstraintSpace().BaselineAlgorithmType() != + NGBaselineAlgorithmType::kInlineBlock) return; - for (const auto& request : requests) { - switch (request.AlgorithmType()) { - case NGBaselineAlgorithmType::kAtomicInline: { - if (Node().UseLogicalBottomMarginEdgeForInlineBlockBaseline()) { - LayoutUnit block_end = container_builder_.BlockSize(); - NGBoxStrut margins = - ComputeMarginsForSelf(ConstraintSpace(), Style()); - container_builder_.AddBaseline(request, - block_end + margins.block_end); - break; - } + if (!Node().UseLogicalBottomMarginEdgeForInlineBlockBaseline()) + return; - const auto& children = container_builder_.Children(); - for (auto it = children.rbegin(); it != children.rend(); ++it) { - if (AddBaseline(request, *it->fragment, it->offset.block_offset)) - break; - } - break; - } - case NGBaselineAlgorithmType::kFirstLine: - for (const auto& child : container_builder_.Children()) { - if (AddBaseline(request, *child.fragment, child.offset.block_offset)) - break; - } - break; - } - } + // 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( @@ -2687,12 +2665,11 @@ bool NGBlockLayoutAlgorithm::PositionOrPropagateListMarker( container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker()); } - NGLineHeightMetrics content_metrics; const NGConstraintSpace& space = ConstraintSpace(); const NGPhysicalFragment& content = layout_result.PhysicalFragment(); FontBaseline baseline_type = Style().GetFontBaseline(); - if (list_marker.CanAddToBox(space, baseline_type, content, - &content_metrics)) { + if (auto content_baseline = + list_marker.ContentAlignmentBaseline(space, baseline_type, content)) { // TODO: We are reusing the ConstraintSpace for LI here. It works well for // now because authors cannot style list-markers currently. If we want to // support `::marker` pseudo, we need to create ConstraintSpace for marker @@ -2713,8 +2690,8 @@ bool NGBlockLayoutAlgorithm::PositionOrPropagateListMarker( } list_marker.AddToBox(space, baseline_type, content, - border_scrollbar_padding_, content_metrics, - *marker_layout_result, content_offset, + border_scrollbar_padding_, *marker_layout_result, + *content_baseline, content_offset, &container_builder_); return true; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h index 8274b1dc201..644bb031ead 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h @@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_LAYOUT_ALGORITHM_H_ #include "base/memory/scoped_refptr.h" +#include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" @@ -17,6 +18,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" namespace blink { @@ -24,7 +26,6 @@ enum class NGBreakStatus; class NGConstraintSpace; class NGEarlyBreak; class NGFragment; -class NGPhysicalLineBoxFragment; // This struct is used for communicating to a child the position of the previous // inflow child. This will be used to calculate the position of the next child. @@ -58,8 +59,8 @@ class CORE_EXPORT NGBlockLayoutAlgorithm void SetBoxType(NGPhysicalFragment::NGBoxType type); - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; scoped_refptr<const NGLayoutResult> Layout() override; private: @@ -75,10 +76,14 @@ class CORE_EXPORT NGBlockLayoutAlgorithm NOINLINE scoped_refptr<const NGLayoutResult> RelayoutAndBreakEarlier( const NGEarlyBreak&); + NOINLINE scoped_refptr<const NGLayoutResult> + RelayoutNoForcedTruncateForLineClamp(); + inline scoped_refptr<const NGLayoutResult> Layout( NGInlineChildLayoutContext* inline_child_layout_context); - scoped_refptr<const NGLayoutResult> FinishLayout(NGPreviousInflowPosition*); + scoped_refptr<const NGLayoutResult> FinishLayout( + NGPreviousInflowPosition* previous_inflow_position); // Return the BFC block offset of this block. LayoutUnit BfcBlockOffset() const { @@ -102,11 +107,6 @@ class CORE_EXPORT NGBlockLayoutAlgorithm bool is_new_fc, bool* margins_fully_resolved); - void StopMarginCollapsing(EMarginCollapse collapse_value, - LayoutUnit this_margin, - LayoutUnit* logical_block_offset, - NGMarginStrut* margin_strut); - // Creates a new constraint space for the current child. NGConstraintSpace CreateConstraintSpaceForChild( const NGLayoutInputNode child, @@ -238,25 +238,19 @@ class CORE_EXPORT NGBlockLayoutAlgorithm NGBreakStatus BreakBeforeChildIfNeeded(NGLayoutInputNode child, const NGLayoutResult&, NGPreviousInflowPosition*, - LayoutUnit block_offset, + LayoutUnit bfc_block_offset, bool has_container_separation); // Look for a better breakpoint (than we already have) between lines (i.e. a // class B breakpoint), and store it. void UpdateEarlyBreakBetweenLines(); - void PropagateBaselinesFromChildren(); - bool AddBaseline(const NGBaselineRequest&, - const NGPhysicalFragment&, - LayoutUnit child_offset); + // Propagates the baseline from the given |child| if needed. + void PropagateBaselineFromChild(const NGPhysicalContainerFragment& child, + LayoutUnit block_offset); - // Compute the baseline offset of a line box from the content box. - // Line boxes are in line-relative coordinates. This function returns the - // offset in flow-relative coordinates. - LayoutUnit ComputeLineBoxBaselineOffset( - const NGBaselineRequest&, - const NGPhysicalLineBoxFragment&, - LayoutUnit line_box_block_offset) const; + // Performs any final baseline adjustments needed. + void FinalizeBaseline(); // If still unresolved, resolve the fragment's BFC block offset. // @@ -396,6 +390,18 @@ class CORE_EXPORT NGBlockLayoutAlgorithm NGExclusionSpace exclusion_space_; + // If set, this is the number of lines until a clamp. A value of 1 indicates + // the current line should be clamped. This may go negative. + base::Optional<int> lines_until_clamp_; + + // If true, truncation is forced at the clamped line regardless of whether + // there is more text. + bool force_truncate_at_line_clamp_ = true; + + // If set, one of the lines was clamped and this is the intrinsic size at the + // time of the clamp. + base::Optional<LayoutUnit> intrinsic_block_size_when_clamped_; + // When set, this will specify where to break before or inside. const NGEarlyBreak* early_break_ = nullptr; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc index 1cc8a99f1ed..f07b9dcfcf3 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc @@ -33,7 +33,7 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest { NGBaseLayoutAlgorithmTest::SetUp(); } - MinMaxSize RunComputeMinAndMax(NGBlockNode node) { + MinMaxSizes RunComputeMinMaxSizes(NGBlockNode node) { // The constraint space is not used for min/max computation, but we need // it to create the algorithm. NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( @@ -43,9 +43,9 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest { CalculateInitialMinMaxFragmentGeometry(space, node); NGBlockLayoutAlgorithm algorithm({node, fragment_geometry, space}); - MinMaxSizeInput input( + MinMaxSizesInput input( /* percentage_resolution_block_size */ (LayoutUnit())); - auto min_max = algorithm.ComputeMinMaxSize(input); + auto min_max = algorithm.ComputeMinMaxSizes(input); EXPECT_TRUE(min_max.has_value()); return *min_max; } @@ -97,10 +97,11 @@ TEST_F(NGBlockLayoutAlgorithmTest, FixedSize) { } TEST_F(NGBlockLayoutAlgorithmTest, Caching) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - + // The inner element exists so that "simplified" layout logic isn't invoked. SetBodyInnerHTML(R"HTML( - <div id="box" style="width:30px; height:40%;"></div> + <div id="box" style="width:30px; height:40%;"> + <div style="height: 100%;"></div> + </div> )HTML"); NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( @@ -132,7 +133,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, Caching) { EXPECT_NE(result.get(), nullptr); // Test a different constraint space that will actually result in a different - // size. + // sized fragment. space = ConstructBlockLayoutTestConstraintSpace( WritingMode::kHorizontalTb, TextDirection::kLtr, LogicalSize(LayoutUnit(200), LayoutUnit(200))); @@ -146,8 +147,6 @@ TEST_F(NGBlockLayoutAlgorithmTest, Caching) { } TEST_F(NGBlockLayoutAlgorithmTest, MinInlineSizeCaching) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <div id="box" style="min-width:30%; width: 10px; height:40px;"></div> )HTML"); @@ -190,8 +189,6 @@ TEST_F(NGBlockLayoutAlgorithmTest, MinInlineSizeCaching) { } TEST_F(NGBlockLayoutAlgorithmTest, PercentageBlockSizeQuirkDescendantsCaching) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Quirks mode triggers the interesting parent-child %-resolution behaviour. GetDocument().SetCompatibilityMode(Document::kQuirksMode); @@ -237,10 +234,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PercentageBlockSizeQuirkDescendantsCaching) { builder.SetAvailableSize(size); builder.SetPercentageResolutionSize(size); builder.SetTextDirection(TextDirection::kLtr); - builder.AddBaselineRequest({NGBaselineAlgorithmType::kAtomicInline, - FontBaseline::kAlphabeticBaseline}); - builder.AddBaselineRequest({NGBaselineAlgorithmType::kFirstLine, - FontBaseline::kAlphabeticBaseline}); + builder.SetNeedsBaseline(true); return builder.ToConstraintSpace(); }; @@ -298,99 +292,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PercentageBlockSizeQuirkDescendantsCaching) { EXPECT_EQ(run_test("box9"), nullptr); } -TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFitCaching) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - - SetBodyInnerHTML(R"HTML( - <div id="container" style="display: flow-root; width: 300px; height: 100px;"> - <div id="box1" style="float: left;"> - <div style="display: inline-block; width: 150px;"></div> - <div style="display: inline-block; width: 50px;"></div> - </div> - <div id="box2" style="float: left;"> - <div style="display: inline-block; width: 350px;"></div> - <div style="display: inline-block; width: 250px;"></div> - </div> - <div id="box3" style="float: left; min-width: 80%;"> - <div style="display: inline-block; width: 150px;"></div> - <div style="display: inline-block; width: 250px;"></div> - </div> - <div id="box4" style="float: left; margin-left: 75px;"> - <div style="display: inline-block; width: 150px;"></div> - <div style="display: inline-block; width: 50px;"></div> - </div> - </div> - )HTML"); - - NGConstraintSpace space100 = ConstructBlockLayoutTestConstraintSpace( - WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(100), LayoutUnit(100)), - /* shrink_to_fit */ true, /* is_new_formatting_context */ true); - NGConstraintSpace space200 = ConstructBlockLayoutTestConstraintSpace( - WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(200), LayoutUnit(100)), - /* shrink_to_fit */ true, /* is_new_formatting_context */ true); - NGConstraintSpace space250 = ConstructBlockLayoutTestConstraintSpace( - WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(250), LayoutUnit(100)), - /* shrink_to_fit */ true, /* is_new_formatting_context */ true); - NGConstraintSpace space300 = ConstructBlockLayoutTestConstraintSpace( - WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(300), LayoutUnit(100)), - /* shrink_to_fit */ true, /* is_new_formatting_context */ true); - NGConstraintSpace space400 = ConstructBlockLayoutTestConstraintSpace( - WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(400), LayoutUnit(100)), - /* shrink_to_fit */ true, /* is_new_formatting_context */ true); - scoped_refptr<const NGLayoutResult> result; - - auto* box1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box1")); - auto* box2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box2")); - auto* box3 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box3")); - auto* box4 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box4")); - - // Ensure we cached the result for box1 in the first layout pass. - result = RunCachedLayoutResult(space300, NGBlockNode(box1)); - EXPECT_NE(result.get(), nullptr); - - // box1 was sized to its max-content size in the first layout pass, passing - // an available size larger than the fragment should hit the cache. - result = RunCachedLayoutResult(space400, NGBlockNode(box1)); - EXPECT_NE(result.get(), nullptr); - - // Passing an available size smaller than the fragment should miss the cache - // as the fragment may shrink. - result = RunCachedLayoutResult(space100, NGBlockNode(box1)); - EXPECT_EQ(result.get(), nullptr); - - // Ensure we cached the result for box2 in the first layout pass. - result = RunCachedLayoutResult(space300, NGBlockNode(box2)); - EXPECT_NE(result.get(), nullptr); - - // box2 was sized to its min-content size in the first layout pass, passing - // an available size smaller than the fragment should hit the cache. - result = RunCachedLayoutResult(space200, NGBlockNode(box2)); - EXPECT_NE(result.get(), nullptr); - - // Passing an available size larger than the fragment should miss the cache - // as the fragment may shrink. - result = RunCachedLayoutResult(space400, NGBlockNode(box2)); - EXPECT_EQ(result.get(), nullptr); - - // box3 was sized to its min-content size in the first layout pass, however - // it should miss the cache as it has a %-min-size. - result = RunCachedLayoutResult(space200, NGBlockNode(box3)); - EXPECT_EQ(result.get(), nullptr); - - // box4 was sized to its max-content size in the first layout pass (the same - // as box1) however it should miss the cache due to its margin. - result = RunCachedLayoutResult(space250, NGBlockNode(box4)); - EXPECT_EQ(result.get(), nullptr); -} - TEST_F(NGBlockLayoutAlgorithmTest, LineOffsetCaching) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <div id="container" style="display: flow-root; width: 300px; height: 100px;"> <div id="box1" style="width: 100px; margin: 0 auto 0 auto;"></div> @@ -404,10 +306,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, LineOffsetCaching) { builder.SetAvailableSize(size); builder.SetPercentageResolutionSize(size); builder.SetTextDirection(TextDirection::kLtr); - builder.AddBaselineRequest({NGBaselineAlgorithmType::kAtomicInline, - FontBaseline::kAlphabeticBaseline}); - builder.AddBaselineRequest({NGBaselineAlgorithmType::kFirstLine, - FontBaseline::kAlphabeticBaseline}); + builder.SetNeedsBaseline(true); builder.SetBfcOffset(bfc_offset); return builder.ToConstraintSpace(); }; @@ -1645,7 +1544,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContent) { NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(kSecondChildWidth, sizes.min_size); EXPECT_EQ(kSecondChildWidth, sizes.max_size); } @@ -1666,7 +1565,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContentFloats) { NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(LayoutUnit(40), sizes.min_size); EXPECT_EQ(LayoutUnit(90), sizes.max_size); } @@ -1687,7 +1586,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContentFloatsClearance) { NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(LayoutUnit(40), sizes.min_size); EXPECT_EQ(LayoutUnit(50), sizes.max_size); } @@ -1708,7 +1607,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContentNewFormattingContext) { NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(LayoutUnit(100), sizes.min_size); EXPECT_EQ(LayoutUnit(100), sizes.max_size); } @@ -1730,7 +1629,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(LayoutUnit(30), sizes.min_size); EXPECT_EQ(LayoutUnit(70), sizes.max_size); } @@ -1748,7 +1647,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container"))); - MinMaxSize sizes = RunComputeMinAndMax(container); + MinMaxSizes sizes = RunComputeMinMaxSizes(container); EXPECT_EQ(LayoutUnit(), sizes.min_size); EXPECT_EQ(LayoutUnit(), sizes.max_size); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc index 301d9e9215b..48871b0c4f8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc @@ -7,8 +7,11 @@ #include <memory> #include "third_party/blink/renderer/core/frame/local_frame_view.h" +#include "third_party/blink/renderer/core/html/forms/html_input_element.h" #include "third_party/blink/renderer/core/html/html_marquee_element.h" +#include "third_party/blink/renderer/core/input_type_names.h" #include "third_party/blink/renderer/core/layout/box_layout_extra_input.h" +#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h" #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_fieldset.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" @@ -17,7 +20,8 @@ #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h" #include "third_party/blink/renderer/core/layout/layout_table.h" #include "third_party/blink/renderer/core/layout/layout_table_cell.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/layout_video.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h" #include "third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" @@ -25,6 +29,10 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h" +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h" +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h" +#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" @@ -43,6 +51,10 @@ #include "third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" +#include "third_party/blink/renderer/core/layout/text_autosizer.h" +#include "third_party/blink/renderer/core/mathml/mathml_element.h" +#include "third_party/blink/renderer/core/mathml/mathml_fraction_element.h" +#include "third_party/blink/renderer/core/mathml/mathml_space_element.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -74,6 +86,24 @@ NOINLINE void CreateAlgorithmAndRun(const NGLayoutAlgorithmParams& params, } template <typename Callback> +NOINLINE void DetermineMathMLAlgorithmAndRun( + const LayoutBox& box, + const NGLayoutAlgorithmParams& params, + const Callback& callback) { + DCHECK(box.IsMathML()); + // Currently math layout algorithms can only apply to MathML elements. + auto* element = box.GetNode(); + DCHECK(element); + if (IsA<MathMLSpaceElement>(element)) + CreateAlgorithmAndRun<NGMathSpaceLayoutAlgorithm>(params, callback); + else if (IsA<MathMLFractionElement>(element) && + IsValidMathMLFraction(params.node)) + CreateAlgorithmAndRun<NGMathFractionLayoutAlgorithm>(params, callback); + else + CreateAlgorithmAndRun<NGMathRowLayoutAlgorithm>(params, callback); +} + +template <typename Callback> NOINLINE void DetermineAlgorithmAndRun(const NGLayoutAlgorithmParams& params, const Callback& callback) { const ComputedStyle& style = params.node.Style(); @@ -82,6 +112,8 @@ NOINLINE void DetermineAlgorithmAndRun(const NGLayoutAlgorithmParams& params, CreateAlgorithmAndRun<NGFlexLayoutAlgorithm>(params, callback); } else if (box.IsLayoutNGCustom()) { CreateAlgorithmAndRun<NGCustomLayoutAlgorithm>(params, callback); + } else if (box.IsMathML()) { + DetermineMathMLAlgorithmAndRun(box, params, callback); } else if (box.IsLayoutNGFieldset()) { CreateAlgorithmAndRun<NGFieldsetLayoutAlgorithm>(params, callback); // If there's a legacy layout box, we can only do block fragmentation if @@ -108,15 +140,15 @@ inline scoped_refptr<const NGLayoutResult> LayoutWithAlgorithm( return result; } -inline base::Optional<MinMaxSize> ComputeMinMaxSizeWithAlgorithm( +inline base::Optional<MinMaxSizes> ComputeMinMaxSizesWithAlgorithm( const NGLayoutAlgorithmParams& params, - const MinMaxSizeInput& input) { - base::Optional<MinMaxSize> minmax; + const MinMaxSizesInput& input) { + base::Optional<MinMaxSizes> min_max_sizes; DetermineAlgorithmAndRun( - params, [&minmax, &input](NGLayoutAlgorithmOperations* algorithm) { - minmax = algorithm->ComputeMinMaxSize(input); + params, [&min_max_sizes, &input](NGLayoutAlgorithmOperations* algorithm) { + min_max_sizes = algorithm->ComputeMinMaxSizes(input); }); - return minmax; + return min_max_sizes; } void UpdateLegacyMultiColumnFlowThread( @@ -131,7 +163,7 @@ void UpdateLegacyMultiColumnFlowThread( // Stitch the columns together. NGBoxStrut border_scrollbar_padding = - ComputeBorders(constraint_space, node) + + ComputeBorders(constraint_space, node.Style()) + ComputeScrollbars(constraint_space, node) + ComputePadding(constraint_space, node.Style()); NGFragment logical_multicol_fragment(writing_mode, fragment); @@ -171,7 +203,8 @@ void UpdateLegacyMultiColumnFlowThread( if (!has_processed_first_column_in_flow_thread) { // The offset of the flow thread should be the same as that of the first // first column. - flow_thread->SetLocation(child.Offset().ToLayoutPoint()); + flow_thread->SetLocationAndUpdateOverflowControlsIfNeeded( + child.Offset().ToLayoutPoint()); flow_thread->SetLogicalWidth(logical_column_fragment.InlineSize()); has_processed_first_column_in_flow_thread = true; } @@ -267,11 +300,36 @@ void SetupBoxLayoutExtraInput(const NGConstraintSpace& space, input->override_block_size = space.AvailableSize().block_size; } +bool CanUseCachedIntrinsicInlineSizes(const MinMaxSizesInput& input, + const LayoutBox& box) { + // Obviously can't use the cache if our intrinsic logical widths are dirty. + if (box.IntrinsicLogicalWidthsDirty()) + return false; + + // We don't store the float inline sizes for comparison, always skip the + // cache in this case. + if (input.float_left_inline_size || input.float_right_inline_size) + return false; + + // Check if we have any percentage inline padding. + const auto& style = box.StyleRef(); + if (style.MayHavePadding() && (style.PaddingStart().IsPercentOrCalc() || + style.PaddingEnd().IsPercentOrCalc())) + return false; + + // Check if the %-block-size matches. + if (input.percentage_resolution_block_size != + box.IntrinsicLogicalWidthsPercentageResolutionBlockSize()) + return false; + + return true; +} + } // namespace scoped_refptr<const NGLayoutResult> NGBlockNode::Layout( const NGConstraintSpace& constraint_space, - const NGBreakToken* break_token, + const NGBlockBreakToken* break_token, const NGEarlyBreak* early_break) { // Use the old layout code and synthesize a fragment. if (!CanUseNewLayout()) @@ -296,8 +354,8 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout( scoped_refptr<const NGLayoutResult> layout_result = box_->CachedLayoutResult(constraint_space, break_token, early_break, &fragment_geometry, &cache_status); - if (layout_result) { - DCHECK_EQ(cache_status, NGLayoutCacheStatus::kHit); + if (cache_status == NGLayoutCacheStatus::kHit) { + DCHECK(layout_result); // We may have to update the margins on box_; we reuse the layout result // even if a percentage margin may have changed. @@ -325,11 +383,13 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout( CalculateInitialFragmentGeometry(constraint_space, *this); } + TextAutosizer::NGLayoutScope text_autosizer_layout_scope( + box_, fragment_geometry->border_box_size.inline_size); + PrepareForLayout(); NGLayoutAlgorithmParams params(*this, *fragment_geometry, constraint_space, - To<NGBlockBreakToken>(break_token), - early_break); + break_token, early_break); // Try to perform "simplified" layout. // TODO(crbug.com/992953): Add a simplified layout pass for custom layout. @@ -339,26 +399,33 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout( !(block_flow->ChildrenInline() && RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) && !block_flow->IsLayoutNGCustom()) { + DCHECK(layout_result); +#if DCHECK_IS_ON() + scoped_refptr<const NGLayoutResult> previous_result = layout_result; +#endif + // A child may have changed size while performing "simplified" layout (it // may have gained or removed scrollbars, changing its size). In these // cases "simplified" layout will return a null layout-result, indicating // we need to perform a full layout. - layout_result = RunSimplifiedLayout(params); + layout_result = RunSimplifiedLayout(params, *layout_result); #if DCHECK_IS_ON() if (layout_result) { layout_result->CheckSameForSimplifiedLayout( - *box_->GetCachedLayoutResult(), /* check_same_block_size */ false); + *previous_result, /* check_same_block_size */ false); } #endif + } else { + layout_result = nullptr; } // Fragment geometry scrollbars are potentially size constrained, and cannot // be used for comparison with their after layout size. NGBoxStrut before_layout_scrollbars = ComputeScrollbars(constraint_space, *this); - bool before_layout_preferred_logical_widths_dirty = - box_->PreferredLogicalWidthsDirty(); + bool before_layout_intrinsic_logical_widths_dirty = + box_->IntrinsicLogicalWidthsDirty(); if (!layout_result) layout_result = LayoutWithAlgorithm(params); @@ -373,10 +440,15 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout( // This mirrors legacy code in PaintLayerScrollableArea::UpdateAfterLayout. if ((before_layout_scrollbars != ComputeScrollbars(constraint_space, *this)) || - (!before_layout_preferred_logical_widths_dirty && - box_->PreferredLogicalWidthsDirty())) { + (!before_layout_intrinsic_logical_widths_dirty && + box_->IntrinsicLogicalWidthsDirty())) { PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbars; + // We need to clear any previous results when scrollbars change. For + // example - we may have stored a "measure" layout result which will be + // incorrect if we try and reuse it. + box_->ClearLayoutResults(); + #if DCHECK_IS_ON() // Ensure turning on/off scrollbars only once at most, when we call // |LayoutWithAlgorithm| recursively. @@ -506,18 +578,32 @@ void NGBlockNode::PrepareForLayout() { void NGBlockNode::FinishLayout( LayoutBlockFlow* block_flow, const NGConstraintSpace& constraint_space, - const NGBreakToken* break_token, + const NGBlockBreakToken* break_token, scoped_refptr<const NGLayoutResult> layout_result) { // If we abort layout and don't clear the cached layout-result, we can end // up in a state where the layout-object tree doesn't match fragment tree // referenced by this layout-result. if (layout_result->Status() != NGLayoutResult::kSuccess) { - box_->ClearCachedLayoutResult(); + box_->ClearLayoutResults(); return; } - if (!constraint_space.HasBlockFragmentation()) - box_->SetCachedLayoutResult(*layout_result, break_token); + // Add all layout results (and fragments) generated from a node to a list in + // the layout object. Some extra care is required to correctly overwrite + // intermediate layout results: The sequence number of an incoming break token + // corresponds with the fragment index in the layout object (off by 1, + // though). When writing back a layout result, we remove any fragments in the + // layout box at higher indices than that of the one we're writing back. + const auto& physical_fragment = + To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()); + wtf_size_t fragment_index = 0; + if (break_token && !break_token->IsBreakBefore()) + fragment_index = break_token->SequenceNumber() + 1; + + if (layout_result->IsSingleUse()) + box_->AddLayoutResult(layout_result, fragment_index); + else + box_->SetCachedLayoutResult(layout_result); if (block_flow) { auto* child = GetLayoutObjectForFirstChildNode(block_flow); @@ -543,40 +629,28 @@ void NGBlockNode::FinishLayout( } if (has_inline_children) { - const auto& physical_fragment = - To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()); if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { CopyFragmentDataToLayoutBoxForInlineChildren( physical_fragment, physical_fragment.Size().width, Style().IsFlippedBlocksWritingMode()); - block_flow->SetPaintFragment(To<NGBlockBreakToken>(break_token), - &physical_fragment); + block_flow->SetPaintFragment(break_token, &physical_fragment); } else { CopyFragmentDataToLayoutBoxForInlineChildren(physical_fragment); - - // Floats are in the fragment tree, not in the item list, and the - // painter relies on |LayoutBox.Location()|. - if (physical_fragment.HasFloatingDescendantsForPaint()) { - CopyFragmentDataToLayoutBoxForInlineChildren( - physical_fragment, physical_fragment.Size().width, - Style().IsFlippedBlocksWritingMode()); - } } } else { // We still need to clear paint fragments in case it had inline children, // and thus had NGPaintFragment. block_flow->ClearNGInlineNodeData(); - block_flow->SetPaintFragment(To<NGBlockBreakToken>(break_token), nullptr); + block_flow->SetPaintFragment(break_token, nullptr); } } - CopyFragmentDataToLayoutBox(constraint_space, *layout_result, - To<NGBlockBreakToken>(break_token)); + CopyFragmentDataToLayoutBox(constraint_space, *layout_result, break_token); } -MinMaxSize NGBlockNode::ComputeMinMaxSize( +MinMaxSizes NGBlockNode::ComputeMinMaxSizes( WritingMode container_writing_mode, - const MinMaxSizeInput& input, + const MinMaxSizesInput& input, const NGConstraintSpace* constraint_space) { // TODO(layoutng) Can UpdateMarkerTextIfNeeded call be moved // somewhere else? List items need up-to-date markers before layout. @@ -586,13 +660,17 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( bool is_orthogonal_flow_root = !IsParallelWritingMode(container_writing_mode, Style().GetWritingMode()); - MinMaxSize sizes; + if (CanUseCachedIntrinsicInlineSizes(input, *box_)) + return box_->IntrinsicLogicalWidths(); + + box_->SetIntrinsicLogicalWidthsDirty(); + + MinMaxSizes sizes; // If we're orthogonal, we have to run layout to compute the sizes. However, // if we're outside of layout, we can't do that. This can happen on Mac. if ((!CanUseNewLayout() && !is_orthogonal_flow_root) || - (is_orthogonal_flow_root && !box_->GetFrameView()->IsInPerformLayout())) { - return ComputeMinMaxSizeFromLegacy(input); - } + (is_orthogonal_flow_root && !box_->GetFrameView()->IsInPerformLayout())) + return ComputeMinMaxSizesFromLegacy(input); NGConstraintSpace zero_constraint_space = CreateConstraintSpaceBuilderForMinMax(*this).ToConstraintSpace(); @@ -614,18 +692,12 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( TextDirection::kLtr, // irrelevant here To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment())); sizes.min_size = sizes.max_size = fragment.Size().inline_size; - if (input.size_type == NGMinMaxSizeType::kContentBoxSize) { - sizes -= fragment.Borders().InlineSum() + fragment.Padding().InlineSum() + - box_->ScrollbarLogicalWidth(); - DCHECK_GE(sizes.min_size, LayoutUnit()); - DCHECK_GE(sizes.max_size, LayoutUnit()); - } return sizes; } NGFragmentGeometry fragment_geometry = CalculateInitialMinMaxFragmentGeometry(*constraint_space, *this); - base::Optional<MinMaxSize> maybe_sizes = ComputeMinMaxSizeWithAlgorithm( + base::Optional<MinMaxSizes> maybe_sizes = ComputeMinMaxSizesWithAlgorithm( NGLayoutAlgorithmParams(*this, fragment_geometry, *constraint_space), input); @@ -633,13 +705,21 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( auto* html_marquee_element = DynamicTo<HTMLMarqueeElement>(box_->GetNode()); if (UNLIKELY(html_marquee_element && html_marquee_element->IsHorizontal())) maybe_sizes->min_size = LayoutUnit(); + else if (UNLIKELY(IsA<HTMLSelectElement>(box_->GetNode()) || + (IsA<HTMLInputElement>(box_->GetNode()) && + To<HTMLInputElement>(box_->GetNode())->type() == + input_type_names::kFile)) && + Style().LogicalWidth().IsPercentOrCalc()) + maybe_sizes->min_size = LayoutUnit(); + box_->SetIntrinsicLogicalWidthsFromNG( + *maybe_sizes, input.percentage_resolution_block_size); return *maybe_sizes; } if (!box_->GetFrameView()->IsInPerformLayout()) { // We can't synthesize these using Layout() if we're not in PerformLayout. // This situation can happen on mac. Fall back to legacy instead. - return ComputeMinMaxSizeFromLegacy(input); + return ComputeMinMaxSizesFromLegacy(input); } // Have to synthesize this value. @@ -662,18 +742,11 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize( TextDirection::kLtr, // irrelevant here To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment())); sizes.max_size = max_fragment.Size().inline_size; - - if (input.size_type == NGMinMaxSizeType::kContentBoxSize) { - sizes -= max_fragment.Borders().InlineSum() + - max_fragment.Padding().InlineSum() + box_->ScrollbarLogicalWidth(); - DCHECK_GE(sizes.min_size, LayoutUnit()); - DCHECK_GE(sizes.max_size, LayoutUnit()); - } return sizes; } -MinMaxSize NGBlockNode::ComputeMinMaxSizeFromLegacy( - const MinMaxSizeInput& input) const { +MinMaxSizes NGBlockNode::ComputeMinMaxSizesFromLegacy( + const MinMaxSizesInput& input) const { bool needs_size_reset = false; if (!box_->HasOverrideContainingBlockContentLogicalHeight()) { box_->SetOverrideContainingBlockContentLogicalHeight( @@ -681,16 +754,13 @@ MinMaxSize NGBlockNode::ComputeMinMaxSizeFromLegacy( needs_size_reset = true; } - MinMaxSize sizes; - // ComputeIntrinsicLogicalWidths returns content-box + scrollbar. - box_->ComputeIntrinsicLogicalWidths(sizes.min_size, sizes.max_size); - if (input.size_type == NGMinMaxSizeType::kContentBoxSize) { - sizes -= LayoutUnit(box_->ScrollbarLogicalWidth()); - DCHECK_GE(sizes.min_size, LayoutUnit()); - DCHECK_GE(sizes.max_size, LayoutUnit()); - } else { - sizes += box_->BorderAndPaddingLogicalWidth(); - } + // Tables don't calculate their min/max content contribution the same way as + // other layout nodes. This is because width/min-width/etc have a different + // meaning for tables. + // + // Due to this the min/max content contribution is their min/max content size. + MinMaxSizes sizes = box_->IsTable() ? box_->PreferredLogicalWidths() + : box_->IntrinsicLogicalWidths(); if (needs_size_reset) box_->ClearOverrideContainingBlockContentSize(); @@ -857,10 +927,9 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( LayoutBlock* block = DynamicTo<LayoutBlock>(box_); bool needs_full_invalidation = false; if (LIKELY(block && is_last_fragment)) { - LayoutUnit intrinsic_block_size = - layout_result.UnconstrainedIntrinsicBlockSize(); + LayoutUnit overflow_block_size = layout_result.OverflowBlockSize(); if (UNLIKELY(previous_break_token)) - intrinsic_block_size += previous_break_token->ConsumedBlockSize(); + overflow_block_size += previous_break_token->ConsumedBlockSize(); #if DCHECK_IS_ON() block->CheckPositionedObjectsNeedLayout(); @@ -881,10 +950,9 @@ void NGBlockNode::CopyFragmentDataToLayoutBox( // |ComputeOverflow()| below calls |AddVisualOverflowFromChildren()|, which // computes visual overflow from |RootInlineBox| if |ChildrenInline()| - // TODO(rego): This causes that ChildNeedsLayoutOverflowRecalc flags are not - // cleared after layout (see https://crbug.com/941180). - block->SetNeedsOverflowRecalc(); - block->ComputeLayoutOverflow(intrinsic_block_size - borders.block_end - + block->SetNeedsOverflowRecalc( + LayoutObject::OverflowRecalcType::kOnlyVisualOverflowRecalc); + block->ComputeLayoutOverflow(overflow_block_size - borders.block_end - scrollbars.block_end); } @@ -915,7 +983,7 @@ void NGBlockNode::PlaceChildrenInLayoutBox( for (const auto& child_fragment : physical_fragment.Children()) { // Skip any line-boxes we have as children, this is handled within // NGInlineNode at the moment. - if (!child_fragment->IsBox() && !child_fragment->IsRenderedLegend()) + if (!child_fragment->IsBox()) continue; const auto& box_fragment = *To<NGPhysicalBoxFragment>(child_fragment.get()); @@ -940,7 +1008,7 @@ void NGBlockNode::PlaceChildrenInLayoutBox( DCHECK(IsA<HTMLFieldSetElement>(content_wrapper->Parent()->GetNode())); LayoutPoint location = rendered_legend->Location(); location -= content_wrapper->Location(); - rendered_legend->SetLocation(location); + rendered_legend->SetLocationAndUpdateOverflowControlsIfNeeded(location); } } @@ -996,7 +1064,8 @@ void NGBlockNode::CopyChildFragmentPosition( offset.left += consumed; } - layout_box->SetLocation(offset.ToLayoutPoint()); + layout_box->SetLocationAndUpdateOverflowControlsIfNeeded( + offset.ToLayoutPoint()); } // For inline children, NG painters handles fragments directly, but there are @@ -1007,6 +1076,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren( LayoutUnit initial_container_width, bool initial_container_is_flipped, PhysicalOffset offset) { + DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); for (const auto& child : container.Children()) { if (child->IsContainer()) { PhysicalOffset child_offset = offset + child.Offset(); @@ -1022,7 +1092,8 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren( child->Size().width - maybe_flipped_offset.left; } - layout_box.SetLocation(maybe_flipped_offset.ToLayoutPoint()); + layout_box.SetLocationAndUpdateOverflowControlsIfNeeded( + maybe_flipped_offset.ToLayoutPoint()); } // Legacy compatibility. This flag is used in paint layer for @@ -1038,7 +1109,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren( // LayoutBlockFlow. If |child| establishes a new block formatting context, // it also creates another inline formatting context. Do not copy to its // descendants in this case. - if (!child->IsBlockFormattingContextRoot()) { + if (!child->IsFormattingContextRoot()) { CopyFragmentDataToLayoutBoxForInlineChildren( To<NGPhysicalContainerFragment>(*child), initial_container_width, initial_container_is_flipped, child_offset); @@ -1055,20 +1126,22 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren( return; bool initial_container_is_flipped = Style().IsFlippedBlocksWritingMode(); for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNext()) { - if (const NGPhysicalBoxFragment* child = cursor.CurrentBoxFragment()) { + if (const NGPhysicalBoxFragment* child = cursor.Current().BoxFragment()) { // Replaced elements and inline blocks need Location() set relative to // their block container. LayoutObject* layout_object = child->GetMutableLayoutObject(); if (!layout_object) continue; if (LayoutBox* layout_box = ToLayoutBoxOrNull(layout_object)) { - PhysicalOffset maybe_flipped_offset = cursor.CurrentOffset(); + PhysicalOffset maybe_flipped_offset = + cursor.Current().OffsetInContainerBlock(); if (initial_container_is_flipped) { maybe_flipped_offset.left = container.Size().width - child->Size().width - maybe_flipped_offset.left; } - layout_box->SetLocation(maybe_flipped_offset.ToLayoutPoint()); + layout_box->SetLocationAndUpdateOverflowControlsIfNeeded( + maybe_flipped_offset.ToLayoutPoint()); continue; } @@ -1085,9 +1158,9 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren( } } -bool NGBlockNode::ChildrenInline() const { +bool NGBlockNode::IsInlineFormattingContextRoot() const { if (const auto* block = DynamicTo<LayoutBlockFlow>(box_)) - return AreNGBlockFlowChildrenInline(block); + return AreNGBlockFlowChildrenInline(block) && FirstChild().IsInline(); return false; } @@ -1101,10 +1174,28 @@ bool NGBlockNode::IsAtomicInlineLevel() const { return GetLayoutBox()->IsAtomicInlineLevel() && GetLayoutBox()->IsInline(); } -bool NGBlockNode::MayHaveAspectRatio() const { +bool NGBlockNode::HasAspectRatio() const { LayoutBox* layout_object = GetLayoutBox(); - return layout_object->IsImage() || layout_object->IsVideo() || - layout_object->IsCanvas(); + if (!layout_object->IsImage() && !IsA<LayoutVideo>(layout_object) && + !layout_object->IsCanvas()) + return false; + + // Retrieving this and throwing it away is wasteful. We could make this method + // return Optional<LogicalSize> that returns the aspect_ratio if there is one. + return !GetAspectRatio().IsEmpty(); +} + +LogicalSize NGBlockNode::GetAspectRatio() const { + base::Optional<LayoutUnit> computed_inline_size; + base::Optional<LayoutUnit> computed_block_size; + GetOverrideIntrinsicSize(&computed_inline_size, &computed_block_size); + if (computed_inline_size && computed_block_size) + return LogicalSize(*computed_inline_size, *computed_block_size); + + IntrinsicSizingInfo legacy_sizing_info; + ToLayoutReplaced(box_)->ComputeIntrinsicSizingInfo(legacy_sizing_info); + return LogicalSize(LayoutUnit(legacy_sizing_info.aspect_ratio.Width()), + LayoutUnit(legacy_sizing_info.aspect_ratio.Height())); } bool NGBlockNode::UseLogicalBottomMarginEdgeForInlineBlockBaseline() const { @@ -1121,21 +1212,17 @@ bool NGBlockNode::IsCustomLayoutLoaded() const { scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline( const NGConstraintSpace& parent_constraint_space, const ComputedStyle& parent_style, - FontBaseline baseline_type, - bool use_first_line_style) { + bool use_first_line_style, + NGBaselineAlgorithmType baseline_algorithm_type) { NGConstraintSpaceBuilder builder( parent_constraint_space, Style().GetWritingMode(), /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(parent_style, *this, &builder); + builder.SetIsPaintedAtomically(true); builder.SetUseFirstLineStyle(use_first_line_style); - // Request to compute baseline during the layout, except when we know the box - // would synthesize box-baseline. - LayoutBox* layout_box = GetLayoutBox(); - if (NGBaseline::ShouldPropagateBaselines(layout_box)) { - builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kAtomicInline, baseline_type}); - } + builder.SetNeedsBaseline(true); + builder.SetBaselineAlgorithmType(baseline_algorithm_type); builder.SetIsShrinkToFit(Style().LogicalWidth().IsAuto()); builder.SetAvailableSize(parent_constraint_space.AvailableSize()); @@ -1148,7 +1235,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline( scoped_refptr<const NGLayoutResult> result = Layout(constraint_space); // TODO(kojii): Investigate why ClearNeedsLayout() isn't called automatically // when it's being laid out. - layout_box->ClearNeedsLayout(); + GetLayoutBox()->ClearNeedsLayout(); return result; } @@ -1208,7 +1295,13 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout( constraint_space.IsNewFormattingContext()); builder.SetInitialFragmentGeometry(fragment_geometry); builder.SetIsLegacyLayoutRoot(); - builder.SetIntrinsicBlockSize(box_->IntrinsicContentLogicalHeight()); + if (box_->ShouldComputeSizeAsReplaced()) { + builder.SetIntrinsicBlockSize(box_->LogicalHeight()); + } else { + builder.SetIntrinsicBlockSize(box_->IntrinsicContentLogicalHeight() + + box_->BorderAndPaddingLogicalHeight() + + box_->ScrollbarLogicalHeight()); + } // If we're block-fragmented, we can only handle monolithic content, since // the two block fragmentation machineries (NG and legacy) cannot cooperate. @@ -1227,7 +1320,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout( CopyBaselinesFromLegacyLayout(constraint_space, &builder); layout_result = builder.ToBoxFragment(); - box_->SetCachedLayoutResult(*layout_result, /* break_token */ nullptr); + box_->SetCachedLayoutResult(layout_result); // If |SetCachedLayoutResult| did not update cached |LayoutResult|, // |NeedsLayout()| flag should not be cleared. @@ -1254,7 +1347,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout( *layout_result, constraint_space, layout_result->EndMarginStrut(), layout_result->BfcLineOffset(), layout_result->BfcBlockOffset(), LayoutUnit() /* block_offset_delta */)); - box_->SetCachedLayoutResult(*layout_result, /* break_token */ nullptr); + box_->SetCachedLayoutResult(layout_result); } } @@ -1265,42 +1358,40 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout( } scoped_refptr<const NGLayoutResult> NGBlockNode::RunSimplifiedLayout( - const NGLayoutAlgorithmParams& params) const { - return NGSimplifiedLayoutAlgorithm(params, *box_->GetCachedLayoutResult()) - .Layout(); + const NGLayoutAlgorithmParams& params, + const NGLayoutResult& result) const { + return NGSimplifiedLayoutAlgorithm(params, result).Layout(); } void NGBlockNode::CopyBaselinesFromLegacyLayout( const NGConstraintSpace& constraint_space, NGBoxFragmentBuilder* builder) { - const NGBaselineRequestList requests = constraint_space.BaselineRequests(); - if (requests.IsEmpty()) + // As the calls to query baselines from legacy layout are potentially + // expensive we only ask for them if needed. + // TODO(layout-dev): Once we have flexbox, and editing switched over to + // LayoutNG we should be able to safely remove this flag without a + // performance penalty. + if (!constraint_space.NeedsBaseline()) return; - if (UNLIKELY(constraint_space.GetWritingMode() != Style().GetWritingMode())) - return; - - for (const auto& request : requests) { - switch (request.AlgorithmType()) { - case NGBaselineAlgorithmType::kAtomicInline: { - LayoutUnit position = - AtomicInlineBaselineFromLegacyLayout(request, constraint_space); - if (position != -1) - builder->AddBaseline(request, position); - break; - } - case NGBaselineAlgorithmType::kFirstLine: { - LayoutUnit position = box_->FirstLineBoxBaseline(); - if (position != -1) - builder->AddBaseline(request, position); - break; - } + switch (constraint_space.BaselineAlgorithmType()) { + case NGBaselineAlgorithmType::kFirstLine: { + LayoutUnit position = box_->FirstLineBoxBaseline(); + if (position != -1) + builder->SetBaseline(position); + break; + } + case NGBaselineAlgorithmType::kInlineBlock: { + LayoutUnit position = + AtomicInlineBaselineFromLegacyLayout(constraint_space); + if (position != -1) + builder->SetBaseline(position); + break; } } } LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout( - const NGBaselineRequest& request, const NGConstraintSpace& constraint_space) { LineDirectionMode line_direction = box_->IsHorizontalWritingMode() ? LineDirectionMode::kHorizontalLine @@ -1310,7 +1401,7 @@ LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout( // classes override it assuming inline layout calls |BaselinePosition()|. if (box_->IsInline()) { LayoutUnit position = LayoutUnit(box_->BaselinePosition( - request.BaselineType(), constraint_space.UseFirstLineStyle(), + box_->Style()->GetFontBaseline(), constraint_space.UseFirstLineStyle(), line_direction, kPositionOnContainingLine)); // BaselinePosition() uses margin edge for atomic inlines. Subtract @@ -1318,6 +1409,9 @@ LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout( if (box_->IsAtomicInlineLevel()) position -= box_->MarginOver(); + if (IsFlippedLinesWritingMode(constraint_space.GetWritingMode())) + return box_->Size().Width() - position; + return position; } @@ -1363,4 +1457,8 @@ void NGBlockNode::StoreMargins(const NGConstraintSpace& constraint_space, box_->SetMargin(physical_margins); } +void NGBlockNode::StoreMargins(const NGPhysicalBoxStrut& physical_margins) { + box_->SetMargin(physical_margins); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h index 91b6986774a..7ccb575505f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h @@ -14,16 +14,14 @@ namespace blink { class LayoutBox; -class NGBaselineRequest; class NGBlockBreakToken; class NGBoxFragmentBuilder; -class NGBreakToken; class NGConstraintSpace; class NGEarlyBreak; class NGLayoutResult; class NGPhysicalBoxFragment; class NGPhysicalContainerFragment; -struct MinMaxSize; +struct MinMaxSizes; struct NGBoxStrut; struct NGLayoutAlgorithmParams; @@ -37,7 +35,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { scoped_refptr<const NGLayoutResult> Layout( const NGConstraintSpace& constraint_space, - const NGBreakToken* break_token = nullptr, + const NGBlockBreakToken* break_token = nullptr, const NGEarlyBreak* = nullptr); // This method is just for use within the |NGSimplifiedLayoutAlgorithm|. @@ -66,7 +64,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { // Computes the value of min-content and max-content for this node's border // box. - // If the underlying layout algorithm's ComputeMinMaxSize returns + // If the underlying layout algorithm's ComputeMinMaxSizes returns // no value, this function will synthesize these sizes using Layout with // special constraint spaces -- infinite available size for max content, zero // available size for min content, and percentage resolution size zero for @@ -82,21 +80,29 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { // The constraint space is also used to perform layout when this block's // writing mode is orthogonal to its parent's, in which case the constraint // space is not optional. - MinMaxSize ComputeMinMaxSize(WritingMode container_writing_mode, - const MinMaxSizeInput&, - const NGConstraintSpace* = nullptr); + MinMaxSizes ComputeMinMaxSizes(WritingMode container_writing_mode, + const MinMaxSizesInput&, + const NGConstraintSpace* = nullptr); - MinMaxSize ComputeMinMaxSizeFromLegacy(const MinMaxSizeInput&) const; + MinMaxSizes ComputeMinMaxSizesFromLegacy(const MinMaxSizesInput&) const; NGLayoutInputNode FirstChild() const; NGBlockNode GetRenderedLegend() const; NGBlockNode GetFieldsetContent() const; - bool ChildrenInline() const; + // Return true if this block node establishes an inline formatting context. + // This will only be the case if there is actual inline content. Empty nodes + // or nodes consisting purely of block-level, floats, and/or out-of-flow + // positioned children will return false. + bool IsInlineFormattingContextRoot() const; + bool IsInlineLevel() const; bool IsAtomicInlineLevel() const; - bool MayHaveAspectRatio() const; + bool HasAspectRatio() const; + + // Returns the aspect ratio of a replaced element. + LogicalSize GetAspectRatio() const; // Returns true if this node should fill the viewport. // This occurs when we are in quirks-mode and we are *not* OOF-positioned, @@ -127,8 +133,9 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { scoped_refptr<const NGLayoutResult> LayoutAtomicInline( const NGConstraintSpace& parent_constraint_space, const ComputedStyle& parent_style, - FontBaseline, - bool use_first_line_style); + bool use_first_line_style, + NGBaselineAlgorithmType baseline_algorithm_type = + NGBaselineAlgorithmType::kInlineBlock); // Called if this is an out-of-flow block which needs to be // positioned with legacy layout. @@ -136,6 +143,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { // Write back resolved margins to legacy. void StoreMargins(const NGConstraintSpace&, const NGBoxStrut& margins); + void StoreMargins(const NGPhysicalBoxStrut& margins); static bool CanUseNewLayout(const LayoutBox&); bool CanUseNewLayout() const; @@ -150,13 +158,14 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { scoped_refptr<const NGLayoutResult> RunLegacyLayout(const NGConstraintSpace&); scoped_refptr<const NGLayoutResult> RunSimplifiedLayout( - const NGLayoutAlgorithmParams&) const; + const NGLayoutAlgorithmParams&, + const NGLayoutResult&) const; // If this node is a LayoutNGMixin, the caller must pass the layout object for // this node cast to a LayoutBlockFlow as the first argument. void FinishLayout(LayoutBlockFlow*, const NGConstraintSpace&, - const NGBreakToken*, + const NGBlockBreakToken*, scoped_refptr<const NGLayoutResult>); // After we run the layout algorithm, this function copies back the geometry @@ -183,8 +192,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode { void CopyBaselinesFromLegacyLayout(const NGConstraintSpace&, NGBoxFragmentBuilder*); - LayoutUnit AtomicInlineBaselineFromLegacyLayout(const NGBaselineRequest&, - const NGConstraintSpace&); + LayoutUnit AtomicInlineBaselineFromLegacyLayout(const NGConstraintSpace&); void UpdateShapeOutsideInfoIfNeeded( const NGLayoutResult&, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc index 8190b808e58..f42f0ef6ff5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc @@ -4,7 +4,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h" namespace blink { @@ -174,9 +174,9 @@ TEST_F(NGBlockNodeForTest, MinAndMaxContent) { const int kWidth = 30; NGBlockNode box(ToLayoutBox(GetLayoutObjectByElementId("box"))); - MinMaxSize sizes = box.ComputeMinMaxSize( + MinMaxSizes sizes = box.ComputeMinMaxSizes( WritingMode::kHorizontalTb, - MinMaxSizeInput(/* percentage_resolution_block_size */ LayoutUnit())); + MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit())); EXPECT_EQ(LayoutUnit(kWidth), sizes.min_size); EXPECT_EQ(LayoutUnit(kWidth), sizes.max_size); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc index 20aef602f4e..f0b62aff415 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc @@ -12,75 +12,46 @@ namespace blink { -NGLineHeightMetrics NGBoxFragment::BaselineMetricsWithoutSynthesize( - const NGBaselineRequest& request) const { +NGLineHeightMetrics NGBoxFragment::BaselineMetrics( + const NGLineBoxStrut& margins, + FontBaseline baseline_type) const { + DCHECK(physical_fragment_.IsAtomicInline() || + physical_fragment_.IsListMarker()); + const ComputedStyle& style = physical_fragment_.Style(); + // For "leaf" theme objects, let the theme decide what the baseline position // is. The theme baseline wins over the propagated baselines. - const auto& physical_fragment = To<NGPhysicalBoxFragment>(physical_fragment_); - DCHECK(physical_fragment_.GetLayoutObject()); - const LayoutBox& layout_box = - ToLayoutBox(*physical_fragment_.GetLayoutObject()); - const ComputedStyle& style = physical_fragment.Style(); if (style.HasEffectiveAppearance() && !LayoutTheme::GetTheme().IsControlContainer( style.EffectiveAppearance())) { return NGLineHeightMetrics( - BlockSize() + layout_box.MarginOver() + + BlockSize() + margins.line_over + LayoutTheme::GetTheme().BaselinePositionAdjustment(style), - layout_box.MarginUnder()); + margins.line_under); } - // Check if we have a propagated baseline. - if (base::Optional<LayoutUnit> baseline = - physical_fragment.Baseline(request)) { - LayoutUnit ascent = *baseline; - LayoutUnit descent = BlockSize() - ascent; + base::Optional<LayoutUnit> baseline = Baseline(); + if (baseline) { + NGLineHeightMetrics metrics = + IsFlippedLinesWritingMode(GetWritingMode()) + ? NGLineHeightMetrics(BlockSize() - *baseline, *baseline) + : NGLineHeightMetrics(*baseline, BlockSize() - *baseline); - // For replaced elements, inline-block elements, and inline-table - // elements, the height is the height of their margin box. + // For replaced elements, inline-block elements, and inline-table elements, + // the height is the height of their margin-box. // https://drafts.csswg.org/css2/visudet.html#line-height - if (layout_box.IsAtomicInlineLevel()) { - ascent += layout_box.MarginOver(); - descent += layout_box.MarginUnder(); - } + metrics.ascent += margins.line_over; + metrics.descent += margins.line_under; - return NGLineHeightMetrics(ascent, descent); - } - - return NGLineHeightMetrics(); -} - -NGLineHeightMetrics NGBoxFragment::BaselineMetrics( - const NGBaselineRequest& request, - const NGConstraintSpace& constraint_space) const { - // Try to compute the baseline if the writing-modes are the same. - if (constraint_space.GetWritingMode() == GetWritingMode()) { - NGLineHeightMetrics metrics = BaselineMetricsWithoutSynthesize(request); - if (!metrics.IsEmpty()) - return metrics; + return metrics; } // The baseline type was not found. This is either this box should synthesize // box-baseline without propagating from children, or caller forgot to add // baseline requests to constraint space when it called Layout(). - LayoutUnit block_size = BlockSize(); - - // If atomic inline, use the margin box. See above. - const auto& physical_fragment = To<NGPhysicalBoxFragment>(physical_fragment_); - DCHECK(physical_fragment_.GetLayoutObject()); - const LayoutBox& layout_box = - ToLayoutBox(*physical_fragment_.GetLayoutObject()); - if (layout_box.IsAtomicInlineLevel()) { - bool is_parallel_writing_mode = - IsParallelWritingMode(constraint_space.GetWritingMode(), - physical_fragment.Style().GetWritingMode()); - if (is_parallel_writing_mode) - block_size += layout_box.MarginLogicalHeight(); - else - block_size += layout_box.MarginLogicalWidth(); - } + LayoutUnit block_size = BlockSize() + margins.BlockSum(); - if (request.BaselineType() == kAlphabeticBaseline) + if (baseline_type == kAlphabeticBaseline) return NGLineHeightMetrics(block_size, LayoutUnit()); return NGLineHeightMetrics(block_size - block_size / 2, block_size / 2); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h index dbfd474e7c8..f31585b148d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h @@ -14,7 +14,6 @@ namespace blink { -class NGBaselineRequest; struct NGLineHeightMetrics; class CORE_EXPORT NGBoxFragment final : public NGFragment { @@ -24,18 +23,34 @@ class CORE_EXPORT NGBoxFragment final : public NGFragment { const NGPhysicalBoxFragment& physical_fragment) : NGFragment(writing_mode, physical_fragment), direction_(direction) {} + base::Optional<LayoutUnit> FirstBaseline() const { + if (GetWritingMode() != physical_fragment_.Style().GetWritingMode()) + return base::nullopt; + + return To<NGPhysicalBoxFragment>(physical_fragment_).Baseline(); + } + + // Returns the baseline for this fragment wrt. the parent writing mode. Will + // return a null baseline if: + // - The fragment has no baseline. + // - The writing modes differ. + base::Optional<LayoutUnit> Baseline() const { + if (GetWritingMode() != physical_fragment_.Style().GetWritingMode()) + return base::nullopt; + + if (auto last_baseline = + To<NGPhysicalBoxFragment>(physical_fragment_).LastBaseline()) + return last_baseline; + + return To<NGPhysicalBoxFragment>(physical_fragment_).Baseline(); + } + // Compute baseline metrics (ascent/descent) for this box. // - // Baseline requests must be added to constraint space when this fragment was - // laid out. - // - // The "WithoutSynthesize" version returns an empty metrics if this box does - // not have any baselines, while the other version synthesize the baseline - // from the box. - NGLineHeightMetrics BaselineMetricsWithoutSynthesize( - const NGBaselineRequest&) const; - NGLineHeightMetrics BaselineMetrics(const NGBaselineRequest&, - const NGConstraintSpace&) const; + // This will synthesize baseline metrics if no baseline is available. See + // |Baseline()| for when this may occur. + NGLineHeightMetrics BaselineMetrics(const NGLineBoxStrut& margins, + FontBaseline) const; NGBoxStrut Borders() const { const NGPhysicalBoxFragment& physical_box_fragment = diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc index 990d5a59d6d..96c7f46d336 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc @@ -77,6 +77,70 @@ void GatherInlineContainerFragmentsFromLinebox( } } +void GatherInlineContainerFragmentsFromItems( + const Vector<std::unique_ptr<NGFragmentItem>>& items, + const PhysicalOffset& box_offset, + NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map, + HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map) { + const NGPhysicalLineBoxFragment* linebox = nullptr; + for (const auto& item : items) { + // Track the current linebox. + if (const NGPhysicalLineBoxFragment* current_linebox = + item->LineBoxFragment()) { + linebox = current_linebox; + continue; + } + + // We only care about inlines which have generated a box fragment. + const NGPhysicalBoxFragment* box = item->BoxFragment(); + if (!box) + continue; + + // The key for the inline is the continuation root if it exists. + const LayoutObject* key = box->GetLayoutObject(); + if (key->IsLayoutInline() && key->GetNode()) + key = key->ContinuationRoot(); + + // See if we need the containing block information for this inline. + auto it = inline_containing_block_map->find(key); + if (it == inline_containing_block_map->end()) + continue; + + base::Optional<NGBoxFragmentBuilder::InlineContainingBlockGeometry>& + containing_block_geometry = it->value; + LineBoxPair& containing_lineboxes = + containing_linebox_map->insert(key, LineBoxPair{nullptr, nullptr}) + .stored_value->value; + DCHECK(containing_block_geometry.has_value() || + !containing_lineboxes.first); + + PhysicalRect fragment_rect = item->RectInContainerBlock(); + fragment_rect.offset += box_offset; + if (containing_lineboxes.first == linebox) { + // Unite the start rect with the fragment's rect. + containing_block_geometry->start_fragment_union_rect.Unite(fragment_rect); + } else if (!containing_lineboxes.first) { + DCHECK(!containing_lineboxes.second); + // This is the first linebox we've encountered, initialize the containing + // block geometry. + containing_lineboxes.first = linebox; + containing_lineboxes.second = linebox; + containing_block_geometry = + NGBoxFragmentBuilder::InlineContainingBlockGeometry{fragment_rect, + fragment_rect}; + } + + if (containing_lineboxes.second == linebox) { + // Unite the end rect with the fragment's rect. + containing_block_geometry->end_fragment_union_rect.Unite(fragment_rect); + } else if (!linebox->IsEmptyLineBox()) { + // We've found a new "end" linebox, update the containing block geometry. + containing_lineboxes.second = linebox; + containing_block_geometry->end_fragment_union_rect = fragment_rect; + } + } +} + } // namespace void NGBoxFragmentBuilder::AddBreakBeforeChild( @@ -121,8 +185,6 @@ void NGBoxFragmentBuilder::AddResult(const NGLayoutResult& child_layout_result, items_builder_->AddLine(*line, offset); // TODO(kojii): We probably don't need to AddChild this line, but there // maybe OOF objects. Investigate how to handle them. - } else { - DCHECK(fragment.IsFloating()); } } AddChild(fragment, offset, inline_container); @@ -156,6 +218,8 @@ NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const { return NGPhysicalFragment::NGBoxType::kFloating; if (layout_object_->IsOutOfFlowPositioned()) return NGPhysicalFragment::NGBoxType::kOutOfFlowPositioned; + if (layout_object_->IsRenderedLegend()) + return NGPhysicalFragment::NGBoxType::kRenderedLegend; if (layout_object_->IsInline()) { // Check |IsAtomicInlineLevel()| after |IsInline()| because |LayoutReplaced| // sets |IsAtomicInlineLevel()| even when it's block-level. crbug.com/567964 @@ -171,15 +235,6 @@ NGPhysicalFragment::NGBoxType NGBoxFragmentBuilder::BoxType() const { return NGPhysicalFragment::NGBoxType::kNormalBox; } -void NGBoxFragmentBuilder::AddBaseline(NGBaselineRequest request, - LayoutUnit offset) { -#if DCHECK_IS_ON() - for (const auto& baseline : baselines_) - DCHECK(baseline.request != request); -#endif - baselines_.emplace_back(request, offset); -} - EBreakBetween NGBoxFragmentBuilder::JoinedBreakBetweenValue( EBreakBetween break_before) const { return JoinFragmentainerBreakValues(previous_break_after_, break_before); @@ -226,8 +281,8 @@ scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::ToBoxFragment( } if (did_break_) { break_token_ = NGBlockBreakToken::Create( - node_, consumed_block_size_, child_break_tokens_, break_appeal_, - has_seen_all_children_); + node_, consumed_block_size_, sequence_number_, child_break_tokens_, + break_appeal_, has_seen_all_children_); } } @@ -240,18 +295,54 @@ scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::ToBoxFragment( NGPhysicalBoxFragment::Create(this, block_or_line_writing_mode); fragment->CheckType(); - return base::AdoptRef(new NGLayoutResult(std::move(fragment), this)); + return base::AdoptRef( + new NGLayoutResult(NGLayoutResult::NGBoxFragmentBuilderPassKey(), + std::move(fragment), this)); } scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::Abort( NGLayoutResult::EStatus status) { - return base::AdoptRef(new NGLayoutResult(status, this)); + return base::AdoptRef(new NGLayoutResult( + NGLayoutResult::NGBoxFragmentBuilderPassKey(), status, this)); +} + +LogicalOffset NGBoxFragmentBuilder::GetChildOffset( + const LayoutObject* object) const { + DCHECK(object); + + if (const NGFragmentItemsBuilder* items_builder = items_builder_) { + if (auto offset = items_builder->LogicalOffsetFor(*object)) + return *offset; + NOTREACHED(); + return LogicalOffset(); + } + + for (const auto& child : children_) { + if (child.fragment->GetLayoutObject() == object) + return child.offset; + + // TODO(layout-dev): ikilpatrick thinks we may need to traverse + // further than the initial line-box children for a nested inline + // container. We could not come up with a testcase, it would be + // something with split inlines, and nested oof/fixed descendants maybe. + if (child.fragment->IsLineBox()) { + const auto& line_box_fragment = + To<NGPhysicalLineBoxFragment>(*child.fragment); + for (const auto& line_box_child : line_box_fragment.Children()) { + if (line_box_child->GetLayoutObject() == object) { + return child.offset + line_box_child.Offset().ConvertToLogical( + GetWritingMode(), Direction(), + line_box_fragment.Size(), + line_box_child->Size()); + } + } + } + } + NOTREACHED(); + return LogicalOffset(); } -// Computes the geometry required for any inline containing blocks. -// |inline_containing_block_map| is a map whose keys specify which inline -// containing block geometry is required. -void NGBoxFragmentBuilder::ComputeInlineContainerFragments( +void NGBoxFragmentBuilder::ComputeInlineContainerGeometryFromFragmentTree( InlineContainingBlockMap* inline_containing_block_map) { if (inline_containing_block_map->IsEmpty()) return; @@ -307,6 +398,59 @@ void NGBoxFragmentBuilder::ComputeInlineContainerFragments( } } +void NGBoxFragmentBuilder::ComputeInlineContainerGeometry( + InlineContainingBlockMap* inline_containing_block_map) { + if (inline_containing_block_map->IsEmpty()) + return; + + // This function requires that we have the final size of the fragment set + // upon the builder. + DCHECK_GE(InlineSize(), LayoutUnit()); + DCHECK_GE(BlockSize(), LayoutUnit()); + +#if DCHECK_IS_ON() + // Make sure all entries are a continuation root. + for (const auto& entry : *inline_containing_block_map) + DCHECK_EQ(entry.key, entry.key->ContinuationRoot()); +#endif + + HashMap<const LayoutObject*, LineBoxPair> containing_linebox_map; + + if (items_builder_) { + // To access the items correctly we need to convert them to the physical + // coordinate space. + GatherInlineContainerFragmentsFromItems( + items_builder_->Items(GetWritingMode(), Direction(), + ToPhysicalSize(Size(), GetWritingMode())), + PhysicalOffset(), inline_containing_block_map, &containing_linebox_map); + return; + } + + // If we have children which are anonymous block, we might contain split + // inlines, this can occur in the following example: + // <div> + // Some text <span style="position: relative;">text + // <div>block</div> + // text </span> text. + // </div> + for (const auto& child : children_) { + if (!child.fragment->IsAnonymousBlock()) + continue; + + const auto& child_fragment = To<NGPhysicalBoxFragment>(*child.fragment); + const auto* items = child_fragment.Items(); + if (!items) + continue; + + const PhysicalOffset child_offset = child.offset.ConvertToPhysical( + GetWritingMode(), Direction(), ToPhysicalSize(Size(), GetWritingMode()), + child_fragment.Size()); + GatherInlineContainerFragmentsFromItems(items->Items(), child_offset, + inline_containing_block_map, + &containing_linebox_map); + } +} + #if DCHECK_IS_ON() void NGBoxFragmentBuilder::CheckNoBlockFragmentation() const { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h index 0544b7aa751..288530aeceb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h @@ -9,7 +9,6 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_border_edges.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" @@ -38,6 +37,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final writing_mode, direction), box_type_(NGPhysicalFragment::NGBoxType::kNormalBox), + is_inline_formatting_context_(node.IsInline()), did_break_(false) {} // Build a fragment for LayoutObject without NGLayoutInputNode. LayoutInline @@ -52,6 +52,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final writing_mode, direction), box_type_(NGPhysicalFragment::NGBoxType::kNormalBox), + is_inline_formatting_context_(true), did_break_(false) { layout_object_ = layout_object; } @@ -68,9 +69,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final return *initial_fragment_geometry_; } - void SetUnconstrainedIntrinsicBlockSize( - LayoutUnit unconstrained_intrinsic_block_size) { - unconstrained_intrinsic_block_size_ = unconstrained_intrinsic_block_size; + void SetOverflowBlockSize(LayoutUnit overflow_block_size) { + overflow_block_size_ = overflow_block_size; } void SetIntrinsicBlockSize(LayoutUnit intrinsic_block_size) { intrinsic_block_size_ = intrinsic_block_size; @@ -121,6 +121,10 @@ class CORE_EXPORT NGBoxFragmentBuilder final // building now. void SetConsumedBlockSize(LayoutUnit size) { consumed_block_size_ = size; } + void SetSequenceNumber(unsigned sequence_number) { + sequence_number_ = sequence_number; + } + // Specify that we broke. // // This will result in a fragment which has an unfinished break token. @@ -197,6 +201,10 @@ class CORE_EXPORT NGBoxFragmentBuilder final void SetColumnSpanner(NGBlockNode spanner) { column_spanner_ = spanner; } bool FoundColumnSpanner() const { return !!column_spanner_; } + void SetLinesUntilClamp(const base::Optional<int>& value) { + lines_until_clamp_ = value; + } + void SetEarlyBreak(scoped_refptr<const NGEarlyBreak> breakpoint, NGBreakAppeal appeal) { early_break_ = breakpoint; @@ -240,6 +248,12 @@ class CORE_EXPORT NGBoxFragmentBuilder final void SetIsFieldsetContainer() { is_fieldset_container_ = true; } void SetIsLegacyLayoutRoot() { is_legacy_layout_root_ = true; } + void SetIsInlineFormattingContext(bool is_inline_formatting_context) { + is_inline_formatting_context_ = is_inline_formatting_context; + } + + void SetIsMathMLFraction() { is_math_fraction_ = true; } + bool DidBreak() const { return did_break_; } void SetBorderEdges(NGBorderEdges border_edges) { @@ -254,15 +268,16 @@ class CORE_EXPORT NGBoxFragmentBuilder final custom_layout_data_ = std::move(custom_layout_data); } - // Layout algorithms should call this function for each baseline request in - // the constraint space. - // - // If a request should use a synthesized baseline from the box rectangle, - // algorithms can omit the call. - // - // This function should be called at most once for a given algorithm/baseline - // type pair. - void AddBaseline(NGBaselineRequest, LayoutUnit); + // Sets the alignment baseline for this fragment. + void SetBaseline(LayoutUnit baseline) { baseline_ = baseline; } + base::Optional<LayoutUnit> Baseline() const { return baseline_; } + + // Sets the last baseline for this fragment. + void SetLastBaseline(LayoutUnit baseline) { + DCHECK_EQ(space_->BaselineAlgorithmType(), + NGBaselineAlgorithmType::kInlineBlock); + last_baseline_ = baseline; + } // The |NGFragmentItemsBuilder| for the inline formatting context of this box. NGFragmentItemsBuilder* ItemsBuilder() { return items_builder_; } @@ -270,8 +285,12 @@ class CORE_EXPORT NGBoxFragmentBuilder final items_builder_ = builder; } - // Inline containing block geometry is defined by two rectangles defined - // by fragments generated by LayoutInline. + // Returns offset for given child. DCHECK if child not found. + // Warning: Do not call unless necessary. + LogicalOffset GetChildOffset(const LayoutObject* child) const; + + // Inline containing block geometry is defined by two rectangles, generated + // by fragments of the LayoutInline. struct InlineContainingBlockGeometry { DISALLOW_NEW(); // Union of fragments generated on the first line. @@ -283,7 +302,13 @@ class CORE_EXPORT NGBoxFragmentBuilder final using InlineContainingBlockMap = HashMap<const LayoutObject*, base::Optional<InlineContainingBlockGeometry>>; - void ComputeInlineContainerFragments( + + // Computes the geometry required for any inline containing blocks. + // |inline_containing_block_map| is a map whose keys specify which inline + // containing block geometry is required. + void ComputeInlineContainerGeometryFromFragmentTree( + InlineContainingBlockMap* inline_containing_block_map); + void ComputeInlineContainerGeometry( InlineContainingBlockMap* inline_containing_block_map); #if DCHECK_IS_ON() @@ -304,7 +329,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final scoped_refptr<const NGLayoutResult> ToBoxFragment(WritingMode); const NGFragmentGeometry* initial_fragment_geometry_ = nullptr; - LayoutUnit unconstrained_intrinsic_block_size_ = kIndefiniteSize; + LayoutUnit overflow_block_size_ = kIndefiniteSize; LayoutUnit intrinsic_block_size_; NGFragmentItemsBuilder* items_builder_ = nullptr; @@ -314,12 +339,15 @@ class CORE_EXPORT NGBoxFragmentBuilder final NGPhysicalFragment::NGBoxType box_type_; bool is_fieldset_container_ = false; bool is_initial_block_size_indefinite_ = false; + bool is_inline_formatting_context_; bool did_break_; bool has_forced_break_ = false; bool is_new_fc_ = false; bool subtree_modified_margin_strut_ = false; bool has_seen_all_children_ = false; + bool is_math_fraction_ = false; LayoutUnit consumed_block_size_; + unsigned sequence_number_ = 0; LayoutUnit minimal_space_shortage_ = LayoutUnit::Max(); LayoutUnit tallest_unbreakable_block_size_ = LayoutUnit::Min(); @@ -331,11 +359,12 @@ class CORE_EXPORT NGBoxFragmentBuilder final // The break-after value of the previous in-flow sibling. EBreakBetween previous_break_after_ = EBreakBetween::kAuto; - NGBaselineList baselines_; - + base::Optional<LayoutUnit> baseline_; + base::Optional<LayoutUnit> last_baseline_; NGBorderEdges border_edges_; scoped_refptr<SerializedScriptValue> custom_layout_data_; + base::Optional<int> lines_until_clamp_; friend class NGPhysicalBoxFragment; friend class NGLayoutResult; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc index e61e4d42998..f1034c33a1c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc @@ -8,7 +8,6 @@ #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" @@ -138,10 +137,16 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() { intrinsic_block_size_ = border_scrollbar_padding_.block_start; - if (!LayoutChildren()) { + NGBreakStatus break_status = LayoutChildren(); + if (break_status == NGBreakStatus::kNeedsEarlierBreak) { // We need to discard this layout and do it again. We found an earlier break // point that's more appealing than the one we ran out of space at. return RelayoutAndBreakEarlier(); + } else if (break_status == NGBreakStatus::kBrokeBefore) { + // If we want to break before, make sure that we're actually at the start. + DCHECK(!BreakToken()); + + return container_builder_.Abort(NGLayoutResult::kOutOfFragmentainerSpace); } // Figure out how much space we've already been able to process in previous @@ -166,10 +171,9 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() { if (is_constrained_by_outer_fragmentation_context_) { // In addition to establishing one, we're nested inside another // fragmentation context. - FinishFragmentation(ConstraintSpace(), block_size, intrinsic_block_size_, - previously_consumed_block_size, - FragmentainerSpaceAtBfcStart(ConstraintSpace()), - &container_builder_); + FinishFragmentation( + ConstraintSpace(), BreakToken(), block_size, intrinsic_block_size_, + FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_); } else { container_builder_.SetBlockSize(block_size); container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_); @@ -184,19 +188,17 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() { return container_builder_.ToBoxFragment(); } -base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { +base::Optional<MinMaxSizes> NGColumnLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { // First calculate the min/max sizes of columns. NGConstraintSpace space = CreateConstraintSpaceForMinMax(); NGFragmentGeometry fragment_geometry = CalculateInitialMinMaxFragmentGeometry(space, Node()); NGBlockLayoutAlgorithm algorithm({Node(), fragment_geometry, space}); - MinMaxSizeInput child_input(input); - child_input.size_type = NGMinMaxSizeType::kContentBoxSize; - base::Optional<MinMaxSize> min_max_sizes = - algorithm.ComputeMinMaxSize(child_input); + base::Optional<MinMaxSizes> min_max_sizes = + algorithm.ComputeMinMaxSizes(input); DCHECK(min_max_sizes.has_value()); - MinMaxSize sizes = *min_max_sizes; + MinMaxSizes sizes = *min_max_sizes; // If column-width is non-auto, pick the larger of that and intrinsic column // width. @@ -217,14 +219,11 @@ base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize( // TODO(mstensho): Need to include spanners. - if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) { - sizes += border_scrollbar_padding_.InlineSum(); - } - + sizes += border_scrollbar_padding_.InlineSum(); return sizes; } -bool NGColumnLayoutAlgorithm::LayoutChildren() { +NGBreakStatus NGColumnLayoutAlgorithm::LayoutChildren() { NGMarginStrut margin_strut; // First extract incoming child break tokens. @@ -281,7 +280,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { PushSpannerBreakTokens(std::move(spanner_break_token), std::move(next_column_token), &container_builder_); - return true; + return NGBreakStatus::kContinue; } } else { // Breaking before the first element in the fragmentainer isn't allowed, @@ -291,7 +290,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { } if (BreakToken() && BreakToken()->HasSeenAllChildren() && !next_column_token) - return true; + return NGBreakStatus::kContinue; // Entering the child main loop. Here we'll alternate between laying out // column content and column spanners, until we're either done, or until @@ -301,6 +300,26 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { do { scoped_refptr<const NGLayoutResult> result = LayoutRow(next_column_token.get(), &margin_strut); + + if (!result) { + // Not enough outer fragmentainer space to produce any columns at all. + container_builder_.SetDidBreak(); + if (intrinsic_block_size_) { + // We have preceding initial border/padding, or a column spanner + // (possibly preceded by other spanners or even column content). So we + // need to break inside the multicol container. Stop walking the + // children, but "continue" layout, so that we produce a fragment. Note + // that we normally don't want to break right after initial + // border/padding, but will do so as a last resort. It's up to our + // containing block to decide what's best. + FinishAfterBreakBeforeRow(std::move(next_column_token)); + return NGBreakStatus::kContinue; + } + // Otherwise we have nothing here, and need to break before the multicol + // container. No fragment will be produced. + return NGBreakStatus::kBrokeBefore; + } + next_column_token = To<NGBlockBreakToken>(result->PhysicalFragment().BreakToken()); @@ -320,7 +339,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { container_builder_.AddBreakBeforeChild( spanner_node, kBreakAppealPerfect, /* is_forced_break */ false); FinishAfterBreakBeforeSpanner(std::move(next_column_token)); - return true; + return NGBreakStatus::kContinue; } } @@ -328,18 +347,18 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { NGBreakStatus break_status = LayoutSpanner( spanner_node, nullptr, &margin_strut, &spanner_break_token); if (break_status == NGBreakStatus::kNeedsEarlierBreak) { - return false; + return break_status; } else if (break_status == NGBreakStatus::kBrokeBefore) { DCHECK(ConstraintSpace().HasBlockFragmentation()); FinishAfterBreakBeforeSpanner(std::move(next_column_token)); - return true; + return NGBreakStatus::kContinue; } else if (spanner_break_token) { DCHECK_EQ(break_status, NGBreakStatus::kContinue); // We broke inside the spanner. This may happen if we're nested inside // another fragmentation context. PushSpannerBreakTokens(std::move(spanner_break_token), std::move(next_column_token), &container_builder_); - return true; + return NGBreakStatus::kContinue; } } while (next_column_token); @@ -362,7 +381,7 @@ bool NGColumnLayoutAlgorithm::LayoutChildren() { intrinsic_block_size_ += margin_strut.Sum(); } - return true; + return NGBreakStatus::kContinue; } scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow( @@ -401,13 +420,23 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow( LayoutUnit column_block_offset = intrinsic_block_size_ + margin_strut->Sum(); bool needs_more_fragments_in_outer = false; + bool zero_outer_space_left = false; if (is_constrained_by_outer_fragmentation_context_) { LayoutUnit available_outer_space = FragmentainerSpaceAtBfcStart(ConstraintSpace()) - column_block_offset; - // TODO(mstensho): This should never be negative, or even zero. Turn into a - // DCHECK when the underlying problem is fixed. - available_outer_space = available_outer_space.ClampNegativeToZero(); + if (available_outer_space <= LayoutUnit()) { + if (available_outer_space < LayoutUnit()) { + // We're past the end of the outer fragmentainer (typically due to a + // margin). Nothing will fit here, not even zero-size content. + return nullptr; + } + + // We are out of space, but we're exactly at the end of the outer + // fragmentainer. If none of our contents take up space, we're going to + // fit, otherwise not. Lay out and find out. + zero_outer_space_left = true; + } // Check if we can fit everything (that's remaining), block-wise, within the // current outer fragmentainer. If we can't, we need to adjust the block @@ -498,6 +527,11 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow( if (ConstraintSpace().HasBlockFragmentation() && column_break_token && actual_column_count >= used_column_count_ && needs_more_fragments_in_outer) { + // We cannot keep any of this if we have zero space left. Then we need + // to resume in the next outer fragmentainer. + if (zero_outer_space_left) + return nullptr; + container_builder_.SetDidBreak(); container_builder_.SetBreakAppeal(kBreakAppealPerfect); break; @@ -626,7 +660,8 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutSpanner( margin_strut->Append(margins.block_start, /* is_quirky */ false); LayoutUnit block_offset = intrinsic_block_size_ + margin_strut->Sum(); - auto spanner_space = CreateConstraintSpaceForSpanner(block_offset); + auto spanner_space = + CreateConstraintSpaceForSpanner(spanner_node, block_offset); const NGEarlyBreak* early_break_in_child = nullptr; if (early_break_ && early_break_->Type() == NGEarlyBreak::kBlock && @@ -764,6 +799,10 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize( NGBlockLayoutAlgorithm balancing_algorithm( {Node(), fragment_geometry, space, break_token.get()}); scoped_refptr<const NGLayoutResult> result = balancing_algorithm.Layout(); + + // This algorithm should never abort. + DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess); + const NGPhysicalBoxFragment& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment()); LayoutUnit column_block_size = CalculateColumnContentBlockSize( @@ -841,7 +880,7 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize( const ComputedStyle& style = Style(); LayoutUnit max = ResolveMaxBlockLength( - ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), size, + ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), LengthResolvePhase::kLayout); LayoutUnit extent = ResolveMainBlockLength( ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size, @@ -856,6 +895,20 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize( return size - extra; } +void NGColumnLayoutAlgorithm::FinishAfterBreakBeforeRow( + scoped_refptr<const NGBlockBreakToken> next_column_token) { + // We broke before a row for columns. We're done here. Take up the remaining + // space in the outer fragmentation context. + intrinsic_block_size_ = FragmentainerSpaceAtBfcStart(ConstraintSpace()); + + // If we were about to resume column layout after a spanner, add a break token + // for this, so that we resume there in the next outer fragmentainer. If + // there's no such break token, it means that we're at the start of the + // multicol container. + if (next_column_token) + container_builder_.AddBreakToken(std::move(next_column_token)); +} + void NGColumnLayoutAlgorithm::FinishAfterBreakBeforeSpanner( scoped_refptr<const NGBlockBreakToken> next_column_token) { // We broke before the spanner. We're done here. Take up the remaining space @@ -898,9 +951,6 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns( space_builder.SetAvailableSize(column_size); space_builder.SetPercentageResolutionSize(column_size); - if (NGBaseline::ShouldPropagateBaselines(Node())) - space_builder.AddBaselineRequests(ConstraintSpace().BaselineRequests()); - // To ensure progression, we need something larger than 0 here. The spec // actually says that fragmentainers have to accept at least 1px of content. // See https://www.w3.org/TR/css-break-3/#breaking-rules @@ -939,6 +989,7 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForBalancing( } NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner( + const NGBlockNode& spanner, LayoutUnit block_offset) const { NGConstraintSpaceBuilder space_builder( ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true); @@ -946,7 +997,7 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner( space_builder.SetPercentageResolutionSize(content_box_size_); if (ConstraintSpace().HasBlockFragmentation()) { - SetupFragmentation(ConstraintSpace(), block_offset, &space_builder, + SetupFragmentation(ConstraintSpace(), spanner, block_offset, &space_builder, /* is_new_fc */ true); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h index b9108019680..c516f1bafaa 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h @@ -26,17 +26,21 @@ class CORE_EXPORT NGColumnLayoutAlgorithm scoped_refptr<const NGLayoutResult> Layout() override; - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; private: - // Lay out as many children as we can. If false is returned, it means that we - // ran out of space at an unappealing location, and need to relayout and break - // earlier (because we have a better breakpoint there). - bool LayoutChildren(); + // Lay out as many children as we can. If |kNeedsEarlierBreak| is returned, it + // means that we ran out of space at an unappealing location, and need to + // relayout and break earlier (because we have a better breakpoint there). If + // |kBrokeBefore| is returned, it means that we need to break before the + // multicol container, and retry in the next fragmentainer. + NGBreakStatus LayoutChildren(); // Lay out one row of columns. The layout result returned is for the last - // column that was laid out. The rows themselves don't create fragments. + // column that was laid out. The rows themselves don't create fragments. If + // we're in a nested fragmentation context and completely out of outer + // fragmentainer space, nullptr will be returned. scoped_refptr<const NGLayoutResult> LayoutRow( const NGBlockBreakToken* next_column_token, NGMarginStrut*); @@ -66,6 +70,10 @@ class CORE_EXPORT NGColumnLayoutAlgorithm return intrinsic_block_size_ - border_scrollbar_padding_.block_start; } + // Finalize layout after breaking before column contents. + void FinishAfterBreakBeforeRow( + scoped_refptr<const NGBlockBreakToken> next_column_token); + // Finalize layout after breaking before a spanner. void FinishAfterBreakBeforeSpanner( scoped_refptr<const NGBlockBreakToken> next_column_token); @@ -83,6 +91,7 @@ class CORE_EXPORT NGColumnLayoutAlgorithm NGConstraintSpace CreateConstraintSpaceForBalancing( const LogicalSize& column_size) const; NGConstraintSpace CreateConstraintSpaceForSpanner( + const NGBlockNode& spanner, LayoutUnit block_offset) const; NGConstraintSpace CreateConstraintSpaceForMinMax() const; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc index 276c9823f0b..b2d3a0825ec 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc @@ -787,7 +787,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, FloatWithLastResortBreak) { offset:110,0 size:100x100 offset:0,0 size:88x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1434,14 +1433,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, LinesInMulticolExtraSpace) { offset:0,0 size:320x50 offset:0,0 size:100x50 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x50 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1476,14 +1471,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, LinesInMulticolExactFit) { offset:0,0 size:320x40 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1521,15 +1512,11 @@ TEST_F(NGColumnLayoutAlgorithmTest, LinesInMulticolChildExtraSpace) { offset:0,0 size:100x50 offset:0,0 size:77x50 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x50 offset:0,0 size:77x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1567,15 +1554,11 @@ TEST_F(NGColumnLayoutAlgorithmTest, LinesInMulticolChildExactFit) { offset:0,0 size:100x40 offset:0,0 size:77x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x40 offset:0,0 size:77x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1615,13 +1598,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, LinesInMulticolChildNoSpaceForFirst) { offset:110,0 size:100x50 offset:0,0 size:77x50 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x50 offset:0,0 size:77x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1662,13 +1642,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, offset:110,0 size:100x50 offset:0,0 size:77x50 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x50 offset:0,0 size:77x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1713,7 +1690,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, LineAtColumnBoundaryInFirstBlock) { offset:110,0 size:100x50 offset:0,0 size:66x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1752,20 +1728,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_LinesAndFloatsMulticol) { offset:0,0 size:320x70 offset:0,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:10x50 offset:10,20 size:0x20 - offset:0,9 size:0x1 offset:10,40 size:11x30 offset:21,40 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x70 offset:0,0 size:10x70 offset:10,0 size:11x70 offset:21,0 size:0x20 - offset:0,9 size:0x1 offset:21,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x70 offset:0,0 size:11x20 )DUMP"; @@ -1805,18 +1776,13 @@ TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_FloatBelowLastLineInColumn) { offset:0,0 size:320x70 offset:0,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:11x10 offset:110,0 size:100x70 offset:0,0 size:11x70 offset:11,0 size:0x20 - offset:0,9 size:0x1 offset:11,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x70 offset:0,0 size:11x40 )DUMP"; @@ -1858,11 +1824,8 @@ TEST_F(NGColumnLayoutAlgorithmTest, Orphans) { offset:110,0 size:100x90 offset:0,0 size:77x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1899,18 +1862,12 @@ TEST_F(NGColumnLayoutAlgorithmTest, OrphansUnsatisfiable) { offset:0,0 size:320x90 offset:0,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -1948,20 +1905,13 @@ TEST_F(NGColumnLayoutAlgorithmTest, Widows) { offset:0,0 size:320x110 offset:0,0 size:100x110 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x110 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2006,37 +1956,23 @@ TEST_F(NGColumnLayoutAlgorithmTest, WidowsUnsatisfiable) { offset:0,0 size:320x90 offset:0,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:330,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:440,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2071,14 +2007,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, OrphansAndUnsatisfiableWidows) { offset:0,0 size:320x70 offset:0,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2113,14 +2045,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, UnsatisfiableOrphansAndWidows) { offset:0,0 size:320x70 offset:0,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2160,17 +2088,12 @@ TEST_F(NGColumnLayoutAlgorithmTest, WidowsAndAbspos) { offset:0,0 size:100x70 offset:0,0 size:100x70 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x70 offset:0,0 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:33x33 )DUMP"; EXPECT_EQ(expectation, dump); @@ -2214,15 +2137,11 @@ TEST_F(NGColumnLayoutAlgorithmTest, BreakBetweenLinesNotBefore) { offset:0,0 size:44x60 offset:0,60 size:55x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:55x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2262,11 +2181,9 @@ TEST_F(NGColumnLayoutAlgorithmTest, BreakBetweenLinesNotBefore2) { offset:0,0 size:44x80 offset:0,80 size:55x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:55x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2306,11 +2223,9 @@ TEST_F(NGColumnLayoutAlgorithmTest, BreakBetweenLinesNotBefore3) { offset:0,0 size:44x80 offset:0,80 size:55x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:55x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2351,10 +2266,8 @@ TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_FloatInBlockMovedByOrphans) { offset:110,0 size:100x70 offset:0,0 size:77x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:10x10 offset:10,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2392,17 +2305,12 @@ TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_FloatMovedWithWidows) { offset:0,0 size:320x90 offset:0,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x90 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:10x10 offset:10,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -2809,32 +2717,32 @@ TEST_F(NGColumnLayoutAlgorithmTest, MinMax) { NGFragmentGeometry fragment_geometry = CalculateInitialFragmentGeometry(space, node); NGColumnLayoutAlgorithm algorithm({node, fragment_geometry, space}); - base::Optional<MinMaxSize> size; - MinMaxSizeInput zero_input( + base::Optional<MinMaxSizes> sizes; + MinMaxSizesInput zero_input( /* percentage_resolution_block_size */ (LayoutUnit())); // Both column-count and column-width set. style->SetColumnCount(3); style->SetColumnWidth(80); - size = algorithm.ComputeMinMaxSize(zero_input); - ASSERT_TRUE(size.has_value()); - EXPECT_EQ(LayoutUnit(260), size->min_size); - EXPECT_EQ(LayoutUnit(320), size->max_size); + sizes = algorithm.ComputeMinMaxSizes(zero_input); + ASSERT_TRUE(sizes.has_value()); + EXPECT_EQ(LayoutUnit(260), sizes->min_size); + EXPECT_EQ(LayoutUnit(320), sizes->max_size); // Only column-count set. style->SetHasAutoColumnWidth(); - size = algorithm.ComputeMinMaxSize(zero_input); - ASSERT_TRUE(size.has_value()); - EXPECT_EQ(LayoutUnit(170), size->min_size); - EXPECT_EQ(LayoutUnit(320), size->max_size); + sizes = algorithm.ComputeMinMaxSizes(zero_input); + ASSERT_TRUE(sizes.has_value()); + EXPECT_EQ(LayoutUnit(170), sizes->min_size); + EXPECT_EQ(LayoutUnit(320), sizes->max_size); // Only column-width set. style->SetColumnWidth(80); style->SetHasAutoColumnCount(); - size = algorithm.ComputeMinMaxSize(zero_input); - ASSERT_TRUE(size.has_value()); - EXPECT_EQ(LayoutUnit(80), size->min_size); - EXPECT_EQ(LayoutUnit(100), size->max_size); + sizes = algorithm.ComputeMinMaxSizes(zero_input); + ASSERT_TRUE(sizes.has_value()); + EXPECT_EQ(LayoutUnit(80), sizes->min_size); + EXPECT_EQ(LayoutUnit(100), sizes->max_size); } TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancing) { @@ -3196,7 +3104,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingSingleLine) { offset:0,0 size:320x20 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3228,7 +3135,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingSingleLineInNested) { offset:0,0 size:100x20 offset:0,0 size:45x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3262,7 +3168,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingSingleLineInNestedSpanner) { offset:0,0 size:100x20 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3327,17 +3232,12 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLines) { offset:0,0 size:320x40 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3375,21 +3275,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesOrphans) { offset:0,0 size:100x60 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x60 offset:0,0 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x60 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3427,21 +3321,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesForcedBreak) { offset:0,0 size:100x60 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x60 offset:0,0 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x60 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3479,34 +3367,22 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesForcedBreak2) { offset:0,0 size:100x100 offset:0,0 size:100x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:0,80 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x100 offset:0,0 size:99x0 offset:0,0 size:100x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:0,80 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3548,36 +3424,24 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesForcedBreak3) { offset:0,0 size:66x100 offset:0,0 size:66x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:0,80 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:66x100 offset:0,0 size:66x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x100 offset:0,0 size:66x100 offset:0,0 size:99x0 offset:0,0 size:66x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:0,60 size:0x20 - offset:0,9 size:0x1 offset:0,80 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3615,21 +3479,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesAvoidBreakInside) { offset:0,0 size:100x60 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x60 offset:0,0 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x60 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3667,19 +3525,14 @@ TEST_F(NGColumnLayoutAlgorithmTest, ColumnBalancingLinesAvoidBreakInside2) { offset:0,0 size:100x60 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x60 offset:0,0 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 offset:220,0 size:100x60 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -3978,7 +3831,6 @@ TEST_F(NGColumnLayoutAlgorithmTest, ClassCBreakPointBeforeLine) { offset:110,0 size:100x100 offset:0,0 size:55x20 offset:0,0 size:33x20 - offset:0,0 size:33x11 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -4313,6 +4165,168 @@ TEST_F(NGColumnLayoutAlgorithmTest, NestedUnbalancedInnerAutoHeight) { EXPECT_EQ(expectation, dump); } +TEST_F(NGColumnLayoutAlgorithmTest, NestedAtOuterBoundary) { + SetBodyInnerHTML(R"HTML( + <style> + .outer { columns:3; height:100px; width:320px; } + .inner { columns:2; height:50px; } + .outer, .inner { column-gap:10px; column-fill:auto; } + </style> + <div id="container"> + <div class="outer"> + <div style="width:11px; height:100px;"></div> + <div class="inner"> + <div style="width:22px; height:70px;"></div> + </div> + </div> + </div> + )HTML"); + + String dump = DumpFragmentTree(GetElementById("container")); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:320x100 + offset:0,0 size:100x100 + offset:0,0 size:11x100 + offset:110,0 size:100x100 + offset:0,0 size:100x50 + offset:0,0 size:45x50 + offset:0,0 size:22x50 + offset:55,0 size:45x50 + offset:0,0 size:22x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGColumnLayoutAlgorithmTest, NestedZeroHeightAtOuterBoundary) { + SetBodyInnerHTML(R"HTML( + <style> + .outer { columns:3; height:100px; width:320px; } + .inner { columns:2; } + .outer, .inner { column-gap:10px; column-fill:auto; } + </style> + <div id="container"> + <div class="outer"> + <div style="width:11px; height:100px;"></div> + <div class="inner"> + <div style="width:22px;"></div> + </div> + </div> + </div> + )HTML"); + + String dump = DumpFragmentTree(GetElementById("container")); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:320x100 + offset:0,0 size:100x100 + offset:0,0 size:11x100 + offset:0,100 size:100x0 + offset:0,0 size:45x1 + offset:0,0 size:22x0 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGColumnLayoutAlgorithmTest, NestedWithMarginAtOuterBoundary) { + SetBodyInnerHTML(R"HTML( + <style> + .outer { columns:3; height:100px; width:320px; } + .inner { columns:2; height:50px; margin-top:20px; } + .outer, .inner { column-gap:10px; column-fill:auto; } + </style> + <div id="container"> + <div class="outer"> + <div style="width:11px; height:90px;"></div> + <div class="inner"> + <div style="width:22px; height:70px;"></div> + </div> + </div> + </div> + )HTML"); + + String dump = DumpFragmentTree(GetElementById("container")); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:320x100 + offset:0,0 size:100x100 + offset:0,0 size:11x90 + offset:110,0 size:100x100 + offset:0,0 size:100x50 + offset:0,0 size:45x50 + offset:0,0 size:22x50 + offset:55,0 size:45x50 + offset:0,0 size:22x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGColumnLayoutAlgorithmTest, NestedWithTallBorder) { + SetBodyInnerHTML(R"HTML( + <style> + .outer { columns:3; height:100px; width:320px; } + .inner { columns:2; height:50px; border-top:100px solid; } + .outer, .inner { column-gap:10px; column-fill:auto; } + </style> + <div id="container"> + <div class="outer"> + <div class="inner"> + <div style="width:22px; height:70px;"></div> + </div> + </div> + </div> + )HTML"); + + String dump = DumpFragmentTree(GetElementById("container")); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:320x100 + offset:0,0 size:100x100 + offset:0,0 size:100x100 + offset:110,0 size:100x100 + offset:0,0 size:100x50 + offset:0,0 size:45x50 + offset:0,0 size:22x50 + offset:55,0 size:45x50 + offset:0,0 size:22x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGColumnLayoutAlgorithmTest, NestedWithTallSpanner) { + SetBodyInnerHTML(R"HTML( + <style> + .outer { columns:3; height:100px; width:320px; column-fill:auto; } + .inner { columns:2; } + .outer, .inner { column-gap:10px; } + </style> + <div id="container"> + <div class="outer"> + <div class="inner"> + <div style="column-span:all; width:22px; height:100px;"></div> + <div style="width:22px; height:70px;"></div> + </div> + </div> + </div> + )HTML"); + + String dump = DumpFragmentTree(GetElementById("container")); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:320x100 + offset:0,0 size:100x100 + offset:0,0 size:100x100 + offset:0,0 size:22x100 + offset:110,0 size:100x100 + offset:0,0 size:100x35 + offset:0,0 size:45x35 + offset:0,0 size:22x35 + offset:55,0 size:45x35 + offset:0,0 size:22x35 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + TEST_F(NGColumnLayoutAlgorithmTest, AbsposFitsInOneColumn) { SetBodyInnerHTML(R"HTML( <div id="container"> @@ -5523,14 +5537,11 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidSoftBreakBetweenSpanners3) { offset:0,0 size:100x100 offset:0,0 size:11x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:100x80 offset:0,0 size:11x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:55x60 )DUMP"; EXPECT_EQ(expectation, dump); @@ -6084,15 +6095,11 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidBreakBetweenHonorOrphansWidows) { offset:0,0 size:100x100 offset:0,0 size:100x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:100x30 )DUMP"; EXPECT_EQ(expectation, dump); @@ -6135,9 +6142,7 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidBreakBetweenHonorOrphansWidows2) { offset:110,0 size:100x100 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:100x30 )DUMP"; EXPECT_EQ(expectation, dump); @@ -6186,22 +6191,15 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidBreakBetweenHonorOrphansWidows3) { offset:0,0 size:100x100 offset:0,0 size:100x100 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:100x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:0x20 - offset:0,9 size:0x1 )DUMP"; EXPECT_EQ(expectation, dump); } @@ -6243,11 +6241,9 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidBreakBetweenIgnoreOrphansWidows) { offset:0,0 size:100x40 offset:0,40 size:100x60 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:110,0 size:100x100 offset:0,0 size:100x20 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:100x30 )DUMP"; EXPECT_EQ(expectation, dump); @@ -6296,9 +6292,7 @@ TEST_F(NGColumnLayoutAlgorithmTest, AvoidBreakBetweenLinesInsideBreakAvoid) { offset:110,0 size:100x100 offset:0,0 size:35x40 offset:0,0 size:0x20 - offset:0,9 size:0x1 offset:0,20 size:0x20 - offset:0,9 size:0x1 offset:0,40 size:36x30 )DUMP"; EXPECT_EQ(expectation, dump); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc index c219e2f14f3..e916c433314 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc @@ -35,8 +35,7 @@ static_assert(sizeof(NGConstraintSpace) == sizeof(SameSizeAsNGConstraintSpace), } // namespace NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject( - const LayoutBlock& block, - bool is_layout_root) { + const LayoutBlock& block) { // We should only ever create a constraint space from legacy layout if the // object is a new formatting context. DCHECK(block.CreatesNewFormattingContext()); @@ -77,28 +76,14 @@ NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject( /* is_new_fc */ true, !parallel_containing_block); - auto* previous_result = block.GetCachedLayoutResult(); - if (is_layout_root && previous_result) { - // Due to layout-roots (starting layout at an arbirary node, instead of the - // |LayoutView|), we can end up with a situation where we'll miss our cache - // due to baseline-requests not matching. - // - // For the case where we start at a layout-root, the baselines don't - // particularly matter, so we just request exactly the same as the previous - // layout. - builder.AddBaselineRequests( - previous_result->GetConstraintSpaceForCaching().BaselineRequests()); - } else if (!block.IsWritingModeRoot() || block.IsGridItem()) { - // Add all types because we don't know which baselines will be requested. - FontBaseline baseline_type = style.GetFontBaseline(); - bool synthesize_inline_block_baseline = - block.UseLogicalBottomMarginEdgeForInlineBlockBaseline(); - if (!synthesize_inline_block_baseline) { - builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kAtomicInline, baseline_type}); - } - builder.AddBaselineRequest( - {NGBaselineAlgorithmType::kFirstLine, baseline_type}); + if (!block.IsWritingModeRoot() || block.IsGridItem()) { + // We don't know if the parent layout will require our baseline, so always + // request it. + builder.SetNeedsBaseline(true); + builder.SetBaselineAlgorithmType(block.IsInline() && + block.IsAtomicInlineLevel() + ? NGBaselineAlgorithmType::kInlineBlock + : NGBaselineAlgorithmType::kFirstLine); } if (block.IsTableCell()) { @@ -123,6 +108,10 @@ NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject( table_style.BorderCollapse() == EBorderCollapse::kSeparate); } + if (block.IsAtomicInlineLevel() || block.IsFlexItem() || block.IsGridItem() || + block.IsFloating()) + builder.SetIsPaintedAtomically(true); + builder.SetAvailableSize(available_size); builder.SetPercentageResolutionSize(percentage_size); builder.SetIsFixedInlineSize(fixed_inline); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index 91ec55842b2..e409701f6ce 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h @@ -13,7 +13,6 @@ #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_appeal.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" #include "third_party/blink/renderer/platform/text/text_direction.h" @@ -68,6 +67,28 @@ enum NGPercentageStorage { kRareDataPercentage }; +// Some layout algorithms (flow, tables) calculate their alignment baseline +// differently if they are within an atomic-inline context. +// +// Other more modern layout algorithms (flex, grid) however ignore this flag +// and always calculate the alignment baseline in the same way (returning the +// "first-line"). +enum class NGBaselineAlgorithmType { + // Compute the baseline of the first line box. + kFirstLine, + // Compute the baseline(s) for when we are within an inline-block context. If + // the child is block-flow it will produce both the first, and last baselines. + kInlineBlock +}; + +// Some layout algorithms have multiple layout passes. Between passes they +// typically have different results which we need to cache separately for +// performance reasons. +// +// This enum gives the caching logic a hint into which cache "slot" it should +// store a result in. +enum class NGCacheSlot { kLayout, kMeasure }; + // The NGConstraintSpace represents a set of constraints and available space // which a layout algorithm may produce a NGFragment within. class CORE_EXPORT NGConstraintSpace final { @@ -134,8 +155,7 @@ class CORE_EXPORT NGConstraintSpace final { // Creates NGConstraintSpace representing LayoutObject's containing block. // This should live on NGBlockNode or another layout bridge and probably take // a root NGConstraintSpace. - static NGConstraintSpace CreateFromLayoutObject(const LayoutBlock&, - bool is_layout_root); + static NGConstraintSpace CreateFromLayoutObject(const LayoutBlock&); const NGExclusionSpace& ExclusionSpace() const { return exclusion_space_; } @@ -239,6 +259,36 @@ class CORE_EXPORT NGConstraintSpace final { return LayoutUnit(); } + // Inline/block target stretch size constraints. + // See: + // https://mathml-refresh.github.io/mathml-core/#dfn-inline-stretch-size-constraint + LayoutUnit TargetStretchInlineSize() const { + return HasRareData() ? rare_data_->TargetStretchInlineSize() + : kIndefiniteSize; + } + + bool HasTargetStretchInlineSize() const { + return TargetStretchInlineSize() != kIndefiniteSize; + } + + LayoutUnit TargetStretchAscentSize() const { + return HasRareData() ? rare_data_->TargetStretchAscentSize() + : kIndefiniteSize; + } + + bool HasTargetStretchAscentSize() const { + return TargetStretchAscentSize() != kIndefiniteSize; + } + + LayoutUnit TargetStretchDescentSize() const { + return HasRareData() ? rare_data_->TargetStretchDescentSize() + : kIndefiniteSize; + } + + bool HasTargetStretchDescentSize() const { + return TargetStretchDescentSize() != kIndefiniteSize; + } + // Return the borders which should be used for a table-cell. NGBoxStrut TableCellBorders() const { return HasRareData() ? rare_data_->TableCellBorders() : NGBoxStrut(); @@ -322,6 +372,24 @@ class CORE_EXPORT NGConstraintSpace final { return bitfields_.ancestor_has_clearance_past_adjoining_floats; } + // Returns if the parent layout needs the baseline from this layout. + // + // This bit is only used for skipping querying baseline information from + // legacy layout. + bool NeedsBaseline() const { return bitfields_.needs_baseline; } + + // How the baseline for the fragment should be calculated, see documentation + // for |NGBaselineAlgorithmType|. + NGBaselineAlgorithmType BaselineAlgorithmType() const { + return static_cast<NGBaselineAlgorithmType>( + bitfields_.baseline_algorithm_type); + } + + // Which cache slot the output layout result should be stored in. + NGCacheSlot CacheSlot() const { + return static_cast<NGCacheSlot>(bitfields_.cache_slot); + } + // Some layout modes “stretch” their children to a fixed size (e.g. flex, // grid). These flags represented whether a layout needs to produce a // fragment that satisfies a fixed constraint in the inline and block @@ -342,6 +410,8 @@ class CORE_EXPORT NGConstraintSpace final { // (ie. fit-content). This is used for inline-block, floats, etc. bool IsShrinkToFit() const { return bitfields_.is_shrink_to_fit; } + bool IsPaintedAtomically() const { return bitfields_.is_painted_atomically; } + // If specified a layout should produce a Fragment which fragments at the // blockSize if possible. NGFragmentationType BlockFragmentationType() const { @@ -387,7 +457,7 @@ class CORE_EXPORT NGConstraintSpace final { // Return true if the block size of the table-cell should be considered // restricted (e.g. height of the cell or its table is non-auto). bool IsRestrictedBlockSizeTableCell() const { - return bitfields_.is_restricted_block_size_table_cell; + return HasRareData() && rare_data_->is_restricted_block_size_table_cell; } NGMarginStrut MarginStrut() const { @@ -488,8 +558,12 @@ class CORE_EXPORT NGConstraintSpace final { return HasRareData() ? rare_data_->ClearanceOffset() : LayoutUnit::Min(); } - const NGBaselineRequestList BaselineRequests() const { - return NGBaselineRequestList(bitfields_.baseline_requests); + bool ForceTruncateAtLineClamp() const { + return HasRareData() ? rare_data_->ForceTruncateAtLineClamp() : true; + } + + base::Optional<int> LinesUntilClamp() const { + return HasRareData() ? rare_data_->LinesUntilClamp() : base::nullopt; } // Return true if the two constraint spaces are similar enough that it *may* @@ -567,9 +641,6 @@ class CORE_EXPORT NGConstraintSpace final { private: friend class NGConstraintSpaceBuilder; - explicit NGConstraintSpace(WritingMode writing_mode) - : bfc_offset_(), bitfields_(writing_mode) {} - // This struct defines all of the inputs to layout which we consider rare. // Primarily this is: // - Percentage resolution sizes which differ from the available size or @@ -577,6 +648,7 @@ class CORE_EXPORT NGConstraintSpace final { // - The margin strut. // - Anything to do with floats (the exclusion space, clearance offset, etc). // - Anything to do with fragmentation. + // - Anything to do with stretching of math operators. // // This information is kept in a separate in this heap-allocated struct to // reduce memory usage. Over time this may have to change based on usage data. @@ -584,9 +656,20 @@ class CORE_EXPORT NGConstraintSpace final { USING_FAST_MALLOC(RareData); public: + // |RareData| unions different types of data which are mutually exclusive. + // They fall into the following categories: + enum DataUnionType { + kNone, + kBlockData, // An inflow block which doesn't establish a new FC. + kTableCellData, // A table-cell (display: table-cell). + kCustomData, // A custom layout (display: layout(foo)). + kStretchData // The target inline/block stretch sizes for MathML. + }; + explicit RareData(const NGBfcOffset bfc_offset) : bfc_offset(bfc_offset), data_union_type(static_cast<unsigned>(kNone)), + is_restricted_block_size_table_cell(false), hide_table_cell_if_empty(false), block_direction_fragmentation_type( static_cast<unsigned>(kFragmentNone)), @@ -601,6 +684,8 @@ class CORE_EXPORT NGConstraintSpace final { fragmentainer_block_size(other.fragmentainer_block_size), fragmentainer_offset_at_bfc(other.fragmentainer_offset_at_bfc), data_union_type(other.data_union_type), + is_restricted_block_size_table_cell( + other.is_restricted_block_size_table_cell), hide_table_cell_if_empty(other.hide_table_cell_if_empty), block_direction_fragmentation_type( other.block_direction_fragmentation_type), @@ -619,6 +704,9 @@ class CORE_EXPORT NGConstraintSpace final { case kCustomData: new (&custom_data_) CustomData(other.custom_data_); break; + case kStretchData: + new (&stretch_data_) StretchData(other.stretch_data_); + break; default: NOTREACHED(); } @@ -636,38 +724,20 @@ class CORE_EXPORT NGConstraintSpace final { case kCustomData: custom_data_.~CustomData(); break; + case kStretchData: + stretch_data_.~StretchData(); + break; default: NOTREACHED(); } } - // |RareData| unions different types of data which are mutually exclusive. - // They fall into the following categories: - enum DataUnionType { - kNone, - kBlockData, // An inflow block which doesn't establish a new FC. - kTableCellData, // A table-cell (display: table-cell). - kCustomData // A custom layout (display: layout(foo)). - }; - - LogicalSize percentage_resolution_size; - LayoutUnit replaced_percentage_resolution_block_size; - NGBfcOffset bfc_offset; - - LayoutUnit fragmentainer_block_size = kIndefiniteSize; - LayoutUnit fragmentainer_offset_at_bfc; - - unsigned data_union_type : 2; - unsigned hide_table_cell_if_empty : 1; - unsigned block_direction_fragmentation_type : 2; - unsigned is_inside_balanced_columns : 1; - unsigned is_in_column_bfc : 1; - unsigned early_break_appeal : 2; // NGBreakAppeal - bool MaySkipLayout(const RareData& other) const { if (fragmentainer_block_size != other.fragmentainer_block_size || fragmentainer_offset_at_bfc != other.fragmentainer_offset_at_bfc || data_union_type != other.data_union_type || + is_restricted_block_size_table_cell != + other.is_restricted_block_size_table_cell || hide_table_cell_if_empty != other.hide_table_cell_if_empty || block_direction_fragmentation_type != other.block_direction_fragmentation_type || @@ -680,19 +750,23 @@ class CORE_EXPORT NGConstraintSpace final { return true; if (data_union_type == kBlockData) - return true; + return block_data_.MaySkipLayout(other.block_data_); if (data_union_type == kTableCellData) return table_cell_data_.MaySkipLayout(other.table_cell_data_); - DCHECK_EQ(data_union_type, kCustomData); - return custom_data_.MaySkipLayout(other.custom_data_); + if (data_union_type == kCustomData) + return custom_data_.MaySkipLayout(other.custom_data_); + + DCHECK_EQ(data_union_type, kStretchData); + return stretch_data_.MaySkipLayout(other.stretch_data_); } // Must be kept in sync with members checked within |MaySkipLayout|. bool IsInitialForMaySkipLayout() const { if (fragmentainer_block_size != kIndefiniteSize || - fragmentainer_offset_at_bfc || hide_table_cell_if_empty || + fragmentainer_offset_at_bfc || is_restricted_block_size_table_cell || + hide_table_cell_if_empty || block_direction_fragmentation_type != kFragmentNone || is_inside_balanced_columns || is_in_column_bfc || early_break_appeal != kBreakAppealLastResort) @@ -702,13 +776,16 @@ class CORE_EXPORT NGConstraintSpace final { return true; if (data_union_type == kBlockData) - return true; + return block_data_.IsInitialForMaySkipLayout(); if (data_union_type == kTableCellData) return table_cell_data_.IsInitialForMaySkipLayout(); - DCHECK_EQ(data_union_type, kCustomData); - return custom_data_.IsInitialForMaySkipLayout(); + if (data_union_type == kCustomData) + return custom_data_.IsInitialForMaySkipLayout(); + + DCHECK_EQ(data_union_type, kStretchData); + return stretch_data_.IsInitialForMaySkipLayout(); } NGMarginStrut MarginStrut() const { @@ -749,6 +826,25 @@ class CORE_EXPORT NGConstraintSpace final { EnsureBlockData()->clearance_offset = clearance_offset; } + base::Optional<int> LinesUntilClamp() const { + return data_union_type == kBlockData ? block_data_.lines_until_clamp + : base::nullopt; + } + + void SetLinesUntilClamp(int value) { + EnsureBlockData()->lines_until_clamp = value; + } + + int ForceTruncateAtLineClamp() const { + return data_union_type == kBlockData + ? block_data_.force_truncate_at_line_clamp + : true; + } + + void SetForceTruncateAtLineClamp(bool value) { + EnsureBlockData()->force_truncate_at_line_clamp = value; + } + NGBoxStrut TableCellBorders() const { return data_union_type == kTableCellData ? table_cell_data_.table_cell_borders @@ -786,28 +882,78 @@ class CORE_EXPORT NGConstraintSpace final { EnsureCustomData()->data = std::move(custom_layout_data); } + LayoutUnit TargetStretchInlineSize() const { + return data_union_type == kStretchData + ? stretch_data_.target_stretch_inline_size + : kIndefiniteSize; + } + + void SetTargetStretchInlineSize(LayoutUnit target_stretch_inline_size) { + EnsureStretchData()->target_stretch_inline_size = + target_stretch_inline_size; + } + + LayoutUnit TargetStretchAscentSize() const { + return data_union_type == kStretchData + ? stretch_data_.target_stretch_ascent_size + : kIndefiniteSize; + } + + void SetTargetStretchAscentSize(LayoutUnit target_stretch_ascent_size) { + EnsureStretchData()->target_stretch_ascent_size = + target_stretch_ascent_size; + } + + LayoutUnit TargetStretchDescentSize() const { + return data_union_type == kStretchData + ? stretch_data_.target_stretch_descent_size + : kIndefiniteSize; + } + + void SetTargetStretchDescentSize(LayoutUnit target_stretch_descent_size) { + EnsureStretchData()->target_stretch_descent_size = + target_stretch_descent_size; + } + + LogicalSize percentage_resolution_size; + LayoutUnit replaced_percentage_resolution_block_size; + NGBfcOffset bfc_offset; + + LayoutUnit fragmentainer_block_size = kIndefiniteSize; + LayoutUnit fragmentainer_offset_at_bfc; + + unsigned data_union_type : 3; + + unsigned is_restricted_block_size_table_cell : 1; + unsigned hide_table_cell_if_empty : 1; + + unsigned block_direction_fragmentation_type : 2; + unsigned is_inside_balanced_columns : 1; + unsigned is_in_column_bfc : 1; + unsigned early_break_appeal : 2; // NGBreakAppeal private: struct BlockData { + bool MaySkipLayout(const BlockData& other) const { + return lines_until_clamp == other.lines_until_clamp && + force_truncate_at_line_clamp == + other.force_truncate_at_line_clamp; + } + + bool IsInitialForMaySkipLayout() const { + return !lines_until_clamp.has_value() && force_truncate_at_line_clamp; + } + NGMarginStrut margin_strut; base::Optional<LayoutUnit> optimistic_bfc_block_offset; base::Optional<LayoutUnit> forced_bfc_block_offset; LayoutUnit clearance_offset = LayoutUnit::Min(); + base::Optional<int> lines_until_clamp; + // If true and |lines_until_clamp| == 1, then the line should be truncated + // regardless of whether there is more text that follows on the line. + bool force_truncate_at_line_clamp = true; }; - BlockData* EnsureBlockData() { - DCHECK(data_union_type == kNone || data_union_type == kBlockData); - if (data_union_type != kBlockData) { - data_union_type = kBlockData; - new (&block_data_) BlockData(); - } - return &block_data_; - } - struct TableCellData { - NGBoxStrut table_cell_borders; - LayoutUnit table_cell_intrinsic_padding_block_start; - LayoutUnit table_cell_intrinsic_padding_block_end; - bool MaySkipLayout(const TableCellData& other) const { return table_cell_borders == other.table_cell_borders && table_cell_intrinsic_padding_block_start == @@ -821,16 +967,11 @@ class CORE_EXPORT NGConstraintSpace final { table_cell_intrinsic_padding_block_start == LayoutUnit() && table_cell_intrinsic_padding_block_end == LayoutUnit(); } - }; - TableCellData* EnsureTableCellData() { - DCHECK(data_union_type == kNone || data_union_type == kTableCellData); - if (data_union_type != kTableCellData) { - data_union_type = kTableCellData; - new (&table_cell_data_) TableCellData(); - } - return &table_cell_data_; - } + NGBoxStrut table_cell_borders; + LayoutUnit table_cell_intrinsic_padding_block_start; + LayoutUnit table_cell_intrinsic_padding_block_end; + }; struct CustomData { scoped_refptr<SerializedScriptValue> data; @@ -842,6 +983,42 @@ class CORE_EXPORT NGConstraintSpace final { bool IsInitialForMaySkipLayout() const { return !data; } }; + struct StretchData { + bool MaySkipLayout(const StretchData& other) const { + return target_stretch_inline_size == other.target_stretch_inline_size && + target_stretch_ascent_size == other.target_stretch_ascent_size && + target_stretch_descent_size == other.target_stretch_descent_size; + } + + bool IsInitialForMaySkipLayout() const { + return target_stretch_inline_size == kIndefiniteSize && + target_stretch_ascent_size == kIndefiniteSize && + target_stretch_descent_size == kIndefiniteSize; + } + + LayoutUnit target_stretch_inline_size = kIndefiniteSize; + LayoutUnit target_stretch_ascent_size = kIndefiniteSize; + LayoutUnit target_stretch_descent_size = kIndefiniteSize; + }; + + BlockData* EnsureBlockData() { + DCHECK(data_union_type == kNone || data_union_type == kBlockData); + if (data_union_type != kBlockData) { + data_union_type = kBlockData; + new (&block_data_) BlockData(); + } + return &block_data_; + } + + TableCellData* EnsureTableCellData() { + DCHECK(data_union_type == kNone || data_union_type == kTableCellData); + if (data_union_type != kTableCellData) { + data_union_type = kTableCellData; + new (&table_cell_data_) TableCellData(); + } + return &table_cell_data_; + } + CustomData* EnsureCustomData() { DCHECK(data_union_type == kNone || data_union_type == kCustomData); if (data_union_type != kCustomData) { @@ -851,10 +1028,20 @@ class CORE_EXPORT NGConstraintSpace final { return &custom_data_; } + StretchData* EnsureStretchData() { + DCHECK(data_union_type == kNone || data_union_type == kStretchData); + if (data_union_type != kStretchData) { + data_union_type = kStretchData; + new (&stretch_data_) StretchData(); + } + return &stretch_data_; + } + union { BlockData block_data_; TableCellData table_cell_data_; CustomData custom_data_; + StretchData stretch_data_; }; }; @@ -876,13 +1063,17 @@ class CORE_EXPORT NGConstraintSpace final { is_anonymous(false), is_new_formatting_context(false), is_orthogonal_writing_mode_root(false), - is_fixed_block_size_indefinite(false), - is_restricted_block_size_table_cell(false), + is_painted_atomically(false), use_first_line_style(false), ancestor_has_clearance_past_adjoining_floats(false), + needs_baseline(false), + baseline_algorithm_type( + static_cast<unsigned>(NGBaselineAlgorithmType::kFirstLine)), + cache_slot(static_cast<unsigned>(NGCacheSlot::kLayout)), is_shrink_to_fit(false), is_fixed_inline_size(false), is_fixed_block_size(false), + is_fixed_block_size_indefinite(false), table_cell_child_layout_mode(static_cast<unsigned>( NGTableCellChildLayoutMode::kNotTableCellChild)), percentage_inline_storage(kSameAsAvailable), @@ -898,20 +1089,20 @@ class CORE_EXPORT NGConstraintSpace final { is_new_formatting_context == other.is_new_formatting_context && is_orthogonal_writing_mode_root == other.is_orthogonal_writing_mode_root && - is_fixed_block_size_indefinite == - other.is_fixed_block_size_indefinite && - is_restricted_block_size_table_cell == - other.is_restricted_block_size_table_cell && + is_painted_atomically == other.is_painted_atomically && use_first_line_style == other.use_first_line_style && ancestor_has_clearance_past_adjoining_floats == other.ancestor_has_clearance_past_adjoining_floats && - baseline_requests == other.baseline_requests; + needs_baseline == other.needs_baseline && + baseline_algorithm_type == other.baseline_algorithm_type; } bool AreSizeConstraintsEqual(const Bitfields& other) const { return is_shrink_to_fit == other.is_shrink_to_fit && is_fixed_inline_size == other.is_fixed_inline_size && is_fixed_block_size == other.is_fixed_block_size && + is_fixed_block_size_indefinite == + other.is_fixed_block_size_indefinite && table_cell_child_layout_mode == other.table_cell_child_layout_mode; } @@ -925,17 +1116,20 @@ class CORE_EXPORT NGConstraintSpace final { unsigned is_new_formatting_context : 1; unsigned is_orthogonal_writing_mode_root : 1; - unsigned is_fixed_block_size_indefinite : 1; - unsigned is_restricted_block_size_table_cell : 1; + unsigned is_painted_atomically : 1; unsigned use_first_line_style : 1; unsigned ancestor_has_clearance_past_adjoining_floats : 1; - unsigned baseline_requests : NGBaselineRequestList::kSerializedBits; + unsigned needs_baseline : 1; + unsigned baseline_algorithm_type : 1; + + unsigned cache_slot : 1; // Size constraints. unsigned is_shrink_to_fit : 1; unsigned is_fixed_inline_size : 1; unsigned is_fixed_block_size : 1; + unsigned is_fixed_block_size_indefinite : 1; unsigned table_cell_child_layout_mode : 2; // NGTableCellChildLayoutMode unsigned percentage_inline_storage : 2; // NGPercentageStorage @@ -943,6 +1137,9 @@ class CORE_EXPORT NGConstraintSpace final { unsigned replaced_percentage_block_storage : 2; // NGPercentageStorage }; + explicit NGConstraintSpace(WritingMode writing_mode) + : bfc_offset_(), bitfields_(writing_mode) {} + inline bool HasRareData() const { return bitfields_.has_rare_data; } RareData* EnsureRareData() { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h index 9a1709ec95f..68cc44fc392 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h @@ -145,6 +145,10 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { void SetIsShrinkToFit(bool b) { space_.bitfields_.is_shrink_to_fit = b; } + void SetIsPaintedAtomically(bool b) { + space_.bitfields_.is_painted_atomically = b; + } + void SetFragmentationType(NGFragmentationType fragmentation_type) { #if DCHECK_IS_ON() DCHECK(!is_block_direction_fragmentation_type_set_); @@ -172,13 +176,16 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { void SetIsRestrictedBlockSizeTableCell(bool b) { DCHECK(space_.bitfields_.is_table_cell); - space_.bitfields_.is_restricted_block_size_table_cell = b; + if (!b && !space_.rare_data_) + return; + space_.EnsureRareData()->is_restricted_block_size_table_cell = b; } void SetHideTableCellIfEmpty(bool b) { DCHECK(space_.bitfields_.is_table_cell); - if (b) - space_.EnsureRareData()->hide_table_cell_if_empty = b; + if (!b && !space_.rare_data_) + return; + space_.EnsureRareData()->hide_table_cell_if_empty = b; } void SetIsAnonymous(bool b) { space_.bitfields_.is_anonymous = b; } @@ -187,10 +194,6 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { space_.bitfields_.use_first_line_style = b; } - void SetAncestorHasClearancePastAdjoiningFloats() { - space_.bitfields_.ancestor_has_clearance_past_adjoining_floats = true; - } - void SetAdjoiningObjectTypes(NGAdjoiningObjectTypes adjoining_object_types) { if (!is_new_fc_) { space_.bitfields_.adjoining_object_types = @@ -198,6 +201,20 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { } } + void SetAncestorHasClearancePastAdjoiningFloats() { + space_.bitfields_.ancestor_has_clearance_past_adjoining_floats = true; + } + + void SetNeedsBaseline(bool b) { space_.bitfields_.needs_baseline = b; } + + void SetBaselineAlgorithmType(NGBaselineAlgorithmType type) { + space_.bitfields_.baseline_algorithm_type = static_cast<unsigned>(type); + } + + void SetCacheSlot(NGCacheSlot slot) { + space_.bitfields_.cache_slot = static_cast<unsigned>(slot); + } + void SetMarginStrut(const NGMarginStrut& margin_strut) { #if DCHECK_IS_ON() DCHECK(!is_margin_strut_set_); @@ -301,12 +318,41 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { } } - void AddBaselineRequests(const NGBaselineRequestList requests) { - DCHECK(baseline_requests_.IsEmpty()); - baseline_requests_.AppendVector(requests); + void SetForceTruncateAtLineClamp(bool value) { +#if DCHECK_IS_ON() + DCHECK(!is_force_truncate_at_line_clamp_set_); + is_force_truncate_at_line_clamp_set_ = true; +#endif + if (!value) + space_.EnsureRareData()->SetForceTruncateAtLineClamp(value); } - void AddBaselineRequest(const NGBaselineRequest request) { - baseline_requests_.push_back(request); + + void SetLinesUntilClamp(const base::Optional<int>& clamp) { +#if DCHECK_IS_ON() + DCHECK(!is_lines_until_clamp_set_); + is_lines_until_clamp_set_ = true; +#endif + DCHECK(!is_new_fc_); + if (clamp) + space_.EnsureRareData()->SetLinesUntilClamp(*clamp); + } + + void SetTargetStretchInlineSize(LayoutUnit target_stretch_inline_size) { + DCHECK_GE(target_stretch_inline_size, LayoutUnit()); + space_.EnsureRareData()->SetTargetStretchInlineSize( + target_stretch_inline_size); + } + + void SetTargetStretchAscentSize(LayoutUnit target_stretch_ascent_size) { + DCHECK_GE(target_stretch_ascent_size, LayoutUnit()); + space_.EnsureRareData()->SetTargetStretchAscentSize( + target_stretch_ascent_size); + } + + void SetTargetStretchDescentSize(LayoutUnit target_stretch_descent_size) { + DCHECK_GE(target_stretch_descent_size, LayoutUnit()); + space_.EnsureRareData()->SetTargetStretchDescentSize( + target_stretch_descent_size); } // Creates a new constraint space. @@ -326,7 +372,6 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { "simultaneously. Inferred means the constraints are in parent " "writing mode, forced means they are in child writing mode."; - space_.bitfields_.baseline_requests = baseline_requests_.Serialize(); return std::move(space_); } @@ -355,11 +400,11 @@ class CORE_EXPORT NGConstraintSpaceBuilder final { bool is_table_cell_borders_set_ = false; bool is_table_cell_intrinsic_padding_set_ = false; bool is_custom_layout_data_set_ = false; + bool is_lines_until_clamp_set_ = false; + bool is_force_truncate_at_line_clamp_set_ = false; bool to_constraint_space_called_ = false; #endif - - NGBaselineRequestList baseline_requests_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc index 6e6115501e8..4c26a00648f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc @@ -101,7 +101,7 @@ void NGContainerFragmentBuilder::PropagateChildData( // have a child positioned above our block-start edge. if ((child_offset.block_offset < LayoutUnit() && !child.IsOutOfFlowPositioned()) || - (!child.IsBlockFormattingContextRoot() && !child.IsLineBox() && + (!child.IsFormattingContextRoot() && !child.IsLineBox() && child.MayHaveDescendantAboveBlockStart())) may_have_descendant_above_block_start_ = true; @@ -120,7 +120,7 @@ void NGContainerFragmentBuilder::PropagateChildData( // If a fragment doesn't have any adjoining object descendants, and is // self-collapsing, it can be "shifted" anywhere. if (!has_adjoining_object_descendants_) { - if (!child.IsBlockFormattingContextRoot() && + if (!child.IsFormattingContextRoot() && child.HasAdjoiningObjectDescendants()) has_adjoining_object_descendants_ = true; } @@ -132,7 +132,6 @@ void NGContainerFragmentBuilder::PropagateChildData( if (const NGBreakToken* child_break_token = child.BreakToken()) { switch (child.Type()) { case NGPhysicalFragment::kFragmentBox: - case NGPhysicalFragment::kFragmentRenderedLegend: child_break_tokens_.push_back(child_break_token); break; case NGPhysicalFragment::kFragmentLineBox: @@ -163,33 +162,6 @@ void NGContainerFragmentBuilder::AddChildInternal( children_.emplace_back(child_offset, std::move(child)); } -LogicalOffset NGContainerFragmentBuilder::GetChildOffset( - const LayoutObject* object) const { - for (const auto& child : children_) { - if (child.fragment->GetLayoutObject() == object) - return child.offset; - - // TODO(layout-dev): ikilpatrick thinks we may need to traverse - // further than the initial line-box children for a nested inline - // container. We could not come up with a testcase, it would be - // something with split inlines, and nested oof/fixed descendants maybe. - if (child.fragment->IsLineBox()) { - const auto& line_box_fragment = - To<NGPhysicalLineBoxFragment>(*child.fragment); - for (const auto& line_box_child : line_box_fragment.Children()) { - if (line_box_child->GetLayoutObject() == object) { - return child.offset + line_box_child.Offset().ConvertToLogical( - GetWritingMode(), Direction(), - line_box_fragment.Size(), - line_box_child->Size()); - } - } - } - } - NOTREACHED(); - return LogicalOffset(); -} - void NGContainerFragmentBuilder::AddOutOfFlowChildCandidate( NGBlockNode child, const LogicalOffset& child_offset, @@ -197,8 +169,17 @@ void NGContainerFragmentBuilder::AddOutOfFlowChildCandidate( NGLogicalStaticPosition::BlockEdge block_edge) { DCHECK(child); + // If an OOF-positioned candidate has a static-position which uses a + // non-block-start edge, we need to adjust its static-position when the final + // block-size is known. + bool needs_block_offset_adjustment = + block_edge != NGLogicalStaticPosition::BlockEdge::kBlockStart; + has_oof_candidate_that_needs_block_offset_adjustment_ |= + needs_block_offset_adjustment; + oof_positioned_candidates_.emplace_back( - child, NGLogicalStaticPosition{child_offset, inline_edge, block_edge}); + child, NGLogicalStaticPosition{child_offset, inline_edge, block_edge}, + /* inline_container */ nullptr, needs_block_offset_adjustment); } void NGContainerFragmentBuilder::AddOutOfFlowInlineChildCandidate( @@ -226,6 +207,27 @@ void NGContainerFragmentBuilder::SwapOutOfFlowPositionedCandidates( Vector<NGLogicalOutOfFlowPositionedNode>* candidates) { DCHECK(candidates->IsEmpty()); std::swap(oof_positioned_candidates_, *candidates); + + if (!has_oof_candidate_that_needs_block_offset_adjustment_) + return; + + using BlockEdge = NGLogicalStaticPosition::BlockEdge; + + // We might have an OOF-positioned candidate whose static-position depends on + // the final block-size of this fragment. + DCHECK_NE(BlockSize(), kIndefiniteSize); + for (auto& candidate : *candidates) { + if (!candidate.needs_block_offset_adjustment) + continue; + + if (candidate.static_position.block_edge == BlockEdge::kBlockCenter) + candidate.static_position.offset.block_offset += BlockSize() / 2; + else if (candidate.static_position.block_edge == BlockEdge::kBlockEnd) + candidate.static_position.offset.block_offset += BlockSize(); + candidate.needs_block_offset_adjustment = false; + } + + has_oof_candidate_that_needs_block_offset_adjustment_ = false; } void NGContainerFragmentBuilder:: diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h index 0ca512fee7f..a76a849df0b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h @@ -88,10 +88,6 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder { const ChildrenVector& Children() const { return children_; } - // Returns offset for given child. DCHECK if child not found. - // Warning: Do not call unless necessary. - LogicalOffset GetChildOffset(const LayoutObject* child) const; - // Builder has non-trivial OOF-positioned methods. // They are intended to be used by a layout algorithm like this: // @@ -183,8 +179,9 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder { #endif protected: - friend class NGPhysicalContainerFragment; + friend class NGInlineLayoutStateStack; friend class NGLayoutResult; + friend class NGPhysicalContainerFragment; NGContainerFragmentBuilder(NGLayoutInputNode node, scoped_refptr<const ComputedStyle> style, @@ -240,6 +237,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder { bool has_block_fragmentation_ = false; bool is_fragmentation_context_root_ = false; bool may_have_descendant_above_block_start_ = false; + + bool has_oof_candidate_that_needs_block_offset_adjustment_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc index 79bf0f09f45..02c3b80ed79 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" @@ -21,16 +22,30 @@ namespace blink { NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm( const NGLayoutAlgorithmParams& params) : NGLayoutAlgorithm(params), + writing_mode_(ConstraintSpace().GetWritingMode()), border_padding_(params.fragment_geometry.border + - params.fragment_geometry.padding) { + params.fragment_geometry.padding), + consumed_block_size_(BreakToken() ? BreakToken()->ConsumedBlockSize() + : LayoutUnit()) { container_builder_.SetIsNewFormattingContext( params.space.IsNewFormattingContext()); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); + + borders_ = container_builder_.Borders(); + padding_ = container_builder_.Padding(); + border_box_size_ = container_builder_.InitialBorderBoxSize(); + block_start_padding_edge_ = borders_.block_start; + + // Leading border and padding should only apply to the first fragment. We + // don't adjust the value of border_padding_ itself so that it can be used + // when calculating the block size of the last fragment. + adjusted_border_padding_ = border_padding_; + AdjustForFragmentation(BreakToken(), &adjusted_border_padding_); } scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { - // TODO(mstensho): Support block fragmentation. - DCHECK(!BreakToken()); + // TODO(almaher): Make sure the border start is handled correctly during + // fragmentation. // Layout of a fieldset container consists of two parts: Create a child // fragment for the rendered legend (if any), and create a child fragment for @@ -42,87 +57,32 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { // with the actual fieldset contents. Since scrollbars are handled by the // anonymous child box, and since padding is inside the scrollport, padding // also needs to be handled by the anonymous child. - NGBoxStrut borders = container_builder_.Borders(); - NGBoxStrut padding = container_builder_.Padding(); - LogicalSize border_box_size = container_builder_.InitialBorderBoxSize(); - const auto writing_mode = ConstraintSpace().GetWritingMode(); - LayoutUnit block_start_padding_edge = - container_builder_.Borders().block_start; - - // TODO(vmpstr): Skip child (including legend) layout for fieldset elements. - if (NGBlockNode legend = Node().GetRenderedLegend()) { - // Lay out the legend. While the fieldset container normally ignores its - // padding, the legend is laid out within what would have been the content - // box had the fieldset been a regular block with no weirdness. - LogicalSize content_box_size = - ShrinkAvailableSize(border_box_size, border_padding_); - auto legend_space = - CreateConstraintSpaceForLegend(legend, content_box_size); - auto result = legend.Layout(legend_space, BreakToken()); - const auto& physical_fragment = result->PhysicalFragment(); - NGBoxStrut legend_margins = - ComputeMarginsFor(legend_space, legend.Style(), ConstraintSpace()); - // If the margin box of the legend is at least as tall as the fieldset - // block-start border width, it will start at the block-start border edge of - // the fieldset. As a paint effect, the block-start border will be pushed so - // that the center of the border will be flush with the center of the - // border-box of the legend. - // TODO(mstensho): inline alignment - LogicalOffset legend_offset = LogicalOffset( - border_padding_.inline_start + legend_margins.inline_start, - legend_margins.block_start); - LayoutUnit legend_margin_box_block_size = - NGFragment(writing_mode, physical_fragment).BlockSize() + - legend_margins.BlockSum(); - LayoutUnit space_left = borders.block_start - legend_margin_box_block_size; - if (space_left > LayoutUnit()) { - // If the border is the larger one, though, it will stay put at the - // border-box block-start edge of the fieldset. Then it's the legend that - // needs to be pushed. We'll center the margin box in this case, to make - // sure that both margins remain within the area occupied by the border - // also after adjustment. - legend_offset.block_offset += space_left / 2; - } else { - // If the legend is larger than the width of the fieldset block-start - // border, the actual padding edge of the fieldset will be moved - // accordingly. This will be the block-start offset for the fieldset - // contents anonymous box. - block_start_padding_edge = legend_margin_box_block_size; - } - - container_builder_.AddChild(physical_fragment, legend_offset); - } - NGBoxStrut borders_with_legend = borders; - borders_with_legend.block_start = block_start_padding_edge; - LayoutUnit intrinsic_block_size = borders_with_legend.BlockSum(); + 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()); - // Proceed with normal fieldset children (excluding the rendered legend). They - // all live inside an anonymous child box of the fieldset container. - if (auto fieldset_content = Node().GetFieldsetContent()) { - LogicalSize adjusted_padding_box_size = - ShrinkAvailableSize(border_box_size, borders_with_legend); - auto child_space = - CreateConstraintSpaceForFieldsetContent(adjusted_padding_box_size); - auto result = fieldset_content.Layout(child_space, BreakToken()); - const auto& physical_fragment = result->PhysicalFragment(); - container_builder_.AddChild(physical_fragment, - borders_with_legend.StartOffset()); + if (ConstraintSpace().IsInitialColumnBalancingPass()) + container_builder_.SetIsInitialColumnBalancingPass(); + } - intrinsic_block_size += - NGFragment(writing_mode, physical_fragment).BlockSize(); - } else { - // There was no anonymous child to provide the padding, so we have to add it - // ourselves. - intrinsic_block_size += padding.BlockSum(); + NGBreakStatus break_status = LayoutChildren(); + if (break_status == NGBreakStatus::kNeedsEarlierBreak) { + // We need to abort the layout. No fragment will be generated. + return container_builder_.Abort(NGLayoutResult::kNeedsEarlierBreak); } - intrinsic_block_size = ClampIntrinsicBlockSize( - ConstraintSpace(), Node(), border_padding_, intrinsic_block_size); + intrinsic_block_size_ = + ClampIntrinsicBlockSize(ConstraintSpace(), Node(), + adjusted_border_padding_, intrinsic_block_size_); // 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); + border_box_size_.block_size = + ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_, + intrinsic_block_size_ + consumed_block_size_); // The above computation utility knows nothing about fieldset weirdness. The // legend may eat from the available content box block size. Make room for @@ -131,34 +91,291 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() { // contents, with the conjecture being that legend is part of the contents. // Thus, only do this adjustment if we do not contain size. if (!Node().ShouldApplySizeContainment()) { - LayoutUnit minimum_border_box_block_size = - borders_with_legend.BlockSum() + padding.BlockSum(); - border_box_size.block_size = - std::max(border_box_size.block_size, minimum_border_box_block_size); + // Similar to how we add the consumed block size to the intrinsic + // block size when calculating border_box_size_.block_size, we also need to + // do so when the fieldset is adjusted to encompass the legend. + border_box_size_.block_size = + std::max(border_box_size_.block_size, + minimum_border_box_block_size_ + consumed_block_size_); } + // TODO(almaher): end border and padding may overflow the parent + // fragmentainer, and we should avoid that. + LayoutUnit block_size = border_box_size_.block_size - consumed_block_size_; + container_builder_.SetIsFieldsetContainer(); - container_builder_.SetIntrinsicBlockSize(intrinsic_block_size); - container_builder_.SetBlockSize(border_box_size.block_size); + if (ConstraintSpace().HasKnownFragmentainerBlockSize()) { + FinishFragmentation( + ConstraintSpace(), BreakToken(), block_size, intrinsic_block_size_, + FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_); + } else { + container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_); + container_builder_.SetBlockSize(block_size); + } - NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_with_legend, + NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_with_legend_, &container_builder_) .Run(); return container_builder_.ToBoxFragment(); } -base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { - MinMaxSize sizes; +NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutChildren() { + scoped_refptr<const NGBlockBreakToken> legend_break_token; + scoped_refptr<const NGBlockBreakToken> content_break_token; + bool has_seen_all_children = false; + if (const auto* token = BreakToken()) { + const auto child_tokens = token->ChildBreakTokens(); + if (wtf_size_t break_token_count = child_tokens.size()) { + DCHECK_LE(break_token_count, 2u); + for (wtf_size_t break_token_idx = 0; break_token_idx < break_token_count; + break_token_idx++) { + scoped_refptr<const NGBlockBreakToken> child_token = + To<NGBlockBreakToken>(child_tokens[break_token_idx]); + if (child_token && child_token->InputNode().IsRenderedLegend()) { + DCHECK_EQ(break_token_idx, 0u); + legend_break_token = child_token; + } else { + content_break_token = child_token; + } + } + } + if (token->HasSeenAllChildren()) { + container_builder_.SetHasSeenAllChildren(); + has_seen_all_children = true; + } + } + + NGBlockNode legend = Node().GetRenderedLegend(); + bool legend_needs_layout = + legend && (legend_break_token || !IsResumingLayout(BreakToken())); - bool apply_size_containment = node_.ShouldApplySizeContainment(); - // TODO(crbug.com/1011842): Need to consider content-size here. - if (apply_size_containment) { - if (input.size_type == NGMinMaxSizeType::kContentBoxSize) - return sizes; + if (legend_needs_layout) { + NGBreakStatus break_status = LayoutLegend(legend, legend_break_token); + if (break_status != NGBreakStatus::kContinue) + return break_status; + } + + borders_with_legend_ = borders_; + borders_with_legend_.block_start = block_start_padding_edge_; + + // The legend may eat from the available content box block size. If the + // border_box_size_ is expanded to encompass the legend, then update the + // border_box_size_ here, as well, to ensure the fieldset content gets the + // correct size. + if (!Node().ShouldApplySizeContainment() && legend_needs_layout) { + minimum_border_box_block_size_ = + borders_with_legend_.BlockSum() + padding_.BlockSum(); + if (border_box_size_.block_size != kIndefiniteSize) { + border_box_size_.block_size = + std::max(border_box_size_.block_size, minimum_border_box_block_size_); + } + } + + LogicalSize adjusted_padding_box_size = + ShrinkAvailableSize(border_box_size_, borders_with_legend_); + + // If the legend has been laid out in previous fragments, + // adjusted_padding_box_size will need to be adjusted further to account for + // block size taken up by the legend. + if (legend && adjusted_padding_box_size.block_size != kIndefiniteSize) { + LayoutUnit content_consumed_block_size = + content_break_token ? content_break_token->ConsumedBlockSize() + : LayoutUnit(); + LayoutUnit legend_block_size = + consumed_block_size_ - content_consumed_block_size; + adjusted_padding_box_size.block_size = + std::max(padding_.BlockSum(), + adjusted_padding_box_size.block_size - legend_block_size); + } + + if ((IsResumingLayout(content_break_token.get())) || + (!block_start_padding_edge_adjusted_ && IsResumingLayout(BreakToken()))) { + borders_with_legend_.block_start = LayoutUnit(); + } + intrinsic_block_size_ = borders_with_legend_.BlockSum(); + + // Proceed with normal fieldset children (excluding the rendered legend). They + // all live inside an anonymous child box of the fieldset container. + auto fieldset_content = Node().GetFieldsetContent(); + if (fieldset_content && (content_break_token || !has_seen_all_children)) { + LayoutUnit fragmentainer_block_offset; + if (ConstraintSpace().HasBlockFragmentation()) { + fragmentainer_block_offset = + ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_; + if (legend_broke_ && + IsFragmentainerOutOfSpace(fragmentainer_block_offset)) + return NGBreakStatus::kContinue; + } + NGBreakStatus break_status = LayoutFieldsetContent( + fieldset_content, content_break_token, adjusted_padding_box_size, + fragmentainer_block_offset, !!legend); + if (break_status == NGBreakStatus::kNeedsEarlierBreak) + return break_status; + } + + if (!fieldset_content) { + container_builder_.SetHasSeenAllChildren(); + // There was no anonymous child to provide the padding, so we have to add it + // ourselves. + intrinsic_block_size_ += padding_.BlockSum(); } + return NGBreakStatus::kContinue; +} + +NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend( + NGBlockNode& legend, + scoped_refptr<const NGBlockBreakToken> legend_break_token) { + // Lay out the legend. While the fieldset container normally ignores its + // padding, the legend is laid out within what would have been the content + // box had the fieldset been a regular block with no weirdness. + LogicalSize content_box_size = + ShrinkAvailableSize(border_box_size_, adjusted_border_padding_); + LogicalSize percentage_size = + CalculateChildPercentageSize(ConstraintSpace(), Node(), content_box_size); + NGBoxStrut legend_margins = ComputeMarginsFor( + legend.Style(), percentage_size.inline_size, + ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction()); + + if (legend_break_token) + legend_margins.block_start = LayoutUnit(); + + LogicalOffset legend_offset; + scoped_refptr<const NGLayoutResult> result; + scoped_refptr<const NGLayoutResult> previous_result; + LayoutUnit block_offset = legend_margins.block_start; + do { + auto legend_space = CreateConstraintSpaceForLegend( + legend, content_box_size, percentage_size, block_offset); + result = legend.Layout(legend_space, legend_break_token.get()); + + // TODO(layout-dev): Handle abortions caused by block fragmentation. + DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess); + + if (ConstraintSpace().HasBlockFragmentation()) { + NGBreakStatus break_status = BreakBeforeChildIfNeeded( + ConstraintSpace(), legend, *result.get(), + ConstraintSpace().FragmentainerOffsetAtBfc() + block_offset, + /*has_container_separation*/ false, &container_builder_); + if (break_status != NGBreakStatus::kContinue) + return break_status; + EBreakBetween break_after = JoinFragmentainerBreakValues( + result->FinalBreakAfter(), legend.Style().BreakAfter()); + container_builder_.SetPreviousBreakAfter(break_after); + } + + const auto& physical_fragment = result->PhysicalFragment(); + legend_broke_ = physical_fragment.BreakToken(); + + // We have already adjusted the legend block offset, no need to adjust + // again. + if (block_offset != legend_margins.block_start) { + // If adjusting the block_offset caused the legend to break, revert back + // to the previous result. + if (legend_broke_) { + result = std::move(previous_result); + block_offset = legend_margins.block_start; + } + break; + } + + LayoutUnit legend_margin_box_block_size = + NGFragment(writing_mode_, physical_fragment).BlockSize() + + legend_margins.BlockSum(); + LayoutUnit space_left = borders_.block_start - legend_margin_box_block_size; + if (space_left > LayoutUnit()) { + // Don't adjust the block_offset if the legend broke. + if (legend_break_token || legend_broke_) + break; + + // If the border is the larger one, though, it will stay put at the + // border-box block-start edge of the fieldset. Then it's the legend + // that needs to be pushed. We'll center the margin box in this case, to + // make sure that both margins remain within the area occupied by the + // border also after adjustment. + block_offset += space_left / 2; + if (ConstraintSpace().HasBlockFragmentation()) { + // Save the previous result in case adjusting the block_offset causes + // the legend to break. + previous_result = std::move(result); + continue; + } + } else { + // If the legend is larger than the width of the fieldset block-start + // border, the actual padding edge of the fieldset will be moved + // accordingly. This will be the block-start offset for the fieldset + // contents anonymous box. + block_start_padding_edge_ = legend_margin_box_block_size; + block_start_padding_edge_adjusted_ = true; + } + break; + } while (true); + + // If the margin box of the legend is at least as tall as the fieldset + // block-start border width, it will start at the block-start border edge + // of the fieldset. As a paint effect, the block-start border will be + // pushed so that the center of the border will be flush with the center + // of the border-box of the legend. + // TODO(mstensho): inline alignment + legend_offset = LogicalOffset( + adjusted_border_padding_.inline_start + legend_margins.inline_start, + block_offset); + + container_builder_.AddResult(*result, legend_offset); + return NGBreakStatus::kContinue; +} + +NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent( + NGBlockNode& fieldset_content, + scoped_refptr<const NGBlockBreakToken> content_break_token, + LogicalSize adjusted_padding_box_size, + LayoutUnit fragmentainer_block_offset, + bool has_legend) { + auto child_space = CreateConstraintSpaceForFieldsetContent( + fieldset_content, adjusted_padding_box_size, + borders_with_legend_.block_start); + auto result = fieldset_content.Layout(child_space, content_break_token.get()); + + // TODO(layout-dev): Handle abortions caused by block fragmentation. + DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess); + + NGBreakStatus break_status = NGBreakStatus::kContinue; + if (ConstraintSpace().HasBlockFragmentation()) { + // TODO(almaher): The legend should be treated as out-of-flow. + break_status = BreakBeforeChildIfNeeded( + ConstraintSpace(), fieldset_content, *result.get(), + fragmentainer_block_offset, + /*has_container_separation*/ has_legend, &container_builder_); + EBreakBetween break_after = JoinFragmentainerBreakValues( + result->FinalBreakAfter(), fieldset_content.Style().BreakAfter()); + container_builder_.SetPreviousBreakAfter(break_after); + } + + if (break_status == NGBreakStatus::kContinue) { + container_builder_.AddResult(*result, borders_with_legend_.StartOffset()); + intrinsic_block_size_ += + NGFragment(writing_mode_, result->PhysicalFragment()).BlockSize(); + container_builder_.SetHasSeenAllChildren(); + } + + return break_status; +} + +bool NGFieldsetLayoutAlgorithm::IsFragmentainerOutOfSpace( + LayoutUnit block_offset) const { + if (!ConstraintSpace().HasKnownFragmentainerBlockSize()) + return false; + return block_offset >= FragmentainerSpaceAtBfcStart(ConstraintSpace()); +} + +base::Optional<MinMaxSizes> NGFieldsetLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + MinMaxSizes sizes; + + // TODO(crbug.com/1011842): Need to consider content-size here. + bool apply_size_containment = Node().ShouldApplySizeContainment(); + // Size containment does not consider the legend for sizing. if (!apply_size_containment) { if (NGBlockNode legend = Node().GetRenderedLegend()) { @@ -170,42 +387,51 @@ base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize( // The fieldset content includes the fieldset padding (and any scrollbars), // while the legend is a regular child and doesn't. We may have a fieldset // without any content or legend, so add the padding here, on the outside. - sizes += ComputePadding(ConstraintSpace(), node_.Style()).InlineSum(); + sizes += ComputePadding(ConstraintSpace(), Style()).InlineSum(); // Size containment does not consider the content for sizing. if (!apply_size_containment) { if (NGBlockNode content = Node().GetFieldsetContent()) { - MinMaxSize content_minmax = + MinMaxSizes content_min_max_sizes = ComputeMinAndMaxContentContribution(Style(), content, input); - content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum(); - sizes.Encompass(content_minmax); + content_min_max_sizes += + ComputeMinMaxMargins(Style(), content).InlineSum(); + sizes.Encompass(content_min_max_sizes); } } - sizes += ComputeBorders(ConstraintSpace(), node_).InlineSum(); + sizes += ComputeBorders(ConstraintSpace(), Style()).InlineSum(); return sizes; } const NGConstraintSpace NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend( NGBlockNode legend, - LogicalSize available_size) { + LogicalSize available_size, + LogicalSize percentage_size, + LayoutUnit block_offset) { NGConstraintSpaceBuilder builder( ConstraintSpace(), legend.Style().GetWritingMode(), /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(Style(), legend, &builder); builder.SetAvailableSize(available_size); - LogicalSize percentage_size = - CalculateChildPercentageSize(ConstraintSpace(), Node(), available_size); builder.SetPercentageResolutionSize(percentage_size); builder.SetIsShrinkToFit(legend.Style().LogicalWidth().IsAuto()); builder.SetTextDirection(legend.Style().Direction()); + + if (ConstraintSpace().HasBlockFragmentation()) { + SetupFragmentation(ConstraintSpace(), legend, block_offset, &builder, + /* is_new_fc */ true); + builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal()); + } return builder.ToConstraintSpace(); } const NGConstraintSpace NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForFieldsetContent( - LogicalSize padding_box_size) { + NGBlockNode fieldset_content, + LogicalSize padding_box_size, + LayoutUnit block_offset) { NGConstraintSpaceBuilder builder(ConstraintSpace(), ConstraintSpace().GetWritingMode(), /* is_new_fc */ true); @@ -213,6 +439,12 @@ NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForFieldsetContent( builder.SetPercentageResolutionSize( ConstraintSpace().PercentageResolutionSize()); builder.SetIsFixedBlockSize(padding_box_size.block_size != kIndefiniteSize); + + if (ConstraintSpace().HasBlockFragmentation()) { + SetupFragmentation(ConstraintSpace(), fieldset_content, block_offset, + &builder, /* is_new_fc */ true); + builder.SetEarlyBreakAppeal(container_builder_.BreakAppeal()); + } return builder.ToConstraintSpace(); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h index df5a8d3538b..c375081737a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h @@ -12,6 +12,7 @@ namespace blink { +enum class NGBreakStatus; class NGBlockBreakToken; class NGConstraintSpace; @@ -24,16 +25,63 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm scoped_refptr<const NGLayoutResult> Layout() override; - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; + + private: + NGBreakStatus LayoutChildren(); + NGBreakStatus LayoutLegend( + NGBlockNode& legend, + scoped_refptr<const NGBlockBreakToken> legend_break_token); + NGBreakStatus LayoutFieldsetContent( + NGBlockNode& fieldset_content, + scoped_refptr<const NGBlockBreakToken> content_break_token, + LogicalSize adjusted_padding_box_size, + LayoutUnit fragmentainer_block_offset, + bool has_legend); const NGConstraintSpace CreateConstraintSpaceForLegend( NGBlockNode legend, - LogicalSize available_size); + LogicalSize available_size, + LogicalSize percentage_size, + LayoutUnit block_offset); const NGConstraintSpace CreateConstraintSpaceForFieldsetContent( - LogicalSize padding_box_size); + NGBlockNode fieldset_content, + LogicalSize padding_box_size, + LayoutUnit block_offset); + + bool IsFragmentainerOutOfSpace(LayoutUnit block_offset) const; + + const WritingMode writing_mode_; const NGBoxStrut border_padding_; + NGBoxStrut borders_; + NGBoxStrut padding_; + + // The border and padding after adjusting to ensure that the leading border + // and padding are only applied to the first fragment. + NGBoxStrut adjusted_border_padding_; + + // The result of borders_ after positioning the fieldset's legend element. + NGBoxStrut borders_with_legend_; + + LayoutUnit block_start_padding_edge_; + LayoutUnit intrinsic_block_size_; + const LayoutUnit consumed_block_size_; + LogicalSize border_box_size_; + + // The legend may eat from the available content box block size. This + // represents the minimum block size needed by the border box to encompass + // the legend. + LayoutUnit minimum_border_box_block_size_; + + // If true, this indicates the block_start_padding_edge_ had changed from its + // initial value during the current layout pass. + bool block_start_padding_edge_adjusted_ = false; + + // If true, this indicates that the legend broke during the current layout + // pass. + bool legend_broke_ = false; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc index ab1718db0e4..36789003311 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc @@ -35,25 +35,26 @@ class NGFieldsetLayoutAlgorithmTest return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space); } - MinMaxSize RunComputeMinAndMax(NGBlockNode node) { + MinMaxSizes RunComputeMinMaxSizes(NGBlockNode node) { NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( WritingMode::kHorizontalTb, TextDirection::kLtr, - LogicalSize(LayoutUnit(), LayoutUnit())); + LogicalSize(LayoutUnit(), LayoutUnit()), false, + node.CreatesNewFormattingContext()); NGFragmentGeometry fragment_geometry = CalculateInitialMinMaxFragmentGeometry(space, node); NGFieldsetLayoutAlgorithm algorithm({node, fragment_geometry, space}); - MinMaxSizeInput input( + MinMaxSizesInput input( /* percentage_resolution_block_size */ (LayoutUnit())); - auto min_max = algorithm.ComputeMinMaxSize(input); + auto min_max = algorithm.ComputeMinMaxSizes(input); EXPECT_TRUE(min_max.has_value()); return *min_max; } - MinMaxSize RunComputeMinAndMax(const char* element_id) { + MinMaxSizes RunComputeMinMaxSizes(const char* element_id) { Element* element = GetDocument().getElementById(element_id); NGBlockNode node(ToLayoutBox(element->GetLayoutObject())); - return RunComputeMinAndMax(node); + return RunComputeMinMaxSizes(node); } String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) { @@ -349,7 +350,7 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, ZeroHeight) { offset:unplaced size:1000x53 offset:0,0 size:126x53 offset:13,0 size:30x30 - offset:3,30 size:120x0 + offset:3,30 size:120x20 offset:10,10 size:100x200 )DUMP"; EXPECT_EQ(expectation, dump); @@ -444,6 +445,46 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, LegendPercentHeightQuirks) { EXPECT_EQ(expectation, dump); } +// This test makes sure that the fieldset content handles fieldset padding +// when the fieldset is expanded to encompass the legend. +TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetPaddingWithLegend) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:10px; width: 150px; height: 100px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 120px; + } + #child { + width: 100px; height: 40px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + <div id="child"></div> + </fieldset> + )HTML"); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext()); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:170x140 + offset:10,0 size:50x120 + offset:0,120 size:170x20 + offset:10,10 size:100x40 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + TEST_F(NGFieldsetLayoutAlgorithmTest, MinMax) { SetBodyInnerHTML(R"HTML( <style> @@ -483,31 +524,1455 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, MinMax) { </div> )HTML"); - MinMaxSize size; + MinMaxSizes sizes; + + sizes = RunComputeMinMaxSizes("fieldset1"); + EXPECT_EQ(sizes.min_size, LayoutUnit(26)); + EXPECT_EQ(sizes.max_size, LayoutUnit(26)); + + sizes = RunComputeMinMaxSizes("fieldset2"); + EXPECT_EQ(sizes.min_size, LayoutUnit(102)); + EXPECT_EQ(sizes.max_size, LayoutUnit(102)); + + sizes = RunComputeMinMaxSizes("fieldset3"); + EXPECT_EQ(sizes.min_size, LayoutUnit(102)); + EXPECT_EQ(sizes.max_size, LayoutUnit(126)); + + sizes = RunComputeMinMaxSizes("fieldset4"); + EXPECT_EQ(sizes.min_size, LayoutUnit(152)); + EXPECT_EQ(sizes.max_size, LayoutUnit(202)); + + sizes = RunComputeMinMaxSizes("fieldset5"); + EXPECT_EQ(sizes.min_size, LayoutUnit(152)); + EXPECT_EQ(sizes.max_size, LayoutUnit(176)); + + sizes = RunComputeMinMaxSizes("fieldset6"); + EXPECT_EQ(sizes.min_size, LayoutUnit(76)); + EXPECT_EQ(sizes.max_size, LayoutUnit(126)); +} + +// Tests that a fieldset won't fragment if it doesn't reach the fragmentation +// line. +TEST_F(NGFieldsetLayoutAlgorithmTest, NoFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; height: 100px; + } + </style> + <fieldset id="fieldset"></fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + // We should only have one 176x126 fragment with no fragmentation. + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + EXPECT_EQ(PhysicalSize(176, 126), fragment->Size()); + ASSERT_FALSE(fragment->BreakToken()); +} + +// Tests that a fieldset will fragment if it reaches the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, SimpleFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; height: 500px; + } + </style> + <fieldset id="fieldset"></fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + EXPECT_EQ(PhysicalSize(176, 200), fragment->Size()); + ASSERT_TRUE(fragment->BreakToken()); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + EXPECT_EQ(PhysicalSize(176, 200), fragment->Size()); + ASSERT_TRUE(fragment->BreakToken()); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + EXPECT_EQ(PhysicalSize(176, 126), fragment->Size()); + ASSERT_FALSE(fragment->BreakToken()); +} + +// Tests that a fieldset with no content or padding will fragment if it reaches +// the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, FragmentationNoPadding) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { margin:0; border:10px solid; padding:0px; width:100px; } + </style> + <fieldset id="fieldset"></fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(10); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:120x10 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:120x10 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with auto height will fragment when its content reaches +// the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentationAutoHeight) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; + } + #child { + margin:0; width: 50px; height: 500px; + } + </style> + <fieldset id="fieldset"> + <div id="child"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:3,3 size:170x197 + offset:10,10 size:50x187 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:3,0 size:170x200 + offset:10,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x126 + offset:3,0 size:170x123 + offset:10,0 size:50x113 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a set height will fragment when its content +// reaches the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, FieldsetContentFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; height: 100px; + } + #child { + margin:0; width: 50px; height: 500px; + } + </style> + <fieldset id="fieldset"> + <div id="child"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x126 + offset:3,3 size:170x120 + offset:10,10 size:50x187 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x0 + offset:3,0 size:170x0 + offset:10,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x0 + offset:3,0 size:170x0 + offset:10,0 size:50x113 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with auto height will fragment when its legend reaches +// the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationAutoHeight) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 500px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x123 + offset:13,0 size:50x100 + offset:3,100 size:170x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a set height will fragment when its legend +// reaches the fragmentation line. The used height should also be extended to +// encompass the legend. +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; height: 100px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 500px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x123 + offset:13,0 size:50x100 + offset:3,100 size:170x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with auto height will fragment when its legend/content +// reaches the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentationAutoHeight) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 500px; + } + #child { + margin:0; width: 100px; height: 200px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + <div id="child"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x100 + offset:3,100 size:170x100 + offset:10,10 size:100x90 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x123 + offset:3,0 size:170x120 + offset:10,0 size:100x110 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a set height will fragment when its legend/content +// reaches the fragmentation line. +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendAndContentFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:3px solid; margin:0; padding:10px; width: 150px; height: 100px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 500px; + } + #child { + margin:0; width: 100px; height: 200px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + <div id="child"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(200); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x200 + offset:13,0 size:50x200 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x123 + offset:13,0 size:50x100 + offset:3,100 size:170x20 + offset:10,10 size:100x90 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:176x0 + offset:3,0 size:170x0 + offset:10,0 size:100x110 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests fragmentation when a legend's child content overflows. +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendFragmentationWithOverflow) { + SetBodyInnerHTML(R"HTML( + <style> + fieldset, legend { margin:0; border:none; padding:0; } + </style> + <fieldset id="fieldset"> + <legend style="height:30px;"> + <div style="width:55px; height:150px;"></div> + </legend> + <div style="width:44px; height:150px;"></div> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:55x30 + offset:0,0 size:55x100 + offset:0,30 size:1000x70 + offset:0,0 size:44x70 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x80 + offset:0,0 size:55x0 + offset:0,0 size:55x50 + offset:0,0 size:1000x80 + offset:0,0 size:44x80 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that fragmentation works as expected when the fieldset content has a +// negative margin block start. +TEST_F(NGFieldsetLayoutAlgorithmTest, + LegendAndContentFragmentationNegativeMargin) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 150px; height: 100px; + } + #legend { + padding:0px; margin:0; width: 50px; height: 100px; + } + #child { + margin-top: -20px; width: 100px; height: 40px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + <div id="child"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:150x100 + offset:0,0 size:50x100 + offset:0,100 size:150x0 + offset:0,-20 size:100x20 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:150x0 + offset:0,0 size:150x0 + offset:0,0 size:100x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, OverflowedLegend) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="width:75%; height:60px;"> + <div id="grandchild1" style="width:50px; height:120px;"></div> + <div id="grandchild2" style="width:40px; height:20px;"></div> + </legend> + <div id="child" style="width:85%; height:10px;"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x100 + offset:0,0 size:75x60 + offset:0,0 size:50x100 + offset:0,60 size:100x40 + offset:0,0 size:85x10 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:75x0 + offset:0,0 size:50x20 + offset:0,20 size:40x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, OverflowedFieldsetContent) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="width:75%; height:10px;"> + <div style="width:50px; height:220px;"></div> + </legend> + <div style="width:85%; height:10px;"></div> + <div id="child" style="width:65%; height:10px;"> + <div style="width:51px; height:220px;"></div> + </div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x100 + offset:0,0 size:75x10 + offset:0,0 size:50x100 + offset:0,10 size:100x90 + offset:0,0 size:85x10 + offset:0,10 size:65x10 + offset:0,0 size:51x80 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:75x0 + offset:0,0 size:50x100 + offset:0,0 size:100x0 + offset:0,0 size:65x0 + offset:0,0 size:51x100 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:75x0 + offset:0,0 size:50x20 + offset:0,0 size:100x0 + offset:0,0 size:65x0 + offset:0,0 size:51x40 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, BreakInsideAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="width:10px; height:50px;"></legend> + <div style="break-inside:avoid; width:20px; height:70px;"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x100 + offset:0,0 size:10x50 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:100x0 + offset:0,0 size:20x70 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, BreakInsideAvoidTallBlock) { + // The block that has break-inside:avoid is too tall to fit in one + // fragmentainer. So a break is unavoidable. Let's check that: + // 1. The block is still shifted to the start of the next fragmentainer + // 2. We give up shifting it any further (would cause infinite an loop) + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="width:10px; height:50px;"></legend> + <div style="break-inside:avoid; width:20px; height:170px;"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x100 + offset:0,0 size:10x50 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:100x0 + offset:0,0 size:20x100 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:100x0 + offset:0,0 size:20x70 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendBreakInsideAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 50px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <div id="container"> + <div style="width:20px; height:50px;"></div> + <fieldset id="fieldset"> + <legend id="legend" style="break-inside:avoid; width:10px; height:60px;"> + </legend> + </fieldset> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:20x50 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x60 + offset:0,0 size:100x60 + offset:0,0 size:10x60 + offset:0,60 size:100x0 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, BreakBeforeAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <div id="container"> + <div style="width:20px; height:50px;"></div> + <fieldset id="fieldset"> + <legend id="legend" style="width:10px; height:25px;"></legend> + <div style="width:30px; height:25px;"></div> + <div style="break-before:avoid; width:15px; height:25px;"></div> + </fieldset> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x75 + offset:0,0 size:20x50 + offset:0,50 size:100x25 + offset:0,0 size:10x25 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x50 + offset:0,0 size:100x50 + offset:0,0 size:100x50 + offset:0,0 size:30x25 + offset:0,25 size:15x25 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendBreakBeforeAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:10px solid; margin:0; padding:0px; width: 100px; + } + #legend { + padding:0px; margin:10px; width:10px; height:25px; + } + </style> + <div id="container"> + <div style="width:20px; height:90px;"></div> + <fieldset id="fieldset"> + <legend id="legend" style="break-before:avoid;"></legend> + </fieldset> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:20x90 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x55 + offset:0,0 size:120x55 + offset:20,10 size:10x25 + offset:10,45 size:100x0 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, BreakAfterAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <div id="container"> + <div style="width:20px; height:50px;"></div> + <fieldset id="fieldset"> + <legend id="legend" style="width:10px; height:25px;"></legend> + <div style="break-after:avoid; width:30px; height:25px;"></div> + <div style="width:15px; height:25px;"></div> + </fieldset> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x75 + offset:0,0 size:20x50 + offset:0,50 size:100x25 + offset:0,0 size:10x25 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x50 + offset:0,0 size:100x50 + offset:0,0 size:100x50 + offset:0,0 size:30x25 + offset:0,25 size:15x25 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, LegendBreakAfterAvoid) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:0px solid; margin:0; padding:0px; width: 100px; + } + #legend { + padding:0px; margin:0px; width:10px; height:50px; + } + </style> + <div id="container"> + <div style="width:20px; height:50px;"></div> + <fieldset id="fieldset"> + <legend id="legend" style="break-after:avoid;"></legend> + <div style="width:15px; height:25px;"></div> + </fieldset> + </div> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x100 + offset:0,0 size:20x50 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:1000x75 + offset:0,0 size:100x75 + offset:0,0 size:10x50 + offset:0,50 size:100x25 + offset:0,0 size:15x25 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, MarginTopPastEndOfFragmentainer) { + // A block whose border box would start past the end of the current + // fragmentainer should start exactly at the start of the next fragmentainer, + // discarding what's left of the margin. + // https://www.w3.org/TR/css-break-3/#break-margins + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="margin-top:60px; width:10px; height:20px;"></legend> + <div style="width:20px; height:20px;"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(50); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x50 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x50 + offset:0,0 size:10x20 + offset:0,20 size:100x30 + offset:0,0 size:20x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +TEST_F(NGFieldsetLayoutAlgorithmTest, MarginBottomPastEndOfFragmentainer) { + // A block whose border box would start past the end of the current + // fragmentainer should start exactly at the start of the next fragmentainer, + // discarding what's left of the margin. + // https://www.w3.org/TR/css-break-3/#break-margins + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { + border:none; margin:0; padding:0px; width: 100px; height: 100px; + } + #legend { + padding:0px; margin:0px; + } + </style> + <fieldset id="fieldset"> + <legend id="legend" style="margin-bottom:20px; height:90px;"></legend> + <div style="width:20px; height:20px;"></div> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(100); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x100 + offset:0,0 size:0x90 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_FALSE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:100x0 + offset:0,0 size:100x0 + offset:0,0 size:20x20 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a large border and a small legend fragment +// correctly. In this case, the legend block offset is not adjusted because the +// legend is broken across multiple fragments. +TEST_F(NGFieldsetLayoutAlgorithmTest, SmallLegendLargeBorderFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { margin:0; border:60px solid; padding:0px; width:100px; + height:10px; } + #legend { padding:0; width:10px; height:50px; } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(40); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 + offset:60,0 size:10x40 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 + offset:60,0 size:10x10 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + // TODO(almaher): There should be no break token here. In this case the bottom + // border never reduces in size, causing fragmentation to continue infinitely. + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x10 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a large border and a small legend fragment +// correctly. In this case, the legend block offset is adjusted because the +// legend fits inside the first fragment. +TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { margin:0; border:60px solid; padding:0px; width:100px; + height:10px; } + #legend { padding:0; width:10px; height:5px; } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(40); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 + offset:60,27.5 size:10x5 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 +)DUMP"; + EXPECT_EQ(expectation, dump); + + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + // TODO(almaher): There should be no break token here. In this case the bottom + // border never reduces in size, causing fragmentation to continue infinitely. + ASSERT_TRUE(fragment->BreakToken()); + + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x10 +)DUMP"; + EXPECT_EQ(expectation, dump); +} + +// Tests that a fieldset with a large border and a small legend fragment +// correctly. In this case, the legend block offset is not adjusted because the +// legend breaks after attempting to adjust the offset. +TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderWithBreak) { + SetBodyInnerHTML(R"HTML( + <style> + #fieldset { margin:0; border:60px solid; padding:0px; width:100px; + height:10px; } + #legend { padding:0; width:10px; height:5px; margin-top:16px; } + </style> + <fieldset id="fieldset"> + <legend id="legend"></legend> + </fieldset> + )HTML"); + + LayoutUnit kFragmentainerSpaceAvailable(40); + + NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("fieldset"))); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, + node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable); + + scoped_refptr<const NGPhysicalBoxFragment> fragment = + NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm(node, space); + ASSERT_TRUE(fragment->BreakToken()); + + String dump = DumpFragmentTree(fragment.get()); + String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 + offset:60,16 size:10x5 +)DUMP"; + EXPECT_EQ(expectation, dump); - size = RunComputeMinAndMax("fieldset1"); - EXPECT_EQ(size.min_size, LayoutUnit(26)); - EXPECT_EQ(size.max_size, LayoutUnit(26)); + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); - size = RunComputeMinAndMax("fieldset2"); - EXPECT_EQ(size.min_size, LayoutUnit(102)); - EXPECT_EQ(size.max_size, LayoutUnit(102)); + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 +)DUMP"; + EXPECT_EQ(expectation, dump); - size = RunComputeMinAndMax("fieldset3"); - EXPECT_EQ(size.min_size, LayoutUnit(102)); - EXPECT_EQ(size.max_size, LayoutUnit(126)); + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + ASSERT_TRUE(fragment->BreakToken()); - size = RunComputeMinAndMax("fieldset4"); - EXPECT_EQ(size.min_size, LayoutUnit(152)); - EXPECT_EQ(size.max_size, LayoutUnit(202)); + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x40 +)DUMP"; + EXPECT_EQ(expectation, dump); - size = RunComputeMinAndMax("fieldset5"); - EXPECT_EQ(size.min_size, LayoutUnit(152)); - EXPECT_EQ(size.max_size, LayoutUnit(176)); + fragment = NGBaseLayoutAlgorithmTest::RunFieldsetLayoutAlgorithm( + node, space, fragment->BreakToken()); + // TODO(almaher): There should be no break token here. In this case the bottom + // border never reduces in size, causing fragmentation to continue infinitely. + ASSERT_TRUE(fragment->BreakToken()); - size = RunComputeMinAndMax("fieldset6"); - EXPECT_EQ(size.min_size, LayoutUnit(76)); - EXPECT_EQ(size.max_size, LayoutUnit(126)); + dump = DumpFragmentTree(fragment.get()); + expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::. + offset:unplaced size:220x10 +)DUMP"; + EXPECT_EQ(expectation, dump); } } // anonymous namespace diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.cc new file mode 100644 index 00000000000..350d605af11 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.cc @@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h" + +namespace blink { + +NGFlexChildIterator::NGFlexChildIterator(const NGBlockNode node) { + bool is_deprecated_webkit_box = node.Style().IsDeprecatedWebkitBox(); + int initial_order = is_deprecated_webkit_box + ? ComputedStyleInitialValues::InitialBoxOrdinalGroup() + : ComputedStyleInitialValues::InitialOrder(); + bool needs_sort = false; + + // Collect all our children, and order them by either their + // -webkit-box-ordinal-group/order property. + for (NGLayoutInputNode child = node.FirstChild(); child; + child = child.NextSibling()) { + int order = is_deprecated_webkit_box ? child.Style().BoxOrdinalGroup() + : child.Style().Order(); + needs_sort |= order != initial_order; + children_.emplace_back(To<NGBlockNode>(child), order); + } + + // We only need to sort this vector if we encountered a non-initial + // -webkit-box-ordinal-group/order property. + if (needs_sort) { + std::stable_sort(children_.begin(), children_.end(), + [](const ChildWithOrder& c1, const ChildWithOrder& c2) { + return c1.order < c2.order; + }); + } + + iterator_ = children_.begin(); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h new file mode 100644 index 00000000000..609483fc790 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h @@ -0,0 +1,53 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FLEX_CHILD_ITERATOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FLEX_CHILD_ITERATOR_H_ + +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// A utility class for flex layout which given the current node will iterate +// through its children. +// +// TODO(layout-dev): Once flex layout supports NG-fragmentation this will need +// to be updated to accept a break-token. +// +// This class does not handle modifications to its arguments after it has been +// constructed. +class CORE_EXPORT NGFlexChildIterator { + STACK_ALLOCATED(); + + public: + NGFlexChildIterator(const NGBlockNode node); + + // Returns the next block node which should be laid out. + NGBlockNode NextChild() { + if (iterator_ == children_.end()) + return nullptr; + + return (*iterator_++).child; + } + + struct ChildWithOrder { + DISALLOW_NEW(); + ChildWithOrder(NGBlockNode child, int order) : child(child), order(order) {} + NGBlockNode child; + int order; + }; + + private: + Vector<ChildWithOrder, 4> children_; + Vector<ChildWithOrder, 4>::const_iterator iterator_; +}; + +} // namespace blink + +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS( + blink::NGFlexChildIterator::ChildWithOrder) + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FLEX_CHILD_ITERATOR_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc index 9bbdcdbedba..21c01f8a5be 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc @@ -9,12 +9,16 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h" #include "third_party/blink/renderer/core/layout/ng/ng_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h" +#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -27,10 +31,18 @@ NGFlexLayoutAlgorithm::NGFlexLayoutAlgorithm( border_scrollbar_padding_(border_padding_ + params.fragment_geometry.scrollbar), is_column_(Style().ResolvedIsColumnFlexDirection()), - is_horizontal_flow_(FlexLayoutAlgorithm::IsHorizontalFlow(Style())) { + is_horizontal_flow_(FlexLayoutAlgorithm::IsHorizontalFlow(Style())), + is_cross_size_definite_(IsContainerCrossSizeDefinite()) { container_builder_.SetIsNewFormattingContext( params.space.IsNewFormattingContext()); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); + + border_box_size_ = container_builder_.InitialBorderBoxSize(); + content_box_size_ = + ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_); + child_percentage_size_ = CalculateChildPercentageSize( + ConstraintSpace(), Node(), content_box_size_); + algorithm_.emplace(&Style(), MainAxisContentExtent(LayoutUnit::Max())); } bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis( @@ -40,26 +52,101 @@ bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis( } LayoutUnit NGFlexLayoutAlgorithm::MainAxisContentExtent( - LayoutUnit sum_hypothetical_main_size) { + LayoutUnit sum_hypothetical_main_size) const { if (Style().ResolvedIsColumnFlexDirection()) { return ComputeBlockSizeForFragment( ConstraintSpace(), Style(), border_padding_, - sum_hypothetical_main_size + (border_padding_).BlockSum()) - + sum_hypothetical_main_size + + border_scrollbar_padding_.BlockSum()) - border_scrollbar_padding_.BlockSum(); } return content_box_size_.inline_size; } +namespace { + +enum AxisEdge { kStart, kCenter, kEnd }; + +// Maps the resolved justify-content value to a static-position edge. +AxisEdge MainAxisStaticPositionEdge(const ComputedStyle& style, + bool is_column) { + const StyleContentAlignmentData justify = + FlexLayoutAlgorithm::ResolvedJustifyContent(style); + const ContentPosition content_position = justify.GetPosition(); + bool is_reverse_flex = is_column + ? style.ResolvedIsColumnReverseFlexDirection() + : style.ResolvedIsRowReverseFlexDirection(); + + if (content_position == ContentPosition::kFlexEnd) + return is_reverse_flex ? AxisEdge::kStart : AxisEdge::kEnd; + + if (content_position == ContentPosition::kCenter || + justify.Distribution() == ContentDistributionType::kSpaceAround || + justify.Distribution() == ContentDistributionType::kSpaceEvenly) + return AxisEdge::kCenter; + + return is_reverse_flex ? AxisEdge::kEnd : AxisEdge::kStart; +} + +// Maps the resolved alignment value to a static-position edge. +AxisEdge CrossAxisStaticPositionEdge(const ComputedStyle& style, + const ComputedStyle& child_style) { + ItemPosition alignment = + FlexLayoutAlgorithm::AlignmentForChild(style, child_style); + bool is_wrap_reverse = style.FlexWrap() == EFlexWrap::kWrapReverse; + + if (alignment == ItemPosition::kFlexEnd) + return is_wrap_reverse ? AxisEdge::kStart : AxisEdge::kEnd; + + if (alignment == ItemPosition::kCenter) + return AxisEdge::kCenter; + + return is_wrap_reverse ? AxisEdge::kEnd : AxisEdge::kStart; +} + +} // namespace + void NGFlexLayoutAlgorithm::HandleOutOfFlowPositioned(NGBlockNode child) { - // TODO(dgrogan): There's stuff from - // https://www.w3.org/TR/css-flexbox-1/#abspos-items that isn't done here. - // Specifically, neither rtl nor alignment is handled here, at least. - // Look at LayoutFlexibleBox::PrepareChildForPositionedLayout and - // SetStaticPositionForPositionedLayout to see how to statically position - // this. - container_builder_.AddOutOfFlowChildCandidate( - child, {border_scrollbar_padding_.inline_start, - border_scrollbar_padding_.block_start}); + AxisEdge main_axis_edge = MainAxisStaticPositionEdge(Style(), is_column_); + AxisEdge cross_axis_edge = + CrossAxisStaticPositionEdge(Style(), child.Style()); + + AxisEdge inline_axis_edge = is_column_ ? cross_axis_edge : main_axis_edge; + AxisEdge block_axis_edge = is_column_ ? main_axis_edge : cross_axis_edge; + + using InlineEdge = NGLogicalStaticPosition::InlineEdge; + using BlockEdge = NGLogicalStaticPosition::BlockEdge; + + InlineEdge inline_edge; + BlockEdge block_edge; + LogicalOffset offset(border_scrollbar_padding_.inline_start, + border_scrollbar_padding_.block_start); + + // Determine the static-position based off the axis-edge. + if (inline_axis_edge == AxisEdge::kStart) { + inline_edge = InlineEdge::kInlineStart; + } else if (inline_axis_edge == AxisEdge::kCenter) { + inline_edge = InlineEdge::kInlineCenter; + offset.inline_offset += content_box_size_.inline_size / 2; + } else { + inline_edge = InlineEdge::kInlineEnd; + offset.inline_offset += content_box_size_.inline_size; + } + + // We may not know the final block-size of the fragment yet. This will be + // adjusted within the |NGContainerFragmentBuilder| once set. + if (block_axis_edge == AxisEdge::kStart) { + block_edge = BlockEdge::kBlockStart; + } else if (block_axis_edge == AxisEdge::kCenter) { + block_edge = BlockEdge::kBlockCenter; + offset.block_offset -= border_scrollbar_padding_.BlockSum() / 2; + } else { + block_edge = BlockEdge::kBlockEnd; + offset.block_offset -= border_scrollbar_padding_.BlockSum(); + } + + container_builder_.AddOutOfFlowChildCandidate(child, offset, inline_edge, + block_edge); } bool NGFlexLayoutAlgorithm::IsColumnContainerMainSizeDefinite() const { @@ -110,13 +197,27 @@ bool NGFlexLayoutAlgorithm::DoesItemStretch(const NGBlockNode& child) const { ItemPosition::kStretch; } +bool NGFlexLayoutAlgorithm::IsItemFlexBasisDefinite( + const NGBlockNode& child) const { + const Length& flex_basis = child.Style().FlexBasis(); + DCHECK(!flex_basis.IsAuto()) + << "This is never called with flex_basis.IsAuto, but it'd be trivial to " + "support."; + if (!is_column_) + return true; + return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child), flex_basis, + LengthResolvePhase::kLayout); +} + // This behavior is under discussion: the item's pre-flexing main size // definiteness may no longer imply post-flexing definiteness. // TODO(dgrogan): Have https://crbug.com/1003506 and // https://github.com/w3c/csswg-drafts/issues/4305 been resolved yet? bool NGFlexLayoutAlgorithm::IsItemMainSizeDefinite( const NGBlockNode& child) const { - DCHECK(is_column_); + DCHECK(is_column_) + << "This method doesn't work with row flexboxes because we assume " + "main size is block size when we call BlockLengthUnresolvable."; // Inline sizes are always definite. // TODO(dgrogan): The relevant tests, the last two cases in // css/css-flexbox/percentage-heights-003.html passed even without this, so it @@ -126,9 +227,8 @@ bool NGFlexLayoutAlgorithm::IsItemMainSizeDefinite( // We need a constraint space for the child to determine resolvability and the // space for flex-basis is sufficient, even though it has some unnecessary // stuff (ShrinkToFit and fixed cross sizes). - NGConstraintSpace child_space = - BuildConstraintSpaceForDeterminingFlexBasis(child); - return !BlockLengthUnresolvable(child_space, child.Style().LogicalHeight(), + return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child), + child.Style().LogicalHeight(), LengthResolvePhase::kLayout); } @@ -143,9 +243,8 @@ bool NGFlexLayoutAlgorithm::IsItemCrossAxisLengthDefinite( if (!MainAxisIsInlineAxis(child)) return true; // If we get here, cross axis is block axis. - return !BlockLengthUnresolvable( - BuildConstraintSpaceForDeterminingFlexBasis(child), length, - LengthResolvePhase::kLayout); + return !BlockLengthUnresolvable(BuildSpaceForFlexBasis(child), length, + LengthResolvePhase::kLayout); } bool NGFlexLayoutAlgorithm::DoesItemCrossSizeComputeToAuto( @@ -197,139 +296,157 @@ bool NGFlexLayoutAlgorithm::ShouldItemShrinkToFit( bool NGFlexLayoutAlgorithm::WillChildCrossSizeBeContainerCrossSize( const NGBlockNode& child) const { - return !algorithm_->IsMultiline() && IsContainerCrossSizeDefinite() && + return !algorithm_->IsMultiline() && is_cross_size_definite_ && DoesItemStretch(child); } -NGConstraintSpace -NGFlexLayoutAlgorithm::BuildConstraintSpaceForDeterminingFlexBasis( - const NGBlockNode& flex_item) const { +double NGFlexLayoutAlgorithm::GetMainOverCrossAspectRatio( + const NGBlockNode& child) const { + DCHECK(child.HasAspectRatio()); + LogicalSize aspect_ratio = child.GetAspectRatio(); + + DCHECK_GT(aspect_ratio.inline_size, 0); + DCHECK_GT(aspect_ratio.block_size, 0); + + double ratio = + aspect_ratio.inline_size.ToDouble() / aspect_ratio.block_size.ToDouble(); + // Multiplying by ratio will take something in the item's block axis and + // convert it to the inline axis. We want to convert from cross size to main + // size. If block axis and cross axis are the same, then we already have what + // we need. Otherwise we need to use the reciprocal. + if (!MainAxisIsInlineAxis(child)) + ratio = 1 / ratio; + return ratio; +} + +namespace { + +LayoutUnit CalculateFixedCrossSize(LayoutUnit available_size, + const MinMaxSizes& cross_axis_min_max, + LayoutUnit margin_sum) { + return cross_axis_min_max.ClampSizeToMinAndMax(available_size - margin_sum); +} + +} // namespace + +NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicBlockSize( + const NGBlockNode& flex_item, + const NGPhysicalBoxStrut& physical_margins, + const MinMaxSizes& cross_axis_min_max) const { const ComputedStyle& child_style = flex_item.Style(); NGConstraintSpaceBuilder space_builder(ConstraintSpace(), child_style.GetWritingMode(), /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item, &space_builder); + space_builder.SetCacheSlot(NGCacheSlot::kMeasure); + space_builder.SetIsPaintedAtomically(true); - if (ShouldItemShrinkToFit(flex_item)) + NGBoxStrut margins = physical_margins.ConvertToLogical( + ConstraintSpace().GetWritingMode(), Style().Direction()); + LogicalSize child_available_size = content_box_size_; + if (ShouldItemShrinkToFit(flex_item)) { space_builder.SetIsShrinkToFit(true); - if (WillChildCrossSizeBeContainerCrossSize(flex_item)) { + } else if (WillChildCrossSizeBeContainerCrossSize(flex_item)) { if (is_column_) { space_builder.SetIsFixedInlineSize(true); + child_available_size.inline_size = + CalculateFixedCrossSize(child_available_size.inline_size, + cross_axis_min_max, margins.InlineSum()); } else { space_builder.SetIsFixedBlockSize(true); DCHECK_NE(content_box_size_.block_size, kIndefiniteSize); + child_available_size.block_size = + CalculateFixedCrossSize(child_available_size.block_size, + cross_axis_min_max, margins.BlockSum()); } } + space_builder.SetNeedsBaseline( + ConstraintSpace().NeedsBaseline() || + FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) == + ItemPosition::kBaseline); + + // For determining the intrinsic block-size we make %-block-sizes resolve + // against an indefinite size. + LogicalSize child_percentage_size = child_percentage_size_; + if (is_column_) + child_percentage_size.block_size = kIndefiniteSize; + + space_builder.SetAvailableSize(child_available_size); + space_builder.SetPercentageResolutionSize(child_percentage_size); + // TODO(dgrogan): The SetReplacedPercentageResolutionSize calls in this file + // may be untested. Write a test or determine why they're unnecessary. + space_builder.SetReplacedPercentageResolutionSize(child_percentage_size); + space_builder.SetTextDirection(child_style.Direction()); + return space_builder.ToConstraintSpace(); +} + +NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForFlexBasis( + const NGBlockNode& flex_item) const { + NGConstraintSpaceBuilder space_builder(ConstraintSpace(), + flex_item.Style().GetWritingMode(), + /* is_new_fc */ true); + SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item, &space_builder); + + // This space is only used for resolving lengths, not for layout. We only + // need the available and percentage sizes. space_builder.SetAvailableSize(content_box_size_); space_builder.SetPercentageResolutionSize(child_percentage_size_); - space_builder.SetTextDirection(child_style.Direction()); + space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_); return space_builder.ToConstraintSpace(); } void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() { - for (NGLayoutInputNode generic_child = Node().FirstChild(); generic_child; - generic_child = generic_child.NextSibling()) { - auto child = To<NGBlockNode>(generic_child); + NGFlexChildIterator iterator(Node()); + for (NGBlockNode child = iterator.NextChild(); child; + child = iterator.NextChild()) { if (child.IsOutOfFlowPositioned()) { HandleOutOfFlowPositioned(child); continue; } const ComputedStyle& child_style = child.Style(); - NGConstraintSpace child_space = - BuildConstraintSpaceForDeterminingFlexBasis(child); + NGConstraintSpace flex_basis_space = BuildSpaceForFlexBasis(child); + + NGPhysicalBoxStrut physical_child_margins = + ComputePhysicalMargins(flex_basis_space, child_style); NGBoxStrut border_padding_in_child_writing_mode = - ComputeBorders(child_space, child) + - ComputePadding(child_space, child_style); - NGBoxStrut border_scrollbar_padding_in_child_writing_mode = - border_padding_in_child_writing_mode + - ComputeScrollbars(child_space, child); + ComputeBorders(flex_basis_space, child_style) + + ComputePadding(flex_basis_space, child_style); NGPhysicalBoxStrut physical_border_padding( border_padding_in_child_writing_mode.ConvertToPhysical( child_style.GetWritingMode(), child_style.Direction())); - NGPhysicalBoxStrut physical_border_scrollbar_padding( - border_scrollbar_padding_in_child_writing_mode.ConvertToPhysical( - child_style.GetWritingMode(), child_style.Direction())); LayoutUnit main_axis_border_padding = is_horizontal_flow_ ? physical_border_padding.HorizontalSum() : physical_border_padding.VerticalSum(); - LayoutUnit main_axis_border_scrollbar_padding = - is_horizontal_flow_ ? physical_border_scrollbar_padding.HorizontalSum() - : physical_border_scrollbar_padding.VerticalSum(); - - // We want the child's min/max size in its writing mode, not ours. We'll - // only ever use it if the child's inline axis is our main axis. - MinMaxSizeInput input( - /* percentage_resolution_block_size */ content_box_size_.block_size); - MinMaxSize intrinsic_sizes_border_box = child.ComputeMinMaxSize( - child_style.GetWritingMode(), input, &child_space); - // TODO(dgrogan): Don't layout every time, just when you need to. - // Use ChildHasIntrinsicMainAxisSize as a guide. - scoped_refptr<const NGLayoutResult> layout_result = - child.Layout(child_space, nullptr /*break token*/); - NGFragment fragment_in_child_writing_mode( - child_style.GetWritingMode(), layout_result->PhysicalFragment()); - - LayoutUnit flex_base_border_box; - const Length& specified_length_in_main_axis = - is_horizontal_flow_ ? child_style.Width() : child_style.Height(); - const Length& flex_basis = child_style.FlexBasis(); - // TODO(dgrogan): Generalize IsAuto: See the <'width'> section of - // https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis - // and https://drafts.csswg.org/css-flexbox/#flex-basis-property, which says - // that if a flex-basis value would resolve to auto (but not literally auto) - // we should interpret it as flex-basis:content. - if (flex_basis.IsAuto() && specified_length_in_main_axis.IsAuto()) { - if (MainAxisIsInlineAxis(child)) - flex_base_border_box = intrinsic_sizes_border_box.max_size; - else - flex_base_border_box = fragment_in_child_writing_mode.BlockSize(); - } else { - // TODO(dgrogan): Check for definiteness. - // This block covers case A in - // https://drafts.csswg.org/css-flexbox/#algo-main-item. - const Length& length_to_resolve = - flex_basis.IsAuto() ? specified_length_in_main_axis : flex_basis; - DCHECK(!length_to_resolve.IsAuto()); - - if (MainAxisIsInlineAxis(child)) { - flex_base_border_box = ResolveMainInlineLength( - child_space, child_style, border_padding_in_child_writing_mode, - intrinsic_sizes_border_box, length_to_resolve); - } else { - // Flex container's main axis is in child's block direction. Child's - // flex basis is in child's block direction. - flex_base_border_box = ResolveMainBlockLength( - child_space, child_style, border_padding_in_child_writing_mode, - length_to_resolve, fragment_in_child_writing_mode.BlockSize(), - LengthResolvePhase::kLayout); + LayoutUnit cross_axis_border_padding = + is_horizontal_flow_ ? physical_border_padding.VerticalSum() + : physical_border_padding.HorizontalSum(); + + base::Optional<MinMaxSizes> min_max_size; + auto MinMaxSizesFunc = [&]() -> MinMaxSizes { + if (!min_max_size) { + if (child.Style().OverflowBlockDirection() == EOverflow::kAuto) { + // Ensure this child has been laid out so its auto scrollbars are + // included in its intrinsic sizes. + child.Layout(flex_basis_space); + } + // We want the child's min/max size in its writing mode, not ours. + // We'll only ever use it if the child's inline axis is our main axis. + min_max_size = child.ComputeMinMaxSizes( + child_style.GetWritingMode(), + MinMaxSizesInput(content_box_size_.block_size), &flex_basis_space); } - } + return *min_max_size; + }; - // Spec calls this "flex base size" - // https://www.w3.org/TR/css-flexbox-1/#algo-main-item - // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths, - // but no padding or border. - LayoutUnit flex_base_content_size = - flex_base_border_box - main_axis_border_padding; - - NGPhysicalBoxStrut physical_child_margins = - ComputePhysicalMargins(child_space, child_style); - // Set margin because FlexibleBoxAlgorithm reads it from legacy. - child.GetLayoutBox()->SetMargin(physical_child_margins); - - LayoutUnit main_axis_margin = is_horizontal_flow_ - ? physical_child_margins.HorizontalSum() - : physical_child_margins.VerticalSum(); - - MinMaxSize min_max_sizes_in_main_axis_direction{LayoutUnit(), - LayoutUnit::Max()}; - MinMaxSize min_max_sizes_in_cross_axis_direction{LayoutUnit(), + MinMaxSizes min_max_sizes_in_main_axis_direction{main_axis_border_padding, LayoutUnit::Max()}; + MinMaxSizes min_max_sizes_in_cross_axis_direction{LayoutUnit(), + LayoutUnit::Max()}; const Length& max_property_in_main_axis = is_horizontal_flow_ ? child.Style().MaxWidth() : child.Style().MaxHeight(); @@ -341,112 +458,282 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() { : child.Style().MinWidth(); if (MainAxisIsInlineAxis(child)) { min_max_sizes_in_main_axis_direction.max_size = ResolveMaxInlineLength( - child_space, child_style, border_padding_in_child_writing_mode, - intrinsic_sizes_border_box, max_property_in_main_axis, + flex_basis_space, child_style, border_padding_in_child_writing_mode, + MinMaxSizesFunc, max_property_in_main_axis, LengthResolvePhase::kLayout); - min_max_sizes_in_cross_axis_direction.max_size = - ResolveMaxBlockLength(child_space, child_style, - border_scrollbar_padding_in_child_writing_mode, - max_property_in_cross_axis, - fragment_in_child_writing_mode.BlockSize(), - LengthResolvePhase::kLayout); - min_max_sizes_in_cross_axis_direction.min_size = - ResolveMinBlockLength(child_space, child_style, - border_scrollbar_padding_in_child_writing_mode, - min_property_in_cross_axis, - fragment_in_child_writing_mode.BlockSize(), - LengthResolvePhase::kLayout); + min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxBlockLength( + flex_basis_space, child_style, border_padding_in_child_writing_mode, + max_property_in_cross_axis, LengthResolvePhase::kLayout); + min_max_sizes_in_cross_axis_direction.min_size = ResolveMinBlockLength( + flex_basis_space, child_style, border_padding_in_child_writing_mode, + min_property_in_cross_axis, LengthResolvePhase::kLayout); } else { min_max_sizes_in_main_axis_direction.max_size = ResolveMaxBlockLength( - child_space, child_style, border_padding_in_child_writing_mode, - max_property_in_main_axis, fragment_in_child_writing_mode.BlockSize(), - LengthResolvePhase::kLayout); + flex_basis_space, child_style, border_padding_in_child_writing_mode, + max_property_in_main_axis, LengthResolvePhase::kLayout); min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxInlineLength( - child_space, child_style, - border_scrollbar_padding_in_child_writing_mode, - intrinsic_sizes_border_box, max_property_in_cross_axis, + flex_basis_space, child_style, border_padding_in_child_writing_mode, + MinMaxSizesFunc, max_property_in_cross_axis, LengthResolvePhase::kLayout); min_max_sizes_in_cross_axis_direction.min_size = ResolveMinInlineLength( - child_space, child_style, - border_scrollbar_padding_in_child_writing_mode, - intrinsic_sizes_border_box, min_property_in_cross_axis, + flex_basis_space, child_style, border_padding_in_child_writing_mode, + MinMaxSizesFunc, min_property_in_cross_axis, LengthResolvePhase::kLayout); } + base::Optional<LayoutUnit> intrinsic_block_size; + auto IntrinsicBlockSizeFunc = [&]() -> LayoutUnit { + if (!intrinsic_block_size) { + NGConstraintSpace child_space = BuildSpaceForIntrinsicBlockSize( + child, physical_child_margins, + min_max_sizes_in_cross_axis_direction); + scoped_refptr<const NGLayoutResult> layout_result = + child.Layout(child_space, /* break_token */ nullptr); + intrinsic_block_size = layout_result->IntrinsicBlockSize(); + } + return *intrinsic_block_size; + }; + + // The logic that calculates flex_base_border_box assumes that the used + // value of the flex-basis property is either definite or 'content'. + LayoutUnit flex_base_border_box; + const Length& specified_length_in_main_axis = + is_horizontal_flow_ ? child_style.Width() : child_style.Height(); + const Length& flex_basis = child_style.FlexBasis(); + Length length_to_resolve = Length::Auto(); + if (flex_basis.IsAuto()) { + if (!is_column_ || IsItemMainSizeDefinite(child)) + length_to_resolve = specified_length_in_main_axis; + } else if (IsItemFlexBasisDefinite(child)) { + length_to_resolve = flex_basis; + } + + if (length_to_resolve.IsAuto()) { + // This block means that the used flex-basis is 'content'. In here we + // implement parts B,C,D,E of 9.2.3 + // https://drafts.csswg.org/css-flexbox/#algo-main-item + const Length& cross_axis_length = + is_horizontal_flow_ ? child.Style().Height() : child.Style().Width(); + if (child.HasAspectRatio() && + (IsItemCrossAxisLengthDefinite(child, cross_axis_length))) { + // This is Part B of 9.2.3 + // https://drafts.csswg.org/css-flexbox/#algo-main-item It requires that + // the item has a definite cross size. + // + // But for determining the flex-basis of aspect ratio items, both legacy + // and FF both ignore part of the flex spec that has a more lenient + // definition of definite. + // https://drafts.csswg.org/css-flexbox/#definite says "If a single-line + // flex container has a definite cross size, the outer cross size of any + // stretched flex items is the flex container's inner cross size + // (clamped to the flex item's min and max cross size) and is considered + // definite". But when this happens, neither legacy nor firefox use the + // container's cross size to calculate the item's main size, they just + // fall to block E. E.g. Legacy and FF show a 16x100 green square + // instead of a 100x100 green square for + // https://jsfiddle.net/dgrogan/djh5wu0x/1/. I think it should be + // 100x100. + LayoutUnit cross_size; + if (MainAxisIsInlineAxis(child)) { + cross_size = ResolveMainBlockLength( + flex_basis_space, child_style, + border_padding_in_child_writing_mode, cross_axis_length, + kIndefiniteSize, LengthResolvePhase::kLayout); + } else { + cross_size = + ResolveMainInlineLength(flex_basis_space, child_style, + border_padding_in_child_writing_mode, + MinMaxSizesFunc, cross_axis_length); + } + cross_size = min_max_sizes_in_cross_axis_direction.ClampSizeToMinAndMax( + cross_size); + flex_base_border_box = + LayoutUnit(cross_size * GetMainOverCrossAspectRatio(child)); + } else if (MainAxisIsInlineAxis(child)) { + // We're now in parts C, D, and E for what are usually (horizontal-tb + // containers AND children) row flex containers. I _think_ the C and D + // cases are correctly handled by this code, which was originally + // written for case E. + if (child.HasAspectRatio()) { + // Legacy uses child.PreferredLogicalWidths() for this case, which + // is not exactly correct. + // TODO(dgrogan): Replace with a variant of ComputeReplacedSize that + // ignores min-width, width, max-width. + flex_base_border_box = + child.GetLayoutBox()->PreferredLogicalWidths().max_size; + } else { + flex_base_border_box = MinMaxSizesFunc().max_size; + } + } else { + // Parts C, D, and E for what are usually column flex containers. + // + // This is the post-layout height for aspect-ratio items, which matches + // legacy but isn't always correct. + // TODO(dgrogan): Replace with a variant of ComputeReplacedSize that + // ignores min-height, height, max-height. + flex_base_border_box = IntrinsicBlockSizeFunc(); + } + } else { + // Part A of 9.2.3 https://drafts.csswg.org/css-flexbox/#algo-main-item + if (MainAxisIsInlineAxis(child)) { + flex_base_border_box = ResolveMainInlineLength( + flex_basis_space, child_style, border_padding_in_child_writing_mode, + MinMaxSizesFunc, length_to_resolve); + } else { + // Flex container's main axis is in child's block direction. Child's + // flex basis is in child's block direction. + flex_base_border_box = ResolveMainBlockLength( + flex_basis_space, child_style, border_padding_in_child_writing_mode, + length_to_resolve, IntrinsicBlockSizeFunc, + LengthResolvePhase::kLayout); + } + } + + // Spec calls this "flex base size" + // https://www.w3.org/TR/css-flexbox-1/#algo-main-item + // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths, + // but no padding or border. + // The ClampNegativeToZero is needed for the last canvas element in + // flexbox-flex-basis-content-001a.html. It's possibly only needed because + // we don't properly account for borders+padding when multiplying by the + // aspect ratio. + LayoutUnit flex_base_content_size = + (flex_base_border_box - main_axis_border_padding).ClampNegativeToZero(); + const Length& min = is_horizontal_flow_ ? child.Style().MinWidth() : child.Style().MinHeight(); + // TODO(dgrogan): min.IsIntrinsic should enter this block when it's in the + // item's block direction. if (min.IsAuto()) { if (algorithm_->ShouldApplyMinSizeAutoForChild(*child.GetLayoutBox())) { - // TODO(dgrogan): Do the aspect ratio parts of - // https://www.w3.org/TR/css-flexbox-1/#min-size-auto - - LayoutUnit content_size_suggestion = - MainAxisIsInlineAxis(child) ? intrinsic_sizes_border_box.min_size - : layout_result->IntrinsicBlockSize(); - content_size_suggestion = - std::min(content_size_suggestion, - min_max_sizes_in_main_axis_direction.max_size); - - if (child.MayHaveAspectRatio()) { - // TODO(dgrogan): We're including borders/padding in both - // content_size_suggestion and min_max_sizes_in_cross_axis_direction. - // Maybe we need to multiply the content size by the aspect ratio and - // then apply the border/padding from the other axis inside the - // Adjust* function. Test legacy/firefox. Start with - // https://jsfiddle.net/dgrogan/9uyg3aro/ - content_size_suggestion = - AdjustChildSizeForAspectRatioCrossAxisMinAndMax( - child, content_size_suggestion, - min_max_sizes_in_cross_axis_direction.min_size, - min_max_sizes_in_cross_axis_direction.max_size); - } + // TODO(dgrogan): This should probably apply to column flexboxes also, + // but that's not what legacy does. + if (child.IsTable() && !is_column_) { + MinMaxSizes table_preferred_widths = + ComputeMinAndMaxContentContribution( + Style(), child, + MinMaxSizesInput(child_percentage_size_.block_size)); + min_max_sizes_in_main_axis_direction.min_size = + table_preferred_widths.min_size; + } else { + LayoutUnit content_size_suggestion; + if (MainAxisIsInlineAxis(child)) { + content_size_suggestion = MinMaxSizesFunc().min_size; + } else { + LayoutUnit intrinsic_block_size; + if (child.HasAspectRatio()) { + base::Optional<LayoutUnit> computed_inline_size; + base::Optional<LayoutUnit> computed_block_size; + child.IntrinsicSize(&computed_inline_size, &computed_block_size); + + // The 150 is for elements that have an aspect ratio but no size, + // which SVG can have (maybe others?). + intrinsic_block_size = + computed_block_size.value_or(LayoutUnit(150)); + } else { + intrinsic_block_size = IntrinsicBlockSizeFunc(); + } + content_size_suggestion = intrinsic_block_size; + } - LayoutUnit specified_size_suggestion(LayoutUnit::Max()); - // If the item’s computed main size property is definite, then the - // specified size suggestion is that size. - if (MainAxisIsInlineAxis(child)) { - if (!specified_length_in_main_axis.IsAuto()) { - // TODO(dgrogan): Optimization opportunity: we may have already - // resolved specified_length_in_main_axis in the flex basis - // calculation. Reuse that if possible. - specified_size_suggestion = ResolveMainInlineLength( - child_space, child_style, border_padding_in_child_writing_mode, - intrinsic_sizes_border_box, specified_length_in_main_axis); + + if (child.HasAspectRatio()) { + // TODO(dgrogan): We're including borders/padding in both + // content_size_suggestion and + // min_max_sizes_in_cross_axis_direction. Maybe we need to multiply + // the content size by the aspect ratio and then apply the + // border/padding from the other axis inside the Adjust* function. + // Test legacy/firefox. Start with + // https://jsfiddle.net/dgrogan/9uyg3aro/ + content_size_suggestion = + AdjustChildSizeForAspectRatioCrossAxisMinAndMax( + child, content_size_suggestion, + min_max_sizes_in_cross_axis_direction.min_size, + min_max_sizes_in_cross_axis_direction.max_size); + } + + LayoutUnit specified_size_suggestion = LayoutUnit::Max(); + // If the item’s computed main size property is definite, then the + // specified size suggestion is that size. + if (MainAxisIsInlineAxis(child)) { + if (!specified_length_in_main_axis.IsAuto()) { + // TODO(dgrogan): Optimization opportunity: we may have already + // resolved specified_length_in_main_axis in the flex basis + // calculation. Reuse that if possible. + specified_size_suggestion = ResolveMainInlineLength( + flex_basis_space, child_style, + border_padding_in_child_writing_mode, MinMaxSizesFunc, + specified_length_in_main_axis); + } + } else if (!BlockLengthUnresolvable(flex_basis_space, + specified_length_in_main_axis, + LengthResolvePhase::kLayout)) { + specified_size_suggestion = ResolveMainBlockLength( + flex_basis_space, child_style, + border_padding_in_child_writing_mode, + specified_length_in_main_axis, IntrinsicBlockSizeFunc, + LengthResolvePhase::kLayout); + DCHECK_NE(specified_size_suggestion, kIndefiniteSize); } - } else if (!BlockLengthUnresolvable(child_space, - specified_length_in_main_axis, - LengthResolvePhase::kLayout)) { - specified_size_suggestion = ResolveMainBlockLength( - child_space, child_style, border_padding_in_child_writing_mode, - specified_length_in_main_axis, - layout_result->IntrinsicBlockSize(), LengthResolvePhase::kLayout); - DCHECK_NE(specified_size_suggestion, kIndefiniteSize); - } - // Spec says to clamp specified_size_suggestion by max size but because - // content_size_suggestion already is, and we take the min of those - // two, we don't need to clamp specified_size_suggestion. - // https://github.com/w3c/csswg-drafts/issues/3669 - min_max_sizes_in_main_axis_direction.min_size = - std::min(specified_size_suggestion, content_size_suggestion); + LayoutUnit transferred_size_suggestion = LayoutUnit::Max(); + if (specified_size_suggestion == LayoutUnit::Max() && + child.HasAspectRatio()) { + const Length& cross_axis_length = is_horizontal_flow_ + ? child_style.Height() + : child_style.Width(); + if (IsItemCrossAxisLengthDefinite(child, cross_axis_length)) { + LayoutUnit cross_axis_size; + if (MainAxisIsInlineAxis(child)) { + cross_axis_size = ResolveMainBlockLength( + flex_basis_space, child_style, + border_padding_in_child_writing_mode, cross_axis_length, + kIndefiniteSize, LengthResolvePhase::kLayout); + DCHECK_NE(cross_axis_size, kIndefiniteSize); + } else { + cross_axis_size = ResolveMainInlineLength( + flex_basis_space, child_style, + border_padding_in_child_writing_mode, MinMaxSizesFunc, + cross_axis_length); + } + double ratio = GetMainOverCrossAspectRatio(child); + transferred_size_suggestion = LayoutUnit( + ratio * + min_max_sizes_in_cross_axis_direction.ClampSizeToMinAndMax( + cross_axis_size)); + } + } + + DCHECK(specified_size_suggestion == LayoutUnit::Max() || + transferred_size_suggestion == LayoutUnit::Max()); + + min_max_sizes_in_main_axis_direction.min_size = + std::min({specified_size_suggestion, content_size_suggestion, + transferred_size_suggestion, + min_max_sizes_in_main_axis_direction.max_size}); + } } } else if (MainAxisIsInlineAxis(child)) { min_max_sizes_in_main_axis_direction.min_size = ResolveMinInlineLength( - child_space, child_style, border_padding_in_child_writing_mode, - intrinsic_sizes_border_box, min, LengthResolvePhase::kLayout); + flex_basis_space, child_style, border_padding_in_child_writing_mode, + MinMaxSizesFunc, min, LengthResolvePhase::kLayout); } else { min_max_sizes_in_main_axis_direction.min_size = ResolveMinBlockLength( - child_space, child_style, border_padding_in_child_writing_mode, min, - fragment_in_child_writing_mode.BlockSize(), - LengthResolvePhase::kLayout); + flex_basis_space, child_style, border_padding_in_child_writing_mode, + min, LengthResolvePhase::kLayout); } - // TODO(dgrogan): Should this include scrollbar? - min_max_sizes_in_main_axis_direction -= main_axis_border_scrollbar_padding; + min_max_sizes_in_main_axis_direction -= main_axis_border_padding; + DCHECK_GE(min_max_sizes_in_main_axis_direction.min_size, 0); + DCHECK_GE(min_max_sizes_in_main_axis_direction.max_size, 0); + + // TODO(dgrogan): Should min_max_sizes_in_cross_axis_direction include + // cross_axis_border_padding? algorithm_ - ->emplace_back(child.GetLayoutBox(), flex_base_content_size, + ->emplace_back(nullptr, child.Style(), flex_base_content_size, min_max_sizes_in_main_axis_direction, min_max_sizes_in_cross_axis_direction, - main_axis_border_padding, main_axis_margin) + main_axis_border_padding, cross_axis_border_padding, + physical_child_margins) .ng_input_node = child; } } @@ -457,33 +744,16 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax( LayoutUnit content_suggestion, LayoutUnit cross_min, LayoutUnit cross_max) { - DCHECK(child.MayHaveAspectRatio()); + DCHECK(child.HasAspectRatio()); + + double ratio = GetMainOverCrossAspectRatio(child); // Clamp content_suggestion by any definite min and max cross size properties // converted through the aspect ratio. - - base::Optional<LayoutUnit> computed_inline_size; - base::Optional<LayoutUnit> computed_block_size; - LogicalSize aspect_ratio; - - child.IntrinsicSize(&computed_inline_size, &computed_block_size, - &aspect_ratio); - - // TODO(dgrogan): Should we quit here if only the denominator is 0? - if (aspect_ratio.inline_size == 0 || aspect_ratio.block_size == 0) - return content_suggestion; - - double ratio = aspect_ratio.inline_size / aspect_ratio.block_size; - - // Multiplying by ratio will take something in the item's block axis and - // convert it to the inline axis. We want to convert from cross size to main - // size. If block axis and cross axis are the same, then we already have what - // we need. Otherwise we need to use the reciprocal. - if (!MainAxisIsInlineAxis(child)) - ratio = 1 / ratio; - const Length& cross_max_length = is_horizontal_flow_ ? child.Style().MaxHeight() : child.Style().MaxWidth(); + // TODO(dgrogan): No tests fail if we unconditionally apply max_main_length. + // Either add a test that needs it or remove it. if (IsItemCrossAxisLengthDefinite(child, cross_max_length)) { LayoutUnit max_main_length = LayoutUnit(cross_max * ratio); content_suggestion = std::min(max_main_length, content_suggestion); @@ -492,6 +762,8 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax( const Length& cross_min_length = is_horizontal_flow_ ? child.Style().MinHeight() : child.Style().MinWidth(); + // TODO(dgrogan): Same as above with min_main_length here -- it may be + // unneeded or untested. if (IsItemCrossAxisLengthDefinite(child, cross_min_length)) { LayoutUnit min_main_length = LayoutUnit(cross_min * ratio); content_suggestion = std::max(min_main_length, content_suggestion); @@ -500,15 +772,7 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax( } scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { - border_box_size_ = container_builder_.InitialBorderBoxSize(); - content_box_size_ = - ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_); - child_percentage_size_ = CalculateChildPercentageSize( - ConstraintSpace(), Node(), content_box_size_); - - const LayoutUnit line_break_length = MainAxisContentExtent(LayoutUnit::Max()); - algorithm_.emplace(&Style(), line_break_length); - + PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope; ConstructAndAppendFlexItems(); LayoutUnit main_axis_start_offset; @@ -542,22 +806,29 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { for (wtf_size_t i = 0; i < line->line_items.size(); ++i) { FlexItem& flex_item = line->line_items[i]; - WritingMode child_writing_mode = - flex_item.ng_input_node.Style().GetWritingMode(); + const ComputedStyle& child_style = flex_item.ng_input_node.Style(); NGConstraintSpaceBuilder space_builder(ConstraintSpace(), - child_writing_mode, + child_style.GetWritingMode(), /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item.ng_input_node, &space_builder); - space_builder.SetTextDirection( - flex_item.ng_input_node.Style().Direction()); + space_builder.SetTextDirection(child_style.Direction()); + space_builder.SetIsPaintedAtomically(true); LogicalSize available_size; + NGBoxStrut margins = flex_item.physical_margins.ConvertToLogical( + ConstraintSpace().GetWritingMode(), Style().Direction()); if (is_column_) { available_size.inline_size = content_box_size_.inline_size; available_size.block_size = flex_item.flexed_content_size + flex_item.main_axis_border_padding; space_builder.SetIsFixedBlockSize(true); + if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) { + space_builder.SetIsFixedInlineSize(true); + available_size.inline_size = CalculateFixedCrossSize( + available_size.inline_size, flex_item.min_max_cross_sizes.value(), + margins.InlineSum()); + } // https://drafts.csswg.org/css-flexbox/#definite-sizes // If the flex container has a definite main size, a flex item's // post-flexing main size is treated as definite, even though it can @@ -571,16 +842,22 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { flex_item.flexed_content_size + flex_item.main_axis_border_padding; available_size.block_size = content_box_size_.block_size; space_builder.SetIsFixedInlineSize(true); - } - if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) { - if (is_column_) - space_builder.SetIsFixedInlineSize(true); - else + if (WillChildCrossSizeBeContainerCrossSize(flex_item.ng_input_node)) { space_builder.SetIsFixedBlockSize(true); + available_size.block_size = CalculateFixedCrossSize( + available_size.block_size, flex_item.min_max_cross_sizes.value(), + margins.BlockSum()); + } } + space_builder.SetNeedsBaseline( + ConstraintSpace().NeedsBaseline() || + FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) == + ItemPosition::kBaseline); + space_builder.SetAvailableSize(available_size); space_builder.SetPercentageResolutionSize(child_percentage_size_); + space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_); // https://drafts.csswg.org/css-flexbox/#algo-cross-item // Determine the hypothetical cross size of each item by performing layout @@ -592,6 +869,10 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { NGConstraintSpace child_space = space_builder.ToConstraintSpace(); flex_item.layout_result = flex_item.ng_input_node.Layout(child_space, nullptr /*break token*/); + + // TODO(layout-dev): Handle abortions caused by block fragmentation. + DCHECK_EQ(flex_item.layout_result->Status(), NGLayoutResult::kSuccess); + flex_item.cross_axis_size = is_horizontal_flow_ ? flex_item.layout_result->PhysicalFragment().Size().height @@ -627,12 +908,13 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() { } void NGFlexLayoutAlgorithm::ApplyStretchAlignmentToChild(FlexItem& flex_item) { - WritingMode child_writing_mode = - flex_item.ng_input_node.Style().GetWritingMode(); - NGConstraintSpaceBuilder space_builder(ConstraintSpace(), child_writing_mode, + const ComputedStyle& child_style = flex_item.ng_input_node.Style(); + NGConstraintSpaceBuilder space_builder(ConstraintSpace(), + child_style.GetWritingMode(), /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item.ng_input_node, &space_builder); + space_builder.SetIsPaintedAtomically(true); LogicalSize available_size( flex_item.flexed_content_size + flex_item.main_axis_border_padding, @@ -644,9 +926,16 @@ void NGFlexLayoutAlgorithm::ApplyStretchAlignmentToChild(FlexItem& flex_item) { space_builder.SetIsFixedBlockSizeIndefinite(true); } } - space_builder.SetTextDirection(flex_item.ng_input_node.Style().Direction()); + + space_builder.SetNeedsBaseline( + ConstraintSpace().NeedsBaseline() || + FlexLayoutAlgorithm::AlignmentForChild(Style(), child_style) == + ItemPosition::kBaseline); + + space_builder.SetTextDirection(child_style.Direction()); space_builder.SetAvailableSize(available_size); space_builder.SetPercentageResolutionSize(child_percentage_size_); + space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_); space_builder.SetIsFixedInlineSize(true); space_builder.SetIsFixedBlockSize(true); NGConstraintSpace child_space = space_builder.ToConstraintSpace(); @@ -687,6 +976,9 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() { border_scrollbar_padding_.block_start); } + base::Optional<LayoutUnit> fallback_baseline; + + LayoutUnit overflow_block_size; for (FlexLine& line_context : line_contexts) { for (wtf_size_t child_number = 0; child_number < line_context.line_items.size(); ++child_number) { @@ -695,6 +987,9 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() { if (DoesItemStretch(flex_item.ng_input_node)) ApplyStretchAlignmentToChild(flex_item); + const auto& physical_fragment = To<NGPhysicalBoxFragment>( + flex_item.layout_result->PhysicalFragment()); + // flex_item.desired_location stores the main axis offset in X and the // cross axis offset in Y. But AddChild wants offset from parent // rectangle, so we have to transpose for columns. AddChild takes care of @@ -702,16 +997,70 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() { LayoutPoint location = is_column_ ? flex_item.desired_location.TransposedPoint() : flex_item.desired_location; - container_builder_.AddChild(flex_item.layout_result->PhysicalFragment(), + + NGBoxFragment fragment(ConstraintSpace().GetWritingMode(), + ConstraintSpace().Direction(), physical_fragment); + // Only propagate baselines from children on the first flex-line. + if (&line_context == line_contexts.begin()) { + PropagateBaselineFromChild(flex_item, fragment, location.Y(), + &fallback_baseline); + } + + container_builder_.AddChild(physical_fragment, {location.X(), location.Y()}); + + flex_item.ng_input_node.StoreMargins(flex_item.physical_margins); + + LayoutUnit margin_block_end = + flex_item.physical_margins + .ConvertToLogical(ConstraintSpace().GetWritingMode(), + ConstraintSpace().Direction()) + .block_end; + overflow_block_size = + std::max(overflow_block_size, + location.Y() + fragment.BlockSize() + margin_block_end); } } + + container_builder_.SetOverflowBlockSize(overflow_block_size + + border_scrollbar_padding_.block_end); + + // Set the baseline to the fallback, if we didn't find any children with + // baseline alignment. + if (!container_builder_.Baseline() && fallback_baseline) + container_builder_.SetBaseline(*fallback_baseline); +} + +void NGFlexLayoutAlgorithm::PropagateBaselineFromChild( + const FlexItem& flex_item, + const NGBoxFragment& fragment, + LayoutUnit block_offset, + base::Optional<LayoutUnit>* fallback_baseline) { + // Check if we've already found an appropriate baseline. + if (container_builder_.Baseline()) + return; + + LayoutUnit baseline_offset = + block_offset + fragment.Baseline().value_or(fragment.BlockSize()); + + // 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 && + !flex_item.HasAutoMarginsInCrossAxis()) { + container_builder_.SetBaseline(baseline_offset); + return; + } + + // Set the fallback baseline if it doesn't have a value yet. + *fallback_baseline = fallback_baseline->value_or(baseline_offset); } -base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { - base::Optional<MinMaxSize> sizes = CalculateMinMaxSizesIgnoringChildren( - Node(), border_scrollbar_padding_, input.size_type); +base::Optional<MinMaxSizes> NGFlexLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { + base::Optional<MinMaxSizes> sizes = + CalculateMinMaxSizesIgnoringChildren(Node(), border_scrollbar_padding_); if (sizes) return sizes; @@ -721,21 +1070,15 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( ConstraintSpace(), Node(), border_padding_, input.percentage_resolution_block_size); - // Use default MinMaxSizeInput: - // - Children of flexbox ignore any specified float properties, so children - // never have to take floated siblings into account, and external floats - // don't make it through the new formatting context that flexbox - // establishes. - // - We want the child's border box MinMaxSize, which is the default. - MinMaxSizeInput child_input(child_percentage_resolution_block_size); - - for (NGLayoutInputNode generic_child = Node().FirstChild(); generic_child; - generic_child = generic_child.NextSibling()) { - auto child = To<NGBlockNode>(generic_child); + MinMaxSizesInput child_input(child_percentage_resolution_block_size); + + NGFlexChildIterator iterator(Node()); + for (NGBlockNode child = iterator.NextChild(); child; + child = iterator.NextChild()) { if (child.IsOutOfFlowPositioned()) continue; - MinMaxSize child_min_max_sizes = + MinMaxSizes child_min_max_sizes = ComputeMinAndMaxContentContribution(Style(), child, child_input); NGBoxStrut child_margins = ComputeMinMaxMargins(Style(), child); child_min_max_sizes += child_margins.InlineSum(); @@ -744,7 +1087,7 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( sizes->max_size = std::max(sizes->max_size, child_min_max_sizes.max_size); } else { sizes->max_size += child_min_max_sizes.max_size; - if (IsMultiline()) { + if (algorithm_->IsMultiline()) { sizes->min_size = std::max(sizes->min_size, child_min_max_sizes.min_size); } else { @@ -757,15 +1100,8 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize( // Due to negative margins, it is possible that we calculated a negative // intrinsic width. Make sure that we never return a negative width. sizes->Encompass(LayoutUnit()); - - if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) - *sizes += border_scrollbar_padding_.InlineSum(); - + *sizes += border_scrollbar_padding_.InlineSum(); return sizes; } -bool NGFlexLayoutAlgorithm::IsMultiline() const { - return Style().FlexWrap() != EFlexWrap::kNowrap; -} - } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h index 16f6aaa02e1..066277e3075 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h @@ -14,6 +14,7 @@ namespace blink { class NGBlockNode; class NGBlockBreakToken; +class NGBoxFragment; class CORE_EXPORT NGFlexLayoutAlgorithm : public NGLayoutAlgorithm<NGBlockNode, @@ -24,15 +25,17 @@ class CORE_EXPORT NGFlexLayoutAlgorithm scoped_refptr<const NGLayoutResult> Layout() override; - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; private: bool DoesItemCrossSizeComputeToAuto(const NGBlockNode& child) const; + bool IsItemFlexBasisDefinite(const NGBlockNode& child) const; bool IsItemMainSizeDefinite(const NGBlockNode& child) const; bool IsItemCrossAxisLengthDefinite(const NGBlockNode& child, const Length& length) const; bool ShouldItemShrinkToFit(const NGBlockNode& child) const; + double GetMainOverCrossAspectRatio(const NGBlockNode& child) const; bool DoesItemStretch(const NGBlockNode& child) const; // This implements the first of the additional scenarios where a flex item // has definite sizes when it would not if it weren't a flex item. @@ -47,8 +50,11 @@ class CORE_EXPORT NGFlexLayoutAlgorithm bool IsColumnContainerMainSizeDefinite() const; bool IsContainerCrossSizeDefinite() const; - NGConstraintSpace BuildConstraintSpaceForDeterminingFlexBasis( - const NGBlockNode& flex_item) const; + NGConstraintSpace BuildSpaceForFlexBasis(const NGBlockNode& flex_item) const; + NGConstraintSpace BuildSpaceForIntrinsicBlockSize( + const NGBlockNode& flex_item, + const NGPhysicalBoxStrut& physical_margins, + const MinMaxSizes& cross_axis) const; void ConstructAndAppendFlexItems(); void ApplyStretchAlignmentToChild(FlexItem& flex_item); void GiveLinesAndItemsFinalPositionAndSize(); @@ -57,20 +63,22 @@ class CORE_EXPORT NGFlexLayoutAlgorithm // This is same method as FlexItem but we need that logic before FlexItem is // constructed. bool MainAxisIsInlineAxis(const NGBlockNode& child) const; - LayoutUnit MainAxisContentExtent(LayoutUnit sum_hypothetical_main_size); + LayoutUnit MainAxisContentExtent(LayoutUnit sum_hypothetical_main_size) const; void HandleOutOfFlowPositioned(NGBlockNode child); - // TODO(dgrogan): This is redundant with FlexLayoutAlgorithm.IsMultiline() but - // it's needed before the algorithm is instantiated. Figure out how to - // not reimplement. - bool IsMultiline() const; + + // Propagates the baseline from the given flex-item if needed. + void PropagateBaselineFromChild( + const FlexItem&, + const NGBoxFragment&, + LayoutUnit block_offset, + base::Optional<LayoutUnit>* fallback_baseline); const NGBoxStrut border_padding_; const NGBoxStrut border_scrollbar_padding_; const bool is_column_; const bool is_horizontal_flow_; - // These are populated at the top of Layout(), so aren't available in - // ComputeMinMaxSize() or anything it calls. + const bool is_cross_size_definite_; LogicalSize border_box_size_; LogicalSize content_box_size_; LogicalSize child_percentage_size_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc index dccc520c12e..3404db2e94f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc @@ -6,7 +6,7 @@ #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/layout/layout_box.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" @@ -66,13 +66,14 @@ NGConstraintSpace CreateConstraintSpaceForFloat( /* is_new_fc */ true); SetOrthogonalFallbackInlineSizeIfNeeded(unpositioned_float.parent_style, unpositioned_float.node, &builder); + builder.SetIsPaintedAtomically(true); if (origin_block_offset) { DCHECK(parent_space.HasBlockFragmentation()); DCHECK_EQ(style.GetWritingMode(), parent_space.GetWritingMode()); - SetupFragmentation(parent_space, *origin_block_offset, &builder, - /* is_new_fc */ true); + SetupFragmentation(parent_space, unpositioned_float.node, + *origin_block_offset, &builder, /* is_new_fc */ true); } else { builder.SetFragmentationType(NGFragmentationType::kFragmentNone); } @@ -118,7 +119,7 @@ std::unique_ptr<NGExclusionShapeData> CreateExclusionShapeData( case CSSBoxType::kContent: const NGConstraintSpace space = CreateConstraintSpaceForFloat(unpositioned_float); - NGBoxStrut strut = ComputeBorders(space, unpositioned_float.node); + NGBoxStrut strut = ComputeBorders(space, style); if (style.ShapeOutside()->CssBox() == CSSBoxType::kContent) strut += ComputePadding(space, style); shape_insets = diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h index a1ceb920f02..f0bd55d5bc0 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h @@ -47,6 +47,9 @@ class CORE_EXPORT NGFragmentBuilder { void SetIsHiddenForPaint(bool value) { is_hidden_for_paint_ = value; } + // Specify whether this will be the first fragment generated for the node. + void SetIsFirstForNode(bool is_first) { is_first_for_node_ = is_first; } + const LayoutObject* GetLayoutObject() const { return layout_object_; } protected: @@ -80,6 +83,7 @@ class CORE_EXPORT NGFragmentBuilder { LayoutObject* layout_object_ = nullptr; scoped_refptr<NGBreakToken> break_token_; bool is_hidden_for_paint_ = false; + bool is_first_for_node_ = true; friend class NGPhysicalFragment; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc new file mode 100644 index 00000000000..167e14136f6 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc @@ -0,0 +1,212 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h" + +#include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + +namespace blink { + +NGFragmentChildIterator::NGFragmentChildIterator( + const NGPhysicalBoxFragment& parent, + const NGBlockBreakToken* parent_break_token) + : parent_fragment_(&parent), + parent_break_token_(parent_break_token), + is_fragmentation_context_root_(parent.IsFragmentationContextRoot()) { + DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + current_.link_.fragment = nullptr; + if (parent_break_token) + child_break_tokens_ = parent_break_token->ChildBreakTokens(); + if (parent.HasItems()) { + current_.cursor_.emplace(*parent.Items()); + current_.block_break_token_ = parent_break_token; + UpdateSelfFromCursor(); + } else { + UpdateSelfFromFragment(); + } +} + +NGFragmentChildIterator::NGFragmentChildIterator( + const NGInlineCursor& parent, + const NGBlockBreakToken* parent_break_token, + base::span<const NGBreakToken* const> child_break_tokens) + : parent_break_token_(parent_break_token), + child_break_tokens_(child_break_tokens) { + current_.block_break_token_ = parent_break_token; + current_.link_.fragment = nullptr; + current_.cursor_ = parent.CursorForDescendants(); + UpdateSelfFromCursor(); +} + +NGFragmentChildIterator NGFragmentChildIterator::Descend() const { + if (current_.cursor_) { + const NGFragmentItem* item = current_.cursor_->CurrentItem(); + // Descend using the cursor if the current item doesn't establish a new + // formatting context. + if (!item->IsFormattingContextRoot()) { + return NGFragmentChildIterator( + *current_.cursor_, + current_.BlockBreakToken() ? parent_break_token_ : nullptr, + child_break_tokens_.subspan(child_break_token_idx_)); + } + } + DCHECK(current_.BoxFragment()); + return NGFragmentChildIterator(*current_.BoxFragment(), + current_.BlockBreakToken()); +} + +bool NGFragmentChildIterator::AdvanceChildFragment() { + DCHECK(parent_fragment_); + const auto children = parent_fragment_->Children(); + const NGPhysicalBoxFragment* previous_fragment = + To<NGPhysicalBoxFragment>(current_.link_.fragment); + DCHECK(previous_fragment); + if (child_fragment_idx_ < children.size()) + child_fragment_idx_++; + // There may be line box fragments among the children, and we're not + // interested in them (lines will already have been handled by the inline + // cursor). + SkipToBoxFragment(); + if (child_fragment_idx_ >= children.size()) + return false; + if (child_break_token_idx_ < child_break_tokens_.size()) + child_break_token_idx_++; + UpdateSelfFromFragment(previous_fragment); + return true; +} + +void NGFragmentChildIterator::UpdateSelfFromFragment( + const NGPhysicalBoxFragment* previous_fragment) { + DCHECK(parent_fragment_); + const auto children = parent_fragment_->Children(); + if (child_fragment_idx_ >= children.size()) + return; + current_.link_ = children[child_fragment_idx_]; + DCHECK(current_.link_.fragment); + SkipToBlockBreakToken(); + if (child_break_token_idx_ < child_break_tokens_.size()) { + current_.block_break_token_ = + To<NGBlockBreakToken>(child_break_tokens_[child_break_token_idx_]); + DCHECK(!current_.link_.fragment->GetLayoutObject() || + current_.block_break_token_->InputNode().GetLayoutBox() == + current_.link_.fragment->GetLayoutObject()); + current_.break_token_for_fragmentainer_only_ = false; + } else if (is_fragmentation_context_root_ && previous_fragment) { + if (previous_fragment->IsColumnBox()) { + // The outgoing break token from one fragmentainer is the incoming break + // token to the next one. This is also true when there are column spanners + // between two columns (fragmentainers); the outgoing break token from the + // former column will be ignored by any intervening spanners, and then fed + // into the first column that comes after them, as an incoming break + // token. + current_.block_break_token_ = + To<NGBlockBreakToken>(previous_fragment->BreakToken()); + current_.break_token_for_fragmentainer_only_ = true; + } else { + // This is a column spanner. We'll leave |current_block_break_token_| + // alone here, as it will be used as in incoming break token when we get + // to the next column. + DCHECK( + NGBlockNode(ToLayoutBox(previous_fragment->GetMutableLayoutObject())) + .IsColumnSpanAll()); + + // If the previous fragment is a column spanner, it's not expected to have + // a break token; if a spanner runs out of space, no columns (or spanners) + // would fit after it. + DCHECK(!previous_fragment->BreakToken()); + } + } else { + current_.block_break_token_ = nullptr; + } +} + +bool NGFragmentChildIterator::AdvanceWithCursor() { + DCHECK(current_.cursor_); + const NGFragmentItem* item = current_.cursor_->CurrentItem(); + if (item->HasChildren()) { + // If we're advancing past a non-atomic inline, we also need to advance past + // any break tokens for fragments in there. + for (wtf_size_t remaining = item->DescendantsCount(); remaining; + remaining--) { + if (item->IsFloating()) { + SkipToBlockBreakToken(); + if (child_break_token_idx_ < child_break_tokens_.size()) { + DCHECK_EQ(child_break_tokens_[child_break_token_idx_] + ->InputNode() + .GetLayoutBox(), + item->GetLayoutObject()); + child_break_token_idx_++; + } + } + current_.cursor_->MoveToNext(); + item = current_.cursor_->CurrentItem(); + } + } else { + current_.cursor_->MoveToNext(); + } + UpdateSelfFromCursor(); + if (current_.cursor_->CurrentItem()) + return true; + // If there are more items, proceed and see if we have box fragment + // children. There may be out-of-flow positioned child fragments. + if (!parent_fragment_) + return false; + current_.cursor_.reset(); + SkipToBoxFragment(); + UpdateSelfFromFragment(); + return !IsAtEnd(); +} + +void NGFragmentChildIterator::UpdateSelfFromCursor() { + DCHECK(current_.cursor_); + // For inline items we just use the incoming break token to the containing + // block. + current_.block_break_token_ = parent_break_token_; + const NGFragmentItem* item = current_.cursor_->CurrentItem(); + if (!item) { + current_.link_.fragment = nullptr; + return; + } + current_.link_ = {item->BoxFragment(), item->OffsetInContainerBlock()}; + if (!current_.link_.fragment || !current_.link_.fragment->IsFloating()) { + DCHECK(!current_.link_.fragment || + current_.link_.fragment->GetLayoutObject()->IsInline()); + return; + } + if (!parent_break_token_) + return; + // Floats may fragment, in which case there's a designated break token for + // them. + SkipToBlockBreakToken(); + if (child_break_token_idx_ >= child_break_tokens_.size()) { + current_.block_break_token_ = nullptr; + return; + } + current_.block_break_token_ = + To<NGBlockBreakToken>(child_break_tokens_[child_break_token_idx_]); + DCHECK(!current_.link_.fragment->GetLayoutObject() || + current_.block_break_token_->InputNode().GetLayoutBox() == + current_.link_.fragment->GetLayoutObject()); +} + +void NGFragmentChildIterator::SkipToBoxFragment() { + for (const auto children = parent_fragment_->Children(); + child_fragment_idx_ < children.size(); child_fragment_idx_++) { + if (children[child_fragment_idx_].fragment->IsBox()) + break; + } +} + +void NGFragmentChildIterator::SkipToBlockBreakToken() { + // There may be inline break tokens here. Ignore them. + while (child_break_token_idx_ < child_break_tokens_.size() && + !child_break_tokens_[child_break_token_idx_]->IsBlockType()) + child_break_token_idx_++; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h new file mode 100644 index 00000000000..c4927f0acad --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h @@ -0,0 +1,141 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_CHILD_ITERATOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_CHILD_ITERATOR_H_ + +#include "base/containers/span.h" +#include "base/optional.h" +#include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" +#include "third_party/blink/renderer/core/layout/ng/ng_link.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" + +namespace blink { + +class LayoutObject; +class NGBlockBreakToken; + +// Iterator for children of a box fragment. Supports fragment items and break +// tokens. To advance to the next sibling, call |Advance()|. To descend into +// children of the current child, call |Descend()|. +// +// Using this class requires LayoutNGFragmentItem to be enabled. While fragment +// items are in a flat list representing the contents of an inline formatting +// context, the iterator will to a certain extent restore the object hierarchy, +// so that we can calculate the global offset of children of a relatively +// positioned inline correctly. +class CORE_EXPORT NGFragmentChildIterator { + STACK_ALLOCATED(); + + public: + explicit NGFragmentChildIterator( + const NGPhysicalBoxFragment& parent, + const NGBlockBreakToken* parent_break_token = nullptr); + + // Create a child iterator for the current child. + NGFragmentChildIterator Descend() const; + + // Move to the next sibling. Return false if there's no next sibling. Once + // false is returned, this object is in an unusable state, with the exception + // that calling IsAtEnd() is allowed. + bool Advance() { + if (current_.cursor_) + return AdvanceWithCursor(); + return AdvanceChildFragment(); + } + + bool IsAtEnd() { + if (current_.cursor_) + return !*current_.cursor_; + DCHECK(parent_fragment_); + const auto children = parent_fragment_->Children(); + return child_fragment_idx_ >= children.size(); + } + + class Current { + friend class NGFragmentChildIterator; + + public: + // Return the current NGLink. Note that its offset is relative to the inline + // formatting context root, if the fragment / item participates in one. + const NGLink& Link() const { return link_; } + + const NGPhysicalBoxFragment* BoxFragment() const { + return To<NGPhysicalBoxFragment>(link_.fragment); + } + const NGFragmentItem* FragmentItem() const { + if (!cursor_) + return nullptr; + return cursor_->CurrentItem(); + } + + // Get the incoming break token for the current child, i.e. the context at + // which layout of this child's node was resumed. Note that for text and + // non-atomic inlines this will be the incoming block break token to the + // inline formatting context root. For monolithic content, no break token + // will be returned (since such content isn't considered to participate in a + // fragmentation context). + const NGBlockBreakToken* BlockBreakToken() const { + if (LIKELY(!block_break_token_)) + return nullptr; + if (link_.fragment) { + // Don't pass the break token into monolithic content. + if (link_.fragment->IsMonolithic()) + return nullptr; + // If the break token we've found is from a fragmentainer, it's only to + // be used by a subsequent fragmentainer. Other fragment types (such as + // column spanners) need to ignore it. + if (break_token_for_fragmentainer_only_ && + !link_.fragment->IsColumnBox()) + return nullptr; + } + return block_break_token_; + } + + const LayoutObject* GetLayoutObject() const { + if (const NGFragmentItem* item = FragmentItem()) + return item->GetLayoutObject(); + return BoxFragment()->GetLayoutObject(); + } + + private: + NGLink link_; + base::Optional<NGInlineCursor> cursor_; + const NGBlockBreakToken* block_break_token_ = nullptr; + bool break_token_for_fragmentainer_only_ = false; + }; + + const Current& GetCurrent() const { return current_; } + const Current& operator*() const { return current_; } + const Current* operator->() const { return ¤t_; } + + private: + NGFragmentChildIterator( + const NGInlineCursor& parent, + const NGBlockBreakToken* parent_break_token, + base::span<const NGBreakToken* const> child_break_tokens); + + bool AdvanceChildFragment(); + void UpdateSelfFromFragment( + const NGPhysicalBoxFragment* previous_fragment = nullptr); + + bool AdvanceWithCursor(); + void UpdateSelfFromCursor(); + void SkipToBoxFragment(); + void SkipToBlockBreakToken(); + + const NGPhysicalBoxFragment* parent_fragment_ = nullptr; + const NGBlockBreakToken* parent_break_token_ = nullptr; + Current current_; + base::span<const NGBreakToken* const> child_break_tokens_; + wtf_size_t child_fragment_idx_ = 0; + wtf_size_t child_break_token_idx_ = 0; + bool is_fragmentation_context_root_ = false; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_CHILD_ITERATOR_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator_test.cc new file mode 100644 index 00000000000..f0304c056d1 --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator_test.cc @@ -0,0 +1,808 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h" +#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" + +namespace blink { +namespace { + +class NGFragmentChildIteratorTest + : public NGBaseLayoutAlgorithmTest, + private ScopedLayoutNGBlockFragmentationForTest, + private ScopedLayoutNGFragmentItemForTest { + protected: + NGFragmentChildIteratorTest() + : ScopedLayoutNGBlockFragmentationForTest(true), + ScopedLayoutNGFragmentItemForTest(true) {} + + scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm( + Element* element) { + NGBlockNode container(ToLayoutBox(element->GetLayoutObject())); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize)); + return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space); + } +}; + +TEST_F(NGFragmentChildIteratorTest, Basic) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="child1"> + <div id="grandchild"></div> + </div> + <div id="child2"></div> + </div> + )HTML"); + + const LayoutObject* child1 = GetLayoutObjectByElementId("child1"); + const LayoutObject* child2 = GetLayoutObjectByElementId("child2"); + const LayoutObject* grandchild = GetLayoutObjectByElementId("grandchild"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + EXPECT_FALSE(iterator1.IsAtEnd()); + + const NGPhysicalBoxFragment* fragment = iterator1->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child1); + EXPECT_FALSE(iterator1.IsAtEnd()); + + NGFragmentChildIterator iterator2 = iterator1.Descend(); + EXPECT_FALSE(iterator2.IsAtEnd()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), grandchild); + EXPECT_FALSE(iterator2.IsAtEnd()); + EXPECT_FALSE(iterator2.Advance()); + EXPECT_TRUE(iterator2.IsAtEnd()); + + EXPECT_TRUE(iterator1.Advance()); + fragment = iterator1->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child2); + EXPECT_FALSE(iterator1.IsAtEnd()); + + // #child2 has no children. + EXPECT_TRUE(iterator1.Descend().IsAtEnd()); + + // No more children left. + EXPECT_FALSE(iterator1.Advance()); + EXPECT_TRUE(iterator1.IsAtEnd()); +} + +TEST_F(NGFragmentChildIteratorTest, BasicInline) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + xxx + <span id="span1" style="border:solid;"> + <div id="float1" style="float:left;"></div> + xxx + </span> + xxx + </div> + )HTML"); + + const LayoutObject* span1 = GetLayoutObjectByElementId("span1"); + const LayoutObject* float1 = GetLayoutObjectByElementId("float1"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + EXPECT_FALSE(iterator1->BoxFragment()); + const NGFragmentItem* fragment_item = iterator1->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_EQ(fragment_item->Type(), NGFragmentItem::kLine); + + // Descend into the line box. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + fragment_item = iterator2->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_TRUE(fragment_item->IsText()); + + EXPECT_TRUE(iterator2.Advance()); + const NGPhysicalBoxFragment* fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), span1); + + // Descend into children of #span1. + NGFragmentChildIterator iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), float1); + + EXPECT_TRUE(iterator3.Advance()); + fragment_item = iterator3->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_TRUE(fragment_item->IsText()); + EXPECT_FALSE(iterator3.Advance()); + + // Continue with siblings of #span1. + EXPECT_TRUE(iterator2.Advance()); + fragment_item = iterator2->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_TRUE(fragment_item->IsText()); + + EXPECT_FALSE(iterator2.Advance()); + EXPECT_FALSE(iterator1.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, InlineBlock) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + xxx + <span id="inlineblock"> + <div id="float1" style="float:left;"></div> + </span> + xxx + </div> + )HTML"); + + const LayoutObject* inlineblock = GetLayoutObjectByElementId("inlineblock"); + const LayoutObject* float1 = GetLayoutObjectByElementId("float1"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + EXPECT_FALSE(iterator1->BoxFragment()); + const NGFragmentItem* fragment_item = iterator1->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_EQ(fragment_item->Type(), NGFragmentItem::kLine); + + // Descend into the line box. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + fragment_item = iterator2->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_TRUE(fragment_item->IsText()); + + EXPECT_TRUE(iterator2.Advance()); + const NGPhysicalBoxFragment* fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), inlineblock); + + // Descend into children of #inlineblock. + NGFragmentChildIterator iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), float1); + EXPECT_FALSE(iterator3.Advance()); + + // Continue with siblings of #inlineblock. + EXPECT_TRUE(iterator2.Advance()); + fragment_item = iterator2->FragmentItem(); + ASSERT_TRUE(fragment_item); + EXPECT_TRUE(fragment_item->IsText()); + + EXPECT_FALSE(iterator2.Advance()); + EXPECT_FALSE(iterator1.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, FloatsInInline) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <span id="span1" style="border:solid;"> + <div id="float1" style="float:left;"> + <div id="child"></div> + </div> + </span> + </div> + )HTML"); + + const LayoutObject* span1 = GetLayoutObjectByElementId("span1"); + const LayoutObject* float1 = GetLayoutObjectByElementId("float1"); + const LayoutObject* child = GetLayoutObjectByElementId("child"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + const NGPhysicalBoxFragment* fragment = iterator1->BoxFragment(); + EXPECT_FALSE(fragment); + const NGFragmentItem* item = iterator1->FragmentItem(); + ASSERT_TRUE(item); + EXPECT_EQ(item->Type(), NGFragmentItem::kLine); + + // Descend into the line box. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), span1); + + // Descend into children of #span1. + NGFragmentChildIterator iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), float1); + + // Descend into children of #float1. + NGFragmentChildIterator iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child); + + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + EXPECT_FALSE(iterator2.Advance()); + EXPECT_FALSE(iterator1.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, AbsposAndLine) { + SetBodyInnerHTML(R"HTML( + <div id="container" style="position:relative;"> + <div id="abspos" style="position:absolute;"></div> + xxx + </div> + )HTML"); + + const LayoutObject* abspos = GetLayoutObjectByElementId("abspos"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + const NGPhysicalBoxFragment* fragment = iterator1->BoxFragment(); + EXPECT_FALSE(fragment); + const NGFragmentItem* item = iterator1->FragmentItem(); + ASSERT_TRUE(item); + EXPECT_EQ(item->Type(), NGFragmentItem::kLine); + + // Descend into the line box. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + + fragment = iterator2->BoxFragment(); + EXPECT_FALSE(fragment); + item = iterator2->FragmentItem(); + ASSERT_TRUE(item); + EXPECT_TRUE(item->IsText()); + EXPECT_FALSE(iterator2.Advance()); + + // The abspos is a sibling of the line box. + EXPECT_TRUE(iterator1.Advance()); + fragment = iterator1->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), abspos); + EXPECT_FALSE(iterator1.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, BasicMulticol) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="mc" style="columns:3; padding:2px; column-fill:auto; column-gap:10px; width:320px; height:100px;"> + <div id="child" style="margin-top:30px; margin-left:4px; height:200px;"></div> + </div> + </div> + )HTML"); + + const LayoutObject* mc = GetLayoutObjectByElementId("mc"); + const LayoutObject* child = GetLayoutObjectByElementId("child"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator(*container.get()); + + const NGPhysicalBoxFragment* fragment = iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), mc); + + // First column. + NGFragmentChildIterator child_iterator = iterator.Descend(); + fragment = child_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(child_iterator->Link().offset.top, LayoutUnit(2)); + EXPECT_EQ(child_iterator->Link().offset.left, LayoutUnit(2)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(100)); + EXPECT_FALSE(fragment->GetLayoutObject()); + EXPECT_FALSE(child_iterator->BlockBreakToken()); + + NGFragmentChildIterator grandchild_iterator = child_iterator.Descend(); + fragment = grandchild_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(grandchild_iterator->Link().offset.top, LayoutUnit(30)); + EXPECT_EQ(grandchild_iterator->Link().offset.left, LayoutUnit(4)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(70)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + EXPECT_FALSE(grandchild_iterator.Advance()); + EXPECT_FALSE(grandchild_iterator->BlockBreakToken()); + + // Second column. + ASSERT_TRUE(child_iterator.Advance()); + fragment = child_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(child_iterator->Link().offset.top, LayoutUnit(2)); + EXPECT_EQ(child_iterator->Link().offset.left, LayoutUnit(112)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(100)); + EXPECT_FALSE(fragment->GetLayoutObject()); + const auto* break_token = child_iterator->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(100)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc); + + grandchild_iterator = child_iterator.Descend(); + fragment = grandchild_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(grandchild_iterator->Link().offset.top, LayoutUnit(0)); + EXPECT_EQ(grandchild_iterator->Link().offset.left, LayoutUnit(4)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(100)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + EXPECT_FALSE(grandchild_iterator.Advance()); + break_token = grandchild_iterator->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(70)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child); + + // Third column. + ASSERT_TRUE(child_iterator.Advance()); + fragment = child_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(child_iterator->Link().offset.top, LayoutUnit(2)); + EXPECT_EQ(child_iterator->Link().offset.left, LayoutUnit(222)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(100)); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = child_iterator->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(200)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc); + + grandchild_iterator = child_iterator.Descend(); + fragment = grandchild_iterator->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(grandchild_iterator->Link().offset.top, LayoutUnit(0)); + EXPECT_EQ(grandchild_iterator->Link().offset.left, LayoutUnit(4)); + EXPECT_EQ(fragment->Size().height, LayoutUnit(30)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + EXPECT_FALSE(grandchild_iterator.Advance()); + break_token = grandchild_iterator->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(170)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child); + + EXPECT_FALSE(child_iterator.Advance()); + EXPECT_FALSE(iterator.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, ColumnSpanner) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="mc" style="columns:2;"> + <div id="child"> + <div id="grandchild1" style="height:150px;"></div> + <div id="spanner" style="column-span:all; height:11px;"></div> + <div id="grandchild2" style="height:66px;"></div> + </div> + </div> + </div> + )HTML"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + const LayoutObject* mc = GetLayoutObjectByElementId("mc"); + const LayoutObject* child = GetLayoutObjectByElementId("child"); + const LayoutObject* spanner = GetLayoutObjectByElementId("spanner"); + const LayoutObject* grandchild1 = GetLayoutObjectByElementId("grandchild1"); + const LayoutObject* grandchild2 = GetLayoutObjectByElementId("grandchild2"); + + const NGPhysicalBoxFragment* fragment = iterator1->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), mc); + + // First column before spanner. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_FALSE(fragment->GetLayoutObject()); + EXPECT_FALSE(iterator2->BlockBreakToken()); + + // First fragment for #child. + NGFragmentChildIterator iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + EXPECT_FALSE(iterator3->BlockBreakToken()); + + // First fragment for #grandchild1. + NGFragmentChildIterator iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_EQ(fragment->GetLayoutObject(), grandchild1); + EXPECT_FALSE(iterator4->BlockBreakToken()); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + + // Second column before spanner. + EXPECT_TRUE(iterator2.Advance()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_FALSE(fragment->GetLayoutObject()); + const auto* break_token = iterator2->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(75)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc); + + // Second fragment for #child. + iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + break_token = iterator3->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(75)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child); + + // Second fragment for #grandchild1. + iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(75)); + EXPECT_EQ(fragment->GetLayoutObject(), grandchild1); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(75)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), grandchild1); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + + // The spanner. + EXPECT_TRUE(iterator2.Advance()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(11)); + EXPECT_EQ(fragment->GetLayoutObject(), spanner); + EXPECT_FALSE(iterator2->BlockBreakToken()); + + // First column after spanner. + EXPECT_TRUE(iterator2.Advance()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator2->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(150)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc); + + // Third fragment for #child. + iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + break_token = iterator3->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(150)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child); + + // First fragment for #grandchild2. + iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_EQ(fragment->GetLayoutObject(), grandchild2); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_TRUE(break_token->IsBreakBefore()); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), grandchild2); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + + // Second column after spanner. + EXPECT_TRUE(iterator2.Advance()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator2->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(183)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc); + + // Fourth fragment for #child. + iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_EQ(fragment->GetLayoutObject(), child); + break_token = iterator3->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(183)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child); + + // Second fragment for #grandchild2. + iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->Size().height, LayoutUnit(33)); + EXPECT_EQ(fragment->GetLayoutObject(), grandchild2); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(33)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), grandchild2); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + + EXPECT_FALSE(iterator2.Advance()); + EXPECT_FALSE(iterator1.Advance()); +} + +TEST_F(NGFragmentChildIteratorTest, NestedWithColumnSpanner) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="mc1" style="columns:2; column-fill:auto; height:100px;"> + <div id="mc2" style="columns:2;"> + <div id="child1" style="height:150px;"></div> + <div id="spanner1" style="column-span:all;"> + <div id="spanner1child" style="height:55px;"></div> + </div> + <div id="child2" style="height:50px;"></div> + <div id="spanner2" style="column-span:all;"> + <div id="spanner2child" style="height:20px;"></div> + </div> + <div id="child3" style="height:20px;"></div> + </div> + </div> + </div> + )HTML"); + + scoped_refptr<const NGPhysicalBoxFragment> container = + RunBlockLayoutAlgorithm(GetElementById("container")); + NGFragmentChildIterator iterator1(*container.get()); + + const LayoutObject* mc1 = GetLayoutObjectByElementId("mc1"); + const LayoutObject* mc2 = GetLayoutObjectByElementId("mc2"); + const LayoutObject* child1 = GetLayoutObjectByElementId("child1"); + const LayoutObject* child2 = GetLayoutObjectByElementId("child2"); + const LayoutObject* child3 = GetLayoutObjectByElementId("child3"); + const LayoutObject* spanner1 = GetLayoutObjectByElementId("spanner1"); + const LayoutObject* spanner2 = GetLayoutObjectByElementId("spanner2"); + const LayoutObject* spanner1child = + GetLayoutObjectByElementId("spanner1child"); + const LayoutObject* spanner2child = + GetLayoutObjectByElementId("spanner2child"); + + const NGPhysicalBoxFragment* fragment = iterator1->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), mc1); + + // First outer column. + NGFragmentChildIterator iterator2 = iterator1.Descend(); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + EXPECT_FALSE(iterator2->BlockBreakToken()); + + // First fragment for #mc2. + NGFragmentChildIterator iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), mc2); + EXPECT_FALSE(iterator3->BlockBreakToken()); + + // First inner column in first outer column. + NGFragmentChildIterator iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + EXPECT_FALSE(iterator4->BlockBreakToken()); + + // First fragment for #child1. + NGFragmentChildIterator iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child1); + EXPECT_FALSE(iterator5->BlockBreakToken()); + EXPECT_FALSE(iterator5.Advance()); + + // Second inner column in first outer column. + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + const auto* break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(75)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // Second fragment for #child1. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child1); + break_token = iterator5->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(75)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child1); + + // First fragment for #spanner1 (it's split into the first and second outer + // columns). + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner1); + EXPECT_FALSE(iterator4->BlockBreakToken()); + + // First fragment for #spanner1child + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner1child); + EXPECT_FALSE(iterator5->BlockBreakToken()); + EXPECT_FALSE(iterator5.Advance()); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + + // Second outer column + EXPECT_TRUE(iterator2.Advance()); + fragment = iterator2->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator2->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(100)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc1); + + // Second fragment for #mc2. + iterator3 = iterator2.Descend(); + fragment = iterator3->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), mc2); + break_token = iterator3->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(100)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // Second fragment for #spanner1 (it's split into the first and second outer + // columns). + iterator4 = iterator3.Descend(); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner1); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(25)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), spanner1); + + // Second fragment for #spanner1child. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner1child); + break_token = iterator5->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(25)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), spanner1child); + EXPECT_FALSE(iterator5.Advance()); + + // First inner column after first spanner in second outer column. + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(150)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // First fragment for #child2. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child2); + break_token = iterator5->BlockBreakToken(); + EXPECT_TRUE(break_token); + EXPECT_TRUE(break_token->IsBreakBefore()); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child2); + EXPECT_FALSE(iterator5.Advance()); + + // Second inner column after first spanner in second outer column. + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(175)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // Second fragment for #child2. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child2); + break_token = iterator5->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(25)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child2); + EXPECT_FALSE(iterator5.Advance()); + + // The only fragment for #spanner2 + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner2); + EXPECT_FALSE(iterator4->BlockBreakToken()); + + // First fragment for #spanner2child + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), spanner2child); + EXPECT_FALSE(iterator5->BlockBreakToken()); + EXPECT_FALSE(iterator5.Advance()); + + // First inner column after second spanner in second outer column. + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(200)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // First fragment for #child3. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child3); + break_token = iterator5->BlockBreakToken(); + EXPECT_TRUE(break_token); + EXPECT_TRUE(break_token->IsBreakBefore()); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child3); + EXPECT_FALSE(iterator5.Advance()); + + // Second inner column after second spanner in second outer column. + EXPECT_TRUE(iterator4.Advance()); + fragment = iterator4->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_TRUE(fragment->IsColumnBox()); + EXPECT_FALSE(fragment->GetLayoutObject()); + break_token = iterator4->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(210)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), mc2); + + // Second fragment for #child3. + iterator5 = iterator4.Descend(); + fragment = iterator5->BoxFragment(); + ASSERT_TRUE(fragment); + EXPECT_EQ(fragment->GetLayoutObject(), child3); + break_token = iterator5->BlockBreakToken(); + ASSERT_TRUE(break_token); + EXPECT_EQ(break_token->ConsumedBlockSize(), LayoutUnit(10)); + EXPECT_EQ(break_token->InputNode().GetLayoutBox(), child3); + EXPECT_FALSE(iterator5.Advance()); + EXPECT_FALSE(iterator4.Advance()); + EXPECT_FALSE(iterator3.Advance()); + EXPECT_FALSE(iterator2.Advance()); + EXPECT_FALSE(iterator1.Advance()); +} + +} // anonymous namespace +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc new file mode 100644 index 00000000000..a0ddc40690f --- /dev/null +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc @@ -0,0 +1,197 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" +#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" + +namespace blink { +namespace { + +class NGFragmentationTest : public NGBaseLayoutAlgorithmTest, + private ScopedLayoutNGBlockFragmentationForTest { + protected: + NGFragmentationTest() : ScopedLayoutNGBlockFragmentationForTest(true) {} + + scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm( + Element* element) { + NGBlockNode container(ToLayoutBox(element->GetLayoutObject())); + NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace( + WritingMode::kHorizontalTb, TextDirection::kLtr, + LogicalSize(LayoutUnit(1000), kIndefiniteSize)); + return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space); + } +}; + +TEST_F(NGFragmentationTest, MultipleFragments) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div style="columns:3; width:620px; column-fill:auto; height:100px; column-gap:10px;"> + <div id="outer1" style="height:150px;"> + <div id="inner1" style="height:250px;"></div> + <div id="inner2" style="height:10px;"></div> + </div> + <div id="outer2" style="height:90px;"></div> + </div> + </div> + )HTML"); + + RunBlockLayoutAlgorithm(GetElementById("container")); + const LayoutBox* outer1 = ToLayoutBox(GetLayoutObjectByElementId("outer1")); + const LayoutBox* outer2 = ToLayoutBox(GetLayoutObjectByElementId("outer2")); + const LayoutBox* inner1 = ToLayoutBox(GetLayoutObjectByElementId("inner1")); + const LayoutBox* inner2 = ToLayoutBox(GetLayoutObjectByElementId("inner2")); + + EXPECT_EQ(outer1->PhysicalFragmentCount(), 3u); + EXPECT_EQ(outer2->PhysicalFragmentCount(), 2u); + EXPECT_EQ(inner1->PhysicalFragmentCount(), 3u); + EXPECT_EQ(inner2->PhysicalFragmentCount(), 1u); + + // While the #outer1 box itself only needs two fragments, we need to create a + // third fragment to hold the overflowing children in the third column. + EXPECT_EQ(outer1->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 100)); + EXPECT_EQ(outer1->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 50)); + EXPECT_EQ(outer1->GetPhysicalFragment(2)->Size(), PhysicalSize(200, 0)); + + // #inner1 overflows its parent and uses three columns. + EXPECT_EQ(inner1->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 100)); + EXPECT_EQ(inner1->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 100)); + EXPECT_EQ(inner1->GetPhysicalFragment(2)->Size(), PhysicalSize(200, 50)); + + // #inner2 is tiny, and only needs some space in one column (the third one). + EXPECT_EQ(inner2->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 10)); + + // #outer2 starts in the second column and ends in the third. + EXPECT_EQ(outer2->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 50)); + EXPECT_EQ(outer2->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 40)); +} + +TEST_F(NGFragmentationTest, MultipleFragmentsAndColumnSpanner) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="multicol" style="columns:3; width:620px; column-gap:10px; orphans:1; widows:1; line-height:20px;"> + <div id="outer"> + <div id="inner1"><br><br><br><br></div> + <div id="spanner1" style="column-span:all;"></div> + <div id="inner2"><br><br><br><br><br></div> + <div id="spanner2" style="column-span:all;"></div> + <div id="inner3"><br><br><br><br><br><br><br></div> + </div> + </div> + </div> + )HTML"); + + RunBlockLayoutAlgorithm(GetElementById("container")); + const LayoutBox* multicol = + ToLayoutBox(GetLayoutObjectByElementId("multicol")); + const LayoutBox* outer = ToLayoutBox(GetLayoutObjectByElementId("outer")); + const LayoutBox* inner1 = ToLayoutBox(GetLayoutObjectByElementId("inner1")); + const LayoutBox* inner2 = ToLayoutBox(GetLayoutObjectByElementId("inner2")); + const LayoutBox* inner3 = ToLayoutBox(GetLayoutObjectByElementId("inner3")); + const LayoutBox* spanner1 = + ToLayoutBox(GetLayoutObjectByElementId("spanner1")); + const LayoutBox* spanner2 = + ToLayoutBox(GetLayoutObjectByElementId("spanner2")); + + EXPECT_EQ(multicol->PhysicalFragmentCount(), 1u); + + // #outer will create 8 fragments: 2 for the 2 columns before the first + // spanner, 3 for the 3 columns between the two spanners, and 3 for the 3 + // columns after the last spanner. + EXPECT_EQ(outer->PhysicalFragmentCount(), 8u); + + // #inner1 has 4 lines split into 2 columns. + EXPECT_EQ(inner1->PhysicalFragmentCount(), 2u); + + // #inner2 has 5 lines split into 3 columns. + EXPECT_EQ(inner2->PhysicalFragmentCount(), 3u); + + // #inner3 has 8 lines split into 3 columns. + EXPECT_EQ(inner3->PhysicalFragmentCount(), 3u); + + EXPECT_EQ(spanner1->PhysicalFragmentCount(), 1u); + EXPECT_EQ(spanner2->PhysicalFragmentCount(), 1u); + + EXPECT_EQ(multicol->GetPhysicalFragment(0)->Size(), PhysicalSize(620, 140)); + EXPECT_EQ(outer->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(outer->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(outer->GetPhysicalFragment(2)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(outer->GetPhysicalFragment(3)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(outer->GetPhysicalFragment(4)->Size(), PhysicalSize(200, 20)); + EXPECT_EQ(outer->GetPhysicalFragment(5)->Size(), PhysicalSize(200, 60)); + EXPECT_EQ(outer->GetPhysicalFragment(6)->Size(), PhysicalSize(200, 60)); + EXPECT_EQ(outer->GetPhysicalFragment(7)->Size(), PhysicalSize(200, 20)); + EXPECT_EQ(inner1->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(inner1->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(inner2->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(inner2->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 40)); + EXPECT_EQ(inner2->GetPhysicalFragment(2)->Size(), PhysicalSize(200, 20)); + EXPECT_EQ(inner3->GetPhysicalFragment(0)->Size(), PhysicalSize(200, 60)); + EXPECT_EQ(inner3->GetPhysicalFragment(1)->Size(), PhysicalSize(200, 60)); + EXPECT_EQ(inner3->GetPhysicalFragment(2)->Size(), PhysicalSize(200, 20)); + EXPECT_EQ(spanner1->GetPhysicalFragment(0)->Size(), PhysicalSize(620, 0)); + EXPECT_EQ(spanner2->GetPhysicalFragment(0)->Size(), PhysicalSize(620, 0)); +} + +TEST_F(NGFragmentationTest, MultipleFragmentsNestedMulticol) { + SetBodyInnerHTML(R"HTML( + <div id="container"> + <div id="outer_multicol" style="columns:3; column-fill:auto; height:100px; width:620px; column-gap:10px;"> + <div id="inner_multicol" style="columns:2;"> + <div id="child1" style="width:11px; height:350px;"></div> + <div id="child2" style="width:22px; height:350px;"></div> + </div> + </div> + </div> + )HTML"); + + RunBlockLayoutAlgorithm(GetElementById("container")); + const LayoutBox* outer_multicol = + ToLayoutBox(GetLayoutObjectByElementId("outer_multicol")); + const LayoutBox* inner_multicol = + ToLayoutBox(GetLayoutObjectByElementId("inner_multicol")); + const LayoutBox* child1 = ToLayoutBox(GetLayoutObjectByElementId("child1")); + const LayoutBox* child2 = ToLayoutBox(GetLayoutObjectByElementId("child2")); + + EXPECT_EQ(outer_multicol->PhysicalFragmentCount(), 1u); + + // The content is too tall (350px + 350px, column height 100px, 2*3 columns = + // 600px) and will use one more column than we have specified. + EXPECT_EQ(inner_multicol->PhysicalFragmentCount(), 4u); + + // 350px tall content with a column height of 100px will require 4 fragments. + EXPECT_EQ(child1->PhysicalFragmentCount(), 4u); + EXPECT_EQ(child2->PhysicalFragmentCount(), 4u); + + EXPECT_EQ(outer_multicol->GetPhysicalFragment(0)->Size(), + PhysicalSize(620, 100)); + + EXPECT_EQ(inner_multicol->GetPhysicalFragment(0)->Size(), + PhysicalSize(200, 100)); + EXPECT_EQ(inner_multicol->GetPhysicalFragment(1)->Size(), + PhysicalSize(200, 100)); + EXPECT_EQ(inner_multicol->GetPhysicalFragment(2)->Size(), + PhysicalSize(200, 100)); + EXPECT_EQ(inner_multicol->GetPhysicalFragment(3)->Size(), + PhysicalSize(200, 100)); + + // #child1 starts at the beginning of a column, so the last fragment will be + // shorter than the rest. + EXPECT_EQ(child1->GetPhysicalFragment(0)->Size(), PhysicalSize(11, 100)); + EXPECT_EQ(child1->GetPhysicalFragment(1)->Size(), PhysicalSize(11, 100)); + EXPECT_EQ(child1->GetPhysicalFragment(2)->Size(), PhysicalSize(11, 100)); + EXPECT_EQ(child1->GetPhysicalFragment(3)->Size(), PhysicalSize(11, 50)); + + // #child2 starts in the middle of a column, so the first fragment will be + // shorter than the rest. + EXPECT_EQ(child2->GetPhysicalFragment(0)->Size(), PhysicalSize(22, 50)); + EXPECT_EQ(child2->GetPhysicalFragment(1)->Size(), PhysicalSize(22, 100)); + EXPECT_EQ(child2->GetPhysicalFragment(2)->Size(), PhysicalSize(22, 100)); + EXPECT_EQ(child2->GetPhysicalFragment(3)->Size(), PhysicalSize(22, 100)); +} + +} // anonymous namespace +} // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc index 72a107d1f7b..327212ca3a8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc @@ -164,11 +164,20 @@ NGBreakAppeal CalculateBreakAppealInside(const NGConstraintSpace& space, } void SetupFragmentation(const NGConstraintSpace& parent_space, + const NGLayoutInputNode& child, LayoutUnit fragmentainer_offset_delta, NGConstraintSpaceBuilder* builder, bool is_new_fc) { DCHECK(parent_space.HasBlockFragmentation()); + // If the child is truly unbreakable, it won't participate in block + // fragmentation. If it's too tall to fit, it will either overflow the + // fragmentainer or get brutally sliced into pieces (without looking for + // allowed breakpoints, since there are none, by definition), depending on + // fragmentation type (multicol vs. printing). + if (child.IsMonolithic()) + return; + builder->SetFragmentainerBlockSize(parent_space.FragmentainerBlockSize()); builder->SetFragmentainerOffsetAtBfc(parent_space.FragmentainerOffsetAtBfc() + fragmentainer_offset_delta); @@ -179,11 +188,20 @@ void SetupFragmentation(const NGConstraintSpace& parent_space, } void FinishFragmentation(const NGConstraintSpace& space, + const NGBlockBreakToken* previous_break_token, LayoutUnit block_size, LayoutUnit intrinsic_block_size, - LayoutUnit previously_consumed_block_size, LayoutUnit space_left, NGBoxFragmentBuilder* builder) { + LayoutUnit previously_consumed_block_size; + unsigned sequence_number = 0; + if (previous_break_token && !previous_break_token->IsBreakBefore()) { + previously_consumed_block_size = previous_break_token->ConsumedBlockSize(); + sequence_number = previous_break_token->SequenceNumber() + 1; + builder->SetIsFirstForNode(false); + } + builder->SetSequenceNumber(sequence_number); + if (builder->DidBreak()) { // One of our children broke. Even if we fit within the remaining space, we // need to prepare a break token. @@ -271,11 +289,13 @@ void BreakBeforeChild(const NGConstraintSpace& space, bool is_forced_break, NGBoxFragmentBuilder* builder) { #if DCHECK_IS_ON() - // In order to successfully break before a node, this has to be its first - // fragment. - const auto& physical_fragment = layout_result.PhysicalFragment(); - DCHECK(!physical_fragment.IsBox() || - To<NGPhysicalBoxFragment>(physical_fragment).IsFirstForNode()); + if (layout_result.Status() == NGLayoutResult::kSuccess) { + // In order to successfully break before a node, this has to be its first + // fragment. + const auto& physical_fragment = layout_result.PhysicalFragment(); + DCHECK(!physical_fragment.IsBox() || + To<NGPhysicalBoxFragment>(physical_fragment).IsFirstForNode()); + } #endif // Report space shortage. Note that we're not doing this for line boxes here @@ -310,7 +330,10 @@ void PropagateSpaceShortage(const NGConstraintSpace& space, LayoutUnit space_shortage; if (layout_result.MinimalSpaceShortage() == LayoutUnit::Max()) { // Calculate space shortage: Figure out how much more space would have been - // sufficient to make the child fit right here in the current fragment. + // sufficient to make the child fragment fit right here in the current + // fragmentainer. If layout aborted, though, we can't propagate anything. + if (layout_result.Status() != NGLayoutResult::kSuccess) + return; NGFragment fragment(space.GetWritingMode(), layout_result.PhysicalFragment()); space_shortage = fragmentainer_block_offset + fragment.BlockSize() - @@ -335,6 +358,13 @@ bool MovePastBreakpoint(const NGConstraintSpace& space, LayoutUnit fragmentainer_block_offset, NGBreakAppeal appeal_before, NGBoxFragmentBuilder* builder) { + if (layout_result.Status() != NGLayoutResult::kSuccess) { + // Layout aborted - no fragment was produced. There's nothing to move + // past. We need to break before. + DCHECK_EQ(layout_result.Status(), NGLayoutResult::kOutOfFragmentainerSpace); + return false; + } + const auto& physical_fragment = layout_result.PhysicalFragment(); NGFragment fragment(space.GetWritingMode(), physical_fragment); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h index 3f0f844f3a1..48ca63e28a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h @@ -6,6 +6,8 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENTATION_UTILS_H_ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h" @@ -45,6 +47,14 @@ inline bool IsResumingLayout(const NGBlockBreakToken* token) { return token && !token->IsBreakBefore(); } +// Return true if the fragment to be generated for the specified item is going +// to be the first fragment for the node. +inline bool IsFirstForNode(const NGInlineItem& item, + const NGInlineBreakToken* token) { + return item.IsFirstForNode() && + (!token || item.StartOffset() >= token->TextOffset()); +} + // Calculate the final "break-between" value at a class A or C breakpoint. This // is the combination of all break-before and break-after values that met at the // breakpoint. @@ -93,15 +103,16 @@ inline void AdjustForFragmentation(const NGBlockBreakToken* break_token, // formatting context starts in a previous fragmentainer; the offset from the // current fragmentainer block-start. void SetupFragmentation(const NGConstraintSpace& parent_space, + const NGLayoutInputNode& child, LayoutUnit fragmentainer_offset_delta, NGConstraintSpaceBuilder*, bool is_new_fc); // Write fragmentation information to the fragment builder after layout. void FinishFragmentation(const NGConstraintSpace&, + const NGBlockBreakToken* previous_break_token, LayoutUnit block_size, LayoutUnit intrinsic_block_size, - LayoutUnit previously_consumed_block_size, LayoutUnit space_left, NGBoxFragmentBuilder*); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h index 2ef8605d97e..b04949398e8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h @@ -7,7 +7,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" @@ -17,7 +17,7 @@ namespace blink { class ComputedStyle; class NGEarlyBreak; class NGLayoutResult; -struct MinMaxSizeInput; +struct MinMaxSizesInput; // Operations provided by a layout algorithm. class NGLayoutAlgorithmOperations { @@ -33,8 +33,8 @@ class NGLayoutAlgorithmOperations { // account. If the return value is empty, the caller is expected to synthesize // this value from the overflow rect returned from Layout called with an // available width of 0 and LayoutUnit::max(), respectively. - virtual base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const { + virtual base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const { return base::nullopt; } }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc index f46d17d48d1..28d5bcce744 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc @@ -8,9 +8,8 @@ #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h" #include "third_party/blink/renderer/core/layout/layout_replaced.h" #include "third_party/blink/renderer/core/layout/layout_view.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" @@ -61,39 +60,23 @@ void AppendNodeToString(NGLayoutInputNode node, } // namespace -MinMaxSize NGLayoutInputNode::ComputeMinMaxSize( +MinMaxSizes NGLayoutInputNode::ComputeMinMaxSizes( WritingMode writing_mode, - const MinMaxSizeInput& input, + const MinMaxSizesInput& input, const NGConstraintSpace* space) { if (auto* inline_node = DynamicTo<NGInlineNode>(this)) - return inline_node->ComputeMinMaxSize(writing_mode, input, space); - return To<NGBlockNode>(*this).ComputeMinMaxSize(writing_mode, input, space); + return inline_node->ComputeMinMaxSizes(writing_mode, input, space); + return To<NGBlockNode>(*this).ComputeMinMaxSizes(writing_mode, input, space); } void NGLayoutInputNode::IntrinsicSize( base::Optional<LayoutUnit>* computed_inline_size, - base::Optional<LayoutUnit>* computed_block_size, - LogicalSize* aspect_ratio) const { + base::Optional<LayoutUnit>* computed_block_size) const { DCHECK(IsReplaced()); - LayoutUnit override_inline_size = OverrideIntrinsicContentInlineSize(); - if (override_inline_size != kIndefiniteSize) - *computed_inline_size = override_inline_size; - - LayoutUnit override_block_size = OverrideIntrinsicContentBlockSize(); - if (override_block_size != kIndefiniteSize) - *computed_block_size = override_block_size; - - if (ShouldApplySizeContainment()) { - if (!*computed_inline_size) - *computed_inline_size = LayoutUnit(); - if (!*computed_block_size) - *computed_block_size = LayoutUnit(); - } - if (*computed_inline_size && *computed_block_size) { - *aspect_ratio = LogicalSize(**computed_inline_size, **computed_block_size); + GetOverrideIntrinsicSize(computed_inline_size, computed_block_size); + if (*computed_inline_size && *computed_block_size) return; - } IntrinsicSizingInfo legacy_sizing_info; @@ -102,9 +85,6 @@ void NGLayoutInputNode::IntrinsicSize( *computed_inline_size = LayoutUnit(legacy_sizing_info.size.Width()); if (!*computed_block_size && legacy_sizing_info.has_height) *computed_block_size = LayoutUnit(legacy_sizing_info.size.Height()); - *aspect_ratio = - LogicalSize(LayoutUnit(legacy_sizing_info.aspect_ratio.Width()), - LayoutUnit(legacy_sizing_info.aspect_ratio.Height())); } NGLayoutInputNode NGLayoutInputNode::NextSibling() { @@ -134,8 +114,39 @@ void NGLayoutInputNode::ShowNodeTree() const { StringBuilder string_builder; string_builder.Append(".:: LayoutNG Node Tree ::.\n"); AppendNodeToString(*this, &string_builder); - fprintf(stderr, "%s\n", string_builder.ToString().Utf8().c_str()); + DLOG(INFO) << "\n" << string_builder.ToString().Utf8(); } #endif +void NGLayoutInputNode::GetOverrideIntrinsicSize( + base::Optional<LayoutUnit>* computed_inline_size, + base::Optional<LayoutUnit>* computed_block_size) const { + DCHECK(IsReplaced()); + + LayoutUnit override_inline_size = OverrideIntrinsicContentInlineSize(); + if (override_inline_size != kIndefiniteSize) { + *computed_inline_size = override_inline_size; + } else { + LayoutUnit default_inline_size = DefaultIntrinsicContentInlineSize(); + if (default_inline_size != kIndefiniteSize) + *computed_inline_size = default_inline_size; + } + + LayoutUnit override_block_size = OverrideIntrinsicContentBlockSize(); + if (override_block_size != kIndefiniteSize) { + *computed_block_size = override_block_size; + } else { + LayoutUnit default_block_size = DefaultIntrinsicContentBlockSize(); + if (default_block_size != kIndefiniteSize) + *computed_block_size = default_block_size; + } + + if (ShouldApplySizeContainment()) { + if (!*computed_inline_size) + *computed_inline_size = LayoutUnit(); + if (!*computed_block_size) + *computed_block_size = LayoutUnit(); + } +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h index c77e16c4310..45acfcb0f7c 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h @@ -11,7 +11,7 @@ #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h" -#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h" +#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -24,16 +24,13 @@ class LayoutObject; class LayoutBox; class NGConstraintSpace; class NGPaintFragment; -struct MinMaxSize; -struct LogicalSize; +struct MinMaxSizes; struct PhysicalSize; -enum class NGMinMaxSizeType { kContentBoxSize, kBorderBoxSize }; - // Input to the min/max inline size calculation algorithm for child nodes. Child // nodes within the same formatting context need to know which floats are beside // them. -struct MinMaxSizeInput { +struct MinMaxSizesInput { // The min-max size calculation (un-intuitively) requires a percentage // resolution size! // This occurs when a replaced element has an intrinsic size. E.g. @@ -44,14 +41,11 @@ struct MinMaxSizeInput { // // As we don't perform any tree walking, we need to pass the percentage // resolution block-size for min/max down the min/max size calculation. - explicit MinMaxSizeInput(LayoutUnit percentage_resolution_block_size) + explicit MinMaxSizesInput(LayoutUnit percentage_resolution_block_size) : percentage_resolution_block_size(percentage_resolution_block_size) {} LayoutUnit float_left_inline_size; LayoutUnit float_right_inline_size; LayoutUnit percentage_resolution_block_size; - - // Whether to return the size as a content-box size or border-box size. - NGMinMaxSizeType size_type = NGMinMaxSizeType::kBorderBoxSize; }; // Represents the input to a layout algorithm for a given node. The layout @@ -108,11 +102,11 @@ class CORE_EXPORT NGLayoutInputNode { } bool IsListItem() const { return IsBlock() && box_->IsLayoutNGListItem(); } bool IsListMarker() const { - return IsBlock() && box_->IsLayoutNGListMarker(); + return IsBlock() && box_->IsLayoutNGOutsideListMarker(); } bool ListMarkerOccupiesWholeLine() const { DCHECK(IsListMarker()); - return ToLayoutNGListMarker(box_)->NeedsOccupyWholeLine(); + return ToLayoutNGOutsideListMarker(box_)->NeedsOccupyWholeLine(); } bool IsFieldsetContainer() const { return IsBlock() && box_->IsLayoutNGFieldset(); @@ -123,6 +117,9 @@ class CORE_EXPORT NGLayoutInputNode { bool IsRenderedLegend() const { return IsBlock() && box_->IsRenderedLegend(); } + bool IsTable() const { return IsBlock() && box_->IsTable(); } + + bool IsMathRoot() const { return box_->IsMathMLRoot(); } bool IsAnonymousBlock() const { return box_->IsAnonymousBlock(); } @@ -159,16 +156,16 @@ class CORE_EXPORT NGLayoutInputNode { } // Returns border box. - MinMaxSize ComputeMinMaxSize(WritingMode, - const MinMaxSizeInput&, - const NGConstraintSpace* = nullptr); + MinMaxSizes ComputeMinMaxSizes(WritingMode, + const MinMaxSizesInput&, + const NGConstraintSpace* = nullptr); // Returns intrinsic sizing information for replaced elements. // ComputeReplacedSize can use it to compute actual replaced size. // Corresponds to Legacy's LayoutReplaced::IntrinsicSizingInfo. + // Use NGBlockNode::GetAspectRatio to get the aspect ratio. void IntrinsicSize(base::Optional<LayoutUnit>* computed_inline_size, - base::Optional<LayoutUnit>* computed_block_size, - LogicalSize* aspect_ratio) const; + base::Optional<LayoutUnit>* computed_block_size) const; // Returns the next sibling. NGLayoutInputNode NextSibling(); @@ -201,6 +198,13 @@ class CORE_EXPORT NGLayoutInputNode { return kIndefiniteSize; } + LayoutUnit DefaultIntrinsicContentInlineSize() const { + return box_->DefaultIntrinsicContentInlineSize(); + } + LayoutUnit DefaultIntrinsicContentBlockSize() const { + return box_->DefaultIntrinsicContentBlockSize(); + } + // Display locking functionality. const DisplayLockContext& GetDisplayLockContext() const { DCHECK(box_->GetDisplayLockContext()); @@ -240,6 +244,10 @@ class CORE_EXPORT NGLayoutInputNode { NGLayoutInputNode(LayoutBox* box, NGLayoutInputNodeType type) : box_(box), type_(type) {} + void GetOverrideIntrinsicSize( + base::Optional<LayoutUnit>* computed_inline_size, + base::Optional<LayoutUnit>* computed_block_size) const; + LayoutBox* box_; unsigned type_ : 1; // NGLayoutInputNodeType diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index fac77834d22..6dc8f994eee 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc @@ -39,6 +39,7 @@ static_assert(sizeof(NGLayoutResult) == sizeof(SameSizeAsNGLayoutResult), } // namespace NGLayoutResult::NGLayoutResult( + NGBoxFragmentBuilderPassKey passkey, scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, NGBoxFragmentBuilder* builder) : NGLayoutResult(std::move(physical_fragment), @@ -48,6 +49,10 @@ NGLayoutResult::NGLayoutResult( bitfields_.subtree_modified_margin_strut = builder->subtree_modified_margin_strut_; intrinsic_block_size_ = builder->intrinsic_block_size_; + // We don't support fragment caching when block-fragmenting, so mark the + // result as non-reusable. + if (builder->has_block_fragmentation_) + EnsureRareData()->is_single_use = true; if (builder->minimal_space_shortage_ != LayoutUnit::Max()) { #if DCHECK_IS_ON() DCHECK(!HasRareData() || !rare_data_->has_tallest_unbreakable_block_size); @@ -62,10 +67,9 @@ NGLayoutResult::NGLayoutResult( rare_data->has_tallest_unbreakable_block_size = true; #endif } - if (builder->unconstrained_intrinsic_block_size_ != kIndefiniteSize && - builder->unconstrained_intrinsic_block_size_ != intrinsic_block_size_) { - EnsureRareData()->unconstrained_intrinsic_block_size_ = - builder->unconstrained_intrinsic_block_size_; + if (builder->overflow_block_size_ != kIndefiniteSize && + builder->overflow_block_size_ != intrinsic_block_size_) { + EnsureRareData()->overflow_block_size_ = builder->overflow_block_size_; } if (builder->custom_layout_data_) { EnsureRareData()->custom_layout_data = @@ -73,6 +77,8 @@ NGLayoutResult::NGLayoutResult( } if (builder->column_spanner_) EnsureRareData()->column_spanner = builder->column_spanner_; + if (builder->lines_until_clamp_) + EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_; bitfields_.initial_break_before = static_cast<unsigned>(builder->initial_break_before_); bitfields_.final_break_after = @@ -81,15 +87,20 @@ NGLayoutResult::NGLayoutResult( } NGLayoutResult::NGLayoutResult( + NGLineBoxFragmentBuilderPassKey passkey, scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, NGLineBoxFragmentBuilder* builder) : NGLayoutResult(std::move(physical_fragment), static_cast<NGContainerFragmentBuilder*>(builder)) {} -NGLayoutResult::NGLayoutResult(EStatus status, NGBoxFragmentBuilder* builder) +NGLayoutResult::NGLayoutResult(NGBoxFragmentBuilderPassKey key, + EStatus status, + NGBoxFragmentBuilder* builder) : NGLayoutResult(/* physical_fragment */ nullptr, static_cast<NGContainerFragmentBuilder*>(builder)) { bitfields_.status = status; + if (builder->lines_until_clamp_) + EnsureRareData()->lines_until_clamp = *builder->lines_until_clamp_; DCHECK_NE(status, kSuccess) << "Use the other constructor for successful layout"; } @@ -152,7 +163,7 @@ NGLayoutResult::NGLayoutResult( #if DCHECK_IS_ON() if (bitfields_.is_self_collapsing && physical_fragment_) { // A new formatting-context shouldn't be self-collapsing. - DCHECK(!physical_fragment_->IsBlockFormattingContextRoot()); + DCHECK(!physical_fragment_->IsFormattingContextRoot()); // Self-collapsing children must have a block-size of zero. NGFragment fragment(physical_fragment_->Style().GetWritingMode(), diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index 77ff5c5b9b9..3faf3aa43aa 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h @@ -41,6 +41,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { kSuccess = 0, kBfcBlockOffsetResolved = 1, kNeedsEarlierBreak = 2, + kOutOfFragmentainerSpace = 3, + kNeedsRelayoutWithNoForcedTruncateAtLineClamp = 4, // When adding new values, make sure the bit size of |Bitfields::status| is // large enough to store. }; @@ -62,6 +64,10 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { return *physical_fragment_; } + int LinesUntilClamp() const { + return HasRareData() ? rare_data_->lines_until_clamp : 0; + } + LogicalOffset OutOfFlowPositionedOffset() const { DCHECK(bitfields_.has_oof_positioned_offset); return HasRareData() ? rare_data_->oof_positioned_offset @@ -140,16 +146,13 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { } const LayoutUnit IntrinsicBlockSize() const { - DCHECK(physical_fragment_->Type() == NGPhysicalFragment::kFragmentBox || - physical_fragment_->Type() == - NGPhysicalFragment::kFragmentRenderedLegend); + DCHECK(physical_fragment_->IsBox()); return intrinsic_block_size_; } - LayoutUnit UnconstrainedIntrinsicBlockSize() const { - return HasRareData() && rare_data_->unconstrained_intrinsic_block_size_ != - kIndefiniteSize - ? rare_data_->unconstrained_intrinsic_block_size_ + LayoutUnit OverflowBlockSize() const { + return HasRareData() && rare_data_->overflow_block_size_ != kIndefiniteSize + ? rare_data_->overflow_block_size_ : intrinsic_block_size_; } @@ -173,6 +176,14 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { return rare_data_->tallest_unbreakable_block_size; } + // Return whether this result is single-use only (true), or if it is allowed + // to be involved in cache hits in future layout passes (false). + // For example, this happens when a block is fragmented, since we don't yet + // support caching of block-fragmented results. + bool IsSingleUse() const { + return HasRareData() && rare_data_->is_single_use; + } + SerializedScriptValue* CustomLayoutData() const { return HasRareData() ? rare_data_->custom_layout_data.get() : nullptr; } @@ -285,21 +296,24 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { bool check_same_block_size = true) const; #endif - private: - friend class NGBoxFragmentBuilder; - friend class NGLineBoxFragmentBuilder; - friend class MutableForOutOfFlow; - + using NGBoxFragmentBuilderPassKey = util::PassKey<NGBoxFragmentBuilder>; + // This constructor is for a non-success status. + NGLayoutResult(NGBoxFragmentBuilderPassKey, EStatus, NGBoxFragmentBuilder*); // This constructor requires a non-null fragment and sets a success status. NGLayoutResult( + NGBoxFragmentBuilderPassKey, scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, NGBoxFragmentBuilder*); + using NGLineBoxFragmentBuilderPassKey = + util::PassKey<NGLineBoxFragmentBuilder>; // This constructor requires a non-null fragment and sets a success status. NGLayoutResult( + NGLineBoxFragmentBuilderPassKey, scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, NGLineBoxFragmentBuilder*); - // This constructor is for a non-success status. - NGLayoutResult(EStatus, NGBoxFragmentBuilder*); + + private: + friend class MutableForOutOfFlow; // We don't need the copy constructor, move constructor, copy // assigmnment-operator, or move assignment-operator today. @@ -358,10 +372,12 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { }; NGExclusionSpace exclusion_space; scoped_refptr<SerializedScriptValue> custom_layout_data; - LayoutUnit unconstrained_intrinsic_block_size_ = kIndefiniteSize; + LayoutUnit overflow_block_size_ = kIndefiniteSize; #if DCHECK_IS_ON() bool has_tallest_unbreakable_block_size = false; #endif + bool is_single_use = false; + int lines_until_clamp = 0; }; bool HasRareData() const { return bitfields_.has_rare_data; } @@ -421,7 +437,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> { unsigned initial_break_before : 4; // EBreakBetween unsigned final_break_after : 4; // EBreakBetween - unsigned status : 2; // EStatus + unsigned status : 3; // EStatus }; // The constraint space which generated this layout result, may not be valid diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc index 107e1cc3dc8..24df10ed942 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc @@ -19,8 +19,6 @@ namespace { class NGLayoutResultCachingTest : public NGLayoutTest {}; TEST_F(NGLayoutResultCachingTest, HitDifferentExclusionSpace) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space. SetBodyInnerHTML(R"HTML( <style> @@ -58,8 +56,6 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentExclusionSpace) { } TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffset) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space. SetBodyInnerHTML(R"HTML( <style> @@ -123,8 +119,6 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffset) { } TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffsetSameMarginStrut) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same margin-strut. SetBodyInnerHTML(R"HTML( <style> @@ -155,8 +149,6 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffsetSameMarginStrut) { } TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart1) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space, descendant above // block start. SetBodyInnerHTML(R"HTML( @@ -195,8 +187,6 @@ TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart1) { } TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart2) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, descendant above // block start. SetBodyInnerHTML(R"HTML( @@ -235,8 +225,6 @@ TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart2) { } TEST_F(NGLayoutResultCachingTest, HitOOFDescendantAboveBlockStart) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, OOF-descendant above // block start. SetBodyInnerHTML(R"HTML( @@ -275,8 +263,6 @@ TEST_F(NGLayoutResultCachingTest, HitOOFDescendantAboveBlockStart) { } TEST_F(NGLayoutResultCachingTest, HitLineBoxDescendantAboveBlockStart) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, line-box descendant above // block start. SetBodyInnerHTML(R"HTML( @@ -320,8 +306,6 @@ TEST_F(NGLayoutResultCachingTest, HitLineBoxDescendantAboveBlockStart) { } TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding1) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space, float initially // intruding. SetBodyInnerHTML(R"HTML( @@ -358,8 +342,6 @@ TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding1) { } TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding2) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, float initially // intruding. SetBodyInnerHTML(R"HTML( @@ -396,8 +378,6 @@ TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding2) { } TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude1) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space, float will intrude. SetBodyInnerHTML(R"HTML( <style> @@ -433,8 +413,6 @@ TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude1) { } TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude2) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, float will intrude. SetBodyInnerHTML(R"HTML( <style> @@ -470,8 +448,6 @@ TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude2) { } TEST_F(NGLayoutResultCachingTest, HitPushedByFloats1) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space, pushed by floats. SetBodyInnerHTML(R"HTML( <style> @@ -507,8 +483,6 @@ TEST_F(NGLayoutResultCachingTest, HitPushedByFloats1) { } TEST_F(NGLayoutResultCachingTest, HitPushedByFloats2) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, pushed by floats. SetBodyInnerHTML(R"HTML( <style> @@ -544,8 +518,6 @@ TEST_F(NGLayoutResultCachingTest, HitPushedByFloats2) { } TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same BFC offset, different exclusion space, pushed by floats. // Miss due to shrinking offset. SetBodyInnerHTML(R"HTML( @@ -582,8 +554,6 @@ TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) { } TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Different BFC offset, same exclusion space, pushed by floats. // Miss due to shrinking offset. SetBodyInnerHTML(R"HTML( @@ -620,8 +590,6 @@ TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) { } TEST_F(NGLayoutResultCachingTest, HitDifferentRareData) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Same absolute fixed constraints. SetBodyInnerHTML(R"HTML( <style> @@ -651,8 +619,6 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentRareData) { } TEST_F(NGLayoutResultCachingTest, HitPercentageMinWidth) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // min-width calculates to different values, but doesn't change size. SetBodyInnerHTML(R"HTML( <style> @@ -682,8 +648,6 @@ TEST_F(NGLayoutResultCachingTest, HitPercentageMinWidth) { } TEST_F(NGLayoutResultCachingTest, HitFixedMinWidth) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // min-width is always larger than the available size. SetBodyInnerHTML(R"HTML( <style> @@ -712,9 +676,151 @@ TEST_F(NGLayoutResultCachingTest, HitFixedMinWidth) { EXPECT_NE(result.get(), nullptr); } -TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); +TEST_F(NGLayoutResultCachingTest, HitShrinkToFit) { + SetBodyInnerHTML(R"HTML( + <div style="display: flow-root; width: 300px; height: 100px;"> + <div id="test1" style="float: left;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + <div id="test2" style="float: left;"> + <div style="display: inline-block; width: 350px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + </div> + <div style="display: flow-root; width: 400px; height: 100px;"> + <div id="src1" style="float: left;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + </div> + <div style="display: flow-root; width: 200px; height: 100px;"> + <div id="src2" style="float: left;"> + <div style="display: inline-block; width: 350px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + </div> + )HTML"); + + auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1")); + auto* test2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test2")); + auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1")); + auto* src2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src2")); + + NGLayoutCacheStatus cache_status; + base::Optional<NGFragmentGeometry> fragment_geometry; + NGConstraintSpace space = + src1->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult( + space, nullptr, nullptr, &fragment_geometry, &cache_status); + // test1 was sized to its max-content size, passing an available size larger + // than the fragment should hit the cache. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit); + EXPECT_NE(result.get(), nullptr); + + fragment_geometry.reset(); + space = src2->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + result = test2->CachedLayoutResult(space, nullptr, nullptr, + &fragment_geometry, &cache_status); + // test2 was sized to its min-content size in, passing an available size + // smaller than the fragment should hit the cache. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit); + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissShrinkToFit) { + SetBodyInnerHTML(R"HTML( + <div style="display: flow-root; width: 300px; height: 100px;"> + <div id="test1" style="float: left;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + <div id="test2" style="float: left;"> + <div style="display: inline-block; width: 350px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + <div id="test3" style="float: left; min-width: 80%;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + <div id="test4" style="float: left; margin-left: 75px;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + </div> + <div style="display: flow-root; width: 100px; height: 100px;"> + <div id="src1" style="float: left;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + </div> + <div style="display: flow-root; width: 400px; height: 100px;"> + <div id="src2" style="float: left;"> + <div style="display: inline-block; width: 350px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + <div id="src3" style="float: left; min-width: 80%;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 250px;"></div> + </div> + </div> + <div style="display: flow-root; width: 250px; height: 100px;"> + <div id="src4" style="float: left; margin-left: 75px;"> + <div style="display: inline-block; width: 150px;"></div> + <div style="display: inline-block; width: 50px;"></div> + </div> + </div> + )HTML"); + + auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1")); + auto* test2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test2")); + auto* test3 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test3")); + auto* test4 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test4")); + auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1")); + auto* src2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src2")); + auto* src3 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src3")); + auto* src4 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src4")); + + NGLayoutCacheStatus cache_status; + base::Optional<NGFragmentGeometry> fragment_geometry; + NGConstraintSpace space = + src1->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult( + space, nullptr, nullptr, &fragment_geometry, &cache_status); + // test1 was sized to its max-content size, passing an available size smaller + // than the fragment should miss the cache. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); + EXPECT_EQ(result.get(), nullptr); + + fragment_geometry.reset(); + space = src2->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + result = test2->CachedLayoutResult(space, nullptr, nullptr, + &fragment_geometry, &cache_status); + // test2 was sized to its min-content size, passing an available size + // larger than the fragment should miss the cache. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); + EXPECT_EQ(result.get(), nullptr); + + fragment_geometry.reset(); + space = src3->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + result = test3->CachedLayoutResult(space, nullptr, nullptr, + &fragment_geometry, &cache_status); + // test3 was sized to its min-content size, however it should miss the cache + // as it has a %-min-size. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); + EXPECT_EQ(result.get(), nullptr); + + fragment_geometry.reset(); + space = src4->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + result = test4->CachedLayoutResult(space, nullptr, nullptr, + &fragment_geometry, &cache_status); + // test4 was sized to its max-content size, however it should miss the cache + // due to its margin. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); + EXPECT_EQ(result.get(), nullptr); +} +TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) { // We have a shrink-to-fit node, with the min, and max intrinsic sizes being // equal (the available size doesn't affect the final size). SetBodyInnerHTML(R"HTML( @@ -750,8 +856,6 @@ TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) { } TEST_F(NGLayoutResultCachingTest, HitShrinkToFitDifferentParent) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // The parent "bfc" node changes from shrink-to-fit, to a fixed width. But // these calculate as the same available space to the "test" element. SetBodyInnerHTML(R"HTML( @@ -786,8 +890,6 @@ TEST_F(NGLayoutResultCachingTest, HitShrinkToFitDifferentParent) { } TEST_F(NGLayoutResultCachingTest, MissQuirksModePercentageBasedChild) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Quirks-mode %-block-size child. GetDocument().SetCompatibilityMode(Document::kQuirksMode); SetBodyInnerHTML(R"HTML( @@ -822,8 +924,6 @@ TEST_F(NGLayoutResultCachingTest, MissQuirksModePercentageBasedChild) { } TEST_F(NGLayoutResultCachingTest, HitQuirksModePercentageBasedParentAndChild) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Quirks-mode %-block-size parent *and* child. Here we mark the parent as // depending on %-block-size changes, however itself doesn't change in // height. @@ -863,8 +963,6 @@ TEST_F(NGLayoutResultCachingTest, HitQuirksModePercentageBasedParentAndChild) { } TEST_F(NGLayoutResultCachingTest, HitStandardsModePercentageBasedChild) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - // Standards-mode %-block-size child. SetBodyInnerHTML(R"HTML( <style> @@ -898,8 +996,6 @@ TEST_F(NGLayoutResultCachingTest, HitStandardsModePercentageBasedChild) { } TEST_F(NGLayoutResultCachingTest, ChangeTableCellBlockSizeConstrainedness) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .table { display: table; width: 300px; } @@ -964,12 +1060,9 @@ TEST_F(NGLayoutResultCachingTest, ChangeTableCellBlockSizeConstrainedness) { // height or not. We're only going to need simplified layout, though, since no // children will be affected by its height change. EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsSimplifiedLayout); - EXPECT_EQ(result.get(), nullptr); } TEST_F(NGLayoutResultCachingTest, OptimisticFloatPlacementNoRelayout) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .root { display: flow-root; width: 300px; } @@ -994,8 +1087,6 @@ TEST_F(NGLayoutResultCachingTest, OptimisticFloatPlacementNoRelayout) { } TEST_F(NGLayoutResultCachingTest, SelfCollapsingShifting) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .bfc { display: flow-root; width: 300px; height: 300px; } @@ -1080,8 +1171,6 @@ TEST_F(NGLayoutResultCachingTest, SelfCollapsingShifting) { } TEST_F(NGLayoutResultCachingTest, ClearancePastAdjoiningFloatsMovement) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .bfc { display: flow-root; width: 300px; height: 300px; } @@ -1146,8 +1235,6 @@ TEST_F(NGLayoutResultCachingTest, ClearancePastAdjoiningFloatsMovement) { } TEST_F(NGLayoutResultCachingTest, MarginStrutMovementSelfCollapsing) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .bfc { display: flow-root; width: 300px; height: 300px; } @@ -1217,8 +1304,6 @@ TEST_F(NGLayoutResultCachingTest, MarginStrutMovementSelfCollapsing) { } TEST_F(NGLayoutResultCachingTest, MarginStrutMovementInFlow) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .bfc { display: flow-root; width: 300px; height: 300px; } @@ -1315,8 +1400,6 @@ TEST_F(NGLayoutResultCachingTest, MarginStrutMovementInFlow) { } TEST_F(NGLayoutResultCachingTest, MarginStrutMovementPercentage) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - SetBodyInnerHTML(R"HTML( <style> .bfc { display: flow-root; width: 300px; height: 300px; } @@ -1354,53 +1437,55 @@ TEST_F(NGLayoutResultCachingTest, MarginStrutMovementPercentage) { EXPECT_EQ(result.get(), nullptr); } -TEST_F(NGLayoutResultCachingTest, MarginStrutMovementDiscard) { - ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true); - +TEST_F(NGLayoutResultCachingTest, HitIsFixedBlockSizeIndefinite) { SetBodyInnerHTML(R"HTML( - <style> - .bfc { display: flow-root; width: 300px; height: 300px; } - </style> - <div class="bfc"> - <div style="margin-top: 10px;"> - <div id="test1"> - <div style="-webkit-margin-top-collapse: discard;">text</div> - </div> + <div style="display: flex; width: 100px; height: 100px;"> + <div id="test1" style="flex-grow: 1; min-height: 100px;"> + <div style="height: 50px;">text</div> </div> </div> - <div class="bfc"> - <div style="margin-top: 5px;"> - <div id="src1"> - <div style="-webkit-margin-top-collapse: discard;">text</div> - </div> + <div style="display: flex; width: 100px; height: 100px; align-items: stretch;"> + <div id="src1" style="flex-grow: 1; min-height: 100px;"> + <div style="height: 50px;">text</div> </div> </div> - <div class="bfc"> - <div style="margin-top: 10px;"> - <div id="test2"> - <div> - <div style="-webkit-margin-bottom-collapse: discard;"></div> - </div> - <div>text</div> - </div> + )HTML"); + + auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1")); + auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1")); + + NGLayoutCacheStatus cache_status; + base::Optional<NGFragmentGeometry> fragment_geometry; + + NGConstraintSpace space = + src1->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult( + space, nullptr, nullptr, &fragment_geometry, &cache_status); + + // Even though the "align-items: stretch" will make the final fixed + // block-size indefinite, we don't have any %-block-size children, so we can + // hit the cache. + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit); + EXPECT_NE(result.get(), nullptr); +} + +TEST_F(NGLayoutResultCachingTest, MissIsFixedBlockSizeIndefinite) { + SetBodyInnerHTML(R"HTML( + <!DOCTYPE html> + <div style="display: flex; width: 100px; height: 100px; align-items: start;"> + <div id="src1" style="flex-grow: 1; min-height: 100px;"> + <div style="height: 50%;">text</div> </div> </div> - <div class="bfc"> - <div style="margin-top: 5px;"> - <div id="src2"> - <div> - <div style="-webkit-margin-bottom-collapse: discard;"></div> - </div> - <div>text</div> - </div> + <div style="display: flex; width: 100px; height: 100px; align-items: stretch;"> + <div id="test1" style="flex-grow: 1; min-height: 100px;"> + <div style="height: 50%;">text</div> </div> </div> )HTML"); auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1")); - auto* test2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test2")); auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1")); - auto* src2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src2")); NGLayoutCacheStatus cache_status; base::Optional<NGFragmentGeometry> fragment_geometry; @@ -1410,18 +1495,62 @@ TEST_F(NGLayoutResultCachingTest, MarginStrutMovementDiscard) { scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult( space, nullptr, nullptr, &fragment_geometry, &cache_status); - // Case 1: We can't re-use this fragment as the sub-tree discards margins. + // The "align-items: stretch" will make the final fixed block-size + // indefinite, and we have a %-block-size child, so we need to miss the + // cache. EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); EXPECT_EQ(result.get(), nullptr); +} - fragment_geometry.reset(); +TEST_F(NGLayoutResultCachingTest, HitFlexBoxMeasureAndLayout) { + ScopedLayoutNGFlexBoxForTest layout_ng_flex_box(true); + + SetBodyInnerHTML(R"HTML( + <!DOCTYPE html> + <div style="display: flex; flex-direction: column; width: 100px; height: 100px;"> + <div id="src1" style="flex-grow: 0;"> + <div style="height: 50px;"></div> + </div> + </div> + <div style="display: flex; flex-direction: column; width: 100px; height: 100px;"> + <div id="src2" style="flex-grow: 1;"> + <div style="height: 50px;"></div> + </div> + </div> + <div style="display: flex; flex-direction: column; width: 100px; height: 100px;"> + <div id="test1" style="flex-grow: 2;"> + <div style="height: 50px;"></div> + </div> + </div> + )HTML"); + + auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1")); + auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1")); + auto* src2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src2")); + + NGLayoutCacheStatus cache_status; + base::Optional<NGFragmentGeometry> fragment_geometry; + + // "src1" only had one "measure" pass performed, and should hit the "measure" + // cache-slot for "test1". + NGConstraintSpace space = + src1->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult( + space, nullptr, nullptr, &fragment_geometry, &cache_status); + + EXPECT_EQ(space.CacheSlot(), NGCacheSlot::kMeasure); + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit); + EXPECT_NE(result.get(), nullptr); + + // "src2" had both a "measure" and "layout" pass performed, and should hit + // the "layout" cache-slot for "test1". space = src2->GetCachedLayoutResult()->GetConstraintSpaceForCaching(); - result = test2->CachedLayoutResult(space, nullptr, nullptr, + result = test1->CachedLayoutResult(space, nullptr, nullptr, &fragment_geometry, &cache_status); - // Case 2: Also check a self-collapsing block with a block-end discard. - EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout); - EXPECT_EQ(result.get(), nullptr); + EXPECT_EQ(space.CacheSlot(), NGCacheSlot::kLayout); + EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit); + EXPECT_NE(result.get(), nullptr); } } // namespace diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc index 229af34617d..3802930ac23 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc @@ -84,6 +84,8 @@ bool SizeMayChange(const NGBlockNode& node, DCHECK_EQ(new_space.IsFixedInlineSize(), old_space.IsFixedInlineSize()); DCHECK_EQ(new_space.IsFixedBlockSize(), old_space.IsFixedBlockSize()); + DCHECK_EQ(new_space.IsFixedBlockSizeIndefinite(), + old_space.IsFixedBlockSizeIndefinite()); DCHECK_EQ(new_space.IsShrinkToFit(), old_space.IsShrinkToFit()); DCHECK_EQ(new_space.TableCellChildLayoutMode(), old_space.TableCellChildLayoutMode()); @@ -180,17 +182,48 @@ NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatusWithGeometry( LayoutUnit block_size = fragment_geometry.border_box_size.block_size; bool is_initial_block_size_indefinite = block_size == kIndefiniteSize; if (is_initial_block_size_indefinite) { - // The intrinsic size of column flex-boxes can depend on the - // %-resolution-block-size. This occurs when a flex-box has "max-height: - // 100%" or similar on itself. - // - // Due to this we can't use cached |NGLayoutResult::IntrinsicBlockSize| - // value, as the following |block_size| calculation would be incorrect. - if (node.IsFlexibleBox() && style.ResolvedIsColumnFlexDirection() && - layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) { - if (new_space.PercentageResolutionBlockSize() != - old_space.PercentageResolutionBlockSize()) + if (node.IsFlexibleBox()) { + // Flex-boxes can have their children calculate their size based in their + // parent's final block-size. E.g. + // <div style="display: flex;"> + // <div style="display: flex;"> + // <!-- Child will stretch to the parent's fixed block-size --> + // <div></div> + // </div> + // </div> + // <div style="display: flex;"> + // <div style="display: flex; flex-direction: column;"> + // <!-- Child will grow to the parent's fixed block-size --> + // <div style="flex: 1;"></div> + // </div> + // </div> + // + // If the previous |layout_result| was produced by a space which had a + // fixed block-size we can't use |NGLayoutResult::IntrinsicBlockSize()|, + // and need to layout. + // + // TODO(ikilpatrick): Similar to %-block-size descendants we could store + // a bit on the |NGLayoutResult| which indicates if it had a child which + // sized itself based on the parent's block-size. + // We should consider this optimization if we are missing this cache + // often within this branch (and could have re-used the result). + // TODO(ikilaptrick): This may occur for other layout modes, e.g. + // grid/custom-layout/etc. + if (old_space.IsFixedBlockSize()) return NGLayoutCacheStatus::kNeedsLayout; + + // The intrinsic size of column flex-boxes can depend on the + // %-resolution-block-size. This occurs when a flex-box has "max-height: + // 100%" or similar on itself. + // + // Due to this we can't use cached |NGLayoutResult::IntrinsicBlockSize| + // value, as the following |block_size| calculation would be incorrect. + if (style.ResolvedIsColumnFlexDirection() && + layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) { + if (new_space.PercentageResolutionBlockSize() != + old_space.PercentageResolutionBlockSize()) + return NGLayoutCacheStatus::kNeedsLayout; + } } block_size = ComputeBlockSizeForFragment( @@ -209,7 +242,7 @@ NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatusWithGeometry( // If a block (within a formatting-context) changes to/from an empty-block, // margins may collapse through this node, requiring full layout. We // approximate this check by checking if the block-size is/was zero. - if (!physical_fragment.IsBlockFormattingContextRoot() && + if (!physical_fragment.IsFormattingContextRoot() && !block_size != !fragment.BlockSize()) return NGLayoutCacheStatus::kNeedsLayout; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc index 53a8071828b..e07c07e0d08 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc @@ -90,7 +90,7 @@ bool BlockLengthUnresolvable( LengthResolvePhase phase, const LayoutUnit* opt_percentage_resolution_block_size_for_min_max) { if (length.IsAuto() || length.IsMinContent() || length.IsMaxContent() || - length.IsFitContent() || length.IsMaxSizeNone()) + length.IsFitContent() || length.IsNone()) return true; if (length.IsPercentOrCalc()) { if (phase == LengthResolvePhase::kIntrinsic) @@ -115,7 +115,7 @@ LayoutUnit ResolveInlineLengthInternal( const NGConstraintSpace& constraint_space, const ComputedStyle& style, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>& min_and_max, + const base::Optional<MinMaxSizes>& min_max_sizes, const Length& length) { DCHECK_GE(constraint_space.AvailableSize().inline_size, LayoutUnit()); DCHECK_GE(constraint_space.PercentageResolutionInlineSize(), LayoutUnit()); @@ -146,20 +146,20 @@ LayoutUnit ResolveInlineLengthInternal( case Length::kMinContent: case Length::kMaxContent: case Length::kFitContent: { - DCHECK(min_and_max.has_value()); + DCHECK(min_max_sizes.has_value()); LayoutUnit available_size = constraint_space.AvailableSize().inline_size; LayoutUnit value; if (length.IsMinContent()) { - value = min_and_max->min_size; + value = min_max_sizes->min_size; } else if (length.IsMaxContent() || available_size == LayoutUnit::Max()) { // If the available space is infinite, fit-content resolves to // max-content. See css-sizing section 2.1. - value = min_and_max->max_size; + value = min_max_sizes->max_size; } else { NGBoxStrut margins = ComputeMarginsForSelf(constraint_space, style); LayoutUnit fill_available = std::max(LayoutUnit(), available_size - margins.InlineSum()); - value = min_and_max->ShrinkToFit(fill_available); + value = min_max_sizes->ShrinkToFit(fill_available); } return value; } @@ -168,7 +168,7 @@ LayoutUnit ResolveInlineLengthInternal( case Length::kExtendToZoom: NOTREACHED() << "These should only be used for viewport definitions"; FALLTHROUGH; - case Length::kMaxSizeNone: + case Length::kNone: default: NOTREACHED(); return border_padding.InlineSum(); @@ -235,18 +235,18 @@ LayoutUnit ResolveBlockLengthInternal( case Length::kExtendToZoom: NOTREACHED() << "These should only be used for viewport definitions"; FALLTHROUGH; - case Length::kMaxSizeNone: + case Length::kNone: default: NOTREACHED(); return border_padding.BlockSum(); } } -MinMaxSize ComputeMinAndMaxContentContribution( +MinMaxSizes ComputeMinAndMaxContentContribution( WritingMode parent_writing_mode, const ComputedStyle& style, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>& min_and_max) { + const base::Optional<MinMaxSizes>& min_max_sizes) { WritingMode child_writing_mode = style.GetWritingMode(); // Synthesize a zero-sized constraint space for resolving sizes against. @@ -256,20 +256,20 @@ MinMaxSize ComputeMinAndMaxContentContribution( .ToConstraintSpace(); LayoutUnit content_size = - min_and_max ? min_and_max->max_size : kIndefiniteSize; + min_max_sizes ? min_max_sizes->max_size : kIndefiniteSize; - MinMaxSize computed_sizes; + MinMaxSizes computed_sizes; const Length& inline_size = parent_writing_mode == WritingMode::kHorizontalTb ? style.Width() : style.Height(); if (inline_size.IsAuto() || inline_size.IsPercentOrCalc() || inline_size.IsFillAvailable() || inline_size.IsFitContent()) { - CHECK(min_and_max.has_value()); - computed_sizes = *min_and_max; + CHECK(min_max_sizes.has_value()); + computed_sizes = *min_max_sizes; } else { if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) { computed_sizes = ResolveMainInlineLength(space, style, border_padding, - min_and_max, inline_size); + min_max_sizes, inline_size); } else { computed_sizes = ResolveMainBlockLength(space, style, border_padding, inline_size, @@ -282,11 +282,11 @@ MinMaxSize ComputeMinAndMaxContentContribution( : style.MaxHeight(); LayoutUnit max; if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) { - max = ResolveMaxInlineLength(space, style, border_padding, min_and_max, + max = ResolveMaxInlineLength(space, style, border_padding, min_max_sizes, max_length, LengthResolvePhase::kIntrinsic); } else { max = ResolveMaxBlockLength(space, style, border_padding, max_length, - content_size, LengthResolvePhase::kIntrinsic); + LengthResolvePhase::kIntrinsic); } computed_sizes.Constrain(max); @@ -295,59 +295,51 @@ MinMaxSize ComputeMinAndMaxContentContribution( : style.MinHeight(); LayoutUnit min; if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) { - min = ResolveMinInlineLength(space, style, border_padding, min_and_max, + min = ResolveMinInlineLength(space, style, border_padding, min_max_sizes, min_length, LengthResolvePhase::kIntrinsic); } else { min = ResolveMinBlockLength(space, style, border_padding, min_length, - content_size, LengthResolvePhase::kIntrinsic); + LengthResolvePhase::kIntrinsic); } computed_sizes.Encompass(min); return computed_sizes; } -MinMaxSize ComputeMinAndMaxContentContribution( +MinMaxSizes ComputeMinAndMaxContentContribution( const ComputedStyle& parent_style, NGLayoutInputNode child, - const MinMaxSizeInput& input) { + const MinMaxSizesInput& input) { const ComputedStyle& child_style = child.Style(); WritingMode parent_writing_mode = parent_style.GetWritingMode(); WritingMode child_writing_mode = child_style.GetWritingMode(); - LayoutBox* box = child.GetLayoutBox(); - - if (box->NeedsPreferredWidthsRecalculation()) { - // Some objects (when there's an intrinsic ratio) have their min/max inline - // size affected by the block size of their container. We don't really know - // whether the containing block of this child did change or is going to - // change size. However, this is our only opportunity to make sure that it - // gets its min/max widths calculated. - box->SetPreferredLogicalWidthsDirty(); - } if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) { - if (!box->PreferredLogicalWidthsDirty()) { - return {box->MinPreferredLogicalWidth(), box->MaxPreferredLogicalWidth()}; - } // Tables are special; even if a width is specified, they may end up being // sized different. So we just always let the table code handle this. + if (child.IsTable()) + return child.ComputeMinMaxSizes(parent_writing_mode, input, nullptr); + // Replaced elements may size themselves using aspect ratios and block // sizes, so we pass that on as well. - if (box->IsTable() || box->IsTablePart() || box->IsLayoutReplaced()) { + if (child.IsReplaced()) { + LayoutBox* box = child.GetLayoutBox(); bool needs_size_reset = false; if (!box->HasOverrideContainingBlockContentLogicalHeight()) { box->SetOverrideContainingBlockContentLogicalHeight( input.percentage_resolution_block_size); needs_size_reset = true; } - MinMaxSize result{box->MinPreferredLogicalWidth(), - box->MaxPreferredLogicalWidth()}; + + MinMaxSizes result = box->PreferredLogicalWidths(); + if (needs_size_reset) box->ClearOverrideContainingBlockContentSize(); return result; } } - base::Optional<MinMaxSize> minmax; + base::Optional<MinMaxSizes> min_max_sizes; if (NeedMinMaxSizeForContentContribution(parent_writing_mode, child_style)) { // We need to set up a constraint space with correct fallback available // inline size in case of orthogonal children. @@ -358,8 +350,8 @@ MinMaxSize ComputeMinAndMaxContentContribution( CreateIndefiniteConstraintSpaceForChild(parent_style, child); child_constraint_space = &indefinite_constraint_space; } - minmax = child.ComputeMinMaxSize(parent_writing_mode, input, - child_constraint_space); + min_max_sizes = child.ComputeMinMaxSizes(parent_writing_mode, input, + child_constraint_space); } // Synthesize a zero-sized constraint space for determining the borders, and // padding. @@ -368,50 +360,17 @@ MinMaxSize ComputeMinAndMaxContentContribution( /* is_new_fc */ false) .ToConstraintSpace(); NGBoxStrut border_padding = - ComputeBorders(space, child) + ComputePadding(space, child_style); - - MinMaxSize sizes = ComputeMinAndMaxContentContribution( - parent_writing_mode, child_style, border_padding, minmax); - if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) - box->SetPreferredLogicalWidthsFromNG(sizes); - return sizes; -} - -MinMaxSize ComputeMinAndMaxContentSizeForOutOfFlow( - const NGConstraintSpace& constraint_space, - NGLayoutInputNode node, - const NGBoxStrut& border_padding, - const MinMaxSizeInput& input) { - LayoutBox* box = node.GetLayoutBox(); - // If we've already populated the legacy preferred logical widths cache for - // this node at the bottom of this function, use those cached results here. - // Or, if we're working on a table, use the legacy preferred widths code - // instead of ComputeMinAndMaxContentContribution below because - // ComputeMinAndMaxContentContribution assumes that if an element has a - // specified size, that's its final size, which tables don't follow. - if ((!box->PreferredLogicalWidthsDirty() && - !box->NeedsPreferredWidthsRecalculation()) || - box->IsTable()) { - return MinMaxSize{box->MinPreferredLogicalWidth(), - box->MaxPreferredLogicalWidth()}; - } + ComputeBorders(space, child_style) + ComputePadding(space, child_style); - // Compute the intrinsic sizes without regard to the specified sizes. - MinMaxSize result = node.ComputeMinMaxSize(node.Style().GetWritingMode(), - input, &constraint_space); - // Apply the specified min, main, max sizes. - MinMaxSize contribution = ComputeMinAndMaxContentContribution( - node.Style().GetWritingMode(), node.Style(), border_padding, result); - // Cache these computed values. - box->SetPreferredLogicalWidthsFromNG(contribution); - return result; + return ComputeMinAndMaxContentContribution(parent_writing_mode, child_style, + border_padding, min_max_sizes); } LayoutUnit ComputeInlineSizeForFragment( const NGConstraintSpace& space, NGLayoutInputNode node, const NGBoxStrut& border_padding, - const MinMaxSize* override_minmax_for_test) { + const MinMaxSizes* override_min_max_sizes_for_test) { if (space.IsFixedInlineSize() || space.IsAnonymous()) return space.AvailableSize().inline_size; @@ -420,57 +379,24 @@ LayoutUnit ComputeInlineSizeForFragment( if (logical_width.IsAuto() && space.IsShrinkToFit()) logical_width = Length::FitContent(); - LayoutBox* box = node.GetLayoutBox(); - // If we have usable cached min/max intrinsic sizes, use those if we can. They - // will normally also be constrained to {min,max}-inline-size, but not if - // percentages are involved. In such cases we'll have to calculate and apply - // the constraints on our own. We also need to discard the cached values if - // the box has certain properties (e.g. percentage padding) that cause the - // cached values to be affected by extrinsic sizing. - if (!box->PreferredLogicalWidthsDirty() && !override_minmax_for_test && - !style.LogicalMinWidth().IsPercentOrCalc() && - !style.LogicalMaxWidth().IsPercentOrCalc() && - !box->NeedsPreferredWidthsRecalculation()) { - if (logical_width.IsFitContent()) { - // This is not as easy as {min, max}.ShrinkToFit() because we also need - // to subtract inline margins from the available size. The code in - // ResolveMainInlineLength knows how to handle that, just call that. - - MinMaxSize min_and_max = {box->MinPreferredLogicalWidth(), - box->MaxPreferredLogicalWidth()}; - return ResolveMainInlineLength(space, style, border_padding, min_and_max, - logical_width); - } - if (logical_width.IsMinContent()) - return box->MinPreferredLogicalWidth(); - if (logical_width.IsMaxContent()) - return box->MaxPreferredLogicalWidth(); - } + auto MinMaxSizesFunc = [&]() -> MinMaxSizes { + if (override_min_max_sizes_for_test) + return *override_min_max_sizes_for_test; - base::Optional<MinMaxSize> min_and_max; - if (NeedMinMaxSize(space, style)) { - if (override_minmax_for_test) { - min_and_max = *override_minmax_for_test; - } else { - min_and_max = node.ComputeMinMaxSize( - space.GetWritingMode(), - MinMaxSizeInput(space.PercentageResolutionBlockSize()), &space); - // Cache these computed values - MinMaxSize contribution = ComputeMinAndMaxContentContribution( - style.GetWritingMode(), style, border_padding, min_and_max); - box->SetPreferredLogicalWidthsFromNG(contribution); - } - } + return node.ComputeMinMaxSizes( + space.GetWritingMode(), + MinMaxSizesInput(space.PercentageResolutionBlockSize()), &space); + }; LayoutUnit extent = ResolveMainInlineLength(space, style, border_padding, - min_and_max, logical_width); - - LayoutUnit max = ResolveMaxInlineLength(space, style, border_padding, - min_and_max, style.LogicalMaxWidth(), - LengthResolvePhase::kLayout); - LayoutUnit min = ResolveMinInlineLength(space, style, border_padding, - min_and_max, style.LogicalMinWidth(), - LengthResolvePhase::kLayout); + MinMaxSizesFunc, logical_width); + + LayoutUnit max = ResolveMaxInlineLength( + space, style, border_padding, MinMaxSizesFunc, style.LogicalMaxWidth(), + LengthResolvePhase::kLayout); + LayoutUnit min = ResolveMinInlineLength( + space, style, border_padding, MinMaxSizesFunc, style.LogicalMinWidth(), + LengthResolvePhase::kLayout); return ConstrainByMinMax(extent, min, max); } @@ -486,7 +412,7 @@ LayoutUnit ComputeBlockSizeForFragmentInternal( nullptr) { LayoutUnit min = ResolveMinBlockLength( constraint_space, style, border_padding, style.LogicalMinHeight(), - content_size, LengthResolvePhase::kLayout, + LengthResolvePhase::kLayout, opt_percentage_resolution_block_size_for_min_max); const Length& logical_height = style.LogicalHeight(); // Scrollable percentage-sized children of table cells, in the table @@ -518,7 +444,7 @@ LayoutUnit ComputeBlockSizeForFragmentInternal( LayoutUnit max = ResolveMaxBlockLength( constraint_space, style, border_padding, style.LogicalMaxHeight(), - content_size, LengthResolvePhase::kLayout, + LengthResolvePhase::kLayout, opt_percentage_resolution_block_size_for_min_max); return ConstrainByMinMax(extent, min, max); @@ -546,9 +472,9 @@ LayoutUnit ComputeBlockSizeForFragment( } // Computes size for a replaced element. -void ComputeReplacedSize(const NGLayoutInputNode& node, +void ComputeReplacedSize(const NGBlockNode& node, const NGConstraintSpace& space, - const base::Optional<MinMaxSize>& child_minmax, + const base::Optional<MinMaxSizes>& child_min_max_sizes, base::Optional<LogicalSize>* out_replaced_size, base::Optional<LogicalSize>* out_aspect_ratio) { DCHECK(node.IsReplaced()); @@ -558,26 +484,26 @@ void ComputeReplacedSize(const NGLayoutInputNode& node, const ComputedStyle& style = node.Style(); NGBoxStrut border_padding = - ComputeBorders(space, node) + ComputePadding(space, style); + ComputeBorders(space, style) + ComputePadding(space, style); LayoutUnit inline_min = ResolveMinInlineLength( - space, style, border_padding, child_minmax, style.LogicalMinWidth(), - LengthResolvePhase::kLayout); + space, style, border_padding, child_min_max_sizes, + style.LogicalMinWidth(), LengthResolvePhase::kLayout); LayoutUnit inline_max = ResolveMaxInlineLength( - space, style, border_padding, child_minmax, style.LogicalMaxWidth(), - LengthResolvePhase::kLayout); - LayoutUnit block_min = ResolveMinBlockLength( - space, style, border_padding, style.LogicalMinHeight(), - border_padding.BlockSum(), LengthResolvePhase::kLayout); - LayoutUnit block_max = ResolveMaxBlockLength( - space, style, border_padding, style.LogicalMaxHeight(), LayoutUnit::Max(), - LengthResolvePhase::kLayout); + space, style, border_padding, child_min_max_sizes, + style.LogicalMaxWidth(), LengthResolvePhase::kLayout); + LayoutUnit block_min = ResolveMinBlockLength(space, style, border_padding, + style.LogicalMinHeight(), + LengthResolvePhase::kLayout); + LayoutUnit block_max = ResolveMaxBlockLength(space, style, border_padding, + style.LogicalMaxHeight(), + LengthResolvePhase::kLayout); const Length& inline_length = style.LogicalWidth(); const Length& block_length = style.LogicalHeight(); base::Optional<LayoutUnit> replaced_inline; if (!inline_length.IsAuto()) { - replaced_inline = ResolveMainInlineLength(space, style, border_padding, - child_minmax, inline_length); + replaced_inline = ResolveMainInlineLength( + space, style, border_padding, child_min_max_sizes, inline_length); replaced_inline = ConstrainByMinMax(*replaced_inline, inline_min, inline_max); } @@ -595,9 +521,10 @@ void ComputeReplacedSize(const NGLayoutInputNode& node, base::Optional<LayoutUnit> intrinsic_inline; base::Optional<LayoutUnit> intrinsic_block; - LogicalSize aspect_ratio; + node.IntrinsicSize(&intrinsic_inline, &intrinsic_block); + + LogicalSize aspect_ratio = node.GetAspectRatio(); - node.IntrinsicSize(&intrinsic_inline, &intrinsic_block, &aspect_ratio); // Computing intrinsic size is complicated by the fact that // intrinsic_inline, intrinsic_block, and aspect_ratio can all // be empty independent of each other. @@ -850,7 +777,7 @@ NGBoxStrut ComputeBordersInternal(const ComputedStyle& style) { } // namespace NGBoxStrut ComputeBorders(const NGConstraintSpace& constraint_space, - const NGLayoutInputNode node) { + const ComputedStyle& style) { // If we are producing an anonymous fragment (e.g. a column), it has no // borders, padding or scrollbars. Using the ones from the container can only // cause trouble. @@ -862,7 +789,7 @@ NGBoxStrut ComputeBorders(const NGConstraintSpace& constraint_space, if (constraint_space.IsTableCell()) return constraint_space.TableCellBorders(); - return ComputeBordersInternal(node.Style()); + return ComputeBordersInternal(style); } NGBoxStrut ComputeBordersForInline(const ComputedStyle& style) { @@ -1058,7 +985,7 @@ NGFragmentGeometry CalculateInitialFragmentGeometry( const NGBlockNode& node) { const ComputedStyle& style = node.Style(); - NGBoxStrut border = ComputeBorders(constraint_space, node); + NGBoxStrut border = ComputeBorders(constraint_space, style); NGBoxStrut padding = ComputePadding(constraint_space, style); NGBoxStrut scrollbar = ComputeScrollbars(constraint_space, node); NGBoxStrut border_padding = border + padding; @@ -1096,8 +1023,9 @@ NGFragmentGeometry CalculateInitialFragmentGeometry( NGFragmentGeometry CalculateInitialMinMaxFragmentGeometry( const NGConstraintSpace& constraint_space, const NGBlockNode& node) { - NGBoxStrut border = ComputeBorders(constraint_space, node); - NGBoxStrut padding = ComputePadding(constraint_space, node.Style()); + const ComputedStyle& style = node.Style(); + NGBoxStrut border = ComputeBorders(constraint_space, style); + NGBoxStrut padding = ComputePadding(constraint_space, style); NGBoxStrut scrollbar = ComputeScrollbars(constraint_space, node); return {/* border_box_size */ LogicalSize(), border, scrollbar, padding}; @@ -1210,7 +1138,10 @@ LayoutUnit CalculateChildPercentageBlockSizeForMinMax( const NGBoxStrut& border_padding, LayoutUnit parent_percentage_block_size) { // Anonymous block or spaces should pass the percent size straight through. - if (space.IsAnonymous() || node.IsAnonymousBlock()) + // If this node is OOF-positioned, our size was pre-calculated and we should + // pass this through to our children. + if (space.IsAnonymous() || node.IsAnonymousBlock() || + node.IsOutOfFlowPositioned()) return parent_percentage_block_size; LayoutUnit block_size = ComputeBlockSizeForFragmentInternal( @@ -1225,8 +1156,7 @@ LayoutUnit CalculateChildPercentageBlockSizeForMinMax( // For OOF-positioned nodes, use the parent (containing-block) size. if (child_percentage_block_size == kIndefiniteSize && - (node.UseParentPercentageResolutionBlockSizeForChildren() || - node.IsOutOfFlowPositioned())) + node.UseParentPercentageResolutionBlockSizeForChildren()) child_percentage_block_size = parent_percentage_block_size; return child_percentage_block_size; @@ -1255,8 +1185,13 @@ LayoutUnit ClampIntrinsicBlockSize( // If the intrinsic size was overridden, then use that. LayoutUnit intrinsic_size_override = node.OverrideIntrinsicContentBlockSize(); - if (intrinsic_size_override != kIndefiniteSize) + if (intrinsic_size_override != kIndefiniteSize) { return intrinsic_size_override + border_scrollbar_padding.BlockSum(); + } else { + LayoutUnit default_intrinsic_size = node.DefaultIntrinsicContentBlockSize(); + if (default_intrinsic_size != kIndefiniteSize) + return default_intrinsic_size + border_scrollbar_padding.BlockSum(); + } // If we have size containment, we ignore child contributions to intrinsic // sizing. @@ -1265,13 +1200,11 @@ LayoutUnit ClampIntrinsicBlockSize( return current_intrinsic_block_size; } -base::Optional<MinMaxSize> CalculateMinMaxSizesIgnoringChildren( +base::Optional<MinMaxSizes> CalculateMinMaxSizesIgnoringChildren( const NGBlockNode& node, - const NGBoxStrut& border_scrollbar_padding, - NGMinMaxSizeType type) { - MinMaxSize sizes; - if (type == NGMinMaxSizeType::kBorderBoxSize) - sizes += border_scrollbar_padding.InlineSum(); + const NGBoxStrut& border_scrollbar_padding) { + MinMaxSizes sizes; + sizes += border_scrollbar_padding.InlineSum(); // If intrinsic size was overridden, then use that. const LayoutUnit intrinsic_size_override = @@ -1279,6 +1212,12 @@ base::Optional<MinMaxSize> CalculateMinMaxSizesIgnoringChildren( if (intrinsic_size_override != kIndefiniteSize) { sizes += intrinsic_size_override; return sizes; + } else { + LayoutUnit default_inline_size = node.DefaultIntrinsicContentInlineSize(); + if (default_inline_size != kIndefiniteSize) { + sizes += default_inline_size; + return sizes; + } } // Size contained elements don't consider children for intrinsic sizing. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h index 525a28b6cec..2a618216acc 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h @@ -9,7 +9,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" #include "third_party/blink/renderer/core/layout/geometry/physical_size.h" -#include "third_party/blink/renderer/core/layout/min_max_size.h" +#include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h" @@ -22,7 +22,7 @@ namespace blink { class ComputedStyle; class Length; -struct MinMaxSizeInput; +struct MinMaxSizesInput; class NGConstraintSpace; class NGBlockNode; class NGLayoutInputNode; @@ -41,15 +41,6 @@ inline bool NeedMinMaxSize(const ComputedStyle& style) { style.LogicalMaxWidth().IsIntrinsic(); } -// Whether the caller needs to compute min-content and max-content sizes to -// pass them to ResolveMainInlineLength / ComputeInlineSizeForFragment. -// If this function returns false, it is safe to pass an empty -// MinMaxSize struct to those functions. -inline bool NeedMinMaxSize(const NGConstraintSpace& constraint_space, - const ComputedStyle& style) { - return constraint_space.IsShrinkToFit() || NeedMinMaxSize(style); -} - // Like NeedMinMaxSize, but for use when calling // ComputeMinAndMaxContentContribution. // Because content contributions are commonly needed by a block's parent, @@ -75,17 +66,17 @@ CORE_EXPORT bool BlockLengthUnresolvable( // available-size. // - |ComputedStyle| the style of the node. // - |border_padding| the resolved border, and padding of the node. -// - |MinMaxSize| is only used when the length is intrinsic (fit-content). +// - |MinMaxSizes| is only used when the length is intrinsic (fit-content). // - |Length| is the length to resolve. CORE_EXPORT LayoutUnit ResolveInlineLengthInternal(const NGConstraintSpace&, const ComputedStyle&, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>&, + const base::Optional<MinMaxSizes>&, const Length&); // Same as ResolveInlineLengthInternal, except here |content_size| roughly plays -// the part of |MinMaxSize|. +// the part of |MinMaxSizes|. CORE_EXPORT LayoutUnit ResolveBlockLengthInternal( const NGConstraintSpace&, const ComputedStyle&, @@ -97,44 +88,100 @@ CORE_EXPORT LayoutUnit ResolveBlockLengthInternal( nullptr); // Used for resolving min inline lengths, (|ComputedStyle::MinLogicalWidth|). +template <typename MinMaxSizesFunc> inline LayoutUnit ResolveMinInlineLength( const NGConstraintSpace& constraint_space, const ComputedStyle& style, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>& min_and_max, + const MinMaxSizesFunc& min_max_sizes_func, + const Length& length, + LengthResolvePhase phase) { + if (LIKELY(length.IsAuto() || InlineLengthUnresolvable(length, phase))) + return border_padding.InlineSum(); + + base::Optional<MinMaxSizes> min_max_sizes; + if (length.IsIntrinsic()) + min_max_sizes = min_max_sizes_func(); + + return ResolveInlineLengthInternal(constraint_space, style, border_padding, + min_max_sizes, length); +} + +template <> +inline LayoutUnit ResolveMinInlineLength<base::Optional<MinMaxSizes>>( + const NGConstraintSpace& constraint_space, + const ComputedStyle& style, + const NGBoxStrut& border_padding, + const base::Optional<MinMaxSizes>& min_max_sizes, const Length& length, LengthResolvePhase phase) { if (LIKELY(length.IsAuto() || InlineLengthUnresolvable(length, phase))) return border_padding.InlineSum(); return ResolveInlineLengthInternal(constraint_space, style, border_padding, - min_and_max, length); + min_max_sizes, length); } // Used for resolving max inline lengths, (|ComputedStyle::MaxLogicalWidth|). +template <typename MinMaxSizesFunc> inline LayoutUnit ResolveMaxInlineLength( const NGConstraintSpace& constraint_space, const ComputedStyle& style, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>& min_and_max, + const MinMaxSizesFunc& min_max_sizes_func, const Length& length, LengthResolvePhase phase) { - if (LIKELY(length.IsMaxSizeNone() || InlineLengthUnresolvable(length, phase))) + if (LIKELY(length.IsNone() || InlineLengthUnresolvable(length, phase))) return LayoutUnit::Max(); + base::Optional<MinMaxSizes> min_max_sizes; + if (length.IsIntrinsic()) + min_max_sizes = min_max_sizes_func(); + return ResolveInlineLengthInternal(constraint_space, style, border_padding, - min_and_max, length); + min_max_sizes, length); +} + +template <> +inline LayoutUnit ResolveMaxInlineLength<base::Optional<MinMaxSizes>>( + const NGConstraintSpace& constraint_space, + const ComputedStyle& style, + const NGBoxStrut& border_padding, + const base::Optional<MinMaxSizes>& min_max_sizes, + const Length& length, + LengthResolvePhase phase) { + if (LIKELY(length.IsNone() || InlineLengthUnresolvable(length, phase))) + return LayoutUnit::Max(); + + return ResolveInlineLengthInternal(constraint_space, style, border_padding, + min_max_sizes, length); } // Used for resolving main inline lengths, (|ComputedStyle::LogicalWidth|). +template <typename MinMaxSizesFunc> inline LayoutUnit ResolveMainInlineLength( const NGConstraintSpace& constraint_space, const ComputedStyle& style, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>& min_and_max, + const MinMaxSizesFunc& min_max_sizes_func, + const Length& length) { + base::Optional<MinMaxSizes> min_max_sizes; + if (length.IsIntrinsic()) + min_max_sizes = min_max_sizes_func(); + + return ResolveInlineLengthInternal(constraint_space, style, border_padding, + min_max_sizes, length); +} + +template <> +inline LayoutUnit ResolveMainInlineLength<base::Optional<MinMaxSizes>>( + const NGConstraintSpace& constraint_space, + const ComputedStyle& style, + const NGBoxStrut& border_padding, + const base::Optional<MinMaxSizes>& min_max_sizes, const Length& length) { return ResolveInlineLengthInternal(constraint_space, style, border_padding, - min_and_max, length); + min_max_sizes, length); } // Used for resolving min block lengths, (|ComputedStyle::MinLogicalHeight|). @@ -143,7 +190,6 @@ inline LayoutUnit ResolveMinBlockLength( const ComputedStyle& style, const NGBoxStrut& border_padding, const Length& length, - LayoutUnit content_size, LengthResolvePhase phase, const LayoutUnit* opt_percentage_resolution_block_size_for_min_max = nullptr) { @@ -153,7 +199,7 @@ inline LayoutUnit ResolveMinBlockLength( return border_padding.BlockSum(); return ResolveBlockLengthInternal( - constraint_space, style, border_padding, length, content_size, phase, + constraint_space, style, border_padding, length, kIndefiniteSize, phase, opt_percentage_resolution_block_size_for_min_max); } @@ -163,7 +209,6 @@ inline LayoutUnit ResolveMaxBlockLength( const ComputedStyle& style, const NGBoxStrut& border_padding, const Length& length, - LayoutUnit content_size, LengthResolvePhase phase, const LayoutUnit* opt_percentage_resolution_block_size_for_min_max = nullptr) { @@ -173,7 +218,7 @@ inline LayoutUnit ResolveMaxBlockLength( return LayoutUnit::Max(); return ResolveBlockLengthInternal( - constraint_space, style, border_padding, length, content_size, phase, + constraint_space, style, border_padding, length, kIndefiniteSize, phase, opt_percentage_resolution_block_size_for_min_max); } @@ -198,6 +243,31 @@ inline LayoutUnit ResolveMainBlockLength( opt_percentage_resolution_block_size_for_min_max); } +template <typename IntrinsicBlockSizeFunc> +inline LayoutUnit ResolveMainBlockLength( + const NGConstraintSpace& constraint_space, + const ComputedStyle& style, + const NGBoxStrut& border_padding, + const Length& length, + const IntrinsicBlockSizeFunc& intrinsic_block_size_func, + LengthResolvePhase phase, + const LayoutUnit* opt_percentage_resolution_block_size_for_min_max = + nullptr) { + if (UNLIKELY((length.IsPercentOrCalc() || length.IsFillAvailable()) && + BlockLengthUnresolvable( + constraint_space, length, phase, + opt_percentage_resolution_block_size_for_min_max))) + return intrinsic_block_size_func(); + + LayoutUnit intrinsic_block_size = kIndefiniteSize; + if (length.IsIntrinsicOrAuto()) + intrinsic_block_size = intrinsic_block_size_func(); + + return ResolveBlockLengthInternal( + constraint_space, style, border_padding, length, intrinsic_block_size, + phase, opt_percentage_resolution_block_size_for_min_max); +} + // For the given style and min/max content sizes, computes the min and max // content contribution (https://drafts.csswg.org/css-sizing/#contributions). // This is similar to ComputeInlineSizeForFragment except that it does not @@ -208,11 +278,11 @@ inline LayoutUnit ResolveMainBlockLength( // Because content contributions are commonly needed by a block's parent, // we also take a writing mode here so we can compute this in the parent's // coordinate system. -CORE_EXPORT MinMaxSize +CORE_EXPORT MinMaxSizes ComputeMinAndMaxContentContribution(WritingMode writing_mode, const ComputedStyle&, const NGBoxStrut& border_padding, - const base::Optional<MinMaxSize>&); + const base::Optional<MinMaxSizes>&); // A version of ComputeMinAndMaxContentContribution that does not require you // to compute the min/max content size of the child. Instead, this function @@ -222,31 +292,22 @@ ComputeMinAndMaxContentContribution(WritingMode writing_mode, // parent, we'll still return the inline min/max contribution in the writing // mode of the parent (i.e. typically something based on the preferred *block* // size of the child). -MinMaxSize ComputeMinAndMaxContentContribution( +MinMaxSizes ComputeMinAndMaxContentContribution( const ComputedStyle& parent_style, NGLayoutInputNode child, - const MinMaxSizeInput&); - -// Computes the min/max-content size for an out-of-flow positioned node and -// returns it, using the cache where possible. ALways computes it in the writing -// mode of the node itself. -MinMaxSize ComputeMinAndMaxContentSizeForOutOfFlow( - const NGConstraintSpace&, - NGLayoutInputNode, - const NGBoxStrut& border_padding, - const MinMaxSizeInput&); + const MinMaxSizesInput&); // Returns inline size of the node's border box by resolving the computed value // in style.logicalWidth (Length) to a layout unit, adding border and padding, // then constraining the result by the resolved min logical width and max // logical width from the ComputedStyle object. Calls Node::ComputeMinMaxSize // if needed. -// |override_minmax_for_test| is provided *solely* for use by unit tests. +// |override_min_max_sizes_for_test| is provided *solely* for use by unit tests. CORE_EXPORT LayoutUnit ComputeInlineSizeForFragment( const NGConstraintSpace&, NGLayoutInputNode, const NGBoxStrut& border_padding, - const MinMaxSize* override_minmax_for_test = nullptr); + const MinMaxSizes* override_min_max_sizes_for_test = nullptr); // Same as ComputeInlineSizeForFragment, but uses height instead of width. CORE_EXPORT LayoutUnit @@ -265,9 +326,9 @@ ComputeBlockSizeForFragment(const NGConstraintSpace&, // - neither out_aspect_ratio, nor out_replaced_size // SVG elements can return any of the three options above. CORE_EXPORT void ComputeReplacedSize( - const NGLayoutInputNode&, + const NGBlockNode&, const NGConstraintSpace&, - const base::Optional<MinMaxSize>&, + const base::Optional<MinMaxSizes>&, base::Optional<LogicalSize>* out_replaced_size, base::Optional<LogicalSize>* out_aspect_ratio); @@ -364,7 +425,7 @@ CORE_EXPORT NGBoxStrut ComputeMinMaxMargins(const ComputedStyle& parent_style, NGLayoutInputNode child); CORE_EXPORT NGBoxStrut ComputeBorders(const NGConstraintSpace&, - const NGLayoutInputNode); + const ComputedStyle&); CORE_EXPORT NGBoxStrut ComputeBordersForInline(const ComputedStyle& style); @@ -480,11 +541,9 @@ LayoutUnit ClampIntrinsicBlockSize( // without considering children. If so, it returns the calculated size. // Otherwise, it returns base::nullopt and the caller has to compute the size // itself. -base::Optional<MinMaxSize> CalculateMinMaxSizesIgnoringChildren( +base::Optional<MinMaxSizes> CalculateMinMaxSizesIgnoringChildren( const NGBlockNode&, - const NGBoxStrut& border_scrollbar_padding, - NGMinMaxSizeType); - + const NGBoxStrut& border_scrollbar_padding); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc index 6b63229023f..f374f55a0cb 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc @@ -41,7 +41,7 @@ class NGLengthUtilsTest : public testing::Test { LayoutUnit ResolveMainInlineLength( const Length& length, - const base::Optional<MinMaxSize>& sizes = base::nullopt) { + const base::Optional<MinMaxSizes>& sizes = base::nullopt) { NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300); NGBoxStrut border_padding = ComputeBordersForTest(*style_) + ComputePadding(constraint_space, *style_); @@ -53,7 +53,7 @@ class NGLengthUtilsTest : public testing::Test { LayoutUnit ResolveMinInlineLength( const Length& length, LengthResolvePhase phase = LengthResolvePhase::kLayout, - const base::Optional<MinMaxSize>& sizes = base::nullopt) { + const base::Optional<MinMaxSizes>& sizes = base::nullopt) { NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300); NGBoxStrut border_padding = ComputeBordersForTest(*style_) + ComputePadding(constraint_space, *style_); @@ -65,7 +65,7 @@ class NGLengthUtilsTest : public testing::Test { LayoutUnit ResolveMaxInlineLength( const Length& length, LengthResolvePhase phase = LengthResolvePhase::kLayout, - const base::Optional<MinMaxSize>& sizes = base::nullopt) { + const base::Optional<MinMaxSizes>& sizes = base::nullopt) { NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300); NGBoxStrut border_padding = ComputeBordersForTest(*style_) + ComputePadding(constraint_space, *style_); @@ -97,10 +97,10 @@ class NGLengthUtilsTestWithNode : public NGLayoutTest { LayoutUnit ComputeInlineSizeForFragment( NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300), - const MinMaxSize& sizes = MinMaxSize()) { + const MinMaxSizes& sizes = MinMaxSizes()) { LayoutBox* body = ToLayoutBox(GetDocument().body()->GetLayoutObject()); body->SetStyle(style_); - body->SetPreferredLogicalWidthsDirty(); + body->SetIntrinsicLogicalWidthsDirty(); NGBlockNode node(body); NGBoxStrut border_padding = ComputeBordersForTest(*style_) + @@ -114,7 +114,7 @@ class NGLengthUtilsTestWithNode : public NGLayoutTest { LayoutUnit content_size = LayoutUnit()) { LayoutBox* body = ToLayoutBox(GetDocument().body()->GetLayoutObject()); body->SetStyle(style_); - body->SetPreferredLogicalWidthsDirty(); + body->SetIntrinsicLogicalWidthsDirty(); NGBoxStrut border_padding = ComputeBordersForTest(*style_) + ComputePadding(constraint_space, *style_); @@ -139,7 +139,7 @@ TEST_F(NGLengthUtilsTest, testResolveInlineLength) { EXPECT_EQ(LayoutUnit::Max(), ResolveMaxInlineLength(Length::FillAvailable(), LengthResolvePhase::kIntrinsic)); - MinMaxSize sizes; + MinMaxSizes sizes; sizes.min_size = LayoutUnit(30); sizes.max_size = LayoutUnit(40); EXPECT_EQ(LayoutUnit(30), @@ -168,13 +168,13 @@ TEST_F(NGLengthUtilsTest, testResolveBlockLength) { } TEST_F(NGLengthUtilsTest, testComputeContentContribution) { - MinMaxSize sizes; + MinMaxSizes sizes; sizes.min_size = LayoutUnit(30); sizes.max_size = LayoutUnit(40); NGBoxStrut border_padding; - MinMaxSize expected = sizes; + MinMaxSizes expected = sizes; style_->SetLogicalWidth(Length::Percent(30)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( @@ -185,7 +185,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding, sizes)); - expected = MinMaxSize{LayoutUnit(150), LayoutUnit(150)}; + expected = MinMaxSizes{LayoutUnit(150), LayoutUnit(150)}; style_->SetLogicalWidth(Length::Fixed(150)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( @@ -197,7 +197,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding, sizes)); - expected = MinMaxSize{LayoutUnit(430), LayoutUnit(440)}; + expected = MinMaxSizes{LayoutUnit(430), LayoutUnit(440)}; style_->SetPaddingLeft(Length::Fixed(400)); auto sizes_padding400 = sizes; sizes_padding400 += LayoutUnit(400); @@ -207,7 +207,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { style_->GetWritingMode(), *style_, border_padding400, sizes_padding400)); - expected = MinMaxSize{LayoutUnit(30), LayoutUnit(40)}; + expected = MinMaxSizes{LayoutUnit(30), LayoutUnit(40)}; style_->SetPaddingLeft(Length::Fixed(0)); style_->SetLogicalWidth(Length(CalculationValue::Create( PixelsAndPercent(100, -10), kValueRangeNonNegative))); @@ -215,21 +215,21 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding, sizes)); - expected = MinMaxSize{LayoutUnit(30), LayoutUnit(35)}; + expected = MinMaxSizes{LayoutUnit(30), LayoutUnit(35)}; style_->SetLogicalWidth(Length::Auto()); style_->SetMaxWidth(Length::Fixed(35)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding, sizes)); - expected = MinMaxSize{LayoutUnit(80), LayoutUnit(80)}; + expected = MinMaxSizes{LayoutUnit(80), LayoutUnit(80)}; style_->SetLogicalWidth(Length::Fixed(50)); style_->SetMinWidth(Length::Fixed(80)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding, sizes)); - expected = MinMaxSize{LayoutUnit(150), LayoutUnit(150)}; + expected = MinMaxSizes{LayoutUnit(150), LayoutUnit(150)}; style_ = ComputedStyle::Create(); style_->SetLogicalWidth(Length::Fixed(100)); style_->SetPaddingLeft(Length::Fixed(50)); @@ -241,7 +241,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { style_->GetWritingMode(), *style_, border_padding50, sizes_padding50)); - expected = MinMaxSize{LayoutUnit(100), LayoutUnit(100)}; + expected = MinMaxSizes{LayoutUnit(100), LayoutUnit(100)}; style_->SetBoxSizing(EBoxSizing::kBorderBox); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding50, @@ -249,7 +249,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { // Content size should never be below zero, even with box-sizing: border-box // and a large padding... - expected = MinMaxSize{LayoutUnit(400), LayoutUnit(400)}; + expected = MinMaxSizes{LayoutUnit(400), LayoutUnit(400)}; style_->SetPaddingLeft(Length::Fixed(400)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding400, @@ -264,11 +264,11 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { style_->SetMaxWidth(Length::MaxContent()); // Due to padding and box-sizing, width computes to 400px and max-width to // 440px, so the result is 400. - expected = MinMaxSize{LayoutUnit(400), LayoutUnit(400)}; + expected = MinMaxSizes{LayoutUnit(400), LayoutUnit(400)}; EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( style_->GetWritingMode(), *style_, border_padding400, sizes_padding400)); - expected = MinMaxSize{LayoutUnit(40), LayoutUnit(40)}; + expected = MinMaxSizes{LayoutUnit(40), LayoutUnit(40)}; style_->SetPaddingLeft(Length::Fixed(0)); EXPECT_EQ(expected, ComputeMinAndMaxContentContribution( @@ -276,7 +276,7 @@ TEST_F(NGLengthUtilsTest, testComputeContentContribution) { } TEST_F(NGLengthUtilsTestWithNode, testComputeInlineSizeForFragment) { - MinMaxSize sizes; + MinMaxSizes sizes; sizes.min_size = LayoutUnit(30); sizes.max_size = LayoutUnit(40); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc index df903e64148..0df01829351 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc @@ -237,10 +237,8 @@ bool NGOutOfFlowLayoutPart::SweepLegacyCandidates( // size first. // We perform a pre-layout to correctly determine the static position. // Copied from LayoutBlock::LayoutPositionedObject + // TODO(layout-dev): Remove this once LayoutFlexibleBox is removed. LayoutBox* layout_box = ToLayoutBox(legacy_object); - // TODO(dgrogan): The NG flexbox implementation doesn't have an - // analogous method yet, so abspos children of NG flexboxes that have a - // legacy containing block will not be positioned correctly. if (layout_box->Parent()->IsFlexibleBox()) { LayoutFlexibleBox* parent = ToLayoutFlexibleBox(layout_box->Parent()); if (parent->SetStaticPositionForPositionedLayout(*layout_box)) { @@ -293,22 +291,22 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks( inline_geometry); } } - // Fetch start/end fragment info. - container_builder_->ComputeInlineContainerFragments( - &inline_container_fragments); + + // Fetch the inline start/end fragment geometry. + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + container_builder_->ComputeInlineContainerGeometry( + &inline_container_fragments); + } else { + container_builder_->ComputeInlineContainerGeometryFromFragmentTree( + &inline_container_fragments); + } + LogicalSize container_builder_size = container_builder_->Size(); PhysicalSize container_builder_physical_size = ToPhysicalSize(container_builder_size, writing_mode_); - // Translate start/end fragments into ContainingBlockInfo. + // Transform the start/end fragments into a ContainingBlockInfo. for (auto& block_info : inline_container_fragments) { - // Variables needed to describe ContainingBlockInfo - const ComputedStyle* inline_cb_style = block_info.key->Style(); - LogicalSize inline_cb_size; - LogicalOffset container_offset; - DCHECK(block_info.value.has_value()); - DCHECK(inline_cb_style); - NGBoxStrut inline_cb_borders = ComputeBordersForInline(*inline_cb_style); // The calculation below determines the size of the inline containing block // rect. @@ -362,7 +360,11 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks( // // Note in cases [2a, 2b] we don't allow a "negative" containing block size, // we clamp negative sizes to zero. + const ComputedStyle* inline_cb_style = block_info.key->Style(); + DCHECK(inline_cb_style); + TextDirection container_direction = default_containing_block_.direction; + NGBoxStrut inline_cb_borders = ComputeBordersForInline(*inline_cb_style); bool is_same_direction = container_direction == inline_cb_style->Direction(); @@ -403,13 +405,14 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks( // Step 3 - determine the logical rectangle. // Determine the logical size of the containing block. - inline_cb_size = {end_offset.inline_offset - start_offset.inline_offset, - end_offset.block_offset - start_offset.block_offset}; + LogicalSize inline_cb_size = { + end_offset.inline_offset - start_offset.inline_offset, + end_offset.block_offset - start_offset.block_offset}; DCHECK_GE(inline_cb_size.inline_size, LayoutUnit()); DCHECK_GE(inline_cb_size.block_size, LayoutUnit()); // Set the container padding-box offset. - container_offset = start_offset; + LogicalOffset container_offset = start_offset; containing_blocks_map_.insert( block_info.key, @@ -525,12 +528,12 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate( // Since out-of-flow positioning sets up a constraint space with fixed // inline-size, the regular layout code (|NGBlockNode::Layout()|) cannot // re-layout if it discovers that a scrollbar was added or removed. Handle - // that situation here. The assumption is that if preferred logical widths - // are dirty after layout, AND its inline-size depends on preferred + // that situation here. The assumption is that if intrinsic logical widths + // are dirty after layout, AND its inline-size depends on the intrinsic // logical widths, it means that scrollbars appeared or disappeared. We // have the same logic in legacy layout in // |LayoutBlockFlow::UpdateBlockLayout()|. - if (node.GetLayoutBox()->PreferredLogicalWidthsDirty() && + if (node.GetLayoutBox()->IntrinsicLogicalWidthsDirty() && AbsoluteNeedsChildInlineSize(candidate_style)) { // Freeze the scrollbars for this layout pass. We don't want them to // change *again*. @@ -561,12 +564,12 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( LogicalSize container_content_size_in_candidate_writing_mode = container_physical_content_size.ConvertToLogical(candidate_writing_mode); NGBoxStrut border_padding = - ComputeBorders(candidate_constraint_space, node) + + ComputeBorders(candidate_constraint_space, candidate_style) + ComputePadding(candidate_constraint_space, candidate_style); // The |block_estimate| is wrt. the candidate's writing mode. base::Optional<LayoutUnit> block_estimate; - base::Optional<MinMaxSize> min_max_size; + base::Optional<MinMaxSizes> min_max_sizes; scoped_refptr<const NGLayoutResult> layout_result = nullptr; // In order to calculate the offsets, we may need to know the size. @@ -577,45 +580,61 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( // words, in that case, we may have to lay out, calculate the offset, and // then lay out again at the correct block-offset. + NGLogicalOutOfFlowDimensions node_dimensions; + bool has_computed_block_dimensions = false; bool is_replaced = node.IsReplaced(); bool should_be_considered_as_replaced = node.ShouldBeConsideredAsReplaced(); + bool absolute_needs_child_block_size = + AbsoluteNeedsChildBlockSize(candidate_style); if (AbsoluteNeedsChildInlineSize(candidate_style) || NeedMinMaxSize(candidate_style) || should_be_considered_as_replaced) { - // This is a new formatting context, so whatever happened on the outside - // doesn't concern us. - MinMaxSizeInput input(container_content_size.block_size); - min_max_size = ComputeMinAndMaxContentSizeForOutOfFlow( - candidate_constraint_space, node, border_padding, input); + MinMaxSizesInput input(kIndefiniteSize); + if (is_replaced) { + input.percentage_resolution_block_size = + container_content_size_in_candidate_writing_mode.block_size; + } else if (!absolute_needs_child_block_size) { + // If we can determine our block-size ahead of time (it doesn't depend on + // our content), we use this for our %-block-size. + ComputeOutOfFlowBlockDimensions( + candidate_constraint_space, candidate_style, border_padding, + candidate_static_position, base::nullopt, base::nullopt, + writing_mode_, container_direction, &node_dimensions); + has_computed_block_dimensions = true; + input.percentage_resolution_block_size = node_dimensions.size.block_size; + } + + min_max_sizes = node.ComputeMinMaxSizes(candidate_writing_mode, input, + &candidate_constraint_space); } base::Optional<LogicalSize> replaced_size; base::Optional<LogicalSize> replaced_aspect_ratio; bool is_replaced_with_only_aspect_ratio = false; if (is_replaced) { - ComputeReplacedSize(node, candidate_constraint_space, min_max_size, + ComputeReplacedSize(node, candidate_constraint_space, min_max_sizes, &replaced_size, &replaced_aspect_ratio); is_replaced_with_only_aspect_ratio = !replaced_size && replaced_aspect_ratio && !replaced_aspect_ratio->IsEmpty(); // If we only have aspect ratio, and no replaced size, intrinsic size - // defaults to 300x150. min_max_size gets computed from the intrinsic size. - // We reset the min_max_size because spec says that OOF-positioned size + // defaults to 300x150. min_max_sizes gets computed from the intrinsic size. + // We reset the min_max_sizes because spec says that OOF-positioned size // should not be constrained by intrinsic size in this case. // https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width if (is_replaced_with_only_aspect_ratio) - min_max_size = MinMaxSize{LayoutUnit(), LayoutUnit::NearlyMax()}; + min_max_sizes = MinMaxSizes{LayoutUnit(), LayoutUnit::NearlyMax()}; } else if (should_be_considered_as_replaced) { replaced_size = - LogicalSize{min_max_size->ShrinkToFit( + LogicalSize{min_max_sizes->ShrinkToFit( candidate_constraint_space.AvailableSize().inline_size), kIndefiniteSize}; } - NGLogicalOutOfFlowPosition node_position = - ComputePartialAbsoluteWithChildInlineSize( - candidate_constraint_space, candidate_style, border_padding, - candidate_static_position, min_max_size, replaced_size, writing_mode_, - container_direction); + + ComputeOutOfFlowInlineDimensions(candidate_constraint_space, candidate_style, + border_padding, candidate_static_position, + min_max_sizes, replaced_size, writing_mode_, + container_direction, &node_dimensions); // |should_be_considered_as_replaced| sets the inline-size. // It does not set the block-size. This is a compatibility quirk. @@ -627,16 +646,20 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( // https://www.w3.org/TR/css-sizing-3/#intrinsic-sizes if (is_replaced_with_only_aspect_ratio) { replaced_size = LogicalSize( - node_position.size.inline_size, + node_dimensions.size.inline_size, (replaced_aspect_ratio->block_size * - ((node_position.size.inline_size - border_padding.InlineSum()) / + ((node_dimensions.size.inline_size - border_padding.InlineSum()) / replaced_aspect_ratio->inline_size)) + border_padding.BlockSum()); } - if (AbsoluteNeedsChildBlockSize(candidate_style)) { + if (absolute_needs_child_block_size) { + DCHECK(!has_computed_block_dimensions); layout_result = GenerateFragment(node, container_content_size_in_candidate_writing_mode, - block_estimate, node_position); + block_estimate, node_dimensions); + + // TODO(layout-dev): Handle abortions caused by block fragmentation. + DCHECK(layout_result->Status() != NGLayoutResult::kOutOfFragmentainerSpace); NGFragment fragment(candidate_writing_mode, layout_result->PhysicalFragment()); @@ -644,15 +667,18 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( block_estimate = fragment.BlockSize(); } - // Calculate the offsets. - - ComputeFullAbsoluteWithChildBlockSize( - candidate_constraint_space, candidate_style, border_padding, - candidate_static_position, block_estimate, replaced_size, writing_mode_, - container_direction, &node_position); + // We may have already pre-computed our block-dimensions when determining our + // |min_max_sizes|, only run if needed. + if (!has_computed_block_dimensions) { + ComputeOutOfFlowBlockDimensions( + candidate_constraint_space, candidate_style, border_padding, + candidate_static_position, block_estimate, replaced_size, writing_mode_, + container_direction, &node_dimensions); + } + // Calculate the offsets. NGBoxStrut inset = - node_position.inset + node_dimensions.inset .ConvertToPhysical(candidate_writing_mode, candidate_direction) .ConvertToLogical(writing_mode_, default_direction); @@ -719,12 +745,15 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( // Skip this step if we produced a fragment when estimating the block-size. if (!layout_result) { - block_estimate = node_position.size.block_size; + block_estimate = node_dimensions.size.block_size; layout_result = GenerateFragment(node, container_content_size_in_candidate_writing_mode, - block_estimate, node_position); + block_estimate, node_dimensions); } + // TODO(layout-dev): Handle abortions caused by block fragmentation. + DCHECK_EQ(layout_result->Status(), NGLayoutResult::kSuccess); + // TODO(mstensho): Move the rest of this method back into LayoutCandidate(). if (node.GetLayoutBox()->IsLayoutNGObject()) { @@ -736,7 +765,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout( if (!container_builder_->GetLayoutObject() ->Style() ->IsDisplayFlexibleOrGridBox()) { - node.GetLayoutBox()->SetMargin(node_position.margins.ConvertToPhysical( + node.GetLayoutBox()->SetMargin(node_dimensions.margins.ConvertToPhysical( candidate_writing_mode, candidate_direction)); } @@ -785,12 +814,12 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment( NGBlockNode node, const LogicalSize& container_content_size_in_candidate_writing_mode, const base::Optional<LayoutUnit>& block_estimate, - const NGLogicalOutOfFlowPosition& node_position) { + const NGLogicalOutOfFlowDimensions& node_dimensions) { // As the |block_estimate| is always in the node's writing mode, we build the // constraint space in the node's writing mode. WritingMode writing_mode = node.Style().GetWritingMode(); - LayoutUnit inline_size = node_position.size.inline_size; + LayoutUnit inline_size = node_dimensions.size.inline_size; LayoutUnit block_size = block_estimate.value_or( container_content_size_in_candidate_writing_mode.block_size); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h index e8d9ad81dbd..777309f62ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h @@ -122,7 +122,7 @@ class CORE_EXPORT NGOutOfFlowLayoutPart { NGBlockNode node, const LogicalSize& container_content_size_in_child_writing_mode, const base::Optional<LayoutUnit>& block_estimate, - const NGLogicalOutOfFlowPosition& node_position); + const NGLogicalOutOfFlowDimensions& node_dimensions); const NGConstraintSpace& container_space_; NGBoxFragmentBuilder* container_builder_; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h index 1037e23bca2..3f9a5127140 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h @@ -54,14 +54,17 @@ struct NGLogicalOutOfFlowPositionedNode { NGLogicalStaticPosition static_position; // Continuation root of the optional inline container. const LayoutInline* inline_container; + bool needs_block_offset_adjustment; NGLogicalOutOfFlowPositionedNode( NGBlockNode node, NGLogicalStaticPosition static_position, - const LayoutInline* inline_container = nullptr) + const LayoutInline* inline_container = nullptr, + bool needs_block_offset_adjustment = false) : node(node), static_position(static_position), - inline_container(inline_container) { + inline_container(inline_container), + needs_block_offset_adjustment(needs_block_offset_adjustment) { DCHECK(!inline_container || inline_container == inline_container->ContinuationRoot()); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc index dece4940984..805794074b6 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc @@ -46,7 +46,7 @@ bool NGOutlineUtils::ShouldPaintOutline( NGInlineCursor cursor; cursor.MoveTo(*layout_object); DCHECK(cursor); - return cursor.CurrentBoxFragment() == &physical_fragment; + return cursor.Current().BoxFragment() == &physical_fragment; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc index 21ab11cf7b5..5328304e619 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc @@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h" #include <algorithm> -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" @@ -81,13 +80,13 @@ scoped_refptr<const NGLayoutResult> NGPageLayoutAlgorithm::Layout() { return container_builder_.ToBoxFragment(); } -base::Optional<MinMaxSize> NGPageLayoutAlgorithm::ComputeMinMaxSize( - const MinMaxSizeInput& input) const { +base::Optional<MinMaxSizes> NGPageLayoutAlgorithm::ComputeMinMaxSizes( + const MinMaxSizesInput& input) const { NGFragmentGeometry fragment_geometry = CalculateInitialMinMaxFragmentGeometry(ConstraintSpace(), Node()); NGBlockLayoutAlgorithm algorithm( {Node(), fragment_geometry, ConstraintSpace()}); - return algorithm.ComputeMinMaxSize(input); + return algorithm.ComputeMinMaxSizes(input); } NGConstraintSpace NGPageLayoutAlgorithm::CreateConstraintSpaceForPages( @@ -97,9 +96,6 @@ NGConstraintSpace NGPageLayoutAlgorithm::CreateConstraintSpaceForPages( space_builder.SetAvailableSize(page_size); space_builder.SetPercentageResolutionSize(page_size); - if (NGBaseline::ShouldPropagateBaselines(Node())) - space_builder.AddBaselineRequests(ConstraintSpace().BaselineRequests()); - // TODO(mstensho): Handle auto block size. space_builder.SetFragmentationType(kFragmentPage); space_builder.SetFragmentainerBlockSize(page_size.block_size); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h index f05b8a8d717..8797b3ba1ac 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h @@ -25,8 +25,8 @@ class CORE_EXPORT NGPageLayoutAlgorithm scoped_refptr<const NGLayoutResult> Layout() override; - base::Optional<MinMaxSize> ComputeMinMaxSize( - const MinMaxSizeInput&) const override; + base::Optional<MinMaxSizes> ComputeMinMaxSizes( + const MinMaxSizesInput&) const override; private: NGConstraintSpace CreateConstraintSpaceForPages( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index d550eea7cd0..2092c9bbe84 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc @@ -10,6 +10,7 @@ #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_object_inlines.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.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_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" @@ -22,7 +23,8 @@ namespace blink { namespace { struct SameSizeAsNGPhysicalBoxFragment : NGPhysicalContainerFragment { - NGBaselineList baselines; + LayoutUnit baseline; + LayoutUnit last_baseline; NGLink children[]; }; @@ -63,25 +65,23 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create( // we pass the buffer as a constructor argument. void* data = ::WTF::Partitions::FastMalloc( byte_size, ::WTF::GetStringWithTypeName<NGPhysicalBoxFragment>()); - new (data) NGPhysicalBoxFragment(builder, borders, padding, + new (data) NGPhysicalBoxFragment(PassKey(), builder, borders, padding, block_or_line_writing_mode); return base::AdoptRef(static_cast<NGPhysicalBoxFragment*>(data)); } NGPhysicalBoxFragment::NGPhysicalBoxFragment( + PassKey key, NGBoxFragmentBuilder* builder, const NGPhysicalBoxStrut& borders, const NGPhysicalBoxStrut& padding, WritingMode block_or_line_writing_mode) - : NGPhysicalContainerFragment( - builder, - block_or_line_writing_mode, - children_, - (builder->node_ && builder->node_.IsRenderedLegend()) - ? kFragmentRenderedLegend - : kFragmentBox, - builder->BoxType()), - baselines_(builder->baselines_) { + : NGPhysicalContainerFragment(builder, + block_or_line_writing_mode, + children_, + kFragmentBox, + builder->BoxType()) { + DCHECK(layout_object_); DCHECK(layout_object_->IsBoxModelObject()); if (NGFragmentItemsBuilder* items_builder = builder->ItemsBuilder()) { has_fragment_items_ = true; @@ -98,15 +98,34 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment( has_padding_ = !padding.IsZero(); if (has_padding_) *const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress()) = padding; - // consumed_block_size_ is only updated if we're in block - // fragmentation. Otherwise it will always be 0. - is_first_for_node_ = - builder->consumed_block_size_ <= builder->size_.block_size; + is_first_for_node_ = builder->is_first_for_node_; is_fieldset_container_ = builder->is_fieldset_container_; is_legacy_layout_root_ = builder->is_legacy_layout_root_; + is_painted_atomically_ = + builder->space_ && builder->space_->IsPaintedAtomically(); border_edge_ = builder->border_edges_.ToPhysical(builder->GetWritingMode()); - children_inline_ = - builder->layout_object_ && builder->layout_object_->ChildrenInline(); + is_inline_formatting_context_ = builder->is_inline_formatting_context_; + is_generated_text_or_math_fraction_ = builder->is_math_fraction_; + + bool has_layout_containment = layout_object_->ShouldApplyLayoutContainment(); + if (builder->baseline_.has_value() && !has_layout_containment) { + has_baseline_ = true; + baseline_ = *builder->baseline_; + } else { + has_baseline_ = false; + baseline_ = LayoutUnit::Min(); + } + if (builder->last_baseline_.has_value() && !has_layout_containment) { + has_last_baseline_ = true; + last_baseline_ = *builder->last_baseline_; + } else { + has_last_baseline_ = false; + last_baseline_ = LayoutUnit::Min(); + } + +#if DCHECK_IS_ON() + CheckIntegrity(); +#endif } scoped_refptr<const NGLayoutResult> @@ -153,7 +172,7 @@ PhysicalRect NGPhysicalBoxFragment::ScrollableOverflow() const { TextDirection container_direction = Style().Direction(); for (const auto& child_fragment : Children()) { PhysicalRect child_overflow = - child_fragment->ScrollableOverflowForPropagation(layout_object); + child_fragment->ScrollableOverflowForPropagation(*this); if (child_fragment->Style() != Style()) { PhysicalOffset relative_offset = ComputeRelativeOffset( child_fragment->Style(), container_writing_mode, @@ -170,6 +189,157 @@ PhysicalRect NGPhysicalBoxFragment::ScrollableOverflow() const { return PhysicalRect({}, Size()); } +PhysicalRect NGPhysicalBoxFragment::ScrollableOverflowFromChildren() const { + const NGFragmentItems* items = Items(); + if (Children().empty() && !items) + return PhysicalRect(); + + // Internal struct to share logic between child fragments and child items. + // - Inline children's overflow expands by padding end/after. + // - Float / OOF overflow is added as is. + // - Children not reachable by scroll overflow do not contribute to it. + struct ComputeOverflowContext { + ComputeOverflowContext(const NGPhysicalBoxFragment& container) + : container(container), + style(container.Style()), + writing_mode(style.GetWritingMode()), + direction(style.Direction()), + border_inline_start(LayoutUnit(style.BorderStartWidth())), + border_block_start(LayoutUnit(style.BorderBeforeWidth())) { + DCHECK_EQ(&style, container.GetLayoutObject()->Style( + container.UsesFirstLineStyle())); + + // End and under padding are added to scroll overflow of inline children. + // https://github.com/w3c/csswg-drafts/issues/129 + DCHECK_EQ(container.HasOverflowClip(), + container.GetLayoutObject()->HasOverflowClip()); + if (container.HasOverflowClip()) { + const LayoutBox* layout_object = + ToLayoutBox(container.GetLayoutObject()); + padding_strut = NGBoxStrut(LayoutUnit(), layout_object->PaddingEnd(), + LayoutUnit(), layout_object->PaddingAfter()) + .ConvertToPhysical(writing_mode, direction); + } + } + + // Rectangles not reachable by scroll should not be added to overflow. + bool IsRectReachableByScroll(const PhysicalRect& rect) { + LogicalOffset rect_logical_end = + rect.offset.ConvertToLogical(writing_mode, direction, + container.Size(), rect.size) + + rect.size.ConvertToLogical(writing_mode); + return rect_logical_end.inline_offset > border_inline_start && + rect_logical_end.block_offset > border_block_start; + } + + void AddChild(const PhysicalRect& child_scrollable_overflow) { + // Do not add overflow if fragment is not reachable by scrolling. + if (IsRectReachableByScroll(child_scrollable_overflow)) + children_overflow.Unite(child_scrollable_overflow); + } + + void AddFloatingOrOutOfFlowPositionedChild( + const NGPhysicalFragment& child, + const PhysicalOffset& child_offset) { + DCHECK(child.IsFloatingOrOutOfFlowPositioned()); + PhysicalRect child_scrollable_overflow = + child.ScrollableOverflowForPropagation(container); + child_scrollable_overflow.offset += ComputeRelativeOffset( + child.Style(), writing_mode, direction, container.Size()); + child_scrollable_overflow.offset += child_offset; + AddChild(child_scrollable_overflow); + } + + void AddLineBoxChild(const NGPhysicalLineBoxFragment& child, + const PhysicalOffset& child_offset) { + if (padding_strut) + AddLineBoxRect({child_offset, child.Size()}); + PhysicalRect child_scrollable_overflow = + child.ScrollableOverflow(container, style); + child_scrollable_overflow.offset += child_offset; + AddChild(child_scrollable_overflow); + } + + void AddLineBoxChild(const NGFragmentItem& child, + const NGInlineCursor& cursor) { + DCHECK_EQ(&child, cursor.CurrentItem()); + DCHECK_EQ(child.Type(), NGFragmentItem::kLine); + if (padding_strut) + AddLineBoxRect(child.RectInContainerBlock()); + const NGPhysicalLineBoxFragment* line_box = child.LineBoxFragment(); + DCHECK(line_box); + PhysicalRect child_scrollable_overflow = + line_box->ScrollableOverflowForLine(container, style, child, cursor); + AddChild(child_scrollable_overflow); + } + + void AddLineBoxRect(const PhysicalRect& linebox_rect) { + DCHECK(padding_strut); + if (lineboxes_enclosing_rect) + lineboxes_enclosing_rect->Unite(linebox_rect); + else + lineboxes_enclosing_rect = linebox_rect; + } + + void AddPaddingToLineBoxChildren() { + if (lineboxes_enclosing_rect) { + DCHECK(padding_strut); + lineboxes_enclosing_rect->Expand(*padding_strut); + AddChild(*lineboxes_enclosing_rect); + } + } + + const NGPhysicalBoxFragment& container; + const ComputedStyle& style; + const WritingMode writing_mode; + const TextDirection direction; + const LayoutUnit border_inline_start; + const LayoutUnit border_block_start; + base::Optional<NGPhysicalBoxStrut> padding_strut; + base::Optional<PhysicalRect> lineboxes_enclosing_rect; + PhysicalRect children_overflow; + } context(*this); + + // Traverse child items. + if (items) { + for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNextSibling()) { + const NGFragmentItem* item = cursor.CurrentItem(); + if (item->Type() == NGFragmentItem::kLine) { + context.AddLineBoxChild(*item, cursor); + continue; + } + + if (const NGPhysicalBoxFragment* child_box = item->BoxFragment()) { + if (child_box->IsFloatingOrOutOfFlowPositioned()) { + context.AddFloatingOrOutOfFlowPositionedChild( + *child_box, item->OffsetInContainerBlock()); + } + } + } + } + + // Traverse child fragments. + const bool children_inline = IsInlineFormattingContext(); + // Only add overflow for fragments NG has not reflected into Legacy. + // These fragments are: + // - inline fragments, + // - out of flow fragments whose css container is inline box. + // TODO(layout-dev) Transforms also need to be applied to compute overflow + // correctly. NG is not yet transform-aware. crbug.com/855965 + for (const auto& child : Children()) { + if (child->IsFloatingOrOutOfFlowPositioned()) { + context.AddFloatingOrOutOfFlowPositionedChild(*child, child.Offset()); + } else if (children_inline && child->IsLineBox()) { + context.AddLineBoxChild(To<NGPhysicalLineBoxFragment>(*child), + child.Offset()); + } + } + + context.AddPaddingToLineBoxChildren(); + + return context.children_overflow; +} + LayoutSize NGPhysicalBoxFragment::PixelSnappedScrolledContentOffset() const { DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox()); const LayoutBox* box = ToLayoutBox(GetLayoutObject()); @@ -318,9 +488,10 @@ void NGPhysicalBoxFragment::CheckSameForSimplifiedLayout( DCHECK_EQ(depends_on_percentage_block_size_, other.depends_on_percentage_block_size_); - DCHECK_EQ(children_inline_, other.children_inline_); + DCHECK_EQ(is_inline_formatting_context_, other.is_inline_formatting_context_); DCHECK_EQ(is_fieldset_container_, other.is_fieldset_container_); DCHECK_EQ(is_legacy_layout_root_, other.is_legacy_layout_root_); + DCHECK_EQ(is_painted_atomically_, other.is_painted_atomically_); DCHECK_EQ(border_edge_, other.border_edge_); // The oof_positioned_descendants_ vector can change during "simplified" @@ -329,10 +500,63 @@ void NGPhysicalBoxFragment::CheckSameForSimplifiedLayout( // Legacy layout can (incorrectly) shift baseline position(s) during // "simplified" layout. - DCHECK(IsLegacyLayoutRoot() || baselines_ == other.baselines_); + DCHECK(IsLegacyLayoutRoot() || Baseline() == other.Baseline()); + DCHECK(IsLegacyLayoutRoot() || LastBaseline() == other.LastBaseline()); DCHECK(Borders() == other.Borders()); DCHECK(Padding() == other.Padding()); } + +// Check our flags represent the actual children correctly. +void NGPhysicalBoxFragment::CheckIntegrity() const { + bool has_inflow_blocks = false; + bool has_inlines = false; + bool has_line_boxes = false; + bool has_floats = false; + bool has_list_markers = false; + + for (const NGLink& child : Children()) { + if (child->IsFloating()) + has_floats = true; + else if (child->IsOutOfFlowPositioned()) + ; // OOF can be in the fragment tree regardless of |HasItems|. + else if (child->IsLineBox()) + has_line_boxes = true; + else if (child->IsListMarker()) + has_list_markers = true; + else if (child->IsInline()) + has_inlines = true; + else + has_inflow_blocks = true; + } + + // If we have line boxes, |IsInlineFormattingContext()| is true, but the + // reverse is not always true. + if (has_line_boxes || has_inlines) + DCHECK(IsInlineFormattingContext()); + + // If display-locked, we may not have any children. + DCHECK(layout_object_); + if (layout_object_ && layout_object_->PaintBlockedByDisplayLock( + DisplayLockLifecycleTarget::kChildren)) + return; + + if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { + if (RuntimeEnabledFeatures::LayoutNGBlockFragmentationEnabled()) { + if (has_line_boxes) + DCHECK(HasItems()); + } else { + DCHECK_EQ(HasItems(), has_line_boxes); + } + + if (has_line_boxes) { + DCHECK(!has_inlines); + DCHECK(!has_inflow_blocks); + // Following objects should be in the items, not in the tree. + DCHECK(!has_floats); + DCHECK(!has_list_markers); + } + } +} #endif } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h index 5fdc31c023e..21527728c01 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h @@ -7,7 +7,6 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h" #include "third_party/blink/renderer/platform/graphics/scroll_types.h" @@ -25,6 +24,13 @@ class CORE_EXPORT NGPhysicalBoxFragment final NGBoxFragmentBuilder* builder, WritingMode block_or_line_writing_mode); + using PassKey = util::PassKey<NGPhysicalBoxFragment>; + NGPhysicalBoxFragment(PassKey, + NGBoxFragmentBuilder* builder, + const NGPhysicalBoxStrut& borders, + const NGPhysicalBoxStrut& padding, + WritingMode block_or_line_writing_mode); + scoped_refptr<const NGLayoutResult> CloneAsHiddenForPaint() const; ~NGPhysicalBoxFragment() { @@ -40,8 +46,16 @@ class CORE_EXPORT NGPhysicalBoxFragment final return has_fragment_items_ ? ComputeItemsAddress() : nullptr; } - base::Optional<LayoutUnit> Baseline(const NGBaselineRequest& request) const { - return baselines_.Offset(request); + base::Optional<LayoutUnit> Baseline() const { + if (has_baseline_) + return baseline_; + return base::nullopt; + } + + base::Optional<LayoutUnit> LastBaseline() const { + if (has_last_baseline_) + return last_baseline_; + return base::nullopt; } const NGPhysicalBoxStrut Borders() const { @@ -63,9 +77,40 @@ class CORE_EXPORT NGPhysicalBoxFragment final } bool HasSelfPaintingLayer() const; - bool ChildrenInline() const { return children_inline_; } + + // Return true if this is either a container that establishes an inline + // formatting context, or if it's non-atomic inline content participating in + // one. Empty blocks don't establish an inline formatting context. + // + // The return value from this method is undefined and irrelevant if the object + // establishes a different type of formatting context than block/inline, such + // as table or flexbox. + // + // Example: + // <div> <!-- false --> + // <div> <!-- true --> + // <div style="float:left;"></div> <!-- false --> + // <div style="float:left;"> <!-- true --> + // xxx <!-- true --> + // </div> + // <div style="float:left;"> <!-- false --> + // <div style="float:left;"></div> <!-- false --> + // </div> + // <span> <!-- true --> + // xxx <!-- true --> + // <span style="display:inline-block;"> <!-- false --> + // <div></div> <!-- false --> + // </span> + // <span style="display:inline-block;"> <!-- true --> + // xxx <!-- true --> + // </span> + // <span style="display:inline-flex;"> <!-- N/A --> + bool IsInlineFormattingContext() const { + return is_inline_formatting_context_; + } PhysicalRect ScrollableOverflow() const; + PhysicalRect ScrollableOverflowFromChildren() const; // TODO(layout-dev): These three methods delegate to legacy layout for now, // update them to use LayoutNG based overflow information from the fragment @@ -79,6 +124,14 @@ class CORE_EXPORT NGPhysicalBoxFragment final // Compute visual overflow of this box in the local coordinate. PhysicalRect ComputeSelfInkOverflow() const; + // Contents ink overflow includes anything that would bleed out of the box and + // would be clipped by the overflow clip ('overflow' != visible). This + // corresponds to children that overflows their parent. + PhysicalRect ContentsInkOverflow() const { + // TODO(layout-dev): Implement box fragment overflow. + return LocalRect(); + } + // Fragment offset is this fragment's offset from parent. // Needed to compensate for LayoutInline Legacy code offsets. void AddSelfOutlineRects(const PhysicalOffset& additional_offset, @@ -91,20 +144,12 @@ class CORE_EXPORT NGPhysicalBoxFragment final unsigned BorderEdges() const { return border_edge_; } NGPixelSnappedPhysicalBoxStrut BorderWidths() const; - // Return true if this is the first fragment generated from a node. - bool IsFirstForNode() const { return is_first_for_node_; } - #if DCHECK_IS_ON() void CheckSameForSimplifiedLayout(const NGPhysicalBoxFragment&, bool check_same_block_size) const; #endif private: - NGPhysicalBoxFragment(NGBoxFragmentBuilder* builder, - const NGPhysicalBoxStrut& borders, - const NGPhysicalBoxStrut& padding, - WritingMode block_or_line_writing_mode); - const NGFragmentItems* ComputeItemsAddress() const { DCHECK(has_fragment_items_ || has_borders_ || has_padding_); const NGLink* children_end = children_ + Children().size(); @@ -125,7 +170,12 @@ class CORE_EXPORT NGPhysicalBoxFragment final return has_borders_ ? address + 1 : address; } - NGBaselineList baselines_; +#if DCHECK_IS_ON() + void CheckIntegrity() const; +#endif + + LayoutUnit baseline_; + LayoutUnit last_baseline_; NGLink children_[]; // borders and padding come from after |children_| if they are not zero. }; @@ -133,8 +183,7 @@ class CORE_EXPORT NGPhysicalBoxFragment final template <> struct DowncastTraits<NGPhysicalBoxFragment> { static bool AllowFrom(const NGPhysicalFragment& fragment) { - return fragment.Type() == NGPhysicalFragment::kFragmentBox || - fragment.Type() == NGPhysicalFragment::kFragmentRenderedLegend; + return fragment.Type() == NGPhysicalFragment::kFragmentBox; } }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc index 327d158404a..78373f21bb6 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc @@ -122,7 +122,7 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_NormalLegacyLayoutRoot) { EXPECT_TRUE(fragment->IsBox()); EXPECT_EQ(NGPhysicalFragment::kNormalBox, fragment->BoxType()); EXPECT_TRUE(fragment->IsLegacyLayoutRoot()); - EXPECT_TRUE(fragment->IsBlockFormattingContextRoot()); + EXPECT_TRUE(fragment->IsFormattingContextRoot()); } // TODO(editing-dev): Once LayoutNG supports editing, we should change this @@ -136,7 +136,7 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_FloatLegacyLayoutRoot) { EXPECT_TRUE(fragment->IsBox()); EXPECT_EQ(NGPhysicalFragment::kFloating, fragment->BoxType()); EXPECT_TRUE(fragment->IsLegacyLayoutRoot()); - EXPECT_TRUE(fragment->IsBlockFormattingContextRoot()); + EXPECT_TRUE(fragment->IsFormattingContextRoot()); } // TODO(editing-dev): Once LayoutNG supports editing, we should change this @@ -152,7 +152,7 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_InlineBlockLegacyLayoutRoot) { EXPECT_TRUE(fragment->IsBox()); EXPECT_EQ(NGPhysicalFragment::kAtomicInline, fragment->BoxType()); EXPECT_TRUE(fragment->IsLegacyLayoutRoot()); - EXPECT_TRUE(fragment->IsBlockFormattingContextRoot()); + EXPECT_TRUE(fragment->IsFormattingContextRoot()); } // TODO(editing-dev): Once LayoutNG supports editing, we should change this @@ -170,7 +170,7 @@ TEST_F(NGPhysicalBoxFragmentTest, EXPECT_TRUE(fragment->IsBox()); EXPECT_EQ(NGPhysicalFragment::kOutOfFlowPositioned, fragment->BoxType()); EXPECT_TRUE(fragment->IsLegacyLayoutRoot()); - EXPECT_TRUE(fragment->IsBlockFormattingContextRoot()); + EXPECT_TRUE(fragment->IsFormattingContextRoot()); } TEST_F(NGPhysicalBoxFragmentTest, ReplacedBlock) { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc index 6e7ac12f47b..82f57612ce4 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc @@ -6,10 +6,12 @@ #include "third_party/blink/renderer/core/layout/layout_block_flow.h" #include "third_party/blink/renderer/core/layout/layout_inline.h" +#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" namespace blink { @@ -88,6 +90,40 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren( const PhysicalOffset& additional_offset, NGOutlineType outline_type, const LayoutBoxModelObject* containing_block) const { + if (const auto* box = DynamicTo<NGPhysicalBoxFragment>(this)) { + if (const NGFragmentItems* items = box->Items()) { + for (NGInlineCursor cursor(*items); cursor; cursor.MoveToNext()) { + DCHECK(cursor.Current().Item()); + const NGFragmentItem& item = *cursor.Current().Item(); + if (item.Type() == NGFragmentItem::kLine) { + AddOutlineRectsForDescendant( + {item.LineBoxFragment(), item.OffsetInContainerBlock()}, + outline_rects, additional_offset, outline_type, containing_block); + continue; + } + if (item.Type() == NGFragmentItem::kBox) { + if (const NGPhysicalBoxFragment* child_box = item.BoxFragment()) { + DCHECK(!child_box->IsOutOfFlowPositioned()); + AddOutlineRectsForDescendant( + {child_box, item.OffsetInContainerBlock()}, outline_rects, + additional_offset, outline_type, containing_block); + } + continue; + } + DCHECK(item.IsText()); + } + // Don't add |Children()|. If |this| has |NGFragmentItems|, children are + // either line box, which we already handled in items, or OOF, which we + // should ignore. + DCHECK(std::all_of(PostLayoutChildren().begin(), + PostLayoutChildren().end(), [](const NGLink& child) { + return child->IsLineBox() || + child->IsOutOfFlowPositioned(); + })); + return; + } + } + for (const auto& child : PostLayoutChildren()) { // Outlines of out-of-flow positioned descendants are handled in // NGPhysicalBoxFragment::AddSelfOutlineRects(). @@ -111,6 +147,84 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren( } } +void NGPhysicalContainerFragment::AddScrollableOverflowForInlineChild( + const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style, + const NGFragmentItem& line, + bool has_hanging, + const NGInlineCursor& cursor, + PhysicalRect* overflow) const { + DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()); + DCHECK(IsLineBox() || IsInlineBox()); + DCHECK(cursor.Current().Item() && + (cursor.Current().Item()->BoxFragment() == this || + cursor.Current().Item()->LineBoxFragment() == this)); + const WritingMode container_writing_mode = container_style.GetWritingMode(); + const TextDirection container_direction = container_style.Direction(); + for (NGInlineCursor descendants = cursor.CursorForDescendants(); + descendants;) { + const NGFragmentItem* item = descendants.CurrentItem(); + DCHECK(item); + if (item->IsText()) { + PhysicalRect child_scroll_overflow = item->RectInContainerBlock(); + if (UNLIKELY(has_hanging)) { + AdjustScrollableOverflowForHanging(line.RectInContainerBlock(), + container_writing_mode, + &child_scroll_overflow); + } + overflow->Unite(child_scroll_overflow); + descendants.MoveToNextSkippingChildren(); + continue; + } + + if (const NGPhysicalBoxFragment* child_box = item->BoxFragment()) { + PhysicalRect child_scroll_overflow = item->RectInContainerBlock(); + if (child_box->IsInlineBox()) { + child_box->AddScrollableOverflowForInlineChild( + container, container_style, line, has_hanging, descendants, + &child_scroll_overflow); + child_box->AdjustScrollableOverflowForPropagation( + container, &child_scroll_overflow); + } else { + child_scroll_overflow = + child_box->ScrollableOverflowForPropagation(container); + child_scroll_overflow.offset += item->OffsetInContainerBlock(); + } + child_scroll_overflow.offset += + ComputeRelativeOffset(child_box->Style(), container_writing_mode, + container_direction, container.Size()); + overflow->Unite(child_scroll_overflow); + descendants.MoveToNextSkippingChildren(); + continue; + } + + // Add all children of a culled inline box; i.e., an inline box without + // margin/border/padding etc. + DCHECK_EQ(item->Type(), NGFragmentItem::kBox); + descendants.MoveToNext(); + } +} + +// Chop the hanging part from scrollable overflow. Children overflow in inline +// direction should hang, which should not cause scroll. +// TODO(kojii): Should move to text fragment to make this more accurate. +void NGPhysicalContainerFragment::AdjustScrollableOverflowForHanging( + const PhysicalRect& rect, + const WritingMode container_writing_mode, + PhysicalRect* overflow) { + if (IsHorizontalWritingMode(container_writing_mode)) { + if (overflow->offset.left < rect.offset.left) + overflow->offset.left = rect.offset.left; + if (overflow->Right() > rect.Right()) + overflow->ShiftRightEdgeTo(rect.Right()); + } else { + if (overflow->offset.top < rect.offset.top) + overflow->offset.top = rect.offset.top; + if (overflow->Bottom() > rect.Bottom()) + overflow->ShiftBottomEdgeTo(rect.Bottom()); + } +} + // additional_offset must be offset from the containing_block because // LocalToAncestorRect returns rects wrt containing_block. void NGPhysicalContainerFragment::AddOutlineRectsForDescendant( @@ -212,16 +326,23 @@ bool NGPhysicalContainerFragment::DependsOnPercentageBlockSize( // element if it has a percentage block-size however, but this will return // the correct result from below. + // There are two conditions where we need to know about an (arbitrary) + // descendant which depends on a %-block-size. + // - In quirks mode, the arbitrary descendant may depend the percentage + // resolution block-size given (to this node), and need to relayout if + // this size changes. + // - A flex-item may have its "definiteness" change, (e.g. if itself is a + // flex item which is being stretched). This definiteness change will + // affect any %-block-size children. + // + // NOTE(ikilpatrick): For the flex-item case this is potentially too general. + // We only need to know about if this flex-item has a %-block-size child if + // the "definiteness" changes, not if the percentage resolution size changes. if ((builder.has_descendant_that_depends_on_percentage_block_size_ || builder.is_legacy_layout_root_) && - node.UseParentPercentageResolutionBlockSizeForChildren()) { - // Quirks mode has different %-block-size behaviour, than standards mode. - // An arbitrary descendant may depend on the percentage resolution - // block-size given. - // If this is also an anonymous block we need to mark ourselves dependent - // if we have a dependent child. + (node.UseParentPercentageResolutionBlockSizeForChildren() || + node.IsFlexItem())) return true; - } const ComputedStyle& style = builder.Style(); if (style.LogicalHeight().IsPercentOrCalc() || diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h index 4cf2b93116b..b3c7624f494 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h @@ -17,6 +17,7 @@ namespace blink { class NGContainerFragmentBuilder; +class NGFragmentItem; struct NGPhysicalOutOfFlowPositionedNode; enum class NGOutlineType; @@ -149,6 +150,19 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment { NGFragmentType, unsigned sub_type); + void AddScrollableOverflowForInlineChild( + const NGPhysicalBoxFragment& container, + const ComputedStyle& container_style, + const NGFragmentItem& line, + bool has_hanging, + const NGInlineCursor& cursor, + PhysicalRect* overflow) const; + + static void AdjustScrollableOverflowForHanging( + const PhysicalRect& rect, + const WritingMode container_writing_mode, + PhysicalRect* overflow); + void AddOutlineRectsForNormalChildren( Vector<PhysicalRect>* outline_rects, const PhysicalOffset& additional_offset, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc index 02c51c05535..d5f70584bc9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc @@ -80,6 +80,9 @@ String StringForBoxType(const NGPhysicalFragment& fragment) { case NGPhysicalFragment::NGBoxType::kBlockFlowRoot: result.Append("block-flow-root"); break; + case NGPhysicalFragment::NGBoxType::kRenderedLegend: + result.Append("rendered-legend"); + break; } if (fragment.IsLegacyLayoutRoot()) { if (result.length()) @@ -91,18 +94,13 @@ String StringForBoxType(const NGPhysicalFragment& fragment) { result.Append(" "); result.Append("block-flow"); } - if (fragment.IsRenderedLegend()) { - if (result.length()) - result.Append(" "); - result.Append("rendered-legend"); - } if (fragment.IsFieldsetContainer()) { if (result.length()) result.Append(" "); result.Append("fieldset-container"); } if (fragment.IsBox() && - static_cast<const NGPhysicalBoxFragment&>(fragment).ChildrenInline()) { + To<NGPhysicalBoxFragment>(fragment).IsInlineFormattingContext()) { if (result.length()) result.Append(" "); result.Append("children-inline"); @@ -124,10 +122,7 @@ void AppendFragmentToString(const NGPhysicalFragment* fragment, bool has_content = false; if (const auto* box = DynamicTo<NGPhysicalBoxFragment>(fragment)) { if (flags & NGPhysicalFragment::DumpType) { - if (fragment->IsRenderedLegend()) - builder->Append("RenderedLegend"); - else - builder->Append("Box"); + builder->Append("Box"); String box_type = StringForBoxType(*fragment); has_content = true; if (!box_type.IsEmpty()) { @@ -224,9 +219,12 @@ NGPhysicalFragment::NGPhysicalFragment(NGFragmentBuilder* builder, sub_type_(sub_type), style_variant_((unsigned)builder->style_variant_), is_hidden_for_paint_(builder->is_hidden_for_paint_), + is_first_for_node_(true), has_floating_descendants_for_paint_(false), is_fieldset_container_(false), - is_legacy_layout_root_(false) { + is_legacy_layout_root_(false), + is_painted_atomically_(false), + has_baseline_(false) { DCHECK(builder->layout_object_); } @@ -243,7 +241,9 @@ NGPhysicalFragment::NGPhysicalFragment(LayoutObject* layout_object, is_hidden_for_paint_(false), has_floating_descendants_for_paint_(false), is_fieldset_container_(false), - is_legacy_layout_root_(false) { + is_legacy_layout_root_(false), + is_painted_atomically_(false), + has_baseline_(false) { DCHECK(layout_object); } @@ -254,7 +254,6 @@ NGPhysicalFragment::~NGPhysicalFragment() = default; void NGPhysicalFragment::Destroy() const { switch (Type()) { case kFragmentBox: - case kFragmentRenderedLegend: delete static_cast<const NGPhysicalBoxFragment*>(this); break; case kFragmentText: @@ -305,6 +304,19 @@ bool NGPhysicalFragment::IsPlacedByLayoutNG() const { return container->IsLayoutNGMixin(); } +const FragmentData* NGPhysicalFragment::GetFragmentData() const { + DCHECK(CanTraverse()); + const LayoutObject* layout_object = GetLayoutObject(); + if (!layout_object) + return nullptr; + // TODO(mstensho): Actually return the correct FragmentData. For now this + // method only behaves if there's just one FragmentData associated with the + // LayoutObject. + const FragmentData& first_fragment_data = layout_object->FirstFragment(); + DCHECK(!first_fragment_data.NextFragment()); + return &first_fragment_data; +} + const NGPhysicalFragment* NGPhysicalFragment::PostLayout() const { if (IsBox() && !IsInlineBox()) { if (const auto* block = DynamicTo<LayoutBlockFlow>(GetLayoutObject())) { @@ -322,7 +334,6 @@ const NGPhysicalFragment* NGPhysicalFragment::PostLayout() const { void NGPhysicalFragment::CheckType() const { switch (Type()) { case kFragmentBox: - case kFragmentRenderedLegend: if (IsInlineBox()) { DCHECK(layout_object_->IsLayoutInline()); } else { @@ -337,10 +348,10 @@ void NGPhysicalFragment::CheckType() const { DCHECK(!IsFloating()); DCHECK(!IsOutOfFlowPositioned()); DCHECK(!IsAtomicInline()); - DCHECK(!IsBlockFormattingContextRoot()); + DCHECK(!IsFormattingContextRoot()); break; } - if (layout_object_->IsLayoutNGListMarker()) { + if (layout_object_->IsLayoutNGOutsideListMarker()) { // List marker is an atomic inline if it appears in a line box, or a // block box. DCHECK(!IsFloating()); @@ -392,7 +403,6 @@ void NGPhysicalFragment::CheckCanUpdateInkOverflow() const { PhysicalRect NGPhysicalFragment::ScrollableOverflow() const { switch (Type()) { case kFragmentBox: - case kFragmentRenderedLegend: return To<NGPhysicalBoxFragment>(*this).ScrollableOverflow(); case kFragmentText: return {{}, Size()}; @@ -406,18 +416,30 @@ PhysicalRect NGPhysicalFragment::ScrollableOverflow() const { } PhysicalRect NGPhysicalFragment::ScrollableOverflowForPropagation( - const LayoutObject* container) const { - DCHECK(container); + const NGPhysicalBoxFragment& container) const { PhysicalRect overflow = ScrollableOverflow(); - if (GetLayoutObject() && - GetLayoutObject()->ShouldUseTransformFromContainer(container)) { + AdjustScrollableOverflowForPropagation(container, &overflow); + return overflow; +} + +void NGPhysicalFragment::AdjustScrollableOverflowForPropagation( + const NGPhysicalBoxFragment& container, + PhysicalRect* overflow) const { + DCHECK(!IsLineBox()); + if (!IsCSSBox()) + return; + + const LayoutObject* layout_object = GetLayoutObject(); + DCHECK(layout_object); + const LayoutObject* container_layout_object = container.GetLayoutObject(); + DCHECK(container_layout_object); + if (layout_object->ShouldUseTransformFromContainer(container_layout_object)) { TransformationMatrix transform; - GetLayoutObject()->GetTransformFromContainer(container, PhysicalOffset(), - transform); - overflow = - PhysicalRect::EnclosingRect(transform.MapRect(FloatRect(overflow))); + layout_object->GetTransformFromContainer(container_layout_object, + PhysicalOffset(), transform); + *overflow = + PhysicalRect::EnclosingRect(transform.MapRect(FloatRect(*overflow))); } - return overflow; } const Vector<NGInlineItem>& NGPhysicalFragment::InlineItemsOfContainingBlock() @@ -430,6 +452,7 @@ const Vector<NGInlineItem>& NGPhysicalFragment::InlineItemsOfContainingBlock() // modification. Unify them. DCHECK(block_flow); NGBlockNode block_node = NGBlockNode(block_flow); + DCHECK(block_node.IsInlineFormattingContextRoot()); DCHECK(block_node.CanUseNewLayout()); NGLayoutInputNode node = block_node.FirstChild(); @@ -447,7 +470,6 @@ UBiDiLevel NGPhysicalFragment::BidiLevel() const { case kFragmentText: return To<NGPhysicalTextFragment>(*this).BidiLevel(); case kFragmentBox: - case kFragmentRenderedLegend: return To<NGPhysicalBoxFragment>(*this).BidiLevel(); case kFragmentLineBox: break; @@ -461,7 +483,6 @@ TextDirection NGPhysicalFragment::ResolvedDirection() const { case kFragmentText: return To<NGPhysicalTextFragment>(*this).ResolvedDirection(); case kFragmentBox: - case kFragmentRenderedLegend: DCHECK(IsInline() && IsAtomicInline()); // TODO(xiaochengh): Store direction in |base_direction_| flag. return DirectionFromLevel(BidiLevel()); @@ -494,7 +515,6 @@ String NGPhysicalFragment::ToString() const { Size().ToString().Ascii().c_str()); switch (Type()) { case kFragmentBox: - case kFragmentRenderedLegend: output.AppendFormat(", BoxType: '%s'", StringForBoxType(*this).Ascii().c_str()); break; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h index e3b64e26d96..2fcd7049c96 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h @@ -11,7 +11,7 @@ #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h" #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h" #include "third_party/blink/renderer/core/layout/geometry/physical_size.h" -#include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h" #include "third_party/blink/renderer/platform/graphics/touch_action.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -21,6 +21,7 @@ namespace blink { class ComputedStyle; +class FragmentData; class Node; class NGFragmentBuilder; class NGInlineItem; @@ -50,7 +51,6 @@ class CORE_EXPORT NGPhysicalFragment kFragmentBox = 0, kFragmentText = 1, kFragmentLineBox = 2, - kFragmentRenderedLegend = 3, // When adding new values, make sure the bit size of |type_| is large // enough to store. }; @@ -64,13 +64,14 @@ class CORE_EXPORT NGPhysicalFragment kFloating, kOutOfFlowPositioned, kBlockFlowRoot, + kRenderedLegend, // When adding new values, make sure the bit size of |sub_type_| is large // enough to store. - // Also, add after kMinimumBlockFormattingContextRoot if the box type is a - // block formatting context root, or before otherwise. See - // IsBlockFormattingContextRoot(). - kMinimumBlockFormattingContextRoot = kAtomicInline + // Also, add after kMinimumFormattingContextRoot if the box type is a + // formatting context root, or before otherwise. See + // IsFormattingContextRoot(). + kMinimumFormattingContextRoot = kAtomicInline }; ~NGPhysicalFragment(); @@ -78,19 +79,12 @@ class CORE_EXPORT NGPhysicalFragment NGFragmentType Type() const { return static_cast<NGFragmentType>(type_); } bool IsContainer() const { return Type() == NGFragmentType::kFragmentBox || - Type() == NGFragmentType::kFragmentLineBox || - Type() == NGFragmentType::kFragmentRenderedLegend; + Type() == NGFragmentType::kFragmentLineBox; } bool IsBox() const { return Type() == NGFragmentType::kFragmentBox; } bool IsText() const { return Type() == NGFragmentType::kFragmentText; } bool IsLineBox() const { return Type() == NGFragmentType::kFragmentLineBox; } - // Return true if this is the legend child of a fieldset that gets special - // treatment (i.e. placed over the block-start border). - bool IsRenderedLegend() const { - return Type() == NGFragmentType::kFragmentRenderedLegend; - } - // Returns the box type of this fragment. NGBoxType BoxType() const { DCHECK(IsBox()); @@ -122,6 +116,14 @@ class CORE_EXPORT NGPhysicalFragment bool IsFloatingOrOutOfFlowPositioned() const { return IsFloating() || IsOutOfFlowPositioned(); } + // Return true if this is the legend child of a fieldset that gets special + // treatment (i.e. placed over the block-start border). + bool IsRenderedLegend() const { + return IsBox() && BoxType() == NGBoxType::kRenderedLegend; + } + bool IsMathMLFraction() const { + return IsBox() && is_generated_text_or_math_fraction_; + } // Return true if this fragment corresponds directly to an entry in the CSS // box tree [1]. Note that anonymous blocks also exist in the CSS box @@ -140,7 +142,7 @@ class CORE_EXPORT NGPhysicalFragment return IsCSSBox() && layout_object_->IsAnonymousBlock(); } bool IsListMarker() const { - return IsCSSBox() && layout_object_->IsLayoutNGListMarker(); + return IsCSSBox() && layout_object_->IsLayoutNGOutsideListMarker(); } // Return true if this fragment is a container established by a fieldset @@ -152,9 +154,11 @@ class CORE_EXPORT NGPhysicalFragment // Returns whether the fragment is legacy layout root. bool IsLegacyLayoutRoot() const { return is_legacy_layout_root_; } - bool IsBlockFormattingContextRoot() const { - return (IsBox() && - BoxType() >= NGBoxType::kMinimumBlockFormattingContextRoot) || + // Returns whether the fragment should be atomically painted. + bool IsPaintedAtomically() const { return is_painted_atomically_; } + + bool IsFormattingContextRoot() const { + return (IsBox() && BoxType() >= NGBoxType::kMinimumFormattingContextRoot) || IsLegacyLayoutRoot(); } @@ -164,6 +168,9 @@ class CORE_EXPORT NGPhysicalFragment // |LayoutNGBlockFlow::UpdateBlockLayout()| and crbug.com/788590 bool IsPlacedByLayoutNG() const; + // Return true if this is the first fragment generated from a node. + bool IsFirstForNode() const { return is_first_for_node_; } + // The accessors in this class shouldn't be used by layout code directly, // instead should be accessed by the NGFragmentBase classes. These accessors // exist for paint, hit-testing, etc. @@ -206,6 +213,17 @@ class CORE_EXPORT NGPhysicalFragment // from GetNode() when this fragment is content of a pseudo node. Node* NodeForHitTest() const { return layout_object_->NodeForHitTest(); } + bool IsInSelfHitTestingPhase(HitTestAction action) const { + if (const auto* box = ToLayoutBoxOrNull(GetLayoutObject())) + return box->IsInSelfHitTestingPhase(action); + if (IsInlineBox()) + return action == kHitTestForeground; + // Assuming this is some sort of container, e.g. a fragmentainer (they don't + // have a LayoutObject associated). + return action == kHitTestBlockBackground || + action == kHitTestChildBlockBackground; + } + // Whether there is a PaintLayer associated with the fragment. bool HasLayer() const { return IsCSSBox() && layout_object_->HasLayer(); } @@ -225,10 +243,31 @@ class CORE_EXPORT NGPhysicalFragment return IsCSSBox() && layout_object_->ShouldClipOverflow(); } + bool IsFragmentationContextRoot() const { + return !IsColumnBox() && IsBlockFlow() && Style().SpecifiesColumns(); + } + + // Return whether we can traverse this fragment and its children directly, for + // painting, hit-testing and other layout read operations. If false is + // returned, we need to traverse the layout object tree instead. + bool CanTraverse() const { + return layout_object_->CanTraversePhysicalFragments(); + } + // This fragment is hidden for paint purpose, but exists for querying layout // information. Used for `text-overflow: ellipsis`. bool IsHiddenForPaint() const { return is_hidden_for_paint_; } + // Return true if this fragment is monolithic, as far as block fragmentation + // is concerned. + bool IsMonolithic() const { + const LayoutObject* layout_object = GetLayoutObject(); + if (!layout_object || !IsBox() || !layout_object->IsBox()) + return false; + return ToLayoutBox(layout_object)->GetPaginationBreakability() == + LayoutBox::kForbidBreaks; + } + // GetLayoutObject should only be used when necessary for compatibility // with LegacyLayout. // @@ -244,6 +283,8 @@ class CORE_EXPORT NGPhysicalFragment return IsCSSBox() ? layout_object_ : nullptr; } + const FragmentData* GetFragmentData() const; + // |NGPhysicalFragment| may live longer than the corresponding |LayoutObject|. // Though |NGPhysicalFragment| is immutable, |layout_object_| is cleared to // |nullptr| when it was destroyed to avoid reading destroyed objects. @@ -260,13 +301,21 @@ class CORE_EXPORT NGPhysicalFragment // check if there were newer generations. const NGPhysicalFragment* PostLayout() const; + PhysicalRect InkOverflow() const { + // TODO(layout-dev): Implement box fragment overflow. + return LocalRect(); + } + // Scrollable overflow. including contents, in the local coordinate. PhysicalRect ScrollableOverflow() const; // ScrollableOverflow(), with transforms applied wrt container if needed. // This does not include any offsets from the parent (including relpos). PhysicalRect ScrollableOverflowForPropagation( - const LayoutObject* container) const; + const NGPhysicalBoxFragment& container) const; + void AdjustScrollableOverflowForPropagation( + const NGPhysicalBoxFragment& container, + PhysicalRect* overflow) const; // The allowed touch action is the union of the effective touch action // (from style) and blocking touch event handlers. @@ -336,6 +385,7 @@ class CORE_EXPORT NGPhysicalFragment const unsigned sub_type_ : 3; // NGBoxType, NGTextType, or NGLineBoxType const unsigned style_variant_ : 2; // NGStyleVariant const unsigned is_hidden_for_paint_ : 1; + unsigned is_first_for_node_ : 1; // The following bitfields are only to be used by NGPhysicalContainerFragment // (it's defined here to save memory, since that class has no bitfields). @@ -348,28 +398,37 @@ class CORE_EXPORT NGPhysicalFragment // The following bitfields are only to be used by NGPhysicalLineBoxFragment // (it's defined here to save memory, since that class has no bitfields). unsigned has_propagated_descendants_ : 1; - unsigned base_direction_ : 1; // TextDirection + // base (line box) or resolve (text) direction + unsigned base_or_resolved_direction_ : 1; // TextDirection unsigned has_hanging_ : 1; // The following bitfields are only to be used by NGPhysicalBoxFragment // (it's defined here to save memory, since that class has no bitfields). - unsigned children_inline_ : 1; + unsigned is_inline_formatting_context_ : 1; unsigned has_fragment_items_ : 1; unsigned border_edge_ : 4; // NGBorderEdges::Physical unsigned has_borders_ : 1; unsigned has_padding_ : 1; - unsigned is_first_for_node_ : 1; // The following are only used by NGPhysicalBoxFragment but are initialized // for all types to allow methods using them to be inlined. unsigned is_fieldset_container_ : 1; unsigned is_legacy_layout_root_ : 1; + unsigned is_painted_atomically_ : 1; + unsigned has_baseline_ : 1; + unsigned has_last_baseline_ : 1; + + // The following bitfield is shared between NGPhysicalTextFragment and + // NGPhysicalBoxFragment. + unsigned is_generated_text_or_math_fraction_ : 1; // The following bitfields are only to be used by NGPhysicalTextFragment // (it's defined here to save memory, since that class has no bitfields). - unsigned is_generated_text_ : 1; mutable unsigned ink_overflow_computed_ : 1; + // Note: We've used 32-bit bit field. If you need more bits, please think to + // share bit fields. + private: friend struct NGPhysicalFragmentTraits; void Destroy() const; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc index 448c3e00d89..a2bec293a78 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc @@ -36,9 +36,11 @@ NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm( const NGPhysicalBoxFragment& physical_fragment = To<NGPhysicalBoxFragment>(result.PhysicalFragment()); + container_builder_.SetIsInlineFormattingContext( + Node().IsInlineFormattingContextRoot()); container_builder_.SetStyleVariant(physical_fragment.StyleVariant()); container_builder_.SetIsNewFormattingContext( - physical_fragment.IsBlockFormattingContextRoot()); + physical_fragment.IsFormattingContextRoot()); container_builder_.SetInitialFragmentGeometry(params.fragment_geometry); NGExclusionSpace exclusion_space = result.ExclusionSpace(); @@ -65,11 +67,10 @@ NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm( container_builder_.SetIsPushedByFloats(); container_builder_.SetAdjoiningObjectTypes(result.AdjoiningObjectTypes()); - for (const auto& request : ConstraintSpace().BaselineRequests()) { - base::Optional<LayoutUnit> baseline = physical_fragment.Baseline(request); - if (baseline) - container_builder_.AddBaseline(request, *baseline); - } + if (physical_fragment.Baseline()) + container_builder_.SetBaseline(*physical_fragment.Baseline()); + if (physical_fragment.LastBaseline()) + container_builder_.SetLastBaseline(*physical_fragment.LastBaseline()); container_builder_.SetBlockSize(ComputeBlockSizeForFragment( ConstraintSpace(), Style(), @@ -171,7 +172,7 @@ scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() { // Only take exclusion spaces from children which don't establish their own // formatting context. - if (!fragment.IsBlockFormattingContextRoot()) + if (!fragment.IsFormattingContextRoot()) exclusion_space_ = result->ExclusionSpace(); ++it; } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc index 3ae73180868..11eec2a89c9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc @@ -38,6 +38,7 @@ NGConstraintSpace CreateIndefiniteConstraintSpaceForChild( builder.SetAvailableSize(indefinite_size); builder.SetPercentageResolutionSize(indefinite_size); builder.SetReplacedPercentageResolutionSize(indefinite_size); + builder.SetIsShrinkToFit(child.Style().LogicalWidth().IsAuto()); return builder.ToConstraintSpace(); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc index 13a027e0b88..b67b5756cc9 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc @@ -4,7 +4,6 @@ #include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h" -#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -19,15 +18,8 @@ int NGTextDecorationOffset::ComputeUnderlineOffsetForUnder( const ComputedStyle& style = text_style_; FontBaseline baseline_type = style.GetFontBaseline(); - if (decorating_box_) { - NGBaselineRequest baseline_request = { - NGBaselineAlgorithmType::kAtomicInline, - FontBaseline::kIdeographicBaseline}; - - if (base::Optional<LayoutUnit> baseline = - decorating_box_->Baseline(baseline_request)) - offset = *baseline; - } + if (decorating_box_) + offset = decorating_box_->Baseline().value_or(offset); if (offset == LayoutUnit::Max()) { // TODO(layout-dev): How do we compute the baseline offset with a diff --git a/chromium/third_party/blink/renderer/core/layout/scroll_anchor.cc b/chromium/third_party/blink/renderer/core/layout/scroll_anchor.cc index 0d86a6fc07c..d796a7f10bb 100644 --- a/chromium/third_party/blink/renderer/core/layout/scroll_anchor.cc +++ b/chromium/third_party/blink/renderer/core/layout/scroll_anchor.cc @@ -463,7 +463,8 @@ void ScrollAnchor::Adjust() { } scroller_->SetScrollOffset( - scroller_->GetScrollOffset() + FloatSize(adjustment), kAnchoringScroll); + scroller_->GetScrollOffset() + FloatSize(adjustment), + mojom::blink::ScrollType::kAnchoring); // Update UMA metric. DEFINE_STATIC_LOCAL(EnumerationHistogram, adjusted_offset_histogram, @@ -538,13 +539,15 @@ bool ScrollAnchor::RestoreAnchor(const SerializedAnchor& serialized_anchor) { ScrollOffset delta = ScrollOffset(RoundedIntSize(serialized_anchor.relative_offset)); desired_offset -= delta; - scroller_->SetScrollOffset(desired_offset, kAnchoringScroll); + scroller_->SetScrollOffset(desired_offset, + mojom::blink::ScrollType::kAnchoring); FindAnchor(); // If the above FindAnchor call failed, reset the scroll position and try // again with the next found element. if (!anchor_object_) { - scroller_->SetScrollOffset(current_offset, kAnchoringScroll); + scroller_->SetScrollOffset(current_offset, + mojom::blink::ScrollType::kAnchoring); continue; } diff --git a/chromium/third_party/blink/renderer/core/layout/scroll_anchor.h b/chromium/third_party/blink/renderer/core/layout/scroll_anchor.h index 56ebdf6a503..c637c9c9e20 100644 --- a/chromium/third_party/blink/renderer/core/layout/scroll_anchor.h +++ b/chromium/third_party/blink/renderer/core/layout/scroll_anchor.h @@ -108,7 +108,7 @@ class CORE_EXPORT ScrollAnchor final { // Notifies us that an object will be removed from the layout tree. void NotifyRemoved(LayoutObject*); - void Trace(blink::Visitor* visitor) { visitor->Trace(scroller_); } + void Trace(Visitor* visitor) { visitor->Trace(scroller_); } private: void FindAnchor(); diff --git a/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc b/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc index ae0ac5b2816..e14100e6593 100644 --- a/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/scroll_anchor_test.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/scroll_anchor.h" #include "build/build_config.h" +#include "third_party/blink/public/common/input/web_mouse_event.h" #include "third_party/blink/renderer/core/dom/static_node_list.h" #include "third_party/blink/renderer/core/frame/root_frame_viewport.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h" @@ -28,6 +29,11 @@ class ScrollAnchorTest : public testing::WithParamInterface<bool>, ScrollAnchorTest() : ScopedLayoutNGForTest(GetParam()) {} protected: + void SetUp() override { + EnableCompositing(); + RenderingTest::SetUp(); + } + void Update() { // TODO(skobes): Use SimTest instead of RenderingTest and move into // Source/web? @@ -79,6 +85,52 @@ class ScrollAnchorTest : public testing::WithParamInterface<bool>, GetDocument().QuerySelectorAll(AtomicString(serialized.selector)); EXPECT_EQ(ele_list->length(), 1u); } + + Scrollbar* VerticalScrollbarForElement(Element* element) { + return ToLayoutBox(element->GetLayoutObject()) + ->GetScrollableArea() + ->VerticalScrollbar(); + } + + void MouseDownOnVerticalScrollbar(Scrollbar* scrollbar) { + DCHECK_EQ(true, scrollbar->GetTheme().AllowsHitTest()); + int thumb_center = scrollbar->GetTheme().ThumbPosition(*scrollbar) + + scrollbar->GetTheme().ThumbLength(*scrollbar) / 2; + scrollbar_drag_point_ = + gfx::PointF(scrollbar->GetScrollableArea() + ->ConvertFromScrollbarToContainingEmbeddedContentView( + *scrollbar, IntPoint(0, thumb_center))); + scrollbar->MouseDown(blink::WebMouseEvent( + blink::WebInputEvent::kMouseDown, *scrollbar_drag_point_, + *scrollbar_drag_point_, blink::WebPointerProperties::Button::kLeft, 0, + blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now())); + } + + void MouseDragVerticalScrollbar(Scrollbar* scrollbar, float scroll_delta_y) { + DCHECK(scrollbar_drag_point_); + ScrollableArea* scroller = scrollbar->GetScrollableArea(); + scrollbar_drag_point_->Offset( + 0, scroll_delta_y * + (scrollbar->GetTheme().TrackLength(*scrollbar) - + scrollbar->GetTheme().ThumbLength(*scrollbar)) / + (scroller->MaximumScrollOffset().Height() - + scroller->MinimumScrollOffset().Height())); + scrollbar->MouseMoved(blink::WebMouseEvent( + blink::WebInputEvent::kMouseMove, *scrollbar_drag_point_, + *scrollbar_drag_point_, blink::WebPointerProperties::Button::kLeft, 0, + blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now())); + } + + void MouseUpOnVerticalScrollbar(Scrollbar* scrollbar) { + DCHECK(scrollbar_drag_point_); + scrollbar->MouseDown(blink::WebMouseEvent( + blink::WebInputEvent::kMouseUp, *scrollbar_drag_point_, + *scrollbar_drag_point_, blink::WebPointerProperties::Button::kLeft, 0, + blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now())); + scrollbar_drag_point_.reset(); + } + + base::Optional<gfx::PointF> scrollbar_drag_point_; }; INSTANTIATE_TEST_SUITE_P(All, ScrollAnchorTest, testing::Bool()); @@ -213,7 +265,7 @@ TEST_P(ScrollAnchorTest, ClearScrollAnchorsOnAncestors) { // Scrolling the nested scroller should clear the anchor on the main frame. ScrollableArea* scroller = ScrollerForElement(GetDocument().getElementById("scroller")); - scroller->ScrollBy(ScrollOffset(0, 100), kUserScroll); + scroller->ScrollBy(ScrollOffset(0, 100), mojom::blink::ScrollType::kUser); EXPECT_EQ(nullptr, GetScrollAnchor(viewport).AnchorObject()); } @@ -313,7 +365,7 @@ TEST_P(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) { Element* block1 = GetDocument().getElementById("block1"); Element* block2 = GetDocument().getElementById("block2"); - scroller->ScrollBy(ScrollOffset(0, 150), kUserScroll); + scroller->ScrollBy(ScrollOffset(0, 150), mojom::blink::ScrollType::kUser); // In this layout pass we will anchor to #block2 which has its own PaintLayer. SetHeight(block1, 200); @@ -328,6 +380,51 @@ TEST_P(ScrollAnchorTest, AnchorWithLayerInScrollingDiv) { EXPECT_EQ(250, scroller->ScrollOffsetInt().Height()); } +TEST_P(ScrollAnchorTest, AnchorWhileDraggingScrollbar) { + // Dragging the scrollbar is inherently inaccurate. Allow many pixels slop in + // the scroll position. + const int kScrollbarDragAccuracy = 10; + USE_NON_OVERLAY_SCROLLBARS(); + SetBodyInnerHTML(R"HTML( + <style> + #scroller { overflow: scroll; width: 500px; height: 400px; } + div { height: 100px } + #block2 { overflow: hidden } + #space { height: 1000px; } + </style> + <div id='scroller'><div id='space'> + <div id='block1'>abc</div> + <div id='block2'>def</div> + </div></div> + )HTML"); + Element* scroller_element = GetDocument().getElementById("scroller"); + ScrollableArea* scroller = ScrollerForElement(scroller_element); + + Element* block1 = GetDocument().getElementById("block1"); + Element* block2 = GetDocument().getElementById("block2"); + + Scrollbar* scrollbar = VerticalScrollbarForElement(scroller_element); + scroller->MouseEnteredScrollbar(*scrollbar); + MouseDownOnVerticalScrollbar(scrollbar); + MouseDragVerticalScrollbar(scrollbar, 150); + EXPECT_NEAR(150, scroller->GetScrollOffset().Height(), + kScrollbarDragAccuracy); + + // In this layout pass we will anchor to #block2 which has its own PaintLayer. + SetHeight(block1, 200); + EXPECT_NEAR(250, scroller->ScrollOffsetInt().Height(), + kScrollbarDragAccuracy); + EXPECT_EQ(block2->GetLayoutObject(), + GetScrollAnchor(scroller).AnchorObject()); + + // If we continue dragging the scroller should scroll from the newly anchored + // position. + MouseDragVerticalScrollbar(scrollbar, 10); + EXPECT_NEAR(260, scroller->ScrollOffsetInt().Height(), + kScrollbarDragAccuracy); + MouseUpOnVerticalScrollbar(scrollbar); +} + // Verify that a nested scroller with a div that has its own PaintLayer can be // removed without causing a crash. This test passes if it doesn't crash. TEST_P(ScrollAnchorTest, RemoveScrollerWithLayerInScrollingDiv) { @@ -353,7 +450,7 @@ TEST_P(ScrollAnchorTest, RemoveScrollerWithLayerInScrollingDiv) { Element* changer2 = GetDocument().getElementById("changer2"); Element* anchor = GetDocument().getElementById("anchor"); - scroller->ScrollBy(ScrollOffset(0, 150), kUserScroll); + scroller->ScrollBy(ScrollOffset(0, 150), mojom::blink::ScrollType::kUser); ScrollLayoutViewport(ScrollOffset(0, 50)); // In this layout pass both the inner and outer scroller will anchor to @@ -949,11 +1046,12 @@ TEST_P(ScrollAnchorTest, ClampAdjustsAnchorAnimation) { <div class="content" id=three></div> <div class="content" id=four></div> )HTML"); - LayoutViewport()->SetScrollOffset(ScrollOffset(0, 2000), kUserScroll); + LayoutViewport()->SetScrollOffset(ScrollOffset(0, 2000), + mojom::blink::ScrollType::kUser); Update(); GetDocument().getElementById("hidden")->setAttribute(html_names::kStyleAttr, "display:block"); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); #if !defined(OS_MACOSX) EXPECT_EQ(IntSize(0, 200), LayoutViewport() ->GetScrollAnimator() @@ -961,7 +1059,7 @@ TEST_P(ScrollAnchorTest, ClampAdjustsAnchorAnimation) { #endif GetDocument().getElementById("hidden")->setAttribute(html_names::kStyleAttr, ""); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); // The clamping scroll after resizing layout overflow to be smaller // should adjust the animation back to 0. EXPECT_EQ(IntSize(0, 0), LayoutViewport() diff --git a/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc b/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc index 174a0472eec..5ffb764ec5e 100644 --- a/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/scrollbars_test.cc @@ -30,6 +30,8 @@ #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/mojom/cursor_type.mojom-blink.h" namespace blink { @@ -74,19 +76,8 @@ class StubWebThemeEngine : public WebThemeEngine { return painted_color_scheme_[part]; } - blink::PreferredColorScheme PreferredColorScheme() const override { - return preferred_color_scheme_; - } - - void SetPreferredColorScheme( - const blink::PreferredColorScheme preferred_color_scheme) override { - preferred_color_scheme_ = preferred_color_scheme; - } - private: std::array<WebColorScheme, kPartProgressBar + 1> painted_color_scheme_; - blink::PreferredColorScheme preferred_color_scheme_ = - blink::PreferredColorScheme::kNoPreference; }; constexpr int StubWebThemeEngine::kMinimumHorizontalLength; @@ -136,8 +127,8 @@ class ScrollbarsTest : public SimTest { } void HandleMouseMoveEvent(int x, int y) { - WebMouseEvent event(WebInputEvent::kMouseMove, WebFloatPoint(x, y), - WebFloatPoint(x, y), + WebMouseEvent event(WebInputEvent::kMouseMove, gfx::PointF(x, y), + gfx::PointF(x, y), WebPointerProperties::Button::kNoButton, 0, WebInputEvent::kNoModifiers, base::TimeTicks::Now()); event.SetFrameScale(1); @@ -146,17 +137,17 @@ class ScrollbarsTest : public SimTest { } void HandleMousePressEvent(int x, int y) { - WebMouseEvent event( - WebInputEvent::kMouseDown, WebFloatPoint(x, y), WebFloatPoint(x, y), - WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kLeftButtonDown, base::TimeTicks::Now()); + WebMouseEvent event(WebInputEvent::kMouseDown, gfx::PointF(x, y), + gfx::PointF(x, y), WebPointerProperties::Button::kLeft, + 0, WebInputEvent::Modifiers::kLeftButtonDown, + base::TimeTicks::Now()); event.SetFrameScale(1); GetEventHandler().HandleMousePressEvent(event); } void HandleContextMenuEvent(int x, int y) { WebMouseEvent event( - WebInputEvent::kMouseDown, WebFloatPoint(x, y), WebFloatPoint(x, y), + WebInputEvent::kMouseDown, gfx::PointF(x, y), gfx::PointF(x, y), WebPointerProperties::Button::kNoButton, 0, WebInputEvent::Modifiers::kNoModifiers, base::TimeTicks::Now()); event.SetFrameScale(1); @@ -164,17 +155,17 @@ class ScrollbarsTest : public SimTest { } void HandleMouseReleaseEvent(int x, int y) { - WebMouseEvent event( - WebInputEvent::kMouseUp, WebFloatPoint(x, y), WebFloatPoint(x, y), - WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kNoModifiers, base::TimeTicks::Now()); + WebMouseEvent event(WebInputEvent::kMouseUp, gfx::PointF(x, y), + gfx::PointF(x, y), WebPointerProperties::Button::kLeft, + 0, WebInputEvent::Modifiers::kNoModifiers, + base::TimeTicks::Now()); event.SetFrameScale(1); GetEventHandler().HandleMouseReleaseEvent(event); } void HandleMouseMiddlePressEvent(int x, int y) { WebMouseEvent event( - WebInputEvent::kMouseDown, WebFloatPoint(x, y), WebFloatPoint(x, y), + WebInputEvent::kMouseDown, gfx::PointF(x, y), gfx::PointF(x, y), WebPointerProperties::Button::kMiddle, 0, WebInputEvent::Modifiers::kMiddleButtonDown, base::TimeTicks::Now()); event.SetFrameScale(1); @@ -183,7 +174,7 @@ class ScrollbarsTest : public SimTest { void HandleMouseMiddleReleaseEvent(int x, int y) { WebMouseEvent event( - WebInputEvent::kMouseUp, WebFloatPoint(x, y), WebFloatPoint(x, y), + WebInputEvent::kMouseUp, gfx::PointF(x, y), gfx::PointF(x, y), WebPointerProperties::Button::kMiddle, 0, WebInputEvent::Modifiers::kMiddleButtonDown, base::TimeTicks::Now()); event.SetFrameScale(1); @@ -191,10 +182,10 @@ class ScrollbarsTest : public SimTest { } void HandleMouseLeaveEvent() { - WebMouseEvent event( - WebInputEvent::kMouseMove, WebFloatPoint(1, 1), WebFloatPoint(1, 1), - WebPointerProperties::Button::kLeft, 0, - WebInputEvent::Modifiers::kLeftButtonDown, base::TimeTicks::Now()); + WebMouseEvent event(WebInputEvent::kMouseLeave, gfx::PointF(1, 1), + gfx::PointF(1, 1), WebPointerProperties::Button::kLeft, + 0, WebInputEvent::Modifiers::kLeftButtonDown, + base::TimeTicks::Now()); event.SetFrameScale(1); GetEventHandler().HandleMouseLeaveEvent(event); } @@ -215,12 +206,12 @@ class ScrollbarsTest : public SimTest { offset); } - ui::CursorType CursorType() { + ui::mojom::blink::CursorType CursorType() { return GetDocument() .GetFrame() ->GetChromeClient() .LastSetCursorForTesting() - .GetType(); + .type(); } ScrollbarTheme& GetScrollbarTheme() { @@ -235,7 +226,7 @@ class ScrollbarsTest : public SimTest { WebGestureEvent event(type, WebInputEvent::kNoModifiers, base::TimeTicks::Now(), device); - event.SetPositionInWidget(WebFloatPoint(position.X(), position.Y())); + event.SetPositionInWidget(gfx::PointF(position.X(), position.Y())); if (type == WebInputEvent::kGestureScrollUpdate) { event.data.scroll_update.delta_x = offset.Width(); @@ -370,7 +361,7 @@ TEST(ScrollbarsTestWithOwnWebViewHelper, ScrollbarSizeForUseZoomDSF) { "</body>", base_url); web_view_impl->MainFrameWidget()->UpdateAllLifecyclePhases( - WebWidget::LifecycleUpdateReason::kTest); + DocumentUpdateReason::kTest); Document* document = To<LocalFrame>(web_view_impl->GetPage()->MainFrame())->GetDocument(); @@ -577,6 +568,45 @@ TEST_F(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) { EXPECT_TRUE(scrollable_root->HorizontalScrollbar()->FrameRect().IsEmpty()); } +// Ensure that overlay scrollbars are not created, even in overflow:scroll, +// situations when there's no overflow. Specifically, after style-only changes. +TEST_F(ScrollbarsTest, OverlayScrolblarNotCreatedInUnscrollableAxis) { + // This test is specifically checking the behavior when overlay scrollbars + // are enabled. + ENABLE_OVERLAY_SCROLLBARS(true); + + WebView().MainFrameWidget()->Resize(WebSize(800, 600)); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <style> + #target { + width: 100px; + height: 100px; + overflow-y: scroll; + opacity: 0.5; + } + </style> + <div id="target"></div> + )HTML"); + + Compositor().BeginFrame(); + + auto* target = GetDocument().getElementById("target"); + auto* scrollable_area = target->GetLayoutBox()->GetScrollableArea(); + + ASSERT_FALSE(scrollable_area->VerticalScrollbar()); + ASSERT_FALSE(scrollable_area->HorizontalScrollbar()); + + // Mutate the opacity so that we cause a style-only change. + target->setAttribute(html_names::kStyleAttr, "opacity: 0.9"); + Compositor().BeginFrame(); + + ASSERT_FALSE(scrollable_area->VerticalScrollbar()); + ASSERT_FALSE(scrollable_area->HorizontalScrollbar()); +} + TEST_F(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { WebView().MainFrameWidget()->Resize(WebSize(200, 200)); SimRequest request("https://example.com/test.html", "text/html"); @@ -603,11 +633,11 @@ TEST_F(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) { WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, base::TimeTicks::Now(), WebGestureDevice::kTouchpad); scroll_begin.SetPositionInWidget( - WebFloatPoint(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2, - scrollable->OffsetTop())); + gfx::PointF(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2, + scrollable->OffsetTop())); scroll_begin.SetPositionInScreen( - WebFloatPoint(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2, - scrollable->OffsetTop())); + gfx::PointF(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2, + scrollable->OffsetTop())); scroll_begin.data.scroll_begin.delta_x_hint = 0; scroll_begin.data.scroll_begin.delta_y_hint = 10; scroll_begin.SetFrameScale(1); @@ -647,8 +677,8 @@ TEST_F(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) { ScrollableArea* frame_scroller_area = frame_view->LayoutViewport(); // Scrollbars are hidden at start. - scroller_area->SetScrollbarsHiddenIfOverlay(true); - frame_scroller_area->SetScrollbarsHiddenIfOverlay(true); + scroller_area->SetScrollbarsHiddenForTesting(true); + frame_scroller_area->SetScrollbarsHiddenForTesting(true); ASSERT_TRUE(scroller_area->HorizontalScrollbar()); ASSERT_TRUE(scroller_area->VerticalScrollbar()); ASSERT_TRUE(frame_scroller_area->HorizontalScrollbar()); @@ -666,23 +696,23 @@ TEST_F(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) { EXPECT_FALSE( scroller_area->VerticalScrollbar()->ShouldParticipateInHitTesting()); - frame_scroller_area->SetScrollbarsHiddenIfOverlay(false); + frame_scroller_area->SetScrollbarsHiddenForTesting(false); EXPECT_TRUE(frame_scroller_area->HorizontalScrollbar() ->ShouldParticipateInHitTesting()); EXPECT_TRUE(frame_scroller_area->VerticalScrollbar() ->ShouldParticipateInHitTesting()); - frame_scroller_area->SetScrollbarsHiddenIfOverlay(true); + frame_scroller_area->SetScrollbarsHiddenForTesting(true); EXPECT_FALSE(frame_scroller_area->HorizontalScrollbar() ->ShouldParticipateInHitTesting()); EXPECT_FALSE(frame_scroller_area->VerticalScrollbar() ->ShouldParticipateInHitTesting()); - scroller_area->SetScrollbarsHiddenIfOverlay(false); + scroller_area->SetScrollbarsHiddenForTesting(false); EXPECT_TRUE( scroller_area->HorizontalScrollbar()->ShouldParticipateInHitTesting()); EXPECT_TRUE( scroller_area->VerticalScrollbar()->ShouldParticipateInHitTesting()); - scroller_area->SetScrollbarsHiddenIfOverlay(true); + scroller_area->SetScrollbarsHiddenForTesting(true); EXPECT_FALSE( scroller_area->HorizontalScrollbar()->ShouldParticipateInHitTesting()); EXPECT_FALSE( @@ -733,7 +763,7 @@ TEST_F(ScrollbarsTest, MouseOverScrollbarInCustomCursorElement) { HandleMouseMoveEvent(195, 5); - EXPECT_EQ(ui::CursorType::kPointer, CursorType()); + EXPECT_EQ(ui::mojom::blink::CursorType::kPointer, CursorType()); } // Ensure mouse cursor should be override when hovering over the custom @@ -789,7 +819,7 @@ TEST_F(ScrollbarsTest, MouseOverCustomScrollbarInCustomCursorElement) { HandleMouseMoveEvent(195, 5); - EXPECT_EQ(ui::CursorType::kMove, CursorType()); + EXPECT_EQ(ui::mojom::blink::CursorType::kMove, CursorType()); } // Makes sure that mouse hover over an overlay scrollbar doesn't activate @@ -824,7 +854,7 @@ TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { .MainFrameImpl() ->GetFrameView() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(false); + ->SetScrollbarsHiddenForTesting(false); Document& document = GetDocument(); Element* a_tag = document.getElementById("a"); @@ -844,13 +874,13 @@ TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { // Mouse over link. Mouse cursor should be hand. HandleMouseMoveEvent(a_tag->OffsetLeft(), a_tag->OffsetTop()); - EXPECT_EQ(ui::CursorType::kHand, CursorType()); + EXPECT_EQ(ui::mojom::blink::CursorType::kHand, CursorType()); // Mouse over enabled overlay scrollbar. Mouse cursor should be pointer and no // active hover element. HandleMouseMoveEvent(x, y); - EXPECT_EQ(ui::CursorType::kPointer, CursorType()); + EXPECT_EQ(ui::mojom::blink::CursorType::kPointer, CursorType()); HandleMousePressEvent(x, y); @@ -865,7 +895,7 @@ TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { .MainFrameImpl() ->GetFrameView() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(true); + ->SetScrollbarsHiddenForTesting(true); // Ensure hittest only has link hit_test_result = HitTest(x, y); @@ -876,7 +906,7 @@ TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) { HandleMouseMoveEvent(x, y); - EXPECT_EQ(ui::CursorType::kHand, CursorType()); + EXPECT_EQ(ui::mojom::blink::CursorType::kHand, CursorType()); HandleMousePressEvent(x, y); @@ -988,7 +1018,7 @@ TEST_F(ScrollbarsTest, MouseOverScrollbarAndIFrame) { .MainFrameImpl() ->GetFrameView() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(false); + ->SetScrollbarsHiddenForTesting(false); frame_resource.Complete("<!DOCTYPE html>"); Compositor().BeginFrame(); @@ -1026,7 +1056,7 @@ TEST_F(ScrollbarsTest, MouseOverScrollbarAndIFrame) { .MainFrameImpl() ->GetFrameView() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(true); + ->SetScrollbarsHiddenForTesting(true); // Ensure hittest has IFRAME and no scrollbar. hit_test_result = HitTest(196, 5); @@ -1510,8 +1540,9 @@ TEST_F(ScrollbarsTestWithVirtualTimer, TestNonCompositedOverlayScrollbarsFade) { RunTasksForPeriod(kMockOverlayFadeOutDelay); EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay()); - scrollable_area->SetScrollOffset(ScrollOffset(10, 10), kProgrammaticScroll, - kScrollBehaviorInstant); + scrollable_area->SetScrollOffset(ScrollOffset(10, 10), + mojom::blink::ScrollType::kProgrammatic, + mojom::blink::ScrollBehavior::kInstant); EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay()); RunTasksForPeriod(kMockOverlayFadeOutDelay); @@ -1533,8 +1564,9 @@ TEST_F(ScrollbarsTestWithVirtualTimer, TestNonCompositedOverlayScrollbarsFade) { // Non-composited scrollbars don't fade out while mouse is over. EXPECT_TRUE(scrollable_area->VerticalScrollbar()); - scrollable_area->SetScrollOffset(ScrollOffset(20, 20), kProgrammaticScroll, - kScrollBehaviorInstant); + scrollable_area->SetScrollOffset(ScrollOffset(20, 20), + mojom::blink::ScrollType::kProgrammatic, + mojom::blink::ScrollBehavior::kInstant); EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay()); scrollable_area->MouseEnteredScrollbar(*scrollable_area->VerticalScrollbar()); RunTasksForPeriod(kMockOverlayFadeOutDelay); @@ -1693,7 +1725,7 @@ TEST_P(ScrollbarAppearanceTest, HugeScrollingThumbPosition) { Compositor().BeginFrame(); scrollable_area->SetScrollOffset(ScrollOffset(0, 10000000), - kProgrammaticScroll); + mojom::blink::ScrollType::kProgrammatic); Compositor().BeginFrame(); @@ -2124,7 +2156,7 @@ TEST_F(ScrollbarsTest, PaintLayerScrollableArea* scrollable_div = ToLayoutBox(div->GetLayoutObject())->GetScrollableArea(); - scrollable_div->SetScrollbarsHiddenIfOverlay(false); + scrollable_div->SetScrollbarsHiddenForTesting(false); ASSERT_TRUE(scrollable_div); ASSERT_TRUE(scrollable_div->GetPageScrollbarTheme().UsesOverlayScrollbars()); ASSERT_TRUE(scrollable_div->VerticalScrollbar()); @@ -2137,7 +2169,7 @@ TEST_F(ScrollbarsTest, // After paint layer in scrollable dispose, we can still call scrollbar hidden // just not change scrollbar. - scrollable_div->SetScrollbarsHiddenIfOverlay(true); + scrollable_div->SetScrollbarsHiddenForTesting(true); EXPECT_FALSE(scrollable_div->ScrollbarsHiddenIfOverlay()); } @@ -2179,7 +2211,7 @@ TEST_F(ScrollbarsTest, PLSADisposeShouldClearPointerInLayers) { ASSERT_TRUE(graphics_layer); div->setAttribute(html_names::kClassAttr, "hide"); - document.UpdateStyleAndLayout(); + document.UpdateStyleAndLayout(DocumentUpdateReason::kTest); EXPECT_FALSE(paint_layer->GetScrollableArea()); } @@ -2219,7 +2251,7 @@ TEST_F(ScrollbarsTest, OverlayScrollbarHitTest) { .MainFrameImpl() ->GetFrameView() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(false); + ->SetScrollbarsHiddenForTesting(false); frame_resource.Complete("<!DOCTYPE html><body style='height: 999px'></body>"); Compositor().BeginFrame(); @@ -2230,7 +2262,7 @@ TEST_F(ScrollbarsTest, OverlayScrollbarHitTest) { iframe_element->contentDocument() ->View() ->LayoutViewport() - ->SetScrollbarsHiddenIfOverlay(false); + ->SetScrollbarsHiddenForTesting(false); // Hit test on and off the main frame scrollbar. HitTestResult hit_test_result = HitTest(295, 5); @@ -2315,12 +2347,13 @@ TEST_F(ScrollbarsTest, MiddleDownShouldNotAffectScrollbarPress) { EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); // Move mouse out of scrollbar with press. - WebMouseEvent event(WebInputEvent::kMouseMove, WebFloatPoint(5, 5), - WebFloatPoint(5, 5), WebPointerProperties::Button::kLeft, - 0, WebInputEvent::Modifiers::kLeftButtonDown, + WebMouseEvent event(WebInputEvent::kMouseMove, gfx::PointF(5, 5), + gfx::PointF(5, 5), WebPointerProperties::Button::kLeft, 0, + WebInputEvent::Modifiers::kLeftButtonDown, base::TimeTicks::Now()); event.SetFrameScale(1); - GetEventHandler().HandleMouseLeaveEvent(event); + GetEventHandler().HandleMouseMoveEvent(event, Vector<WebMouseEvent>(), + Vector<WebMouseEvent>()); EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart); // Middle click should not release scrollbar press state. @@ -2614,7 +2647,7 @@ TEST_F(ScrollbarsTest, CheckScrollCornerIfThereIsNoScrollbar) { // Make the container non-scrollable so the scrollbar and corner disappear. element->setAttribute(html_names::kStyleAttr, "width: 100px;"); - GetDocument().UpdateStyleAndLayout(); + GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); EXPECT_FALSE(scrollable_container->HasScrollbar()); EXPECT_FALSE(scrollable_container->ScrollCorner()); @@ -2675,8 +2708,9 @@ TEST_F(ScrollbarsTestWithVirtualTimer, Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); // Scroll to bottom. - scrollable_area->SetScrollOffset(ScrollOffset(0, 400), kProgrammaticScroll, - kScrollBehaviorInstant); + scrollable_area->SetScrollOffset(ScrollOffset(0, 400), + mojom::blink::ScrollType::kProgrammatic, + mojom::blink::ScrollBehavior::kInstant); EXPECT_EQ(scrollable_area->ScrollOffsetInt(), IntSize(0, 200)); HandleMouseMoveEvent(195, 195); @@ -2803,7 +2837,8 @@ INSTANTIATE_TEST_SUITE_P(NonOverlay, TEST_P(ScrollbarColorSchemeTest, MAYBE_ThemeEnginePaint) { ScopedTestingPlatformSupport<ScrollbarTestingPlatformSupport> platform; - ScopedCSSColorSchemeForTest css_feature_scope(true); + ScopedCSSColorSchemeForTest color_scheme_scope(true); + ScopedCSSColorSchemeUARenderingForTest color_scheme_ua_scope(true); WebView().MainFrameWidget()->Resize(WebSize(800, 600)); SimRequest request("https://example.com/test.html", "text/html"); @@ -2827,9 +2862,8 @@ TEST_P(ScrollbarColorSchemeTest, MAYBE_ThemeEnginePaint) { </div> )HTML"); - ColorSchemeHelper color_scheme_helper; - color_scheme_helper.SetPreferredColorScheme(GetDocument(), - PreferredColorScheme::kDark); + ColorSchemeHelper color_scheme_helper(GetDocument()); + color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark); Compositor().BeginFrame(); diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape.cc b/chromium/third_party/blink/renderer/core/layout/shapes/shape.cc index f961184aee3..702e55999e0 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape.cc +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape.cc @@ -234,7 +234,8 @@ std::unique_ptr<Shape> Shape::CreateEmptyRasterShape(WritingMode writing_mode, static bool ExtractImageData(Image* image, const IntSize& image_size, - ArrayBufferContents& contents) { + ArrayBufferContents& contents, + RespectImageOrientationEnum respect_orientation) { if (!image) return false; @@ -260,8 +261,8 @@ static bool ExtractImageData(Image* image, canvas.clear(SK_ColorTRANSPARENT); image->Draw(&canvas, flags, FloatRect(image_dest_rect), image_source_rect, - kDoNotRespectImageOrientation, - Image::kDoNotClampImageToSourceRect, Image::kSyncDecode); + respect_orientation, Image::kDoNotClampImageToSourceRect, + Image::kSyncDecode); size_t size_in_bytes; if (!StaticBitmapImage::GetSizeInBytes(image_dest_rect, color_params) @@ -332,12 +333,14 @@ static bool IsValidRasterShapeSize(const IntSize& size) { return size.Area() * 4 < max_image_size_bytes; } -std::unique_ptr<Shape> Shape::CreateRasterShape(Image* image, - float threshold, - const LayoutRect& image_r, - const LayoutRect& margin_r, - WritingMode writing_mode, - float margin) { +std::unique_ptr<Shape> Shape::CreateRasterShape( + Image* image, + float threshold, + const LayoutRect& image_r, + const LayoutRect& margin_r, + WritingMode writing_mode, + float margin, + RespectImageOrientationEnum respect_orientation) { IntRect image_rect = PixelSnappedIntRect(image_r); IntRect margin_rect = PixelSnappedIntRect(margin_r); @@ -347,8 +350,10 @@ std::unique_ptr<Shape> Shape::CreateRasterShape(Image* image, } ArrayBufferContents contents; - if (!ExtractImageData(image, image_rect.Size(), contents)) + if (!ExtractImageData(image, image_rect.Size(), contents, + respect_orientation)) { return CreateEmptyRasterShape(writing_mode, margin); + } std::unique_ptr<RasterShapeIntervals> intervals = ExtractIntervalsFromImageData(contents, threshold, image_rect, diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape.h b/chromium/third_party/blink/renderer/core/layout/shapes/shape.h index 45be4d014bc..77f7cfacb31 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape.h +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape.h @@ -35,6 +35,7 @@ #include "third_party/blink/renderer/core/style/basic_shapes.h" #include "third_party/blink/renderer/core/style/style_image.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h" +#include "third_party/blink/renderer/platform/graphics/image_orientation.h" #include "third_party/blink/renderer/platform/graphics/path.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" @@ -84,7 +85,8 @@ class CORE_EXPORT Shape { const LayoutRect& image_rect, const LayoutRect& margin_rect, WritingMode, - float margin); + float margin, + RespectImageOrientationEnum); static std::unique_ptr<Shape> CreateLayoutBoxShape(const FloatRoundedRect&, WritingMode, float margin); diff --git a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc index 96cbb7e0912..c9f477b2f0d 100644 --- a/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc +++ b/chromium/third_party/blink/renderer/core/layout/shapes/shape_outside_info.cc @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h" #include <memory> + #include "base/auto_reset.h" #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/inspector/console_message.h" @@ -39,6 +40,7 @@ #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" +#include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" namespace blink { @@ -152,10 +154,10 @@ static bool CheckShapeImageOrigin(Document& document, const KURL& url = image_resource.Url(); String url_string = url.IsNull() ? "''" : url.ElidedString(); - document.AddConsoleMessage( - ConsoleMessage::Create(mojom::ConsoleMessageSource::kSecurity, - mojom::ConsoleMessageLevel::kError, - "Unsafe attempt to load URL " + url_string + ".")); + document.AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( + mojom::ConsoleMessageSource::kSecurity, + mojom::ConsoleMessageLevel::kError, + "Unsafe attempt to load URL " + url_string + ".")); return false; } @@ -186,7 +188,8 @@ std::unique_ptr<Shape> ShapeOutsideInfo::CreateShapeForImage( DCHECK(!style_image->IsPendingImage()); const LayoutSize& image_size = RoundedLayoutSize(style_image->ImageSize( layout_box_.GetDocument(), layout_box_.StyleRef().EffectiveZoom(), - reference_box_logical_size_)); + reference_box_logical_size_, + LayoutObject::ShouldRespectImageOrientation(&layout_box_))); const LayoutRect& margin_rect = GetShapeImageMarginRect(layout_box_, reference_box_logical_size_); @@ -199,9 +202,9 @@ std::unique_ptr<Shape> ShapeOutsideInfo::CreateShapeForImage( style_image->GetImage(layout_box_, layout_box_.GetDocument(), layout_box_.StyleRef(), FloatSize(image_size)); - return Shape::CreateRasterShape(image.get(), shape_image_threshold, - image_rect, margin_rect, writing_mode, - margin); + return Shape::CreateRasterShape( + image.get(), shape_image_threshold, image_rect, margin_rect, writing_mode, + margin, LayoutObject::ShouldRespectImageOrientation(&layout_box_)); } const Shape& ShapeOutsideInfo::ComputedShape() const { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/BUILD.gn b/chromium/third_party/blink/renderer/core/layout/svg/BUILD.gn index 3c0d78bbf16..b01361cc458 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/BUILD.gn +++ b/chromium/third_party/blink/renderer/core/layout/svg/BUILD.gn @@ -12,6 +12,8 @@ blink_core_sources("svg_layout") { "layout_svg_container.h", "layout_svg_ellipse.cc", "layout_svg_ellipse.h", + "layout_svg_filter_primitive.cc", + "layout_svg_filter_primitive.h", "layout_svg_foreign_object.cc", "layout_svg_foreign_object.h", "layout_svg_hidden_container.cc", @@ -34,8 +36,6 @@ blink_core_sources("svg_layout") { "layout_svg_resource_container.h", "layout_svg_resource_filter.cc", "layout_svg_resource_filter.h", - "layout_svg_resource_filter_primitive.cc", - "layout_svg_resource_filter_primitive.h", "layout_svg_resource_gradient.cc", "layout_svg_resource_gradient.h", "layout_svg_resource_linear_gradient.cc", diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.h index 8c8dfbfff4a..e95571ca527 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_block.h @@ -82,11 +82,6 @@ class LayoutSVGBlock : public LayoutBlockFlow { const HitTestLocation&, const PhysicalOffset& accumulated_offset, HitTestAction) override; - - // The inherited version doesn't check for SVG effects. - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override { - return false; - } }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc index c9085ba3376..da375b67877 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.cc @@ -84,7 +84,7 @@ void LayoutSVGContainer::UpdateLayout() { void LayoutSVGContainer::AddChild(LayoutObject* child, LayoutObject* before_child) { LayoutSVGModelObject::AddChild(child, before_child); - SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef()); + SVGResourcesCache::ClientWasAddedToTree(*child); bool should_isolate_descendants = (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) || @@ -172,7 +172,6 @@ void LayoutSVGContainer::UpdateCachedBoundaries() { SVGLayoutSupport::ComputeContainerBoundingBoxes( this, object_bounding_box_, object_bounding_box_valid_, stroke_bounding_box_, local_visual_rect_); - GetElement()->SetNeedsResizeObserverUpdate(); } bool LayoutSVGContainer::NodeAtPoint(HitTestResult& result, diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.h index ba23efa422d..c0ec38b1592 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_container.h @@ -24,6 +24,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_CONTAINER_H_ #include "third_party/blink/renderer/core/layout/svg/layout_svg_model_object.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -108,7 +109,12 @@ class LayoutSVGContainer : public LayoutSVGModelObject { mutable bool has_non_isolated_blending_descendants_dirty_ : 1; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGContainer, IsSVGContainer()); +template <> +struct DowncastTraits<LayoutSVGContainer> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsSVGContainer(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter_primitive.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc index bbc7bb39112..7ab539720ab 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter_primitive.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.cc @@ -25,12 +25,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter_primitive.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.h" #include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" namespace blink { +LayoutSVGFilterPrimitive::LayoutSVGFilterPrimitive( + SVGFilterPrimitiveStandardAttributes* filter_primitive_element) + : LayoutObject(filter_primitive_element) {} + static bool CurrentColorChanged(StyleDifference diff, const StyleColor& color) { return diff.TextDecorationOrColorChanged() && color.IsCurrentColor(); } @@ -51,15 +55,11 @@ static void CheckForColorChange(SVGFilterPrimitiveStandardAttributes& element, element.PrimitiveAttributeChanged(attr_name); } -void LayoutSVGResourceFilterPrimitive::StyleDidChange( - StyleDifference diff, - const ComputedStyle* old_style) { - LayoutSVGHiddenContainer::StyleDidChange(diff, old_style); - +void LayoutSVGFilterPrimitive::StyleDidChange(StyleDifference diff, + const ComputedStyle* old_style) { if (!old_style) return; - DCHECK(GetElement()); - auto& element = To<SVGFilterPrimitiveStandardAttributes>(*GetElement()); + auto& element = To<SVGFilterPrimitiveStandardAttributes>(*GetNode()); const SVGComputedStyle& new_style = StyleRef().SvgStyle(); if (IsA<SVGFEFloodElement>(element) || IsA<SVGFEDropShadowElement>(element)) { CheckForColorChange(element, svg_names::kFloodColorAttr, diff, @@ -80,4 +80,8 @@ void LayoutSVGResourceFilterPrimitive::StyleDidChange( } } +void LayoutSVGFilterPrimitive::UpdateLayout() { + ClearNeedsLayout(); +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter_primitive.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.h index d52bdae9df2..b0e16f447fe 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter_primitive.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_filter_primitive.h @@ -24,34 +24,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_FILTER_PRIMITIVE_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_FILTER_PRIMITIVE_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_FILTER_PRIMITIVE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_FILTER_PRIMITIVE_H_ -#include "third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h" +#include "third_party/blink/renderer/core/layout/layout_object.h" namespace blink { -class LayoutSVGResourceFilterPrimitive final : public LayoutSVGHiddenContainer { +class SVGFilterPrimitiveStandardAttributes; + +class LayoutSVGFilterPrimitive final : public LayoutObject { public: - explicit LayoutSVGResourceFilterPrimitive( - SVGElement* filter_primitive_element) - : LayoutSVGHiddenContainer(filter_primitive_element) {} + explicit LayoutSVGFilterPrimitive(SVGFilterPrimitiveStandardAttributes*); + private: bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override { return false; } void StyleDidChange(StyleDifference, const ComputedStyle*) override; + void UpdateLayout() override; - const char* GetName() const override { - return "LayoutSVGResourceFilterPrimitive"; - } + const char* GetName() const override { return "LayoutSVGFilterPrimitive"; } bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectSVGResourceFilterPrimitive || - LayoutSVGHiddenContainer::IsOfType(type); + return type == kLayoutObjectSVG || + type == kLayoutObjectSVGFilterPrimitive || + LayoutObject::IsOfType(type); + } + FloatRect ObjectBoundingBox() const override { return FloatRect(); } + FloatRect VisualRectInLocalSVGCoordinates() const override { + return FloatRect(); + } + FloatRect LocalBoundingBoxRectForAccessibility() const override { + return FloatRect(); } }; } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_FILTER_PRIMITIVE_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_FILTER_PRIMITIVE_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h index 838ac43a38f..9d38aab5c10 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h @@ -22,6 +22,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_FOREIGN_OBJECT_H_ #include "third_party/blink/renderer/core/layout/svg/layout_svg_block.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -104,7 +105,12 @@ class LayoutSVGForeignObject final : public LayoutSVGBlock { bool needs_transform_update_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGForeignObject, IsSVGForeignObject()); +template <> +struct DowncastTraits<LayoutSVGForeignObject> { + static bool AllowFrom(const LayoutObject& object) { + return object.IsSVGForeignObject(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h index 6f01462ff3c..06e9e78248a 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h @@ -45,9 +45,6 @@ class LayoutSVGHiddenContainer : public LayoutSVGContainer { private: // LayoutSVGHiddenContainer paints nothing. void Paint(const PaintInfo&) const final {} - bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final { - return true; - } PhysicalRect VisualRectInDocument(VisualRectFlags) const final { return PhysicalRect(); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc index df479631785..964f0769a77 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.cc @@ -27,8 +27,10 @@ #include "third_party/blink/renderer/core/html/media/media_element_parser_helpers.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" +#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h" #include "third_party/blink/renderer/core/layout/layout_analyzer.h" #include "third_party/blink/renderer/core/layout/layout_image_resource.h" +#include "third_party/blink/renderer/core/layout/layout_replaced.h" #include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" @@ -38,6 +40,7 @@ #include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h" #include "third_party/blink/renderer/core/paint/image_element_timing.h" #include "third_party/blink/renderer/core/paint/svg_image_painter.h" +#include "third_party/blink/renderer/core/svg/graphics/svg_image.h" #include "third_party/blink/renderer/core/svg/svg_image_element.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h" @@ -78,37 +81,55 @@ static float ResolveHeightForRatio(float width, return width * intrinsic_ratio.Height() / intrinsic_ratio.Width(); } -IntSize LayoutSVGImage::GetOverriddenIntrinsicSize() const { - if (auto* svg_image = DynamicTo<SVGImageElement>(GetElement())) { - if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) - return svg_image->GetOverriddenIntrinsicSize(); - } - return IntSize(); +bool LayoutSVGImage::HasOverriddenIntrinsicSize() const { + if (!RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled()) + return false; + auto* svg_image_element = DynamicTo<SVGImageElement>(GetElement()); + return svg_image_element && svg_image_element->IsDefaultIntrinsicSize(); } FloatSize LayoutSVGImage::CalculateObjectSize() const { - FloatSize intrinsic_size = FloatSize(GetOverriddenIntrinsicSize()); + FloatSize intrinsic_size; ImageResourceContent* cached_image = image_resource_->CachedImage(); - if (intrinsic_size.IsEmpty()) { + bool has_intrinsic_ratio = true; + if (HasOverriddenIntrinsicSize()) { + intrinsic_size = FloatSize(LayoutReplaced::kDefaultWidth, + LayoutReplaced::kDefaultHeight); + } else { if (!cached_image || cached_image->ErrorOccurred() || !cached_image->IsSizeAvailable()) return object_bounding_box_.Size(); - intrinsic_size = FloatSize(cached_image->GetImage()->Size()); + RespectImageOrientationEnum respect_orientation = + LayoutObject::ShouldRespectImageOrientation(this); + intrinsic_size = cached_image->GetImage()->SizeAsFloat(respect_orientation); + if (auto* svg_image = DynamicTo<SVGImage>(cached_image->GetImage())) { + IntrinsicSizingInfo intrinsic_sizing_info; + has_intrinsic_ratio &= svg_image->GetIntrinsicSizingInfo(intrinsic_sizing_info); + has_intrinsic_ratio &= !intrinsic_sizing_info.aspect_ratio.IsEmpty(); + } } if (StyleRef().Width().IsAuto() && StyleRef().Height().IsAuto()) return intrinsic_size; - if (StyleRef().Height().IsAuto()) - return FloatSize( - object_bounding_box_.Width(), - ResolveHeightForRatio(object_bounding_box_.Width(), intrinsic_size)); + if (StyleRef().Height().IsAuto()) { + if (has_intrinsic_ratio) { + return FloatSize( + object_bounding_box_.Width(), + ResolveHeightForRatio(object_bounding_box_.Width(), intrinsic_size)); + } + return FloatSize(object_bounding_box_.Width(), intrinsic_size.Height()); + } DCHECK(StyleRef().Width().IsAuto()); - return FloatSize( - ResolveWidthForRatio(object_bounding_box_.Height(), intrinsic_size), - object_bounding_box_.Height()); + if (has_intrinsic_ratio) { + return FloatSize( + ResolveWidthForRatio(object_bounding_box_.Height(), intrinsic_size), + object_bounding_box_.Height()); + } + + return FloatSize(intrinsic_size.Width(), object_bounding_box_.Height()); } bool LayoutSVGImage::UpdateBoundingBox() { @@ -126,7 +147,6 @@ bool LayoutSVGImage::UpdateBoundingBox() { object_bounding_box_.SetSize(CalculateObjectSize()); if (old_object_bounding_box != object_bounding_box_) { - GetElement()->SetNeedsResizeObserverUpdate(); SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kImage); needs_boundaries_update_ = true; } @@ -174,7 +194,7 @@ void LayoutSVGImage::UpdateLayout() { DCHECK(!needs_transform_update_); if (auto* svg_image_element = DynamicTo<SVGImageElement>(GetElement())) { - media_element_parser_helpers::ReportUnsizedMediaViolation( + media_element_parser_helpers::CheckUnsizedMediaViolation( this, svg_image_element->IsDefaultIntrinsicSize()); } ClearNeedsLayout(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.h index a56500db2f8..bc8b9227afa 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_image.h @@ -80,7 +80,7 @@ class LayoutSVGImage final : public LayoutSVGModelObject { } FloatSize CalculateObjectSize() const; - IntSize GetOverriddenIntrinsicSize() const; + bool HasOverriddenIntrinsicSize() const; bool needs_boundaries_update_ : 1; bool needs_transform_update_ : 1; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc index 78e884af5cf..04b832c1ddf 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc @@ -60,27 +60,27 @@ InlineFlowBox* LayoutSVGInline::CreateInlineFlowBox() { } FloatRect LayoutSVGInline::ObjectBoundingBox() const { - if (const LayoutSVGText* text_root = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - return text_root->ObjectBoundingBox(); - - return FloatRect(); + FloatRect bounds; + for (InlineFlowBox* box : *LineBoxes()) + bounds.Unite(FloatRect(box->FrameRect())); + return bounds; } FloatRect LayoutSVGInline::StrokeBoundingBox() const { - if (const LayoutSVGText* text_root = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - return text_root->StrokeBoundingBox(); - - return FloatRect(); + if (!FirstLineBox()) + return FloatRect(); + return SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, ObjectBoundingBox()); } FloatRect LayoutSVGInline::VisualRectInLocalSVGCoordinates() const { - if (const LayoutSVGText* text_root = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - return text_root->VisualRectInLocalSVGCoordinates(); - - return FloatRect(); + if (!FirstLineBox()) + return FloatRect(); + const LayoutSVGText* text_root = + LayoutSVGText::LocateLayoutSVGTextAncestor(this); + if (!text_root) + return FloatRect(); + return SVGLayoutSupport::ComputeVisualRectForText( + *this, ObjectBoundingBox(), text_root->ObjectBoundingBox()); } PhysicalRect LayoutSVGInline::VisualRectInDocument( @@ -103,18 +103,10 @@ const LayoutObject* LayoutSVGInline::PushMappingToContainer( void LayoutSVGInline::AbsoluteQuads(Vector<FloatQuad>& quads, MapCoordinatesFlags mode) const { - const LayoutSVGText* text_root = - LayoutSVGText::LocateLayoutSVGTextAncestor(this); - if (!text_root) - return; - - FloatRect text_bounding_box = text_root->StrokeBoundingBox(); for (InlineFlowBox* box : *LineBoxes()) { + FloatRect box_rect(box->FrameRect()); quads.push_back(LocalToAbsoluteQuad( - FloatRect(text_bounding_box.X() + box->X().ToFloat(), - text_bounding_box.Y() + box->Y().ToFloat(), - box->Width().ToFloat(), box->Height().ToFloat()), - mode)); + SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, box_rect), mode)); } } @@ -145,19 +137,15 @@ void LayoutSVGInline::StyleDidChange(StyleDifference diff, void LayoutSVGInline::AddChild(LayoutObject* child, LayoutObject* before_child) { LayoutInline::AddChild(child, before_child); - SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef()); - - if (LayoutSVGText* text_layout_object = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - text_layout_object->SubtreeChildWasAdded(); + SVGResourcesCache::ClientWasAddedToTree(*child); + LayoutSVGText::NotifySubtreeStructureChanged( + this, layout_invalidation_reason::kChildChanged); } void LayoutSVGInline::RemoveChild(LayoutObject* child) { SVGResourcesCache::ClientWillBeRemovedFromTree(*child); - - if (LayoutSVGText* text_layout_object = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - text_layout_object->SubtreeChildWillBeRemoved(); + LayoutSVGText::NotifySubtreeStructureChanged( + this, layout_invalidation_reason::kChildChanged); LayoutInline::RemoveChild(child); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h index 6322b6218cd..19b362d26fa 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline.h @@ -38,11 +38,6 @@ class LayoutSVGInline : public LayoutInline { bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override; - // Chapter 10.4 of the SVG Specification say that we should use the - // object bounding box of the parent text element. - // We search for the root text element and take its bounding box. - // It is also necessary to take the stroke and visual rect of this element, - // since we need it for filters. FloatRect ObjectBoundingBox() const final; FloatRect StrokeBoundingBox() const final; FloatRect VisualRectInLocalSVGCoordinates() const final; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc index 5a089322736..4df02e440c4 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.cc @@ -59,9 +59,8 @@ LayoutSVGInlineText::LayoutSVGInlineText(Node* n, void LayoutSVGInlineText::TextDidChange() { SetTextInternal(NormalizeWhitespace(GetText().Impl())); LayoutText::TextDidChange(); - if (LayoutSVGText* text_layout_object = - LayoutSVGText::LocateLayoutSVGTextAncestor(this)) - text_layout_object->SubtreeTextDidChange(); + LayoutSVGText::NotifySubtreeStructureChanged( + this, layout_invalidation_reason::kTextChanged); } void LayoutSVGInlineText::StyleDidChange(StyleDifference diff, @@ -69,10 +68,9 @@ void LayoutSVGInlineText::StyleDidChange(StyleDifference diff, LayoutText::StyleDidChange(diff, old_style); UpdateScaledFont(); - bool new_preserves = - Style() ? StyleRef().WhiteSpace() == EWhiteSpace::kPre : false; + bool new_preserves = StyleRef().WhiteSpace() == EWhiteSpace::kPre; bool old_preserves = - old_style ? old_style->WhiteSpace() == EWhiteSpace::kPre : false; + old_style && old_style->WhiteSpace() == EWhiteSpace::kPre; if (old_preserves != new_preserves) { ForceSetText(OriginalText()); return; @@ -153,7 +151,7 @@ bool LayoutSVGInlineText::CharacterStartsNewTextChunk(int position) const { PositionWithAffinity LayoutSVGInlineText::PositionForPoint( const PhysicalOffset& point) const { - if (!HasTextBoxes() || !TextLength()) + if (!HasInlineFragments() || !TextLength()) return CreatePositionWithAffinity(0); DCHECK(scaling_factor_); @@ -178,10 +176,10 @@ PositionWithAffinity LayoutSVGInlineText::PositionForPoint( SVGInlineTextBox* closest_distance_box = nullptr; for (InlineTextBox* box : TextBoxes()) { - if (!box->IsSVGInlineTextBox()) + auto* text_box = DynamicTo<SVGInlineTextBox>(box); + if (!text_box) continue; - SVGInlineTextBox* text_box = ToSVGInlineTextBox(box); for (const SVGTextFragment& fragment : text_box->TextFragments()) { FloatRect fragment_rect = fragment.BoundingBox(baseline); @@ -416,8 +414,8 @@ void LayoutSVGInlineText::ComputeNewScaledFontForStyle( FontDescription font_description = unscaled_font_description; font_description.SetComputedSize(scaled_font_size); - scaled_font = Font(font_description); - scaled_font.Update(document.GetStyleEngine().GetFontSelector()); + scaled_font = + Font(font_description, document.GetStyleEngine().GetFontSelector()); } PhysicalRect LayoutSVGInlineText::VisualRectInDocument( diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc index 61279e3cb9b..9f5bd0a1a6a 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.cc @@ -56,6 +56,12 @@ void LayoutSVGPath::UpdateShapeFromElement() { UpdateMarkers(); } +const StylePath* LayoutSVGPath::GetStylePath() const { + if (!IsA<SVGPathElement>(*GetElement())) + return nullptr; + return StyleRef().SvgStyle().D(); +} + void LayoutSVGPath::UpdateMarkers() { marker_positions_.clear(); @@ -74,12 +80,16 @@ void LayoutSVGPath::UpdateMarkers() { if (!(marker_start || marker_mid || marker_end)) return; - SVGMarkerDataBuilder(marker_positions_).Build(GetPath()); + SVGMarkerDataBuilder builder(marker_positions_); + if (const StylePath* style_path = GetStylePath()) + builder.Build(style_path->ByteStream()); + else + builder.Build(GetPath()); if (marker_positions_.IsEmpty()) return; - const float stroke_width = StrokeWidth(); + const float stroke_width = StrokeWidthForMarkerUnits(); FloatRect boundaries; for (const auto& position : marker_positions_) { if (LayoutSVGResourceMarker* marker = diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h index 94cdea0c76c..97b0056bd1b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_path.h @@ -47,6 +47,7 @@ class LayoutSVGPath final : public LayoutSVGShape { void UpdateShapeFromElement() override; + const StylePath* GetStylePath() const; void UpdateMarkers(); Vector<MarkerPosition> marker_positions_; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc index 8778ec876b2..fcfbf011d02 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc @@ -95,8 +95,8 @@ bool ContributesToClip(const SVGElement& element) { } Path PathFromElement(const SVGElement& element) { - if (IsSVGGeometryElement(element)) - return ToSVGGeometryElement(element).ToClipPath(); + if (auto* geometry_element = DynamicTo<SVGGeometryElement>(element)) + return geometry_element->ToClipPath(); // Guaranteed by DetermineClipStrategy() above, only <use> element and // SVGGraphicsElement that has a LayoutSVGShape can reach here. @@ -106,20 +106,17 @@ Path PathFromElement(const SVGElement& element) { } // namespace LayoutSVGResourceClipper::LayoutSVGResourceClipper(SVGClipPathElement* node) - : LayoutSVGResourceContainer(node), in_clip_expansion_(false) {} + : LayoutSVGResourceContainer(node) {} LayoutSVGResourceClipper::~LayoutSVGResourceClipper() = default; -void LayoutSVGResourceClipper::RemoveAllClientsFromCache( - bool mark_for_invalidation) { +void LayoutSVGResourceClipper::RemoveAllClientsFromCache() { clip_content_path_validity_ = kClipContentPathUnknown; clip_content_path_.Clear(); cached_paint_record_.reset(); local_clip_bounds_ = FloatRect(); - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation | - SVGResourceClient::kBoundariesInvalidation - : SVGResourceClient::kParentOnlyInvalidation); + MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation | + SVGResourceClient::kBoundariesInvalidation); } base::Optional<Path> LayoutSVGResourceClipper::AsPath() { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h index b8f71fbb74e..661f7b0dd95 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h @@ -21,6 +21,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_CLIPPER_H_ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" +#include "third_party/blink/renderer/core/style/reference_clip_path_operation.h" #include "third_party/blink/renderer/core/svg/svg_unit_types.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -35,7 +36,7 @@ class LayoutSVGResourceClipper final : public LayoutSVGResourceContainer { const char* GetName() const override { return "LayoutSVGResourceClipper"; } - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; + void RemoveAllClientsFromCache() override; FloatRect ResourceBoundingBox(const FloatRect& reference_box); @@ -50,16 +51,6 @@ class LayoutSVGResourceClipper final : public LayoutSVGResourceContainer { base::Optional<Path> AsPath(); sk_sp<const PaintRecord> CreatePaintRecord(); - bool HasCycle() { return in_clip_expansion_; } - void BeginClipExpansion() { - DCHECK(!in_clip_expansion_); - in_clip_expansion_ = true; - } - void EndClipExpansion() { - DCHECK(in_clip_expansion_); - in_clip_expansion_ = false; - } - protected: void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void WillBeDestroyed() override; @@ -80,14 +71,21 @@ class LayoutSVGResourceClipper final : public LayoutSVGResourceContainer { sk_sp<const PaintRecord> cached_paint_record_; FloatRect local_clip_bounds_; - - // Reference cycle detection. - bool in_clip_expansion_; }; DEFINE_LAYOUT_SVG_RESOURCE_TYPE_CASTS(LayoutSVGResourceClipper, kClipperResourceType); +inline LayoutSVGResourceClipper* GetSVGResourceAsType( + const ClipPathOperation* clip_path_operation) { + const auto* reference_clip = + DynamicTo<ReferenceClipPathOperation>(clip_path_operation); + if (!reference_clip) + return nullptr; + return GetSVGResourceAsType<LayoutSVGResourceClipper>( + reference_clip->Resource()); +} + } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc index ce06ebc5c63..da7fd935d74 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc @@ -22,6 +22,7 @@ #include "base/auto_reset.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" +#include "third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" #include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h" @@ -83,12 +84,66 @@ void LayoutSVGResourceContainer::StyleDidChange( resource->NotifyResourceAttached(*this); } +bool LayoutSVGResourceContainer::FindCycle( + SVGResourcesCycleSolver& solver) const { + if (solver.IsKnownAcyclic(this)) + return false; + SVGResourcesCycleSolver::Scope scope(solver); + if (!scope.Enter(this) || FindCycleFromSelf(solver)) + return true; + solver.AddAcyclicSubgraph(this); + return false; +} + +bool LayoutSVGResourceContainer::FindCycleInResources( + SVGResourcesCycleSolver& solver, + const LayoutObject& layout_object) const { + SVGResources* resources = + SVGResourcesCache::CachedResourcesForLayoutObject(layout_object); + if (!resources) + return false; + // Fetch all the referenced resources. + HashSet<LayoutSVGResourceContainer*> local_resources; + resources->BuildSetOfResources(local_resources); + // This performs a depth-first search for a back-edge in all the + // (potentially disjoint) graphs formed by the referenced resources. + for (auto* local_resource : local_resources) { + if (local_resource->FindCycle(solver)) + return true; + } + return false; +} + +bool LayoutSVGResourceContainer::FindCycleFromSelf( + SVGResourcesCycleSolver& solver) const { + if (FindCycleInResources(solver, *this)) + return true; + return FindCycleInDescendants(solver); +} + +bool LayoutSVGResourceContainer::FindCycleInDescendants( + SVGResourcesCycleSolver& solver) const { + LayoutObject* node = FirstChild(); + while (node) { + // Skip subtrees which are themselves resources. (They will be + // processed - if needed - when they are actually referenced.) + if (node->IsSVGResourceContainer()) { + node = node->NextInPreOrderAfterChildren(this); + continue; + } + if (FindCycleInResources(solver, *node)) + return true; + node = node->NextInPreOrder(this); + } + return false; +} + void LayoutSVGResourceContainer::MarkAllClientsForInvalidation( InvalidationModeMask invalidation_mask) { if (is_invalidating_) return; LocalSVGResource* resource = ResourceForContainer(*this); - if (!resource || !resource->HasClients()) + if (!resource) return; // Remove modes for which invalidations have already been // performed. If no modes remain we are done. @@ -100,8 +155,7 @@ void LayoutSVGResourceContainer::MarkAllClientsForInvalidation( is_invalidating_ = true; // Invalidate clients registered via an SVGResource. - if (resource) - resource->NotifyContentChanged(invalidation_mask); + resource->NotifyContentChanged(invalidation_mask); is_invalidating_ = false; } @@ -154,7 +208,7 @@ static inline void RemoveFromCacheAndInvalidateDependencies( if (SVGResources* resources = SVGResourcesCache::CachedResourcesForLayoutObject(object)) { - SVGResourceClient* client = element->GetSVGResourceClient(); + SVGElementResourceClient* client = element->GetSVGResourceClient(); if (InvalidationModeMask invalidation_mask = resources->RemoveClientFromCacheAffectingObjectBounds(*client)) { LayoutSVGResourceContainer::MarkClientForInvalidation(object, diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h index 56cfe674218..9e5ef616025 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h @@ -21,10 +21,14 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LAYOUT_SVG_RESOURCE_CONTAINER_H_ #include "third_party/blink/renderer/core/layout/svg/layout_svg_hidden_container.h" +#include "third_party/blink/renderer/core/style/style_svg_resource.h" +#include "third_party/blink/renderer/core/svg/svg_resource.h" #include "third_party/blink/renderer/core/svg/svg_resource_client.h" namespace blink { +class SVGResourcesCycleSolver; + enum LayoutSVGResourceType { kMaskerResourceType, kMarkerResourceType, @@ -40,7 +44,7 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { explicit LayoutSVGResourceContainer(SVGElement*); ~LayoutSVGResourceContainer() override; - virtual void RemoveAllClientsFromCache(bool mark_for_invalidation = true) = 0; + virtual void RemoveAllClientsFromCache() = 0; // Remove any cached data for the |client|, and return true if so. virtual bool RemoveClientFromCache(SVGResourceClient&) { return false; } @@ -64,6 +68,8 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { SubtreeLayoutScope* = nullptr); void InvalidateCacheAndMarkForLayout(SubtreeLayoutScope* = nullptr); + bool FindCycle(SVGResourcesCycleSolver&) const; + static void MarkForLayoutAndParentResourceInvalidation( LayoutObject&, bool needs_layout = true); @@ -75,6 +81,11 @@ class LayoutSVGResourceContainer : public LayoutSVGHiddenContainer { // Used from RemoveAllClientsFromCache methods. void MarkAllClientsForInvalidation(InvalidationModeMask); + bool FindCycleFromSelf(SVGResourcesCycleSolver&) const; + bool FindCycleInDescendants(SVGResourcesCycleSolver&) const; + bool FindCycleInResources(SVGResourcesCycleSolver&, + const LayoutObject&) const; + void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void WillBeDestroyed() override; @@ -97,6 +108,30 @@ DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGResourceContainer, resource->ResourceType() == typeName, \ resource.ResourceType() == typeName) +template <typename ContainerType> +inline bool IsResourceOfType(const LayoutSVGResourceContainer* container) { + return container->ResourceType() == ContainerType::kResourceType; +} + +template <typename ContainerType> +inline ContainerType* GetSVGResourceAsType(const SVGResource* resource) { + if (!resource) + return nullptr; + if (LayoutSVGResourceContainer* container = resource->ResourceContainer()) { + if (IsResourceOfType<ContainerType>(container)) + return static_cast<ContainerType*>(container); + } + return nullptr; +} + +template <typename ContainerType> +inline ContainerType* GetSVGResourceAsType( + const StyleSVGResource* style_resource) { + if (!style_resource) + return nullptr; + return GetSVGResourceAsType<ContainerType>(style_resource->Resource()); +} + } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc index 9fcc0e045dd..09784e4b5e8 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc @@ -23,66 +23,23 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h" -#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" #include "third_party/blink/renderer/core/svg/svg_filter_element.h" -#include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" -#include "third_party/blink/renderer/core/svg/svg_resource.h" namespace blink { -void FilterData::Trace(blink::Visitor* visitor) { - visitor->Trace(last_effect); - visitor->Trace(node_map); -} - -void FilterData::Dispose() { - node_map = nullptr; - if (last_effect) - last_effect->DisposeImageFiltersRecursive(); - last_effect = nullptr; -} - LayoutSVGResourceFilter::LayoutSVGResourceFilter(SVGFilterElement* node) - : LayoutSVGResourceContainer(node), - filter_(MakeGarbageCollected<FilterMap>()) {} + : LayoutSVGResourceContainer(node) {} LayoutSVGResourceFilter::~LayoutSVGResourceFilter() = default; -void LayoutSVGResourceFilter::DisposeFilterMap() { - for (auto& entry : *filter_) - entry.value->Dispose(); - filter_->clear(); -} - -void LayoutSVGResourceFilter::WillBeDestroyed() { - DisposeFilterMap(); - LayoutSVGResourceContainer::WillBeDestroyed(); -} - bool LayoutSVGResourceFilter::IsChildAllowed(LayoutObject* child, const ComputedStyle&) const { - return child->IsSVGResourceFilterPrimitive(); -} - -void LayoutSVGResourceFilter::RemoveAllClientsFromCache( - bool mark_for_invalidation) { - // LayoutSVGResourceFilter::removeClientFromCache will be called for - // all clients through markAllClientsForInvalidation so no explicit - // display item invalidation is needed here. - DisposeFilterMap(); - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation | - SVGResourceClient::kBoundariesInvalidation - : SVGResourceClient::kParentOnlyInvalidation); + return child->IsSVGFilterPrimitive(); } -bool LayoutSVGResourceFilter::RemoveClientFromCache(SVGResourceClient& client) { - auto entry = filter_->find(&client); - if (entry == filter_->end()) - return false; - entry->value->Dispose(); - filter_->erase(entry); - return true; +void LayoutSVGResourceFilter::RemoveAllClientsFromCache() { + MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation | + SVGResourceClient::kBoundariesInvalidation); } FloatRect LayoutSVGResourceFilter::ResourceBoundingBox( @@ -106,32 +63,18 @@ SVGUnitTypes::SVGUnitType LayoutSVGResourceFilter::PrimitiveUnits() const { ->EnumValue(); } -void LayoutSVGResourceFilter::PrimitiveAttributeChanged( - SVGFilterPrimitiveStandardAttributes& primitive, - const QualifiedName& attribute) { - LayoutObject* object = primitive.GetLayoutObject(); - - for (auto& filter : *filter_) { - FilterData* filter_data = filter.value.Get(); - if (filter_data->state_ != FilterData::kReadyToPaint) - continue; - - SVGFilterGraphNodeMap* node_map = filter_data->node_map.Get(); - FilterEffect* effect = node_map->EffectByRenderer(object); - if (!effect) - continue; - // Since all effects shares the same attribute value, all - // or none of them will be changed. - if (!primitive.SetFilterEffectAttribute(effect, attribute)) - return; - node_map->InvalidateDependentEffects(effect); - } - if (auto* resource = - To<SVGFilterElement>(GetElement())->AssociatedResource()) { - resource->NotifyContentChanged( - SVGResourceClient::kPaintInvalidation | - SVGResourceClient::kSkipAncestorInvalidation); - } +LayoutSVGResourceFilter* GetFilterResourceForSVG(const ComputedStyle& style) { + if (!style.HasFilter()) + return nullptr; + const FilterOperations& operations = style.Filter(); + if (operations.size() != 1) + return nullptr; + const auto* reference_filter = + DynamicTo<ReferenceFilterOperation>(*operations.at(0)); + if (!reference_filter) + return nullptr; + return GetSVGResourceAsType<LayoutSVGResourceFilter>( + reference_filter->Resource()); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h index 49907fcf609..e1584b2f26c 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h @@ -29,39 +29,7 @@ namespace blink { -class FilterEffect; class SVGFilterElement; -class SVGFilterGraphNodeMap; -class SVGFilterPrimitiveStandardAttributes; - -class FilterData final : public GarbageCollected<FilterData> { - public: - /* - * The state transitions should follow the following: - * Initial->RecordingContent->ReadyToPaint->PaintingFilter->ReadyToPaint - * | ^ | ^ - * v | v | - * RecordingContentCycleDetected PaintingFilterCycle - */ - enum FilterDataState { - kInitial, - kRecordingContent, - kRecordingContentCycleDetected, - kReadyToPaint, - kPaintingFilter, - kPaintingFilterCycleDetected - }; - - FilterData() : state_(kInitial) {} - - void Dispose(); - - void Trace(blink::Visitor*); - - Member<FilterEffect> last_effect; - Member<SVGFilterGraphNodeMap> node_map; - FilterDataState state_; -}; class LayoutSVGResourceFilter final : public LayoutSVGResourceContainer { public: @@ -71,44 +39,24 @@ class LayoutSVGResourceFilter final : public LayoutSVGResourceContainer { bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override; const char* GetName() const override { return "LayoutSVGResourceFilter"; } - bool IsOfType(LayoutObjectType type) const override { - return type == kLayoutObjectSVGResourceFilter || - LayoutSVGResourceContainer::IsOfType(type); - } - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; - bool RemoveClientFromCache(SVGResourceClient&) override; + void RemoveAllClientsFromCache() override; FloatRect ResourceBoundingBox(const FloatRect& reference_box) const; SVGUnitTypes::SVGUnitType FilterUnits() const; SVGUnitTypes::SVGUnitType PrimitiveUnits() const; - void PrimitiveAttributeChanged(SVGFilterPrimitiveStandardAttributes&, - const QualifiedName&); - static const LayoutSVGResourceType kResourceType = kFilterResourceType; LayoutSVGResourceType ResourceType() const override { return kResourceType; } - - FilterData* GetFilterDataForClient(const SVGResourceClient* client) { - return filter_->at(const_cast<SVGResourceClient*>(client)); - } - void SetFilterDataForClient(const SVGResourceClient* client, - FilterData* filter_data) { - filter_->Set(const_cast<SVGResourceClient*>(client), filter_data); - } - - protected: - void WillBeDestroyed() override; - - private: - void DisposeFilterMap(); - - using FilterMap = HeapHashMap<Member<SVGResourceClient>, Member<FilterData>>; - Persistent<FilterMap> filter_; }; -DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutSVGResourceFilter, IsSVGResourceFilter()); +// Get the LayoutSVGResourceFilter from the 'filter' property iff the 'filter' +// is a single url(...) reference. +LayoutSVGResourceFilter* GetFilterResourceForSVG(const ComputedStyle&); + +DEFINE_LAYOUT_SVG_RESOURCE_TYPE_CASTS(LayoutSVGResourceFilter, + kFilterResourceType); } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc index 62d1617857a..2461347012a 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc @@ -24,21 +24,28 @@ #include <memory> +#include "third_party/blink/renderer/platform/graphics/gradient.h" + namespace blink { +struct GradientData { + USING_FAST_MALLOC(GradientData); + + public: + scoped_refptr<Gradient> gradient; + AffineTransform userspace_transform; +}; + LayoutSVGResourceGradient::LayoutSVGResourceGradient(SVGGradientElement* node) : LayoutSVGResourcePaintServer(node), should_collect_gradient_attributes_(true), gradient_map_(MakeGarbageCollected<GradientMap>()) {} -void LayoutSVGResourceGradient::RemoveAllClientsFromCache( - bool mark_for_invalidation) { +void LayoutSVGResourceGradient::RemoveAllClientsFromCache() { gradient_map_->clear(); should_collect_gradient_attributes_ = true; To<SVGGradientElement>(*GetElement()).InvalidateDependentGradients(); - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kPaintInvalidation - : SVGResourceClient::kParentOnlyInvalidation); + MarkAllClientsForInvalidation(SVGResourceClient::kPaintInvalidation); } bool LayoutSVGResourceGradient::RemoveClientFromCache( @@ -50,50 +57,52 @@ bool LayoutSVGResourceGradient::RemoveClientFromCache( return true; } -SVGPaintServer LayoutSVGResourceGradient::PreparePaintServer( - const SVGResourceClient& client, +std::unique_ptr<GradientData> LayoutSVGResourceGradient::BuildGradientData( const FloatRect& object_bounding_box) { - ClearInvalidationMask(); + // Create gradient object + auto gradient_data = std::make_unique<GradientData>(); // Validate gradient DOM state before building the actual // gradient. This should avoid tearing down the gradient we're // currently working on. Preferably the state validation should have // no side-effects though. if (should_collect_gradient_attributes_) { - if (!CollectGradientAttributes()) - return SVGPaintServer::Invalid(); + CollectGradientAttributes(); should_collect_gradient_attributes_ = false; } - // Spec: When the geometry of the applicable element has no width or height - // and objectBoundingBox is specified, then the given effect (e.g. a gradient - // or a filter) will be ignored. - if (GradientUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox && - object_bounding_box.IsEmpty()) - return SVGPaintServer::Invalid(); + // We want the text bounding box applied to the gradient space transform + // now, so the gradient shader can use it. + if (GradientUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { + // Spec: When the geometry of the applicable element has no width or height + // and objectBoundingBox is specified, then the given effect (e.g. a + // gradient or a filter) will be ignored. + if (object_bounding_box.IsEmpty()) + return gradient_data; + gradient_data->userspace_transform.Translate(object_bounding_box.X(), + object_bounding_box.Y()); + gradient_data->userspace_transform.ScaleNonUniform( + object_bounding_box.Width(), object_bounding_box.Height()); + } + + // Create gradient object + gradient_data->gradient = BuildGradient(); + + AffineTransform gradient_transform = CalculateGradientTransform(); + gradient_data->userspace_transform *= gradient_transform; + + return gradient_data; +} + +SVGPaintServer LayoutSVGResourceGradient::PreparePaintServer( + const SVGResourceClient& client, + const FloatRect& object_bounding_box) { + ClearInvalidationMask(); std::unique_ptr<GradientData>& gradient_data = gradient_map_->insert(&client, nullptr).stored_value->value; if (!gradient_data) - gradient_data = std::make_unique<GradientData>(); - - // Create gradient object - if (!gradient_data->gradient) { - gradient_data->gradient = BuildGradient(); - - // We want the text bounding box applied to the gradient space transform - // now, so the gradient shader can use it. - if (GradientUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox && - !object_bounding_box.IsEmpty()) { - gradient_data->userspace_transform.Translate(object_bounding_box.X(), - object_bounding_box.Y()); - gradient_data->userspace_transform.ScaleNonUniform( - object_bounding_box.Width(), object_bounding_box.Height()); - } - - AffineTransform gradient_transform = CalculateGradientTransform(); - gradient_data->userspace_transform *= gradient_transform; - } + gradient_data = BuildGradientData(object_bounding_box); if (!gradient_data->gradient) return SVGPaintServer::Invalid(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h index 6b974699227..530933fdc0d 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h @@ -25,25 +25,18 @@ #include <memory> #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" #include "third_party/blink/renderer/core/svg/svg_gradient_element.h" -#include "third_party/blink/renderer/platform/graphics/gradient.h" #include "third_party/blink/renderer/platform/transforms/affine_transform.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" namespace blink { -struct GradientData { - USING_FAST_MALLOC(GradientData); - - public: - scoped_refptr<Gradient> gradient; - AffineTransform userspace_transform; -}; +struct GradientData; class LayoutSVGResourceGradient : public LayoutSVGResourcePaintServer { public: explicit LayoutSVGResourceGradient(SVGGradientElement*); - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) final; + void RemoveAllClientsFromCache() final; bool RemoveClientFromCache(SVGResourceClient&) final; SVGPaintServer PreparePaintServer(const SVGResourceClient&, @@ -54,13 +47,16 @@ class LayoutSVGResourceGradient : public LayoutSVGResourcePaintServer { protected: virtual SVGUnitTypes::SVGUnitType GradientUnits() const = 0; virtual AffineTransform CalculateGradientTransform() const = 0; - virtual bool CollectGradientAttributes() = 0; + virtual void CollectGradientAttributes() = 0; virtual scoped_refptr<Gradient> BuildGradient() const = 0; static GradientSpreadMethod PlatformSpreadMethodFromSVGType( SVGSpreadMethodType); private: + std::unique_ptr<GradientData> BuildGradientData( + const FloatRect& object_bounding_box); + bool should_collect_gradient_attributes_ : 1; using GradientMap = HeapHashMap<Member<const SVGResourceClient>, std::unique_ptr<GradientData>>; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.cc index 460a76c10da..8d88444f724 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.cc @@ -33,10 +33,10 @@ LayoutSVGResourceLinearGradient::LayoutSVGResourceLinearGradient( LayoutSVGResourceLinearGradient::~LayoutSVGResourceLinearGradient() = default; -bool LayoutSVGResourceLinearGradient::CollectGradientAttributes() { +void LayoutSVGResourceLinearGradient::CollectGradientAttributes() { DCHECK(GetElement()); attributes_wrapper_->Set(LinearGradientAttributes()); - return To<SVGLinearGradientElement>(GetElement()) + To<SVGLinearGradientElement>(GetElement()) ->CollectGradientAttributes(MutableAttributes()); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.h index bbad601c2c1..48c43d53806 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_linear_gradient.h @@ -47,7 +47,7 @@ class LayoutSVGResourceLinearGradient final : public LayoutSVGResourceGradient { AffineTransform CalculateGradientTransform() const override { return Attributes().GradientTransform(); } - bool CollectGradientAttributes() override; + void CollectGradientAttributes() override; scoped_refptr<Gradient> BuildGradient() const override; FloatPoint StartPoint(const LinearGradientAttributes&) const; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc index ba43918adba..491b18a5c92 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc @@ -47,12 +47,9 @@ void LayoutSVGResourceMarker::UpdateLayout() { ClearInvalidationMask(); } -void LayoutSVGResourceMarker::RemoveAllClientsFromCache( - bool mark_for_invalidation) { - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation | - SVGResourceClient::kBoundariesInvalidation - : SVGResourceClient::kParentOnlyInvalidation); +void LayoutSVGResourceMarker::RemoveAllClientsFromCache() { + MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation | + SVGResourceClient::kBoundariesInvalidation); } FloatRect LayoutSVGResourceMarker::MarkerBoundaries( diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h index d8596cdafdf..58a23afc549 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h @@ -35,7 +35,7 @@ class LayoutSVGResourceMarker final : public LayoutSVGResourceContainer { const char* GetName() const override { return "LayoutSVGResourceMarker"; } - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; + void RemoveAllClientsFromCache() override; // Calculates marker boundaries, mapped to the target element's coordinate // space. diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc index df8d4817416..b1e201d9a8b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc @@ -36,14 +36,11 @@ LayoutSVGResourceMasker::LayoutSVGResourceMasker(SVGMaskElement* node) LayoutSVGResourceMasker::~LayoutSVGResourceMasker() = default; -void LayoutSVGResourceMasker::RemoveAllClientsFromCache( - bool mark_for_invalidation) { +void LayoutSVGResourceMasker::RemoveAllClientsFromCache() { cached_paint_record_.reset(); mask_content_boundaries_ = FloatRect(); - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation | - SVGResourceClient::kBoundariesInvalidation - : SVGResourceClient::kParentOnlyInvalidation); + MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation | + SVGResourceClient::kBoundariesInvalidation); } sk_sp<const PaintRecord> LayoutSVGResourceMasker::CreatePaintRecord( diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h index 70442a2ddbd..6bb360059c0 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h @@ -38,7 +38,7 @@ class LayoutSVGResourceMasker final : public LayoutSVGResourceContainer { const char* GetName() const override { return "LayoutSVGResourceMasker"; } - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; + void RemoveAllClientsFromCache() override; FloatRect ResourceBoundingBox(const FloatRect& reference_box); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc index fdc8bc00edd..58231ff80fc 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.cc @@ -147,7 +147,7 @@ SVGPaintServer SVGPaintServer::RequestForLayoutObject( return SVGPaintServer(paint_description.color); SVGPaintServer paint_server = paint_description.resource->PreparePaintServer( *SVGResources::GetClient(layout_object), - layout_object.ObjectBoundingBox()); + SVGResources::ReferenceBoxForEffects(layout_object)); if (paint_server.IsValid()) return paint_server; if (paint_description.has_fallback) diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h index fad76f41cb6..9706a786c85 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h @@ -114,11 +114,11 @@ class LayoutSVGResourcePaintServer : public LayoutSVGResourceContainer { LayoutSVGResourceMode); }; -DEFINE_TYPE_CASTS(LayoutSVGResourcePaintServer, - LayoutSVGResourceContainer, - resource, - resource->IsSVGPaintServer(), - resource.IsSVGPaintServer()); +template <> +inline bool IsResourceOfType<LayoutSVGResourcePaintServer>( + const LayoutSVGResourceContainer* container) { + return container->IsSVGPaintServer(); +} } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc index 754d3cef50d..6944d125c20 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc @@ -52,13 +52,10 @@ LayoutSVGResourcePattern::LayoutSVGResourcePattern(SVGPatternElement* node) attributes_wrapper_(MakeGarbageCollected<PatternAttributesWrapper>()), pattern_map_(MakeGarbageCollected<PatternMap>()) {} -void LayoutSVGResourcePattern::RemoveAllClientsFromCache( - bool mark_for_invalidation) { +void LayoutSVGResourcePattern::RemoveAllClientsFromCache() { pattern_map_->clear(); should_collect_pattern_attributes_ = true; - MarkAllClientsForInvalidation( - mark_for_invalidation ? SVGResourceClient::kPaintInvalidation - : SVGResourceClient::kParentOnlyInvalidation); + MarkAllClientsForInvalidation(SVGResourceClient::kPaintInvalidation); } bool LayoutSVGResourcePattern::RemoveClientFromCache( @@ -70,51 +67,53 @@ bool LayoutSVGResourcePattern::RemoveClientFromCache( return true; } -PatternData* LayoutSVGResourcePattern::PatternForClient( - const SVGResourceClient& client, +std::unique_ptr<PatternData> LayoutSVGResourcePattern::BuildPatternData( const FloatRect& object_bounding_box) { - DCHECK(!should_collect_pattern_attributes_); + auto pattern_data = std::make_unique<PatternData>(); - // FIXME: the double hash lookup is needed to guard against paint-time - // invalidation (painting animated images may trigger layout invals which - // delete our map entry). Hopefully that will be addressed at some point, and - // then we can optimize the lookup. - if (PatternData* current_data = pattern_map_->at(&client)) - return current_data; - - return pattern_map_->Set(&client, BuildPatternData(object_bounding_box)) - .stored_value->value.get(); -} + DCHECK(GetElement()); + // Validate pattern DOM state before building the actual pattern. This should + // avoid tearing down the pattern we're currently working on. Preferably the + // state validation should have no side-effects though. + if (should_collect_pattern_attributes_) { + attributes_wrapper_->Set(PatternAttributes()); + auto* pattern_element = To<SVGPatternElement>(GetElement()); + pattern_element->CollectPatternAttributes(MutableAttributes()); + should_collect_pattern_attributes_ = false; + } -std::unique_ptr<PatternData> LayoutSVGResourcePattern::BuildPatternData( - const FloatRect& object_bounding_box) { - // If we couldn't determine the pattern content element root, stop here. const PatternAttributes& attributes = Attributes(); - if (!attributes.PatternContentElement()) - return nullptr; - // An empty viewBox disables layout. - if (attributes.HasViewBox() && attributes.ViewBox().IsEmpty()) - return nullptr; + // Spec: When the geometry of the applicable element has no width or height + // and objectBoundingBox is specified, then the given effect (e.g. a gradient + // or a filter) will be ignored. + if (attributes.PatternUnits() == + SVGUnitTypes::kSvgUnitTypeObjectboundingbox && + object_bounding_box.IsEmpty()) + return pattern_data; + + // If there's no content disable rendering of the pattern. + if (!attributes.PatternContentElement()) + return pattern_data; - DCHECK(GetElement()); // Compute tile metrics. FloatRect tile_bounds = SVGLengthContext::ResolveRectangle( GetElement(), attributes.PatternUnits(), object_bounding_box, *attributes.X(), *attributes.Y(), *attributes.Width(), *attributes.Height()); if (tile_bounds.IsEmpty()) - return nullptr; + return pattern_data; AffineTransform tile_transform; if (attributes.HasViewBox()) { + // An empty viewBox disables rendering of the pattern. if (attributes.ViewBox().IsEmpty()) - return nullptr; + return pattern_data; tile_transform = SVGFitToViewBox::ViewBoxToViewTransform( attributes.ViewBox(), attributes.PreserveAspectRatio(), tile_bounds.Width(), tile_bounds.Height()); } else { - // A viewbox overrides patternContentUnits, per spec. + // A viewBox overrides patternContentUnits, per spec. if (attributes.PatternContentUnits() == SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { tile_transform.Scale(object_bounding_box.Width(), @@ -122,7 +121,6 @@ std::unique_ptr<PatternData> LayoutSVGResourcePattern::BuildPatternData( } } - std::unique_ptr<PatternData> pattern_data = base::WrapUnique(new PatternData); pattern_data->pattern = Pattern::CreatePaintRecordPattern( AsPaintRecord(tile_bounds.Size(), tile_transform), FloatRect(FloatPoint(), tile_bounds.Size())); @@ -139,27 +137,12 @@ SVGPaintServer LayoutSVGResourcePattern::PreparePaintServer( const FloatRect& object_bounding_box) { ClearInvalidationMask(); - // Validate pattern DOM state before building the actual - // pattern. This should avoid tearing down the pattern we're - // currently working on. Preferably the state validation should have - // no side-effects though. - if (should_collect_pattern_attributes_) { - attributes_wrapper_->Set(PatternAttributes()); - auto* pattern_element = To<SVGPatternElement>(GetElement()); - pattern_element->CollectPatternAttributes(MutableAttributes()); - should_collect_pattern_attributes_ = false; - } - - // Spec: When the geometry of the applicable element has no width or height - // and objectBoundingBox is specified, then the given effect (e.g. a gradient - // or a filter) will be ignored. - if (Attributes().PatternUnits() == - SVGUnitTypes::kSvgUnitTypeObjectboundingbox && - object_bounding_box.IsEmpty()) - return SVGPaintServer::Invalid(); + std::unique_ptr<PatternData>& pattern_data = + pattern_map_->insert(&client, nullptr).stored_value->value; + if (!pattern_data) + pattern_data = BuildPatternData(object_bounding_box); - PatternData* pattern_data = PatternForClient(client, object_bounding_box); - if (!pattern_data || !pattern_data->pattern) + if (!pattern_data->pattern) return SVGPaintServer::Invalid(); return SVGPaintServer(pattern_data->pattern, pattern_data->transform); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h index e293f39d1ba..4630fee0837 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h @@ -41,7 +41,7 @@ class LayoutSVGResourcePattern final : public LayoutSVGResourcePaintServer { const char* GetName() const override { return "LayoutSVGResourcePattern"; } - void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override; + void RemoveAllClientsFromCache() override; bool RemoveClientFromCache(SVGResourceClient&) override; SVGPaintServer PreparePaintServer( @@ -56,8 +56,6 @@ class LayoutSVGResourcePattern final : public LayoutSVGResourcePaintServer { const FloatRect& object_bounding_box); sk_sp<PaintRecord> AsPaintRecord(const FloatSize&, const AffineTransform&) const; - PatternData* PatternForClient(const SVGResourceClient&, - const FloatRect& object_bounding_box); const LayoutSVGResourceContainer* ResolveContentElement() const; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.cc index 9b335862268..bf85cc000e6 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.cc @@ -34,10 +34,10 @@ LayoutSVGResourceRadialGradient::LayoutSVGResourceRadialGradient( LayoutSVGResourceRadialGradient::~LayoutSVGResourceRadialGradient() = default; -bool LayoutSVGResourceRadialGradient::CollectGradientAttributes() { +void LayoutSVGResourceRadialGradient::CollectGradientAttributes() { DCHECK(GetElement()); attributes_wrapper_->Set(RadialGradientAttributes()); - return To<SVGRadialGradientElement>(GetElement()) + To<SVGRadialGradientElement>(GetElement()) ->CollectGradientAttributes(MutableAttributes()); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.h index d6f1b760124..41771233f1f 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_resource_radial_gradient.h @@ -47,7 +47,7 @@ class LayoutSVGResourceRadialGradient final : public LayoutSVGResourceGradient { AffineTransform CalculateGradientTransform() const override { return Attributes().GradientTransform(); } - bool CollectGradientAttributes() override; + void CollectGradientAttributes() override; scoped_refptr<Gradient> BuildGradient() const override; FloatPoint CenterPoint(const RadialGradientAttributes&) const; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc index 534abb8f071..551f84f910c 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc @@ -130,7 +130,7 @@ LayoutUnit LayoutSVGRoot::ComputeReplacedLogicalWidth( // (border-image/background-image/<html:img>/...) we're forced to resize to a // specific size. if (!container_size_.IsEmpty()) - return LayoutUnit(container_size_.Width()); + return container_size_.Width(); if (IsEmbeddedThroughFrameContainingSVGDocument()) return ContainingBlock()->AvailableLogicalWidth(); @@ -144,7 +144,7 @@ LayoutUnit LayoutSVGRoot::ComputeReplacedLogicalHeight( // (border-image/background-image/<html:img>/...) we're forced to resize to a // specific size. if (!container_size_.IsEmpty()) - return LayoutUnit(container_size_.Height()); + return container_size_.Height(); if (IsEmbeddedThroughFrameContainingSVGDocument()) return ContainingBlock()->AvailableLogicalHeight( @@ -300,7 +300,7 @@ bool LayoutSVGRoot::StyleChangeAffectsIntrinsicSize( } void LayoutSVGRoot::IntrinsicSizingInfoChanged() { - SetPreferredLogicalWidthsDirty(); + SetIntrinsicLogicalWidthsDirty(); // TODO(fs): Merge with IntrinsicSizeChanged()? (from LayoutReplaced) // Ignore changes to intrinsic dimensions if the <svg> is not in an SVG @@ -315,7 +315,7 @@ void LayoutSVGRoot::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { if (diff.NeedsFullLayout()) SetNeedsBoundariesUpdate(); - if (diff.NeedsFullPaintInvalidation()) { + if (diff.NeedsPaintInvalidation()) { // Box decorations may have appeared/disappeared - recompute status. has_box_decoration_background_ = StyleRef().HasBoxDecorationBackground(); } @@ -336,7 +336,7 @@ bool LayoutSVGRoot::IsChildAllowed(LayoutObject* child, void LayoutSVGRoot::AddChild(LayoutObject* child, LayoutObject* before_child) { LayoutReplaced::AddChild(child, before_child); - SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef()); + SVGResourcesCache::ClientWasAddedToTree(*child); bool should_isolate_descendants = (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) || @@ -383,7 +383,7 @@ void LayoutSVGRoot::DescendantIsolationRequirementsChanged( void LayoutSVGRoot::InsertedIntoTree() { LayoutReplaced::InsertedIntoTree(); - SVGResourcesCache::ClientWasAddedToTree(*this, StyleRef()); + SVGResourcesCache::ClientWasAddedToTree(*this); } void LayoutSVGRoot::WillBeRemovedFromTree() { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h index 3246ac260bf..4bba76f4efb 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root.h @@ -65,8 +65,8 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced { needs_boundaries_or_transform_update_ = true; } - IntSize ContainerSize() const { return container_size_; } - void SetContainerSize(const IntSize& container_size) { + LayoutSize ContainerSize() const { return container_size_; } + void SetContainerSize(const LayoutSize& container_size) { // SVGImage::draw() does a view layout prior to painting, // and we need that layout to know of the new size otherwise // the layout may be incorrectly using the old size. @@ -162,7 +162,7 @@ class CORE_EXPORT LayoutSVGRoot final : public LayoutReplaced { PositionWithAffinity PositionForPoint(const PhysicalOffset&) const final; LayoutObjectChildList children_; - IntSize container_size_; + LayoutSize container_size_; FloatRect object_bounding_box_; bool object_bounding_box_valid_; FloatRect stroke_bounding_box_; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc index cc0f2983d0b..40e7ae0d1b4 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc @@ -96,39 +96,6 @@ TEST_F(LayoutSVGRootTest, VisualRectMappingWithViewportClipAndBorder) { EXPECT_EQ(PhysicalRect(0, 0, 220, 120), root_visual_rect); } -TEST_F(LayoutSVGRootTest, - PaintedOutputOfObjectHasNoEffectRegardlessOfSizeEmpty) { - SetBodyInnerHTML(R"HTML( - <svg id="svg" width="100.1%" height="16"> - <rect width="100%" height="16" fill="#fff"></rect> - </svg> - )HTML"); - - const LayoutSVGRoot& root = - *ToLayoutSVGRoot(GetLayoutObjectByElementId("svg")); - EXPECT_FALSE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()); -} - -TEST_F(LayoutSVGRootTest, - PaintedOutputOfObjectHasNoEffectRegardlessOfSizeMask) { - SetBodyInnerHTML(R"HTML( - <svg id="svg" width="16" height="16" mask="url(#test)"> - <rect width="100%" height="16" fill="#fff"></rect> - <defs> - <mask id="test"> - <g> - <rect width="100%" height="100%" fill="#ffffff" style=""></rect> - </g> - </mask> - </defs> - </svg> - )HTML"); - - const LayoutSVGRoot& root = - *ToLayoutSVGRoot(GetLayoutObjectByElementId("svg")); - EXPECT_FALSE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()); -} - TEST_F(LayoutSVGRootTest, RectBasedHitTestPartialOverlap) { SetBodyInnerHTML(R"HTML( <style>body { margin: 0 }</style> diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc index 6286842b095..66bff8d5bd1 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.cc @@ -79,13 +79,13 @@ void LayoutSVGShape::WillBeDestroyed() { void LayoutSVGShape::CreatePath() { if (!path_) path_ = std::make_unique<Path>(); - *path_ = ToSVGGeometryElement(GetElement())->AsPath(); + *path_ = To<SVGGeometryElement>(GetElement())->AsPath(); } float LayoutSVGShape::DashScaleFactor() const { if (StyleRef().SvgStyle().StrokeDashArray()->data.IsEmpty()) return 1; - return ToSVGGeometryElement(*GetElement()).PathLengthScaleFactor(); + return To<SVGGeometryElement>(*GetElement()).PathLengthScaleFactor(); } void LayoutSVGShape::UpdateShapeFromElement() { @@ -229,7 +229,6 @@ void LayoutSVGShape::UpdateLayout() { FloatRect old_object_bounding_box = ObjectBoundingBox(); UpdateShapeFromElement(); if (old_object_bounding_box != ObjectBoundingBox()) { - GetElement()->SetNeedsResizeObserverUpdate(); SetShouldDoFullPaintInvalidation(); bbox_changed = true; } @@ -393,6 +392,21 @@ float LayoutSVGShape::StrokeWidth() const { return length_context.ValueForLength(StyleRef().SvgStyle().StrokeWidth()); } +float LayoutSVGShape::StrokeWidthForMarkerUnits() const { + float stroke_width = StrokeWidth(); + if (HasNonScalingStroke()) { + const auto& non_scaling_transform = NonScalingStrokeTransform(); + if (!non_scaling_transform.IsInvertible()) + return 0; + float scale_factor = + clampTo<float>(sqrt((non_scaling_transform.XScaleSquared() + + non_scaling_transform.YScaleSquared()) / + 2)); + stroke_width /= scale_factor; + } + return stroke_width; +} + LayoutSVGShapeRareData& LayoutSVGShape::EnsureRareData() const { if (!rare_data_) rare_data_ = std::make_unique<LayoutSVGShapeRareData>(); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h index 1b11df34d50..803a52ec322 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_shape.h @@ -101,6 +101,7 @@ class LayoutSVGShape : public LayoutSVGModelObject { } float StrokeWidth() const; + float StrokeWidthForMarkerUnits() const; virtual ShapeGeometryCodePath GeometryCodePath() const { return kPathGeometry; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc index 34c734ab14f..373e99fb652 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.cc @@ -111,15 +111,8 @@ static inline void CollectDescendantTextNodes( } } -void LayoutSVGText::InvalidatePositioningValues( +void LayoutSVGText::SubtreeStructureChanged( LayoutInvalidationReasonForTracing reason) { - descendant_text_nodes_.clear(); - SetNeedsPositioningValuesUpdate(); - // TODO(fs): Restore the passing of |reason| here. - LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this); -} - -void LayoutSVGText::SubtreeChildWasAdded() { if (BeingDestroyed() || !EverHadLayout()) { DCHECK(descendant_text_nodes_.IsEmpty()); return; @@ -128,37 +121,20 @@ void LayoutSVGText::SubtreeChildWasAdded() { return; // The positioning elements cache depends on the size of each text - // layoutObject in the subtree. If this changes, clear the cache. It will be + // LayoutObject in the subtree. If this changes, clear the cache. It will be // rebuilt on the next layout. - InvalidatePositioningValues(layout_invalidation_reason::kChildChanged); - SetNeedsTextMetricsUpdate(); -} - -void LayoutSVGText::SubtreeChildWillBeRemoved() { - if (BeingDestroyed() || !EverHadLayout()) { - DCHECK(descendant_text_nodes_.IsEmpty()); - return; - } - - // The positioning elements cache depends on the size of each text - // layoutObject in the subtree. If this changes, clear the cache. It will be - // rebuilt on the next layout. - InvalidatePositioningValues(layout_invalidation_reason::kChildChanged); + descendant_text_nodes_.clear(); + SetNeedsPositioningValuesUpdate(); SetNeedsTextMetricsUpdate(); + // TODO(fs): Restore the passing of |reason| here. + LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this); } -void LayoutSVGText::SubtreeTextDidChange() { - DCHECK(!BeingDestroyed()); - if (!EverHadLayout()) { - DCHECK(descendant_text_nodes_.IsEmpty()); - return; - } - - // The positioning elements cache depends on the size of each text object in - // the subtree. If this changes, clear the cache and mark it for rebuilding - // in the next layout. - InvalidatePositioningValues(layout_invalidation_reason::kTextChanged); - SetNeedsTextMetricsUpdate(); +void LayoutSVGText::NotifySubtreeStructureChanged( + LayoutObject* object, + LayoutInvalidationReasonForTracing reason) { + if (LayoutSVGText* layout_text = LocateLayoutSVGTextAncestor(object)) + layout_text->SubtreeStructureChanged(reason); } static inline void UpdateFontAndMetrics(LayoutSVGText& text_root) { @@ -365,8 +341,8 @@ PositionWithAffinity LayoutSVGText::PositionForPoint( DCHECK(!root_box->NextRootBox()); DCHECK(ChildrenInline()); - InlineBox* closest_box = - ToSVGRootInlineBox(root_box)->ClosestLeafChildForPosition( + auto* closest_box = + To<SVGRootInlineBox>(root_box)->ClosestLeafChildForPosition( clipped_point_in_contents); if (!closest_box) return CreatePositionWithAffinity(0); @@ -391,27 +367,17 @@ FloatRect LayoutSVGText::ObjectBoundingBox() const { } FloatRect LayoutSVGText::StrokeBoundingBox() const { - FloatRect stroke_boundaries = ObjectBoundingBox(); - const SVGComputedStyle& svg_style = StyleRef().SvgStyle(); - if (!svg_style.HasStroke()) - return stroke_boundaries; - - DCHECK(GetElement()); - SVGLengthContext length_context(GetElement()); - stroke_boundaries.Inflate( - length_context.ValueForLength(svg_style.StrokeWidth())); - return stroke_boundaries; + if (!FirstRootBox()) + return FloatRect(); + return SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, ObjectBoundingBox()); } FloatRect LayoutSVGText::VisualRectInLocalSVGCoordinates() const { - FloatRect visual_rect = StrokeBoundingBox(); - SVGLayoutSupport::AdjustVisualRectWithResources(*this, ObjectBoundingBox(), - visual_rect); - - if (const ShadowList* text_shadow = StyleRef().TextShadow()) - text_shadow->AdjustRectForShadow(visual_rect); - - return visual_rect; + if (!FirstRootBox()) + return FloatRect(); + const FloatRect object_bounds = ObjectBoundingBox(); + return SVGLayoutSupport::ComputeVisualRectForText(*this, object_bounds, + object_bounds); } void LayoutSVGText::AddOutlineRects(Vector<PhysicalRect>& rects, @@ -428,13 +394,13 @@ bool LayoutSVGText::IsObjectBoundingBoxValid() const { void LayoutSVGText::AddChild(LayoutObject* child, LayoutObject* before_child) { LayoutSVGBlock::AddChild(child, before_child); - SVGResourcesCache::ClientWasAddedToTree(*child, child->StyleRef()); - SubtreeChildWasAdded(); + SVGResourcesCache::ClientWasAddedToTree(*child); + SubtreeStructureChanged(layout_invalidation_reason::kChildChanged); } void LayoutSVGText::RemoveChild(LayoutObject* child) { SVGResourcesCache::ClientWillBeRemovedFromTree(*child); - SubtreeChildWillBeRemoved(); + SubtreeStructureChanged(layout_invalidation_reason::kChildChanged); LayoutSVGBlock::RemoveChild(child); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h index 2360fe04afd..c5ebeb9f698 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text.h @@ -53,15 +53,14 @@ class LayoutSVGText final : public LayoutSVGBlock { static LayoutSVGText* LocateLayoutSVGTextAncestor(LayoutObject*); static const LayoutSVGText* LocateLayoutSVGTextAncestor(const LayoutObject*); + static void NotifySubtreeStructureChanged(LayoutObject*, + LayoutInvalidationReasonForTracing); + bool NeedsReordering() const { return needs_reordering_; } const Vector<LayoutSVGInlineText*>& DescendantTextNodes() const { return descendant_text_nodes_; } - void SubtreeChildWasAdded(); - void SubtreeChildWillBeRemoved(); - void SubtreeTextDidChange(); - void RecalcVisualOverflow() override; const char* GetName() const override { return "LayoutSVGText"; } @@ -94,7 +93,7 @@ class LayoutSVGText final : public LayoutSVGBlock { RootInlineBox* CreateRootInlineBox() override; - void InvalidatePositioningValues(LayoutInvalidationReasonForTracing); + void SubtreeStructureChanged(LayoutInvalidationReasonForTracing); bool needs_reordering_ : 1; bool needs_positioning_values_update_ : 1; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text_path.cc b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text_path.cc index bcf433726ee..1290e22820c 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text_path.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/layout_svg_text_path.cc @@ -62,8 +62,7 @@ bool LayoutSVGTextPath::IsChildAllowed(LayoutObject* child, std::unique_ptr<PathPositionMapper> LayoutSVGTextPath::LayoutPath() const { const auto& text_path_element = To<SVGTextPathElement>(*GetNode()); Element* target_element = SVGURIReference::TargetElementFromIRIString( - text_path_element.HrefString(), - text_path_element.TreeScopeForIdResolution()); + text_path_element.HrefString(), text_path_element.OriginatingTreeScope()); const auto* path_element = DynamicTo<SVGPathElement>(target_element); if (!path_element) diff --git a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h index b4a32b6a14b..d62be330937 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_flow_box.h @@ -22,6 +22,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LINE_SVG_INLINE_FLOW_BOX_H_ #include "third_party/blink/renderer/core/layout/line/inline_flow_box.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -42,7 +43,12 @@ class SVGInlineFlowBox final : public InlineFlowBox { LayoutUnit logical_height_; }; -DEFINE_INLINE_BOX_TYPE_CASTS(SVGInlineFlowBox); +template <> +struct DowncastTraits<SVGInlineFlowBox> { + static bool AllowFrom(const InlineBox& box) { + return box.IsSVGInlineFlowBox(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h index 3ce95efaac1..6c19bd6f4ba 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h @@ -109,7 +109,12 @@ class SVGInlineTextBox final : public InlineTextBox { Vector<SVGTextFragment> text_fragments_; }; -DEFINE_INLINE_BOX_TYPE_CASTS(SVGInlineTextBox); +template <> +struct DowncastTraits<SVGInlineTextBox> { + static bool AllowFrom(const InlineBox& box) { + return box.IsSVGInlineTextBox(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc index 240be07bf5c..cf3fffe5517 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.cc @@ -86,8 +86,8 @@ void SVGRootInlineBox::ComputePerCharacterLayoutInformation() { FloatRect SVGRootInlineBox::LayoutInlineBoxes(InlineBox& box) { FloatRect rect; - if (box.IsSVGInlineTextBox()) { - rect = ToSVGInlineTextBox(box).CalculateBoundaries(); + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box)) { + rect = svg_inline_text_box->CalculateBoundaries(); } else { for (InlineBox* child = ToInlineFlowBox(box).FirstChild(); child; child = child->NextOnLine()) @@ -101,12 +101,12 @@ FloatRect SVGRootInlineBox::LayoutInlineBoxes(InlineBox& box) { box.SetX(logical_rect.X()); box.SetY(logical_rect.Y()); box.SetLogicalWidth(logical_rect.Width()); - if (box.IsSVGInlineTextBox()) - ToSVGInlineTextBox(box).SetLogicalHeight(logical_rect.Height()); - else if (box.IsSVGInlineFlowBox()) - ToSVGInlineFlowBox(box).SetLogicalHeight(logical_rect.Height()); + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box)) + svg_inline_text_box->SetLogicalHeight(logical_rect.Height()); + else if (auto* svg_inline_flow_box = DynamicTo<SVGInlineFlowBox>(box)) + svg_inline_flow_box->SetLogicalHeight(logical_rect.Height()); else - ToSVGRootInlineBox(box).SetLogicalHeight(logical_rect.Height()); + To<SVGRootInlineBox>(box).SetLogicalHeight(logical_rect.Height()); return rect; } @@ -170,10 +170,9 @@ static inline void ReverseInlineBoxRangeAndValueListsIfNeeded( if (first == last || first == --last) return; - if ((*last)->IsSVGInlineTextBox() && (*first)->IsSVGInlineTextBox()) { - SVGInlineTextBox* first_text_box = ToSVGInlineTextBox(*first); - SVGInlineTextBox* last_text_box = ToSVGInlineTextBox(*last); - + auto* first_text_box = DynamicTo<SVGInlineTextBox>(*first); + auto* last_text_box = DynamicTo<SVGInlineTextBox>(*last); + if (last_text_box && first_text_box) { // Reordering is only necessary for BiDi text that is _absolutely_ // positioned. if (first_text_box->Len() == 1 && diff --git a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h index 8f741b84c92..3fc347d75a5 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h @@ -24,6 +24,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_LINE_SVG_ROOT_INLINE_BOX_H_ #include "third_party/blink/renderer/core/layout/line/root_inline_box.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -60,7 +61,12 @@ class SVGRootInlineBox final : public RootInlineBox { LayoutUnit logical_height_; }; -DEFINE_INLINE_BOX_TYPE_CASTS(SVGRootInlineBox); +template <> +struct DowncastTraits<SVGRootInlineBox> { + static bool AllowFrom(const InlineBox& box) { + return box.IsSVGRootInlineBox(); + } +}; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc index 40000a9c319..14eabbb2fcc 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.cc @@ -210,10 +210,9 @@ inline void SVGLayoutSupport::UpdateObjectBoundingBox( bool& object_bounding_box_valid, LayoutObject* other, FloatRect other_bounding_box) { + auto* svg_container = DynamicTo<LayoutSVGContainer>(other); bool other_valid = - other->IsSVGContainer() - ? ToLayoutSVGContainer(other)->IsObjectBoundingBoxValid() - : true; + svg_container ? svg_container->IsObjectBoundingBoxValid() : true; if (!other_valid) return; @@ -236,8 +235,8 @@ static bool HasValidBoundingBoxForContainer(const LayoutObject* object) { if (object->IsSVGHiddenContainer()) return false; - if (object->IsSVGForeignObject()) - return ToLayoutSVGForeignObject(object)->IsObjectBoundingBoxValid(); + if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(object)) + return foreign_object->IsObjectBoundingBoxValid(); if (object->IsSVGImage()) return ToLayoutSVGImage(object)->IsObjectBoundingBoxValid(); @@ -409,6 +408,34 @@ void SVGLayoutSupport::AdjustVisualRectWithResources( visual_rect.Intersect(masker->ResourceBoundingBox(object_bounding_box)); } +FloatRect SVGLayoutSupport::ExtendTextBBoxWithStroke( + const LayoutObject& layout_object, + const FloatRect& text_bounds) { + DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline()); + FloatRect bounds = text_bounds; + const SVGComputedStyle& svg_style = layout_object.StyleRef().SvgStyle(); + if (svg_style.HasStroke()) { + SVGLengthContext length_context(To<SVGElement>(layout_object.GetNode())); + // TODO(fs): This approximation doesn't appear to be conservative enough + // since while text (usually?) won't have caps it could have joins and thus + // miters. + bounds.Inflate(length_context.ValueForLength(svg_style.StrokeWidth())); + } + return bounds; +} + +FloatRect SVGLayoutSupport::ComputeVisualRectForText( + const LayoutObject& layout_object, + const FloatRect& text_bounds, + const FloatRect& reference_box) { + DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline()); + FloatRect visual_rect = ExtendTextBBoxWithStroke(layout_object, text_bounds); + if (const ShadowList* text_shadow = layout_object.StyleRef().TextShadow()) + text_shadow->AdjustRectForShadow(visual_rect); + AdjustVisualRectWithResources(layout_object, reference_box, visual_rect); + return visual_rect; +} + bool SVGLayoutSupport::HasFilterResource(const LayoutObject& object) { SVGResources* resources = SVGResourcesCache::CachedResourcesForLayoutObject(object); @@ -442,8 +469,8 @@ bool SVGLayoutSupport::HitTestChildren(LayoutObject* last_child, HitTestAction hit_test_action) { for (LayoutObject* child = last_child; child; child = child->PreviousSibling()) { - if (child->IsSVGForeignObject()) { - if (ToLayoutSVGForeignObject(child)->NodeAtPointFromSVG( + if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(child)) { + if (foreign_object->NodeAtPointFromSVG( result, location, accumulated_offset, hit_test_action)) return true; } else { diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.h index 92b33e48eb6..214bf35707b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_support.h @@ -68,6 +68,15 @@ class CORE_EXPORT SVGLayoutSupport { const FloatRect& object_bounding_box, FloatRect&); + // Add any contribution from 'stroke' to a text content bounding rect. + static FloatRect ExtendTextBBoxWithStroke(const LayoutObject&, + const FloatRect& text_bounds); + + // Compute the visual rect for the a text content LayoutObject. + static FloatRect ComputeVisualRectForText(const LayoutObject&, + const FloatRect& text_bounds, + const FloatRect& reference_box); + // Determine if the LayoutObject references a filter resource object. static bool HasFilterResource(const LayoutObject&); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc index 9c5236a6c29..214b062de87 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc @@ -383,12 +383,10 @@ static WTF::TextStream& operator<<(WTF::TextStream& ts, WriteNameValuePair(ts, "r", length_context.ValueForLength(svg_style.R(), style, SVGLengthMode::kOther)); - } else if (IsSVGPolyElement(*svg_element)) { - WriteNameAndQuotedValue(ts, "points", - ToSVGPolyElement(*svg_element) - .Points() - ->CurrentValue() - ->ValueAsString()); + } else if (auto* svg_poly_element = DynamicTo<SVGPolyElement>(svg_element)) { + WriteNameAndQuotedValue( + ts, "points", + svg_poly_element->Points()->CurrentValue()->ValueAsString()); } else if (IsA<SVGPathElement>(*svg_element)) { const StylePath& path = svg_style.D() ? *svg_style.D() : *StylePath::EmptyPath(); @@ -410,7 +408,7 @@ static WTF::TextStream& operator<<(WTF::TextStream& ts, static void WriteLayoutSVGTextBox(WTF::TextStream& ts, const LayoutSVGText& text) { - SVGRootInlineBox* box = ToSVGRootInlineBox(text.FirstRootBox()); + auto* box = To<SVGRootInlineBox>(text.FirstRootBox()); if (!box) return; @@ -495,10 +493,11 @@ static inline void WriteSVGInlineTextBoxes(WTF::TextStream& ts, const LayoutText& text, int indent) { for (InlineTextBox* box : text.TextBoxes()) { - if (!box->IsSVGInlineTextBox()) + auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(box); + if (!svg_inline_text_box) continue; - WriteSVGInlineTextBox(ts, ToSVGInlineTextBox(box), indent); + WriteSVGInlineTextBox(ts, svg_inline_text_box, indent); } } @@ -648,9 +647,6 @@ void WriteSVGResourceContainer(WTF::TextStream& ts, void WriteSVGContainer(WTF::TextStream& ts, const LayoutObject& container, int indent) { - // Currently LayoutSVGResourceFilterPrimitive has no meaningful output. - if (container.IsSVGResourceFilterPrimitive()) - return; WriteStandardPrefix(ts, container, indent); WritePositionAndStyle(ts, container); ts << "\n"; @@ -741,7 +737,7 @@ void WriteResources(WTF::TextStream& ts, WriteStandardPrefix(ts, *clipper, 0); ts << " " << clipper->ResourceBoundingBox(reference_box) << "\n"; } - if (LayoutSVGResourceFilter* filter = resources->Filter()) { + if (LayoutSVGResourceFilter* filter = GetFilterResourceForSVG(style)) { DCHECK(style.HasFilter()); DCHECK_EQ(style.Filter().size(), 1u); const FilterOperation& filter_operation = *style.Filter().at(0); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.cc index 0892cd34cfd..3673515b7c9 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.cc @@ -19,6 +19,9 @@ #include "third_party/blink/renderer/core/layout/svg/svg_marker_data.h" +#include "base/auto_reset.h" +#include "third_party/blink/renderer/core/svg/svg_path_byte_stream_source.h" +#include "third_party/blink/renderer/core/svg/svg_path_parser.h" #include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -32,12 +35,7 @@ static double BisectingAngle(double in_angle, double out_angle) { void SVGMarkerDataBuilder::Build(const Path& path) { path.Apply(this, SVGMarkerDataBuilder::UpdateFromPathElement); - if (positions_.IsEmpty()) - return; - const bool kEndsSubpath = true; - UpdateAngle(kEndsSubpath); - // Mark the last marker as 'end'. - positions_.back().type = kEndMarker; + Flush(); } void SVGMarkerDataBuilder::UpdateFromPathElement(void* info, @@ -45,6 +43,109 @@ void SVGMarkerDataBuilder::UpdateFromPathElement(void* info, static_cast<SVGMarkerDataBuilder*>(info)->UpdateFromPathElement(*element); } +namespace { + +// Path processor that converts an arc segment to a cubic segment with +// equivalent start/end tangents. +class MarkerPathSegmentProcessor : public SVGPathNormalizer { + STACK_ALLOCATED(); + + public: + MarkerPathSegmentProcessor(SVGPathConsumer* consumer) + : SVGPathNormalizer(consumer) {} + + void EmitSegment(const PathSegmentData&); + + private: + Vector<PathSegmentData> DecomposeArc(const PathSegmentData&); +}; + +Vector<PathSegmentData> MarkerPathSegmentProcessor::DecomposeArc( + const PathSegmentData& segment) { + class SegmentCollector : public SVGPathConsumer { + STACK_ALLOCATED(); + + public: + void EmitSegment(const PathSegmentData& segment) override { + DCHECK_EQ(segment.command, kPathSegCurveToCubicAbs); + segments_.push_back(segment); + } + Vector<PathSegmentData> ReturnSegments() { return std::move(segments_); } + + private: + Vector<PathSegmentData> segments_; + } collector; + // Temporarily switch to our "collector" to collect the curve segments + // emitted by DecomposeArcToCubic(), and then switch back to the actual + // consumer. + base::AutoReset<SVGPathConsumer*> consumer_scope(&consumer_, &collector); + DecomposeArcToCubic(current_point_, segment); + return collector.ReturnSegments(); +} + +void MarkerPathSegmentProcessor::EmitSegment( + const PathSegmentData& original_segment) { + PathSegmentData segment = original_segment; + // Convert a relative arc to absolute. + if (segment.command == kPathSegArcRel) { + segment.command = kPathSegArcAbs; + segment.target_point += current_point_; + } + if (segment.command == kPathSegArcAbs) { + // Decompose and then pass/emit a synthesized cubic with matching tangents. + Vector<PathSegmentData> decomposed_arc_curves = DecomposeArc(segment); + if (decomposed_arc_curves.IsEmpty()) { + segment.command = kPathSegLineToAbs; + } else { + // Use the first control point from the first curve and the second and + // last control points from the last curve. (If the decomposition only + // has one curve then the second line just copies the same point again.) + segment = decomposed_arc_curves.back(); + segment.point1 = decomposed_arc_curves[0].point1; + } + } + // Invoke the base class to normalize and emit to the consumer + // (SVGMarkerDataBuilder). + SVGPathNormalizer::EmitSegment(segment); +} + +} // namespace + +void SVGMarkerDataBuilder::Build(const SVGPathByteStream& stream) { + SVGPathByteStreamSource source(stream); + MarkerPathSegmentProcessor processor(this); + svg_path_parser::ParsePath(source, processor); + Flush(); +} + +void SVGMarkerDataBuilder::EmitSegment(const PathSegmentData& segment) { + PathElement element; + FloatPoint points[3]; + element.points = points; + switch (segment.command) { + case kPathSegClosePath: + element.type = kPathElementCloseSubpath; + break; + case kPathSegMoveToAbs: + element.type = kPathElementMoveToPoint; + element.points[0] = segment.target_point; + break; + case kPathSegLineToAbs: + element.type = kPathElementAddLineToPoint; + element.points[0] = segment.target_point; + break; + case kPathSegCurveToCubicAbs: + element.type = kPathElementAddCurveToPoint; + element.points[0] = segment.point1; + element.points[1] = segment.point2; + element.points[2] = segment.target_point; + break; + default: + NOTREACHED(); + } + UpdateFromPathElement(element); +} + double SVGMarkerDataBuilder::CurrentAngle(AngleType type) const { // For details of this calculation, see: // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement @@ -176,4 +277,13 @@ void SVGMarkerDataBuilder::UpdateFromPathElement(const PathElement& element) { positions_.push_back(MarkerPosition(marker_type, origin_, 0)); } +void SVGMarkerDataBuilder::Flush() { + if (positions_.IsEmpty()) + return; + const bool kEndsSubpath = true; + UpdateAngle(kEndsSubpath); + // Mark the last marker as 'end'. + positions_.back().type = kEndMarker; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.h index adbda9ab99c..40d150d0bff 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_marker_data.h @@ -20,6 +20,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_SVG_MARKER_DATA_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_SVG_MARKER_DATA_H_ +#include "third_party/blink/renderer/core/svg/svg_path_consumer.h" #include "third_party/blink/renderer/platform/graphics/path.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/math_extras.h" @@ -29,6 +30,7 @@ namespace blink { enum SVGMarkerType { kStartMarker, kMidMarker, kEndMarker }; class LayoutSVGResourceMarker; +class SVGPathByteStream; struct MarkerPosition { DISALLOW_NEW(); @@ -58,7 +60,7 @@ struct MarkerPosition { float angle; }; -class SVGMarkerDataBuilder { +class SVGMarkerDataBuilder : private SVGPathConsumer { STACK_ALLOCATED(); public: @@ -67,9 +69,21 @@ class SVGMarkerDataBuilder { last_moveto_index_(0), last_element_type_(kPathElementMoveToPoint) {} + // Build marker data for a Path. void Build(const Path&); + // Build marker data for a SVGPathByteStream. + // + // A SVGPathByteStream is semantically higher-level than a Path, and thus + // this allows those higher-level constructs (for example arcs) to be handled + // correctly. This should be used in cases where the original path data can + // contain such higher-level constructs. + void Build(const SVGPathByteStream&); + private: + // SVGPathConsumer + void EmitSegment(const PathSegmentData&) override; + static void UpdateFromPathElement(void* info, const PathElement*); enum AngleType { @@ -95,6 +109,7 @@ class SVGMarkerDataBuilder { const FloatPoint& end); SegmentData ExtractPathElementFeatures(const PathElement&) const; void UpdateFromPathElement(const PathElement&); + void Flush(); Vector<MarkerPosition>& positions_; unsigned last_moveto_index_; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc index 1ff19413ab7..4d1cc23961b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.cc @@ -27,15 +27,22 @@ #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h" #include "third_party/blink/renderer/core/style/style_svg_resource.h" +#include "third_party/blink/renderer/core/svg/graphics/filters/svg_filter_builder.h" +#include "third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.h" #include "third_party/blink/renderer/core/svg/svg_pattern_element.h" #include "third_party/blink/renderer/core/svg/svg_resource.h" #include "third_party/blink/renderer/core/svg/svg_tree_scope_resources.h" #include "third_party/blink/renderer/core/svg/svg_uri_reference.h" #include "third_party/blink/renderer/core/svg_names.h" +#include "third_party/blink/renderer/platform/graphics/filters/filter.h" +#include "third_party/blink/renderer/platform/graphics/filters/filter_effect.h" +#include "third_party/blink/renderer/platform/graphics/filters/paint_filter_builder.h" +#include "third_party/blink/renderer/platform/graphics/filters/source_graphic.h" #if DCHECK_IS_ON() #include <stdio.h> @@ -45,10 +52,32 @@ namespace blink { SVGResources::SVGResources() : linked_resource_(nullptr) {} -SVGResourceClient* SVGResources::GetClient(const LayoutObject& object) { +SVGElementResourceClient* SVGResources::GetClient(const LayoutObject& object) { return To<SVGElement>(object.GetNode())->GetSVGResourceClient(); } +FloatRect SVGResources::ReferenceBoxForEffects( + const LayoutObject& layout_object) { + // For SVG foreign objects, remove the position part of the bounding box. The + // position is already baked into the transform, and we don't want to re-apply + // the offset when, e.g., using "objectBoundingBox" for clipPathUnits. + if (layout_object.IsSVGForeignObject()) { + FloatRect rect = layout_object.ObjectBoundingBox(); + return FloatRect(FloatPoint::Zero(), rect.Size()); + } + + // Text "sub-elements" (<tspan>, <textpath>, <a>) should use the entire + // <text>s object bounding box rather then their own. + // https://svgwg.org/svg2-draft/text.html#ObjectBoundingBoxUnitsTextObjects + const LayoutObject* obb_layout_object = &layout_object; + if (layout_object.IsSVGInline()) { + obb_layout_object = + LayoutSVGText::LocateLayoutSVGTextAncestor(&layout_object); + } + DCHECK(obb_layout_object); + return obb_layout_object->ObjectBoundingBox(); +} + static HashSet<AtomicString>& ClipperFilterMaskerTags() { DEFINE_STATIC_LOCAL( HashSet<AtomicString>, tag_list, @@ -115,37 +144,6 @@ static HashSet<AtomicString>& FillAndStrokeTags() { return tag_list; } -namespace { - -template <typename ContainerType> -bool IsResourceOfType(LayoutSVGResourceContainer* container) { - return container->ResourceType() == ContainerType::kResourceType; -} - -template <> -bool IsResourceOfType<LayoutSVGResourcePaintServer>( - LayoutSVGResourceContainer* container) { - return container->IsSVGPaintServer(); -} - -template <typename ContainerType> -ContainerType* CastResource(SVGResource* resource) { - if (!resource) - return nullptr; - if (LayoutSVGResourceContainer* container = resource->ResourceContainer()) { - if (IsResourceOfType<ContainerType>(container)) - return static_cast<ContainerType*>(container); - } - return nullptr; -} - -template <typename ContainerType> -ContainerType* CastResource(StyleSVGResource& style_resource) { - return CastResource<ContainerType>(style_resource.Resource()); -} - -} // namespace - bool SVGResources::HasResourceData() const { return clipper_filter_masker_data_ || marker_data_ || fill_stroke_data_ || linked_resource_; @@ -176,59 +174,51 @@ std::unique_ptr<SVGResources> SVGResources::BuildResources( std::unique_ptr<SVGResources> resources; if (ClipperFilterMaskerTags().Contains(tag_name)) { if (computed_style.ClipPath() && !object.IsSVGRoot()) { - ClipPathOperation* clip_path_operation = computed_style.ClipPath(); - if (clip_path_operation->GetType() == ClipPathOperation::REFERENCE) { - const ReferenceClipPathOperation& clip_path_reference = - To<ReferenceClipPathOperation>(*clip_path_operation); - EnsureResources(resources).SetClipper( - CastResource<LayoutSVGResourceClipper>( - clip_path_reference.Resource())); + if (LayoutSVGResourceClipper* clipper = + GetSVGResourceAsType(computed_style.ClipPath())) { + EnsureResources(resources).SetClipper(clipper); } } if (computed_style.HasFilter() && !object.IsSVGRoot()) { - const FilterOperations& filter_operations = computed_style.Filter(); - if (filter_operations.size() == 1) { - const FilterOperation& filter_operation = *filter_operations.at(0); - if (const auto* reference_filter_operation = - DynamicTo<ReferenceFilterOperation>(filter_operation)) { - EnsureResources(resources).SetFilter( - CastResource<LayoutSVGResourceFilter>( - reference_filter_operation->Resource())); - } + if (LayoutSVGResourceFilter* filter = + GetFilterResourceForSVG(computed_style)) { + EnsureResources(resources).SetFilter(filter); } } - if (StyleSVGResource* masker_resource = style.MaskerResource()) { - EnsureResources(resources).SetMasker( - CastResource<LayoutSVGResourceMasker>(*masker_resource)); + if (auto* masker = GetSVGResourceAsType<LayoutSVGResourceMasker>( + style.MaskerResource())) { + EnsureResources(resources).SetMasker(masker); } } if (style.HasMarkers() && SupportsMarkers(element)) { - if (StyleSVGResource* marker_start_resource = style.MarkerStartResource()) { - EnsureResources(resources).SetMarkerStart( - CastResource<LayoutSVGResourceMarker>(*marker_start_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerStartResource())) { + EnsureResources(resources).SetMarkerStart(marker); } - if (StyleSVGResource* marker_mid_resource = style.MarkerMidResource()) { - EnsureResources(resources).SetMarkerMid( - CastResource<LayoutSVGResourceMarker>(*marker_mid_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerMidResource())) { + EnsureResources(resources).SetMarkerMid(marker); } - if (StyleSVGResource* marker_end_resource = style.MarkerEndResource()) { - EnsureResources(resources).SetMarkerEnd( - CastResource<LayoutSVGResourceMarker>(*marker_end_resource)); + if (auto* marker = GetSVGResourceAsType<LayoutSVGResourceMarker>( + style.MarkerEndResource())) { + EnsureResources(resources).SetMarkerEnd(marker); } } if (FillAndStrokeTags().Contains(tag_name)) { - if (StyleSVGResource* fill_resource = style.FillPaint().Resource()) { - EnsureResources(resources).SetFill( - CastResource<LayoutSVGResourcePaintServer>(*fill_resource)); + if (auto* paint_resource = + GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + style.FillPaint().Resource())) { + EnsureResources(resources).SetFill(paint_resource); } - if (StyleSVGResource* stroke_resource = style.StrokePaint().Resource()) { - EnsureResources(resources).SetStroke( - CastResource<LayoutSVGResourcePaintServer>(*stroke_resource)); + if (auto* paint_resource = + GetSVGResourceAsType<LayoutSVGResourcePaintServer>( + style.StrokePaint().Resource())) { + EnsureResources(resources).SetStroke(paint_resource); } } @@ -277,41 +267,28 @@ void SVGResources::LayoutIfNeeded() { linked_resource_->LayoutIfNeeded(); } -InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( - SVGResourceClient& client) const { - if (!clipper_filter_masker_data_) - return 0; - InvalidationModeMask invalidation_flags = 0; - if (LayoutSVGResourceClipper* clipper = clipper_filter_masker_data_->clipper) - clipper->RemoveClientFromCache(client); - if (LayoutSVGResourceFilter* filter = clipper_filter_masker_data_->filter) { - if (filter->RemoveClientFromCache(client)) - invalidation_flags |= SVGResourceClient::kPaintInvalidation; - } - if (LayoutSVGResourceMasker* masker = clipper_filter_masker_data_->masker) - masker->RemoveClientFromCache(client); - return invalidation_flags | SVGResourceClient::kBoundariesInvalidation; +bool SVGResources::DifferenceNeedsLayout(const SVGResources* a, + const SVGResources* b) { + bool a_has_bounds_affecting_resource = a && a->clipper_filter_masker_data_; + bool b_has_bounds_affecting_resource = b && b->clipper_filter_masker_data_; + if (a_has_bounds_affecting_resource != b_has_bounds_affecting_resource) + return true; + if (!a_has_bounds_affecting_resource) + return false; + return a->Clipper() != b->Clipper() || a->Filter() != b->Filter() || + a->Masker() != b->Masker(); } -InvalidationModeMask SVGResources::RemoveClientFromCache( - SVGResourceClient& client) const { - if (!HasResourceData()) +InvalidationModeMask SVGResources::RemoveClientFromCacheAffectingObjectBounds( + SVGElementResourceClient& client) const { + if (!clipper_filter_masker_data_) return 0; - - // We never call this method for the elements where this would be non-null. - DCHECK(!linked_resource_); - InvalidationModeMask invalidation_flags = - RemoveClientFromCacheAffectingObjectBounds(client); - - if (fill_stroke_data_) { - if (LayoutSVGResourcePaintServer* fill = fill_stroke_data_->fill) - fill->RemoveClientFromCache(client); - if (LayoutSVGResourcePaintServer* stroke = fill_stroke_data_->stroke) - stroke->RemoveClientFromCache(client); - invalidation_flags |= SVGResourceClient::kPaintInvalidation; + SVGResourceClient::kBoundariesInvalidation; + if (clipper_filter_masker_data_->filter) { + if (client.ClearFilterData()) + invalidation_flags |= SVGResourceClient::kPaintInvalidation; } - return invalidation_flags; } @@ -465,9 +442,7 @@ void SVGResources::BuildSetOfResources( } void SVGResources::SetClipper(LayoutSVGResourceClipper* clipper) { - if (!clipper) - return; - + DCHECK(clipper); DCHECK_EQ(clipper->ResourceType(), kClipperResourceType); if (!clipper_filter_masker_data_) @@ -477,9 +452,7 @@ void SVGResources::SetClipper(LayoutSVGResourceClipper* clipper) { } void SVGResources::SetFilter(LayoutSVGResourceFilter* filter) { - if (!filter) - return; - + DCHECK(filter); DCHECK_EQ(filter->ResourceType(), kFilterResourceType); if (!clipper_filter_masker_data_) @@ -489,9 +462,7 @@ void SVGResources::SetFilter(LayoutSVGResourceFilter* filter) { } void SVGResources::SetMarkerStart(LayoutSVGResourceMarker* marker_start) { - if (!marker_start) - return; - + DCHECK(marker_start); DCHECK_EQ(marker_start->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -501,9 +472,7 @@ void SVGResources::SetMarkerStart(LayoutSVGResourceMarker* marker_start) { } void SVGResources::SetMarkerMid(LayoutSVGResourceMarker* marker_mid) { - if (!marker_mid) - return; - + DCHECK(marker_mid); DCHECK_EQ(marker_mid->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -513,9 +482,7 @@ void SVGResources::SetMarkerMid(LayoutSVGResourceMarker* marker_mid) { } void SVGResources::SetMarkerEnd(LayoutSVGResourceMarker* marker_end) { - if (!marker_end) - return; - + DCHECK(marker_end); DCHECK_EQ(marker_end->ResourceType(), kMarkerResourceType); if (!marker_data_) @@ -525,9 +492,7 @@ void SVGResources::SetMarkerEnd(LayoutSVGResourceMarker* marker_end) { } void SVGResources::SetMasker(LayoutSVGResourceMasker* masker) { - if (!masker) - return; - + DCHECK(masker); DCHECK_EQ(masker->ResourceType(), kMaskerResourceType); if (!clipper_filter_masker_data_) @@ -537,8 +502,7 @@ void SVGResources::SetMasker(LayoutSVGResourceMasker* masker) { } void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) { - if (!fill) - return; + DCHECK(fill); if (!fill_stroke_data_) fill_stroke_data_ = std::make_unique<FillStrokeData>(); @@ -547,8 +511,7 @@ void SVGResources::SetFill(LayoutSVGResourcePaintServer* fill) { } void SVGResources::SetStroke(LayoutSVGResourcePaintServer* stroke) { - if (!stroke) - return; + DCHECK(stroke); if (!fill_stroke_data_) fill_stroke_data_ = std::make_unique<FillStrokeData>(); @@ -705,6 +668,83 @@ void SVGResources::ClearMarkers(SVGElement& element, marker_resource->RemoveClient(*client); } +bool FilterData::UpdateStateOnFinish() { + switch (state_) { + // A cycle can occur when an FeImage references a source that + // makes use of the FeImage itself. This is the first place we + // would hit the cycle so we reset the state and continue. + case kGeneratingFilterCycleDetected: + state_ = kGeneratingFilter; + return false; + case kRecordingContentCycleDetected: + state_ = kRecordingContent; + return false; + default: + return true; + } +} + +void FilterData::UpdateStateOnPrepare() { + switch (state_) { + case kGeneratingFilter: + state_ = kGeneratingFilterCycleDetected; + break; + case kRecordingContent: + state_ = kRecordingContentCycleDetected; + break; + default: + break; + } +} + +void FilterData::UpdateContent(sk_sp<PaintRecord> content) { + DCHECK_EQ(state_, kRecordingContent); + Filter* filter = last_effect_->GetFilter(); + FloatRect bounds = filter->FilterRegion(); + DCHECK(filter->GetSourceGraphic()); + paint_filter_builder::BuildSourceGraphic(filter->GetSourceGraphic(), + std::move(content), bounds); + state_ = kReadyToPaint; +} + +sk_sp<PaintFilter> FilterData::CreateFilter() { + DCHECK_EQ(state_, kReadyToPaint); + state_ = kGeneratingFilter; + sk_sp<PaintFilter> image_filter = + paint_filter_builder::Build(last_effect_, kInterpolationSpaceSRGB); + state_ = kReadyToPaint; + return image_filter; +} + +FloatRect FilterData::MapRect(const FloatRect& input_rect) const { + DCHECK_EQ(state_, kReadyToPaint); + return last_effect_->MapRect(input_rect); +} + +bool FilterData::Invalidate(SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) { + if (state_ != kReadyToPaint) + return true; + if (FilterEffect* effect = node_map_->EffectForElement(primitive)) { + if (!primitive.SetFilterEffectAttribute(effect, attribute)) + return false; // No change + node_map_->InvalidateDependentEffects(effect); + } + return true; +} + +void FilterData::Trace(Visitor* visitor) { + visitor->Trace(last_effect_); + visitor->Trace(node_map_); +} + +void FilterData::Dispose() { + node_map_ = nullptr; + if (last_effect_) + last_effect_->DisposeImageFiltersRecursive(); + last_effect_ = nullptr; +} + SVGElementResourceClient::SVGElementResourceClient(SVGElement* element) : element_(element) {} @@ -713,22 +753,15 @@ void SVGElementResourceClient::ResourceContentChanged( LayoutObject* layout_object = element_->GetLayoutObject(); if (!layout_object) return; - bool mark_for_invalidation = - invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation; if (layout_object->IsSVGResourceContainer()) { - ToLayoutSVGResourceContainer(layout_object) - ->RemoveAllClientsFromCache(mark_for_invalidation); + ToLayoutSVGResourceContainer(layout_object)->RemoveAllClientsFromCache(); return; } - if (mark_for_invalidation) { - LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object, - invalidation_mask); - } + LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object, + invalidation_mask); - // Special case for filter invalidation. - if (invalidation_mask & SVGResourceClient::kSkipAncestorInvalidation) - return; + ClearFilterData(); bool needs_layout = invalidation_mask & SVGResourceClient::kLayoutInvalidation; @@ -737,8 +770,10 @@ void SVGElementResourceClient::ResourceContentChanged( } void SVGElementResourceClient::ResourceElementChanged() { - if (LayoutObject* layout_object = element_->GetLayoutObject()) + if (LayoutObject* layout_object = element_->GetLayoutObject()) { + ClearFilterData(); SVGResourcesCache::ResourceReferenceChanged(*layout_object); + } } void SVGElementResourceClient::ResourceDestroyed( @@ -752,8 +787,32 @@ void SVGElementResourceClient::ResourceDestroyed( resources->ResourceDestroyed(resource); } +void SVGElementResourceClient::FilterPrimitiveChanged( + SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) { + if (filter_data_ && !filter_data_->Invalidate(primitive, attribute)) + return; // No change + LayoutObject* layout_object = element_->GetLayoutObject(); + if (!layout_object) + return; + LayoutSVGResourceContainer::MarkClientForInvalidation( + *layout_object, SVGResourceClient::kPaintInvalidation); +} + +void SVGElementResourceClient::SetFilterData(FilterData* filter_data) { + filter_data_ = filter_data; +} + +bool SVGElementResourceClient::ClearFilterData() { + FilterData* filter_data = filter_data_.Release(); + if (filter_data) + filter_data->Dispose(); + return !!filter_data; +} + void SVGElementResourceClient::Trace(Visitor* visitor) { visitor->Trace(element_); + visitor->Trace(filter_data_); SVGResourceClient::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h index 5a158e2c60d..96db2ccc575 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources.h @@ -32,6 +32,7 @@ namespace blink { class ComputedStyle; +class FilterEffect; class LayoutObject; class LayoutSVGResourceClipper; class LayoutSVGResourceFilter; @@ -39,6 +40,8 @@ class LayoutSVGResourceMarker; class LayoutSVGResourceMasker; class LayoutSVGResourcePaintServer; class SVGElement; +class SVGElementResourceClient; +class SVGFilterGraphNodeMap; // Holds a set of resources associated with a LayoutObject class SVGResources { @@ -47,7 +50,8 @@ class SVGResources { public: SVGResources(); - static SVGResourceClient* GetClient(const LayoutObject&); + static SVGElementResourceClient* GetClient(const LayoutObject&); + static FloatRect ReferenceBoxForEffects(const LayoutObject&); static std::unique_ptr<SVGResources> BuildResources(const LayoutObject&, const ComputedStyle&); @@ -110,12 +114,13 @@ class SVGResources { void BuildSetOfResources(HashSet<LayoutSVGResourceContainer*>&); // Methods operating on all cached resources - InvalidationModeMask RemoveClientFromCache(SVGResourceClient&) const; InvalidationModeMask RemoveClientFromCacheAffectingObjectBounds( - SVGResourceClient&) const; + SVGElementResourceClient&) const; void ResourceDestroyed(LayoutSVGResourceContainer*); void ClearReferencesTo(LayoutSVGResourceContainer*); + static bool DifferenceNeedsLayout(const SVGResources*, const SVGResources*); + #if DCHECK_IS_ON() void Dump(const LayoutObject*); #endif @@ -188,6 +193,51 @@ class SVGResources { DISALLOW_COPY_AND_ASSIGN(SVGResources); }; +class FilterData final : public GarbageCollected<FilterData> { + public: + FilterData(FilterEffect* last_effect, SVGFilterGraphNodeMap* node_map) + : last_effect_(last_effect), + node_map_(node_map), + state_(kRecordingContent) {} + + void UpdateStateOnPrepare(); + bool UpdateStateOnFinish(); + bool ContentNeedsUpdate() const { return state_ == kRecordingContent; } + void UpdateContent(sk_sp<PaintRecord> content); + sk_sp<PaintFilter> CreateFilter(); + FloatRect MapRect(const FloatRect& input_rect) const; + // Perform a finegrained invalidation of the filter chain for the + // specified filter primitive and attribute. Returns false if no + // further invalidation is required, otherwise true. + bool Invalidate(SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute); + + void Dispose(); + + void Trace(Visitor*); + + private: + Member<FilterEffect> last_effect_; + Member<SVGFilterGraphNodeMap> node_map_; + + /* + * The state transitions should follow the following: + * + * RecordingContent->ReadyToPaint->GeneratingFilter->ReadyToPaint + * | ^ | ^ + * v | v | + * RecordingContentCycleDetected GeneratingFilterCycleDetected + */ + enum FilterDataState { + kRecordingContent, + kRecordingContentCycleDetected, + kReadyToPaint, + kGeneratingFilter, + kGeneratingFilterCycleDetected + }; + FilterDataState state_; +}; + class SVGElementResourceClient final : public GarbageCollected<SVGElementResourceClient>, public SVGResourceClient { @@ -200,10 +250,18 @@ class SVGElementResourceClient final void ResourceElementChanged() override; void ResourceDestroyed(LayoutSVGResourceContainer*) override; + void FilterPrimitiveChanged(SVGFilterPrimitiveStandardAttributes& primitive, + const QualifiedName& attribute) override; + + void SetFilterData(FilterData*); + bool ClearFilterData(); + FilterData* GetFilterData() const { return filter_data_; } + void Trace(Visitor*) override; private: Member<SVGElement> element_; + Member<FilterData> filter_data_; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc index 30fc8f834a4..61d0dfb9d2a 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.cc @@ -22,6 +22,7 @@ #include <memory> #include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h" #include "third_party/blink/renderer/core/svg/svg_document_extensions.h" @@ -32,7 +33,7 @@ SVGResourcesCache::SVGResourcesCache() = default; SVGResourcesCache::~SVGResourcesCache() = default; -bool SVGResourcesCache::AddResourcesFromLayoutObject( +SVGResources* SVGResourcesCache::AddResourcesFromLayoutObject( LayoutObject& object, const ComputedStyle& style) { DCHECK(!cache_.Contains(&object)); @@ -41,7 +42,7 @@ bool SVGResourcesCache::AddResourcesFromLayoutObject( std::unique_ptr<SVGResources> new_resources = SVGResources::BuildResources(object, style); if (!new_resources) - return false; + return nullptr; // Put object in cache. SVGResources* resources = @@ -51,12 +52,12 @@ bool SVGResourcesCache::AddResourcesFromLayoutObject( HashSet<LayoutSVGResourceContainer*> resource_set; resources->BuildSetOfResources(resource_set); - SVGResourcesCycleSolver solver(object); + SVGResourcesCycleSolver solver; for (auto* resource_container : resource_set) { - if (solver.FindCycle(resource_container)) + if (resource_container->FindCycle(solver)) resources->ClearReferencesTo(resource_container); } - return true; + return resources; } bool SVGResourcesCache::RemoveResourcesFromLayoutObject(LayoutObject& object) { @@ -64,12 +65,15 @@ bool SVGResourcesCache::RemoveResourcesFromLayoutObject(LayoutObject& object) { return !!resources; } -bool SVGResourcesCache::UpdateResourcesFromLayoutObject( +SVGResourcesCache::ResourceUpdateInfo +SVGResourcesCache::UpdateResourcesFromLayoutObject( LayoutObject& object, const ComputedStyle& new_style) { - bool did_update = RemoveResourcesFromLayoutObject(object); - did_update |= AddResourcesFromLayoutObject(object, new_style); - return did_update; + std::unique_ptr<SVGResources> old_resources = cache_.Take(&object); + SVGResources* new_resources = AddResourcesFromLayoutObject(object, new_style); + return { + old_resources || new_resources, + SVGResources::DifferenceNeedsLayout(old_resources.get(), new_resources)}; } static inline SVGResourcesCache& ResourcesCache(Document& document) { @@ -85,14 +89,22 @@ void SVGResourcesCache::ClientLayoutChanged(LayoutObject& object) { SVGResources* resources = CachedResourcesForLayoutObject(object); if (!resources) return; - // Invalidate the resources if either the LayoutObject itself changed, // or we have filter resources, which could depend on the layout of children. if (!object.SelfNeedsLayout() && !resources->Filter()) return; - SVGResourceClient* client = SVGResources::GetClient(object); - if (InvalidationModeMask invalidation_flags = - resources->RemoveClientFromCache(*client)) { + SVGElementResourceClient* client = SVGResources::GetClient(object); + InvalidationModeMask invalidation_flags = + resources->RemoveClientFromCacheAffectingObjectBounds(*client); + if (LayoutSVGResourcePaintServer* fill = resources->Fill()) { + fill->RemoveClientFromCache(*client); + invalidation_flags |= SVGResourceClient::kPaintInvalidation; + } + if (LayoutSVGResourcePaintServer* stroke = resources->Stroke()) { + stroke->RemoveClientFromCache(*client); + invalidation_flags |= SVGResourceClient::kPaintInvalidation; + } + if (invalidation_flags) { LayoutSVGResourceContainer::MarkClientForInvalidation(object, invalidation_flags); } @@ -124,28 +136,34 @@ void SVGResourcesCache::ClientStyleChanged(LayoutObject& layout_object, if (!diff.HasDifference() || !layout_object.Parent()) return; - // In this case the proper SVGFE*Element will decide whether the modified CSS - // properties require - // a relayout or paintInvalidation. - if (layout_object.IsSVGResourceFilterPrimitive() && !diff.NeedsLayout()) - return; + // LayoutObjects for SVGFE*Element should not be calling this function. + DCHECK(!layout_object.IsSVGFilterPrimitive()); // Dynamic changes of CSS properties like 'clip-path' may require us to // recompute the associated resources for a LayoutObject. // TODO(fs): Avoid passing in a useless StyleDifference, but instead compare // oldStyle/newStyle to see which resources changed to be able to selectively // rebuild individual resources, instead of all of them. + bool needs_layout = false; if (LayoutObjectCanHaveResources(layout_object)) { SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument()); - if (cache.UpdateResourcesFromLayoutObject(layout_object, new_style)) + auto update_info = + cache.UpdateResourcesFromLayoutObject(layout_object, new_style); + if (update_info) { layout_object.SetNeedsPaintPropertyUpdate(); + // Since the visual rect has the bounds of the clip-path, mask and filter + // baked in, and the visual rect is updated during layout, we need to + // trigger layout if the style change could somehow have affected the + // bounds that form the visual rect. + needs_layout = update_info.needs_layout; + } } // If this layoutObject is the child of ResourceContainer and it require // repainting that changes of CSS properties such as 'visibility', // request repainting. - bool needs_layout = diff.NeedsFullPaintInvalidation() && - IsLayoutObjectOfResourceContainer(layout_object); + needs_layout |= diff.NeedsPaintInvalidation() && + IsLayoutObjectOfResourceContainer(layout_object); LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( layout_object, needs_layout); @@ -173,8 +191,7 @@ void SVGResourcesCache::ResourceReferenceChanged(LayoutObject& layout_object) { layout_object, true); } -void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object, - const ComputedStyle& new_style) { +void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object) { if (!layout_object.GetNode()) return; LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation( @@ -183,7 +200,8 @@ void SVGResourcesCache::ClientWasAddedToTree(LayoutObject& layout_object, if (!LayoutObjectCanHaveResources(layout_object)) return; SVGResourcesCache& cache = ResourcesCache(layout_object.GetDocument()); - if (cache.AddResourcesFromLayoutObject(layout_object, new_style)) + if (cache.AddResourcesFromLayoutObject(layout_object, + layout_object.StyleRef())) layout_object.SetNeedsPaintPropertyUpdate(); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h index c44ec39599f..fe545ef76ba 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cache.h @@ -42,8 +42,7 @@ class SVGResourcesCache { static SVGResources* CachedResourcesForLayoutObject(const LayoutObject&); // Called from all SVG layoutObjects addChild() methods. - static void ClientWasAddedToTree(LayoutObject&, - const ComputedStyle& new_style); + static void ClientWasAddedToTree(LayoutObject&); // Called from all SVG layoutObjects removeChild() methods. static void ClientWillBeRemovedFromTree(LayoutObject&); @@ -84,9 +83,17 @@ class SVGResourcesCache { }; private: - bool AddResourcesFromLayoutObject(LayoutObject&, const ComputedStyle&); + struct ResourceUpdateInfo { + bool changed; + bool needs_layout; + + explicit operator bool() const { return changed; } + }; + SVGResources* AddResourcesFromLayoutObject(LayoutObject&, + const ComputedStyle&); bool RemoveResourcesFromLayoutObject(LayoutObject&); - bool UpdateResourcesFromLayoutObject(LayoutObject&, const ComputedStyle&); + ResourceUpdateInfo UpdateResourcesFromLayoutObject(LayoutObject&, + const ComputedStyle&); typedef HashMap<const LayoutObject*, std::unique_ptr<SVGResources>> CacheMap; CacheMap cache_; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.cc index e39121ebe6b..5ede5cc597b 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.cc @@ -19,99 +19,30 @@ #include "third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h" -#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" -#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" -#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" - namespace blink { -SVGResourcesCycleSolver::SVGResourcesCycleSolver(LayoutObject& layout_object) - : layout_object_(layout_object) { - if (layout_object.IsSVGResourceContainer()) - active_resources_.insert(ToLayoutSVGResourceContainer(&layout_object)); -} +SVGResourcesCycleSolver::SVGResourcesCycleSolver() = default; SVGResourcesCycleSolver::~SVGResourcesCycleSolver() = default; -class ScopedTraversalPath { - public: - typedef SVGResourcesCycleSolver::ResourceSet ResourceSet; - - ScopedTraversalPath(ResourceSet& active_set) - : active_set_(active_set), resource_(nullptr) {} - ~ScopedTraversalPath() { - if (resource_) - active_set_.erase(resource_); - } - - bool Enter(LayoutSVGResourceContainer* resource) { - if (!active_set_.insert(resource).is_new_entry) - return false; - resource_ = resource; - return true; - } - - private: - ResourceSet& active_set_; - LayoutSVGResourceContainer* resource_; -}; - -bool SVGResourcesCycleSolver::TraverseResourceContainer( - LayoutSVGResourceContainer* resource) { - // If we've traversed this sub-graph before and no cycles were observed, then - // reuse that result. - if (dag_cache_.Contains(resource)) - return false; - - ScopedTraversalPath scope(active_resources_); - if (!scope.Enter(resource)) - return true; - - LayoutObject* node = resource; - while (node) { - // Skip subtrees which are themselves resources. (They will be - // processed - if needed - when they are actually referenced.) - if (node != resource && node->IsSVGResourceContainer()) { - node = node->NextInPreOrderAfterChildren(resource); - continue; - } - if (TraverseResources(*node)) - return true; - node = node->NextInPreOrder(resource); - } - - // No cycles found in (or from) this resource. Add it to the "DAG cache". - dag_cache_.insert(resource); - return false; +bool SVGResourcesCycleSolver::IsKnownAcyclic( + const LayoutSVGResourceContainer* resource) const { + return dag_cache_.Contains(resource); } -bool SVGResourcesCycleSolver::TraverseResources(LayoutObject& layout_object) { - SVGResources* resources = - SVGResourcesCache::CachedResourcesForLayoutObject(layout_object); - return resources && TraverseResources(resources); +void SVGResourcesCycleSolver::AddAcyclicSubgraph( + const LayoutSVGResourceContainer* resource) { + dag_cache_.insert(resource); } -bool SVGResourcesCycleSolver::TraverseResources(SVGResources* resources) { - // Fetch all the referenced resources. - ResourceSet local_resources; - resources->BuildSetOfResources(local_resources); - - // This performs a depth-first search for a back-edge in all the - // (potentially disjoint) graphs formed by the referenced resources. - for (auto* local_resource : local_resources) { - if (TraverseResourceContainer(local_resource)) - return true; - } - return false; +bool SVGResourcesCycleSolver::EnterResource( + const LayoutSVGResourceContainer* resource) { + return active_resources_.insert(resource).is_new_entry; } -bool SVGResourcesCycleSolver::FindCycle( - LayoutSVGResourceContainer* start_node) { - DCHECK(active_resources_.IsEmpty() || - (active_resources_.size() == 1 && - active_resources_.Contains( - ToLayoutSVGResourceContainer(&layout_object_)))); - return TraverseResourceContainer(start_node); +void SVGResourcesCycleSolver::LeaveResource( + const LayoutSVGResourceContainer* resource) { + active_resources_.erase(resource); } } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h index bf44ebdde60..6a9068479c5 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_resources_cycle_solver.h @@ -21,15 +21,12 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_SVG_SVG_RESOURCES_CYCLE_SOLVER_H_ #include "base/macros.h" +#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" namespace blink { -class LayoutObject; -class LayoutSVGResourceContainer; -class SVGResources; - // This class traverses the graph formed by SVGResources of // LayoutObjects, maintaining the active path as LayoutObjects are // visited. It also maintains a cache of sub-graphs that has already @@ -38,22 +35,40 @@ class SVGResourcesCycleSolver { STACK_ALLOCATED(); public: - SVGResourcesCycleSolver(LayoutObject&); + SVGResourcesCycleSolver(); ~SVGResourcesCycleSolver(); - // Traverse the graph starting at the resource container - // passed. Returns true if a cycle is detected. - bool FindCycle(LayoutSVGResourceContainer*); + bool IsKnownAcyclic(const LayoutSVGResourceContainer*) const; + void AddAcyclicSubgraph(const LayoutSVGResourceContainer*); - typedef HashSet<LayoutSVGResourceContainer*> ResourceSet; + class Scope { + STACK_ALLOCATED(); - private: - bool TraverseResourceContainer(LayoutSVGResourceContainer*); - bool TraverseResources(LayoutObject&); - bool TraverseResources(SVGResources*); + public: + Scope(SVGResourcesCycleSolver& solver) + : solver_(solver), resource_(nullptr) {} + ~Scope() { + if (resource_) + solver_.LeaveResource(resource_); + } + + bool Enter(const LayoutSVGResourceContainer* resource) { + if (!solver_.EnterResource(resource)) + return false; + resource_ = resource; + return true; + } - LayoutObject& layout_object_; + private: + SVGResourcesCycleSolver& solver_; + const LayoutSVGResourceContainer* resource_; + }; + + private: + bool EnterResource(const LayoutSVGResourceContainer*); + void LeaveResource(const LayoutSVGResourceContainer*); + using ResourceSet = HashSet<const LayoutSVGResourceContainer*>; ResourceSet active_resources_; ResourceSet dag_cache_; DISALLOW_COPY_AND_ASSIGN(SVGResourcesCycleSolver); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc index 5c3e12cf096..94ddd75bcb5 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.cc @@ -26,8 +26,6 @@ namespace blink { -namespace { - float CalculateTextAnchorShift(const ComputedStyle& style, float length) { bool is_ltr = style.IsLeftToRightDirection(); switch (style.SvgStyle().TextAnchor()) { @@ -43,6 +41,8 @@ float CalculateTextAnchorShift(const ComputedStyle& style, float length) { } } +namespace { + bool NeedsTextAnchorAdjustment(const ComputedStyle& style) { bool is_ltr = style.IsLeftToRightDirection(); switch (style.SvgStyle().TextAnchor()) { @@ -143,10 +143,7 @@ void SVGTextChunkBuilder::ProcessTextChunks( } SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() - : SVGTextChunkBuilder(), - total_length_(0), - total_characters_(0), - total_text_anchor_shift_(0) {} + : SVGTextChunkBuilder(), total_length_(0), total_characters_(0) {} void SVGTextPathChunkBuilder::HandleTextChunk(BoxListConstIterator box_start, BoxListConstIterator box_end) { @@ -155,10 +152,6 @@ void SVGTextPathChunkBuilder::HandleTextChunk(BoxListConstIterator box_start, ChunkLengthAccumulator length_accumulator(!style.IsHorizontalWritingMode()); length_accumulator.ProcessRange(box_start, box_end); - // Handle text-anchor as additional start offset for text paths. - total_text_anchor_shift_ += - CalculateTextAnchorShift(style, length_accumulator.length()); - total_length_ += length_accumulator.length(); total_characters_ += length_accumulator.NumCharacters(); } diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.h index 4e8da8976a3..b3ba82fc438 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_chunk_builder.h @@ -26,6 +26,7 @@ namespace blink { +class ComputedStyle; class SVGInlineTextBox; struct SVGTextFragment; @@ -76,7 +77,6 @@ class SVGTextPathChunkBuilder final : public SVGTextChunkBuilder { float TotalLength() const { return total_length_; } unsigned TotalCharacters() const { return total_characters_; } - float TotalTextAnchorShift() const { return total_text_anchor_shift_; } private: void HandleTextChunk(BoxListConstIterator box_start, @@ -84,11 +84,13 @@ class SVGTextPathChunkBuilder final : public SVGTextChunkBuilder { float total_length_; unsigned total_characters_; - float total_text_anchor_shift_; DISALLOW_COPY_AND_ASSIGN(SVGTextPathChunkBuilder); }; +// Compute the "shift" induced by the 'text-anchor' property. +float CalculateTextAnchorShift(const ComputedStyle&, float length); + } // namespace blink #endif diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.cc index c2c41825bda..6f537e09faf 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.cc @@ -172,15 +172,15 @@ class AttributeListsIterator { private: SVGLengthContext length_context_; - Member<SVGLengthList> x_list_; + SVGLengthList* x_list_; unsigned x_list_remaining_; - Member<SVGLengthList> y_list_; + SVGLengthList* y_list_; unsigned y_list_remaining_; - Member<SVGLengthList> dx_list_; + SVGLengthList* dx_list_; unsigned dx_list_remaining_; - Member<SVGLengthList> dy_list_; + SVGLengthList* dy_list_; unsigned dy_list_remaining_; - Member<SVGNumberList> rotate_list_; + SVGNumberList* rotate_list_; unsigned rotate_list_remaining_; }; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h index 37d9ed65f2e..956c93cc526 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h @@ -58,7 +58,7 @@ class SVGTextLayoutAttributesBuilder { unsigned new_length = 0) : element(new_element), start(new_start), length(new_length) {} - void Trace(blink::Visitor*); + void Trace(Visitor*); Member<SVGTextPositioningElement> element; unsigned start; diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc index b4af6bc127a..7628afd5795 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_layout_engine.cc @@ -178,11 +178,7 @@ void SVGTextLayoutEngine::BeginTextPathLayout(SVGInlineFlowBox* flow_box) { text_path_chunk_layout_builder.ProcessTextChunks( line_layout.line_layout_boxes_); - text_path_start_offset_ += - text_path_chunk_layout_builder.TotalTextAnchorShift(); - text_path_current_offset_ = text_path_start_offset_; - - // Eventually handle textLength adjustments. + // Handle 'textLength' adjustments. SVGLengthAdjustType length_adjust = kSVGLengthAdjustUnknown; float desired_text_length = 0; @@ -199,20 +195,26 @@ void SVGTextLayoutEngine::BeginTextPathLayout(SVGInlineFlowBox* flow_box) { desired_text_length = 0; } - if (!desired_text_length) - return; - - float total_length = text_path_chunk_layout_builder.TotalLength(); - if (length_adjust == kSVGLengthAdjustSpacing) { - text_path_spacing_ = 0; - if (text_path_chunk_layout_builder.TotalCharacters() > 1) { - text_path_spacing_ = desired_text_length - total_length; - text_path_spacing_ /= - text_path_chunk_layout_builder.TotalCharacters() - 1; + float text_path_content_length = text_path_chunk_layout_builder.TotalLength(); + if (desired_text_length) { + if (length_adjust == kSVGLengthAdjustSpacing) { + text_path_spacing_ = 0; + if (text_path_chunk_layout_builder.TotalCharacters() > 1) { + text_path_spacing_ = desired_text_length - text_path_content_length; + text_path_spacing_ /= + text_path_chunk_layout_builder.TotalCharacters() - 1; + } + } else { + text_path_scaling_ = desired_text_length / text_path_content_length; } - } else { - text_path_scaling_ = desired_text_length / total_length; + text_path_content_length = desired_text_length; } + + // Perform text-anchor adjustment. + float text_anchor_shift = + CalculateTextAnchorShift(text_path.StyleRef(), text_path_content_length); + text_path_start_offset_ += text_anchor_shift; + text_path_current_offset_ = text_path_start_offset_; } void SVGTextLayoutEngine::EndTextPathLayout() { @@ -263,16 +265,16 @@ void SVGTextLayoutEngine::LayoutCharactersInTextBoxes(InlineFlowBox* start) { for (InlineBox* child = start->FirstChild(); child; child = child->NextOnLine()) { - if (child->IsSVGInlineTextBox()) { + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(child)) { DCHECK(child->GetLineLayoutItem().IsSVGInlineText()); - LayoutInlineTextBox(ToSVGInlineTextBox(child)); + LayoutInlineTextBox(svg_inline_text_box); } else { // Skip generated content. Node* node = child->GetLineLayoutItem().GetNode(); if (!node) continue; - SVGInlineFlowBox* flow_box = ToSVGInlineFlowBox(child); + auto* flow_box = To<SVGInlineFlowBox>(child); bool is_text_path = IsA<SVGTextPathElement>(*node); if (is_text_path) BeginTextPathLayout(flow_box); diff --git a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_query.cc b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_query.cc index 93250c14255..45a09e6d0aa 100644 --- a/chromium/third_party/blink/renderer/core/layout/svg/svg_text_query.cc +++ b/chromium/third_party/blink/renderer/core/layout/svg/svg_text_query.cc @@ -93,8 +93,8 @@ static void CollectTextBoxesInFlowBox(InlineFlowBox* flow_box, continue; } - if (child->IsSVGInlineTextBox()) - text_boxes.push_back(ToSVGInlineTextBox(child)); + if (auto* svg_inline_text_box = DynamicTo<SVGInlineTextBox>(child)) + text_boxes.push_back(svg_inline_text_box); } } @@ -139,7 +139,7 @@ static void CollectTextBoxesInLogicalOrder( text_boxes.Shrink(0); for (InlineTextBox* text_box = text_line_layout.FirstTextBox(); text_box; text_box = text_box->NextForSameLayoutObject()) - text_boxes.push_back(ToSVGInlineTextBox(text_box)); + text_boxes.push_back(To<SVGInlineTextBox>(text_box)); std::sort(text_boxes.begin(), text_boxes.end(), InlineTextBox::CompareByStart); } diff --git a/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_auto.cc b/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_auto.cc index 11d8591fd6f..1b571da3649 100644 --- a/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_auto.cc +++ b/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_auto.cc @@ -25,6 +25,7 @@ #include "third_party/blink/renderer/core/layout/layout_table_cell.h" #include "third_party/blink/renderer/core/layout/layout_table_col.h" #include "third_party/blink/renderer/core/layout/layout_table_section.h" +#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell_interface.h" #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_interface.h" #include "third_party/blink/renderer/core/layout/text_autosizer.h" @@ -53,7 +54,7 @@ void TableLayoutAlgorithmAuto::RecalcColumn(unsigned eff_col) { // we need to clear their dirty bits so that if we call // setPreferredWidthsDirty(true) on a col or one of its descendants, we'll // mark it's ancestors as dirty. - ToLayoutTableCol(child)->ClearPreferredLogicalWidthsDirtyBits(); + ToLayoutTableCol(child)->ClearIntrinsicLogicalWidthsDirtyBits(); } else if (child->IsTableSection()) { LayoutTableSection* section = To<LayoutTableSection>(child); unsigned num_rows = section->NumRows(); @@ -67,17 +68,20 @@ void TableLayoutAlgorithmAuto::RecalcColumn(unsigned eff_col) { continue; column_layout.column_has_no_cells = false; - if (cell->MaxPreferredLogicalWidth()) + MinMaxSizes cell_preferred_logical_widths = + cell->PreferredLogicalWidths(); + + if (cell_preferred_logical_widths.max_size) column_layout.empty_cells_only = false; if (cell->ColSpan() == 1) { column_layout.min_logical_width = - std::max<int>(cell->MinPreferredLogicalWidth().ToInt(), + std::max<int>(cell_preferred_logical_widths.min_size.ToInt(), column_layout.min_logical_width); - if (cell->MaxPreferredLogicalWidth() > + if (cell_preferred_logical_widths.max_size > column_layout.max_logical_width) { column_layout.max_logical_width = - cell->MaxPreferredLogicalWidth().ToInt(); + cell_preferred_logical_widths.max_size.ToInt(); max_contributor = cell; } @@ -143,7 +147,7 @@ void TableLayoutAlgorithmAuto::RecalcColumn(unsigned eff_col) { // min/max width of at least 1px for it. column_layout.min_logical_width = std::max<int>(column_layout.min_logical_width, - cell->MaxPreferredLogicalWidth() ? 1 : 0); + cell_preferred_logical_widths.max_size ? 1 : 0); // This spanning cell originates in this column. Insert the cell into // spanning cells list. @@ -220,7 +224,7 @@ static bool ShouldScaleColumnsForParent(LayoutTable* table) { // TODO(layout-dev): We can probably abort before reaching LayoutView in many // cases. For example, if we find an object with contain:size, or even if we // find a regular block with fixed logical width. - while (!cb->IsLayoutView()) { + while (!IsA<LayoutView>(cb)) { // It doesn't matter if our table is auto or fixed: auto means we don't // scale. Fixed doesn't care if we do or not because it doesn't depend // on the cell contents' preferred widths. @@ -262,7 +266,7 @@ static bool ShouldScaleColumnsForSelf(LayoutNGTableInterface* table) { return true; LayoutBlock* cb = layout_table->ContainingBlock(); - while (!cb->IsLayoutView() && !cb->IsTableCell() && + while (!IsA<LayoutView>(cb) && !cb->IsTableCell() && cb->StyleRef().Width().IsAuto() && !cb->IsOutOfFlowPositioned()) cb = cb->ContainingBlock(); @@ -404,10 +408,13 @@ int TableLayoutAlgorithmAuto::CalcEffectiveLogicalWidth() { unsigned eff_col = table_->AbsoluteColumnToEffectiveColumn(cell->AbsoluteColumnIndex()); wtf_size_t last_col = eff_col; + MinMaxSizes cell_preferred_logical_widths = cell->PreferredLogicalWidths(); int cell_min_logical_width = - (cell->MinPreferredLogicalWidth() + spacing_in_row_direction).ToInt(); + (cell_preferred_logical_widths.min_size + spacing_in_row_direction) + .ToInt(); int cell_max_logical_width = - (cell->MaxPreferredLogicalWidth() + spacing_in_row_direction).ToInt(); + (cell_preferred_logical_widths.max_size + spacing_in_row_direction) + .ToInt(); float total_percent = 0; int span_min_logical_width = 0; int span_max_logical_width = 0; @@ -538,8 +545,11 @@ int TableLayoutAlgorithmAuto::CalcEffectiveLogicalWidth() { layout_struct_[pos].effective_min_logical_width = std::max(layout_struct_[pos].effective_min_logical_width, column_min_logical_width); + column_max_logical_width = + std::max(column_max_logical_width, column_min_logical_width); layout_struct_[pos].effective_max_logical_width = - column_max_logical_width; + std::max(layout_struct_[pos].effective_max_logical_width, + column_max_logical_width); allocated_min_logical_width += column_min_logical_width; allocated_max_logical_width += column_max_logical_width; } diff --git a/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_fixed.cc b/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_fixed.cc index f869ceb3a9d..7081480b880 100644 --- a/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_fixed.cc +++ b/chromium/third_party/blink/renderer/core/layout/table_layout_algorithm_fixed.cc @@ -91,9 +91,9 @@ int TableLayoutAlgorithmFixed::CalcWidthArray() { col = col->NextColumn()) { // LayoutTableCols don't have the concept of preferred logical width, but we // need to clear their dirty bits so that if we call - // setPreferredWidthsDirty(true) on a col or one of its descendants, we'll + // SetPreferredWidthsDirty(true) on a col or one of its descendants, we'll // mark it's ancestors as dirty. - col->ClearPreferredLogicalWidthsDirtyBits(); + col->ClearIntrinsicLogicalWidthsDirtyBits(); // Width specified by column-groups that have column child does not affect // column width in fixed layout tables @@ -184,12 +184,12 @@ int TableLayoutAlgorithmFixed::CalcWidthArray() { ++current_column; } - // TableLayoutAlgorithmFixed doesn't use min/maxPreferredLogicalWidths, but - // we need to clear the dirty bit on the cell so that we'll correctly mark - // its ancestors dirty in case we later call - // setPreferredLogicalWidthsDirty() on it later. - if (cell->PreferredLogicalWidthsDirty()) - cell->ClearPreferredLogicalWidthsDirty(); + // TableLayoutAlgorithmFixed doesn't use PreferredLogicalWidths, but we + // need to clear the dirty bit on the cell so that we'll correctly mark its + // ancestors dirty in case we later call SetIntrinsicLogicalWidthsDirty() + // on it later. + if (cell->IntrinsicLogicalWidthsDirty()) + cell->ClearIntrinsicLogicalWidthsDirty(); } return used_width; diff --git a/chromium/third_party/blink/renderer/core/layout/text_autosizer.cc b/chromium/third_party/blink/renderer/core/layout/text_autosizer.cc index 400c1ee28e5..f3609dc9801 100644 --- a/chromium/third_party/blink/renderer/core/layout/text_autosizer.cc +++ b/chromium/third_party/blink/renderer/core/layout/text_autosizer.cc @@ -52,7 +52,6 @@ #include "third_party/blink/renderer/core/layout/layout_table_cell.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h" -#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/style_retain_scope.h" #include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/page.h" @@ -112,7 +111,7 @@ static bool IsPotentialClusterRoot(const LayoutObject* layout_object) { // - Must not be normal list items, as items in the same list should look // consistent, unless they are floating or position:absolute/fixed. Node* node = layout_object->GeneratingNode(); - if (node && !node->hasChildren() && !layout_object->IsLayoutView()) + if (node && !node->hasChildren() && !IsA<LayoutView>(layout_object)) return false; if (!layout_object->IsLayoutBlock()) return false; @@ -130,7 +129,7 @@ static bool IsIndependentDescendant(const LayoutBlock* layout_object) { DCHECK(IsPotentialClusterRoot(layout_object)); LayoutBlock* containing_block = layout_object->ContainingBlock(); - return layout_object->IsLayoutView() || layout_object->IsFloating() || + return IsA<LayoutView>(layout_object) || layout_object->IsFloating() || layout_object->IsOutOfFlowPositioned() || layout_object->IsTableCell() || layout_object->IsTableCaption() || layout_object->IsFlexibleBoxIncludingDeprecatedAndNG() || @@ -196,7 +195,7 @@ static bool BlockHeightConstrained(const LayoutBlock* block) { // height:100%, without intending to constrain the height of the content // within them. return !block->IsDocumentElement() && !block->IsBody() && - !block->IsLayoutView(); + !IsA<LayoutView>(block); } if (block->IsFloating()) return false; @@ -324,7 +323,7 @@ TextAutosizer::BeginLayoutBehavior TextAutosizer::PrepareForLayout( if (!first_block_to_begin_layout_) { first_block_to_begin_layout_ = block; PrepareClusterStack(block->Parent()); - if (block->IsLayoutView()) + if (IsA<LayoutView>(block)) CheckSuperclusterConsistency(); } else if (block == CurrentCluster()->root_) { // Ignore beginLayout on the same block twice. @@ -359,7 +358,7 @@ void TextAutosizer::BeginLayout(LayoutBlock* block, if (block->IsRubyRun() || block->IsRubyBase() || block->IsRubyText()) return; - DCHECK(!cluster_stack_.IsEmpty() || block->IsLayoutView()); + DCHECK(!cluster_stack_.IsEmpty() || IsA<LayoutView>(block)); if (cluster_stack_.IsEmpty()) did_check_cross_site_use_count_ = false; @@ -465,11 +464,11 @@ float TextAutosizer::Inflate(LayoutObject* parent, if (behavior == kDescendToInnerBlocks) { // The ancestor nodes might be inline-blocks. We should - // setPreferredLogicalWidthsDirty for ancestor nodes here. - child->SetPreferredLogicalWidthsDirty(); + // SetIntrinsicLogicalWidthsDirty for ancestor nodes here. + child->SetIntrinsicLogicalWidthsDirty(); } else if (parent->IsLayoutInline()) { // FIXME: Investigate why MarkOnlyThis is sufficient. - child->SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + child->SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); } } else if (child->IsLayoutInline()) { multiplier = Inflate(child, layouter, behavior, multiplier); @@ -509,12 +508,13 @@ float TextAutosizer::Inflate(LayoutObject* parent, else if (parent->IsLayoutNGListItem()) marker = ToLayoutNGListItem(parent)->Marker(); - // A LayoutNGListMarker has a text child that needs its font multiplier - // updated. Just mark the entire subtree, to make sure we get to it. + // A LayoutNGOutsideListMarker has a text child that needs its font + // multiplier updated. Just mark the entire subtree, to make sure we get to + // it. for (LayoutObject* walker = marker; walker; walker = walker->NextInPreOrder(marker)) { ApplyMultiplier(walker, multiplier, layouter); - walker->SetPreferredLogicalWidthsDirty(kMarkOnlyThis); + walker->SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis); } } @@ -604,7 +604,7 @@ void TextAutosizer::UpdatePageInfoInAllFrames(Frame* main_frame) { page_info.shared_info_); // Remember the RemotePageSettings in the mainframe's renderer so we // know when they change. - document->GetPage()->SetTextAutosizePageInfo(page_info.shared_info_); + document->GetPage()->SetTextAutosizerPageInfo(page_info.shared_info_); } } } @@ -881,7 +881,7 @@ TextAutosizer::Cluster* TextAutosizer::MaybeCreateCluster(LayoutBlock* block) { Cluster* parent_cluster = cluster_stack_.IsEmpty() ? nullptr : CurrentCluster(); - DCHECK(parent_cluster || block->IsLayoutView()); + DCHECK(parent_cluster || IsA<LayoutView>(block)); // If a non-independent block would not alter the SUPPRESSING flag, it doesn't // need to be a cluster. @@ -1091,7 +1091,7 @@ const LayoutBlock* TextAutosizer::DeepestBlockContainingAllText( const LayoutBlock* root) const { // To avoid font-size shaking caused by the change of LayoutView's // DeepestBlockContainingAllText. - if (root->IsLayoutView()) + if (IsA<LayoutView>(root)) return root; size_t first_depth = 0; @@ -1171,10 +1171,9 @@ const LayoutObject* TextAutosizer::FindTextLeaf( } static bool IsCrossSite(const Frame& frame1, const Frame& frame2) { - // Cross-site differs from cross-origin (LocalFrame::IsCrossOriginSubframe). - // For example, http://foo.com and http://sub.foo.com are cross-origin but - // same-site. Only cross-site text autosizing is impacted by site isolation - // (crbug.com/393285). + // Cross-site differs from cross-origin. For example, http://foo.com and + // http://sub.foo.com are cross-origin but same-site. Only cross-site text + // autosizing is impacted by site isolation (crbug.com/393285). const auto* origin1 = frame1.GetSecurityContext()->GetSecurityOrigin(); const auto* origin2 = frame2.GetSecurityContext()->GetSecurityOrigin(); @@ -1441,17 +1440,18 @@ TextAutosizer::DeferUpdatePageInfo::DeferUpdatePageInfo(Page* page) } } -TextAutosizer::NGLayoutScope::NGLayoutScope(const NGBlockNode& node, +TextAutosizer::NGLayoutScope::NGLayoutScope(LayoutBox* box, LayoutUnit inline_size) - : text_autosizer_(node.GetLayoutBox()->GetDocument().GetTextAutosizer()), - block_(To<LayoutBlockFlow>(node.GetLayoutBox())) { + : text_autosizer_(box->GetDocument().GetTextAutosizer()), box_(box) { + // Bail if: + // - Text autosizing isn't enabled. + // - If the chid isn't a LayoutBlock. + // - If the child is a LayoutNGOutsideListMarker. (They are super-small + // blocks, and using them to determine if we should autosize the text will + // typically false, overriding whatever its parent has already correctly + // determined). if (!text_autosizer_ || !text_autosizer_->ShouldHandleLayout() || - block_->IsLayoutNGListMarker()) { - // Bail if text autosizing isn't enabled, but also if this is a - // IsLayoutNGListMarker. They are super-small blocks, and using them to - // determine if we should autosize the text will typically always yield - // false, overriding whatever its parent (typically the list item) has - // already correctly determined. + box_->IsLayoutNGOutsideListMarker() || !box_->IsLayoutBlock()) { text_autosizer_ = nullptr; return; } @@ -1460,14 +1460,14 @@ TextAutosizer::NGLayoutScope::NGLayoutScope(const NGBlockNode& node, // know the inline size of the block. So set it. LayoutNG normally writes back // to the legacy tree *after* layout, but this one must be set before, at // least if the autosizer is enabled. - block_->SetLogicalWidth(inline_size); + box_->SetLogicalWidth(inline_size); - text_autosizer_->BeginLayout(block_, nullptr); + text_autosizer_->BeginLayout(To<LayoutBlock>(box_), nullptr); } TextAutosizer::NGLayoutScope::~NGLayoutScope() { if (text_autosizer_) - text_autosizer_->EndLayout(block_); + text_autosizer_->EndLayout(To<LayoutBlock>(box_)); } TextAutosizer::DeferUpdatePageInfo::~DeferUpdatePageInfo() { @@ -1548,7 +1548,7 @@ void TextAutosizer::CheckSuperclusterConsistency() { potentially_inconsistent_superclusters.clear(); } -void TextAutosizer::Trace(blink::Visitor* visitor) { +void TextAutosizer::Trace(Visitor* visitor) { visitor->Trace(document_); } diff --git a/chromium/third_party/blink/renderer/core/layout/text_autosizer.h b/chromium/third_party/blink/renderer/core/layout/text_autosizer.h index 19111b9123b..7a6390abf7b 100644 --- a/chromium/third_party/blink/renderer/core/layout/text_autosizer.h +++ b/chromium/third_party/blink/renderer/core/layout/text_autosizer.h @@ -47,11 +47,11 @@ class Document; class Frame; class IntSize; class LayoutBlock; +class LayoutBox; class LayoutNGTableInterface; class LayoutObject; class LayoutText; class LocalFrame; -class NGBlockNode; class Page; class SubtreeLayoutScope; @@ -80,7 +80,7 @@ class CORE_EXPORT TextAutosizer final : public GarbageCollected<TextAutosizer> { bool PageNeedsAutosizing() const; - void Trace(blink::Visitor*); + void Trace(Visitor*); class LayoutScope { STACK_ALLOCATED(); @@ -90,7 +90,7 @@ class CORE_EXPORT TextAutosizer final : public GarbageCollected<TextAutosizer> { ~LayoutScope(); protected: - Member<TextAutosizer> text_autosizer_; + TextAutosizer* text_autosizer_; LayoutBlock* block_; }; @@ -105,12 +105,12 @@ class CORE_EXPORT TextAutosizer final : public GarbageCollected<TextAutosizer> { STACK_ALLOCATED(); public: - explicit NGLayoutScope(const NGBlockNode& node, LayoutUnit inline_size); + explicit NGLayoutScope(LayoutBox*, LayoutUnit inline_size); ~NGLayoutScope(); protected: - Member<TextAutosizer> text_autosizer_; - LayoutBlock* block_; + TextAutosizer* text_autosizer_; + LayoutBox* box_; }; class CORE_EXPORT DeferUpdatePageInfo { @@ -121,7 +121,7 @@ class CORE_EXPORT TextAutosizer final : public GarbageCollected<TextAutosizer> { ~DeferUpdatePageInfo(); private: - Member<LocalFrame> main_frame_; + LocalFrame* main_frame_; }; private: diff --git a/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc b/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc index fcf01c4cbce..dd9ad6e1102 100644 --- a/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/text_autosizer_test.cc @@ -579,7 +579,7 @@ TEST_F(TextAutosizerTest, ChangingSuperClusterFirstText) { UpdateAllLifecyclePhasesForTest(); Element* long_text_element = GetDocument().getElementById("longText"); - long_text_element->SetInnerHTMLFromString( + long_text_element->setInnerHTML( " Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " "do eiusmod tempor" " incididunt ut labore et dolore magna aliqua. Ut enim ad minim " @@ -624,7 +624,7 @@ TEST_F(TextAutosizerTest, ChangingSuperClusterSecondText) { UpdateAllLifecyclePhasesForTest(); Element* long_text_element = GetDocument().getElementById("longText"); - long_text_element->SetInnerHTMLFromString( + long_text_element->setInnerHTML( " Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " "do eiusmod tempor" " incididunt ut labore et dolore magna aliqua. Ut enim ad minim " @@ -669,7 +669,7 @@ TEST_F(TextAutosizerTest, AddingSuperCluster) { UpdateAllLifecyclePhasesForTest(); Element* container = GetDocument().getElementById("container"); - container->SetInnerHTMLFromString( + container->setInnerHTML( "<div class='supercluster' id='longText'>" " Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " "do eiusmod tempor" @@ -717,7 +717,7 @@ TEST_F(TextAutosizerTest, ChangingInheritedClusterTextInsideSuperCluster) { UpdateAllLifecyclePhasesForTest(); Element* long_text_element = GetDocument().getElementById("longText"); - long_text_element->SetInnerHTMLFromString( + long_text_element->setInnerHTML( " Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " "do eiusmod tempor" " incididunt ut labore et dolore magna aliqua. Ut enim ad minim " @@ -803,7 +803,7 @@ TEST_F(TextAutosizerTest, ResizeAndGlyphOverflowChanged) { GetDocument().GetSettings()->SetTextAutosizingWindowSizeOverride( IntSize(360, 640)); Element* html = GetDocument().body()->parentElement(); - html->SetInnerHTMLFromString( + html->setInnerHTML( "<head>" " <meta name='viewport' content='width=800'>" " <style>" @@ -843,7 +843,7 @@ TEST_F(TextAutosizerTest, ResizeAndGlyphOverflowChanged) { TEST_F(TextAutosizerTest, narrowContentInsideNestedWideBlock) { Element* html = GetDocument().body()->parentElement(); - html->SetInnerHTMLFromString( + html->setInnerHTML( "<head>" " <meta name='viewport' content='width=800'>" " <style>" @@ -879,7 +879,7 @@ TEST_F(TextAutosizerTest, narrowContentInsideNestedWideBlock) { TEST_F(TextAutosizerTest, LayoutViewWidthProvider) { Element* html = GetDocument().body()->parentElement(); - html->SetInnerHTMLFromString( + html->setInnerHTML( "<head>" " <meta name='viewport' content='width=800'>" " <style>" @@ -908,8 +908,8 @@ TEST_F(TextAutosizerTest, LayoutViewWidthProvider) { EXPECT_FLOAT_EQ(40.f, content->GetLayoutObject()->StyleRef().ComputedFontSize()); - GetDocument().getElementById("panel")->SetInnerHTMLFromString("insert text"); - content->SetInnerHTMLFromString(content->InnerHTMLAsString()); + GetDocument().getElementById("panel")->setInnerHTML("insert text"); + content->setInnerHTML(content->innerHTML()); UpdateAllLifecyclePhasesForTest(); // (specified font-size = 16px) * (viewport width = 800px) / @@ -920,7 +920,7 @@ TEST_F(TextAutosizerTest, LayoutViewWidthProvider) { TEST_F(TextAutosizerTest, MultiColumns) { Element* html = GetDocument().body()->parentElement(); - html->SetInnerHTMLFromString( + html->setInnerHTML( "<head>" " <meta name='viewport' content='width=800'>" " <style>" diff --git a/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc index d65144043d6..cd5349c1f8f 100644 --- a/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc @@ -287,7 +287,7 @@ TEST_P(VisualRectMappingTest, LayoutView) { // This case involves clipping: frame height is 50, y-coordinate of result // rect is 13, so height should be clipped to (50 - 13) == 37. ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0, 47), kProgrammaticScroll); + ScrollOffset(0, 47), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PhysicalRect original_rect(4, 60, 20, 80); @@ -363,7 +363,7 @@ TEST_P(VisualRectMappingTest, LayoutViewDisplayNone) { // This part is copied from the LayoutView test, just to ensure that the // mapped rect is valid before display:none is set on the iframe. ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0, 47), kProgrammaticScroll); + ScrollOffset(0, 47), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PhysicalRect original_rect(4, 60, 20, 80); @@ -761,7 +761,8 @@ TEST_P(VisualRectMappingTest, To<LayoutBlock>(GetLayoutObjectByElementId("stacking-context")); auto* absolute = To<LayoutBlock>(GetLayoutObjectByElementId("absolute")); auto* container = To<LayoutBlock>(GetLayoutObjectByElementId("container")); - EXPECT_EQ(absolute->View(), &absolute->ContainerForPaintInvalidation()); + if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) + EXPECT_EQ(absolute->View(), &absolute->ContainerForPaintInvalidation()); EXPECT_EQ(container, absolute->Container()); PhysicalRect absolute_visual_rect = absolute->LocalVisualRect(); @@ -1188,7 +1189,7 @@ TEST_P(VisualRectMappingTest, FixedContentsInIframe) { root_view, kDefaultVisualRectFlags, true); ChildDocument().View()->LayoutViewport()->SetScrollOffset( - ScrollOffset(0, 50), kProgrammaticScroll); + ScrollOffset(0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // The fixed element should not scroll so the mapped visual rect should not @@ -1221,8 +1222,8 @@ TEST_P(VisualRectMappingTest, FixedContentsWithScrollOffset) { PhysicalRect(0, -10, 400, 300), fixed, ancestor, kDefaultVisualRectFlags, true); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // The fixed element does not scroll but the ancestor does which changes the @@ -1249,8 +1250,8 @@ TEST_P(VisualRectMappingTest, FixedContentsUnderViewWithScrollOffset) { PhysicalRect(0, 0, 400, 300), PhysicalRect(0, 0, 400, 300), fixed, fixed->View(), kDefaultVisualRectFlags, true); - GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 50), - kProgrammaticScroll); + GetDocument().View()->LayoutViewport()->SetScrollOffset( + ScrollOffset(0, 50), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Results of mapping to ancestor are in absolute coordinates of the |