summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/third_party/blink/renderer/core/layout/ng
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-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/ng')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc205
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h44
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.cc30
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h53
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc35
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_scope.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.cc77
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_work_task.h31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/document_layout_definition.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/fragment_result_options.idl1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes.idl15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/intrinsic_sizes_result_options.idl10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_child.idl4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_fragment.idl2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.cc26
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.idl4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.cc79
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/pending_layout_registry.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc295
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.cc89
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h207
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc76
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc60
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc202
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h108
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc57
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc113
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc315
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc1024
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h301
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc122
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc93
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h26
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc219
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc193
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc56
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h55
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc123
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc56
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h58
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc234
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc192
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc368
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc83
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h21
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h38
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc26
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc206
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc49
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.h5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc125
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_progress.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/README.md9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc399
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h40
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc61
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.cc41
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h (renamed from chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h)28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.cc256
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.h71
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc60
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc46
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc323
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc97
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h54
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc180
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.cc42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc70
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc443
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc21
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator_test.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc427
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc133
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc358
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node_test.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.cc73
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h37
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc182
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h67
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc119
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h23
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc358
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc37
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h339
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h75
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc64
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc434
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h56
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc1517
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.cc38
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_child_iterator.h53
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc858
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h30
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc212
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h141
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator_test.cc808
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc197
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc44
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc69
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc23
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc331
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc55
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc257
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h153
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc40
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc135
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc264
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h79
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc135
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc78
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h105
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc12
196 files changed, 12245 insertions, 5532 deletions
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>(
- &current_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&shy;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&#9;&#9;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&#9;&#9;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 &current_; }
+
+ 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