summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/README.md2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/css_layout_definition.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_intrinsic_sizes.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_constraints.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/custom_layout_fragment.h2
-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.cc3
-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/layout_worklet.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope.cc2
-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_proxy.cc2
-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.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/custom/ng_custom_layout_algorithm.h2
-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/flex/ng_flex_layout_algorithm.cc246
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc (renamed from chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.cc)2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h (renamed from chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.h)6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.cc37
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h54
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator_test.cc98
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc76
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h (renamed from chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.h)27
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc106
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc510
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h207
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc248
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/README.md98
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text_test.cc49
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.cc31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h30
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph_test.cc40
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc27
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc51
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc218
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h90
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc146
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h16
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc200
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h66
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc165
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc337
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h46
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor_test.cc186
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h21
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc59
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc24
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc59
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc196
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc108
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h43
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc55
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc91
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h258
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc156
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc44
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc219
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.h47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc121
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h301
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc64
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc305
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h85
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.cc61
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h2
-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.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.cc38
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_fraction_layout_algorithm.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.cc47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h31
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.cc19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h37
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h3
-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.cc506
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc127
-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.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h181
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc166
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc63
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc57
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc79
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h30
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc46
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_test.cc51
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc131
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.cc27
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h27
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc76
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc79
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc149
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc82
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h25
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc23
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc53
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h55
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc16
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc47
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h41
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc68
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc52
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc53
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc631
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc360
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h287
182 files changed, 7974 insertions, 2999 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/README.md b/chromium/third_party/blink/renderer/core/layout/ng/README.md
index 09c24da1f24..961a60cb9d3 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/README.md
+++ b/chromium/third_party/blink/renderer/core/layout/ng/README.md
@@ -113,7 +113,7 @@ Here is the instruction how to generate a new result.
`chromium\src>for %file in (*.log) do DynamoRIO\tools\bin64\drcov2lcov.exe -input %file -output %file.info -src_filter layout/ng -src_skip_filter _test`
* Merge all lcov files into one file
`chromium\src>node lcov-result-merger\bin\lcov-result-merger.js *.info output.info`
-* Generate the coverage html from the master lcov file
+* Generate the coverage html from the lcov file
`chromium\src>C:\Perl64\bin\perl.exe dynamorio.git\third_party\lcov\genhtml output.info -o output`
### Debugging, logging and testing ###
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 3a2d2beb0b1..43f6f426a4e 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
@@ -353,12 +353,12 @@ CSSLayoutDefinition::Instance* CSSLayoutDefinition::CreateInstance() {
return MakeGarbageCollected<Instance>(this, instance.V8Value());
}
-void CSSLayoutDefinition::Instance::Trace(Visitor* visitor) {
+void CSSLayoutDefinition::Instance::Trace(Visitor* visitor) const {
visitor->Trace(definition_);
visitor->Trace(instance_);
}
-void CSSLayoutDefinition::Trace(Visitor* visitor) {
+void CSSLayoutDefinition::Trace(Visitor* visitor) const {
visitor->Trace(constructor_);
visitor->Trace(intrinsic_sizes_);
visitor->Trace(layout_);
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 d2bef60636a..5596cc1d38f 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
@@ -78,7 +78,7 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>,
IntrinsicSizesResultOptions**,
bool* child_depends_on_percentage_block_size);
- void Trace(Visitor*);
+ void Trace(Visitor*) const;
private:
void ReportException(ExceptionState*);
@@ -106,7 +106,7 @@ class CSSLayoutDefinition final : public GarbageCollected<CSSLayoutDefinition>,
ScriptState* GetScriptState() const { return script_state_; }
- virtual void Trace(Visitor* visitor);
+ virtual void Trace(Visitor* visitor) const;
const char* NameInHeapSnapshot() const override {
return "CSSLayoutDefinition";
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
index 7dd9c992f83..55bf4282702 100644
--- 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
@@ -21,7 +21,7 @@ const NGLayoutInputNode& CustomIntrinsicSizes::GetLayoutNode() const {
return child_->GetLayoutNode();
}
-void CustomIntrinsicSizes::Trace(Visitor* visitor) {
+void CustomIntrinsicSizes::Trace(Visitor* visitor) const {
visitor->Trace(child_);
visitor->Trace(token_);
ScriptWrappable::Trace(visitor);
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
index 1bfa9cea738..7c81a6e61cb 100644
--- 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
@@ -37,7 +37,7 @@ class CustomIntrinsicSizes : public ScriptWrappable {
bool IsValid() const { return token_->IsValid(); }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
private:
Member<CustomLayoutChild> child_;
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 a4ed4148eca..d7e43154fd7 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
@@ -86,7 +86,7 @@ ScriptPromise CustomLayoutChild::layoutNextFragment(
return resolver->Promise();
}
-void CustomLayoutChild::Trace(Visitor* visitor) {
+void CustomLayoutChild::Trace(Visitor* visitor) const {
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 7943adc0ac3..2b1a0a51a42 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
@@ -46,7 +46,7 @@ class CustomLayoutChild : public ScriptWrappable {
void SetCustomLayoutToken(CustomLayoutToken* token) { token_ = token; }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const 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 9ed31898a5c..28b8883540f 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
@@ -47,7 +47,7 @@ ScriptValue CustomLayoutConstraints::data(ScriptState* script_state) const {
layout_worklet_world_v8_data_.NewLocal(script_state->GetIsolate()));
}
-void CustomLayoutConstraints::Trace(Visitor* visitor) {
+void CustomLayoutConstraints::Trace(Visitor* visitor) const {
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 6a496623aab..c3dfb58244b 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
@@ -32,7 +32,7 @@ class CustomLayoutConstraints : public ScriptWrappable {
base::Optional<double> fixedBlockSize() const;
ScriptValue data(ScriptState*) const;
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const 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 1d724aa4f68..60cecbc6440 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
@@ -53,7 +53,7 @@ ScriptValue CustomLayoutFragment::data(ScriptState* script_state) const {
layout_worklet_world_v8_data_.NewLocal(script_state->GetIsolate()));
}
-void CustomLayoutFragment::Trace(Visitor* visitor) {
+void CustomLayoutFragment::Trace(Visitor* visitor) const {
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 c9b5dde905d..13fc70ef9e2 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
@@ -61,7 +61,7 @@ class CustomLayoutFragment : public ScriptWrappable {
bool IsValid() const { return token_->IsValid(); }
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const override;
private:
Member<CustomLayoutChild> child_;
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 3cc70061f06..8071f4c7a53 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
@@ -22,7 +22,7 @@ typedef Vector<CustomLayoutWorkTask, 4> CustomLayoutWorkQueue;
class CustomLayoutToken : public GarbageCollected<CustomLayoutToken> {
public:
CustomLayoutToken() : is_detached_(false) {}
- void Trace(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
bool IsValid() const;
private:
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 b24800c68ef..14ce056a557 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
@@ -150,7 +150,8 @@ void CustomLayoutWorkTask::RunIntrinsicSizesTask(
DCHECK_EQ(type_, CustomLayoutWorkTask::TaskType::kIntrinsicSizes);
DCHECK(resolver_);
- MinMaxSizesInput input(child_percentage_resolution_block_size_for_min_max);
+ MinMaxSizesInput input(child_percentage_resolution_block_size_for_min_max,
+ MinMaxSizesType::kContent);
MinMaxSizesResult result =
ComputeMinAndMaxContentContribution(parent_style, child, input);
resolver_->Resolve(MakeGarbageCollected<CustomIntrinsicSizes>(
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 c68765b9107..40266002ee6 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(Visitor* visitor) {
+void DocumentLayoutDefinition::Trace(Visitor* visitor) const {
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 0c2d8bce246..2a2c1052afb 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(Visitor*);
+ virtual void Trace(Visitor*) const;
private:
bool IsEqual(const CSSLayoutDefinition&);
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 be7fd1d4847..7340fed04c8 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(Visitor* visitor) {
+void LayoutWorklet::Trace(Visitor* visitor) const {
visitor->Trace(document_definition_map_);
visitor->Trace(pending_layout_registry_);
Worklet::Trace(visitor);
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 7f852c563e2..20475e9697b 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
@@ -47,7 +47,7 @@ class CORE_EXPORT LayoutWorklet : public Worklet,
void AddPendingLayout(const AtomicString& name, Node*);
LayoutWorkletGlobalScopeProxy* Proxy();
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const 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 51bb52ea69d..317d8921237 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
@@ -167,7 +167,7 @@ CSSLayoutDefinition* LayoutWorkletGlobalScope::FindDefinition(
return layout_definitions_.at(name);
}
-void LayoutWorkletGlobalScope::Trace(Visitor* visitor) {
+void LayoutWorkletGlobalScope::Trace(Visitor* visitor) const {
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 c02359b83b2..9bb6d62a34c 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
@@ -44,7 +44,7 @@ class CORE_EXPORT LayoutWorkletGlobalScope final : public WorkletGlobalScope {
CSSLayoutDefinition* FindDefinition(const AtomicString& name);
- void Trace(Visitor*) override;
+ void Trace(Visitor*) const 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_proxy.cc b/chromium/third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.cc
index 4a161756a61..76f95c12604 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
@@ -92,7 +92,7 @@ CSSLayoutDefinition* LayoutWorkletGlobalScopeProxy::FindDefinition(
return global_scope_->FindDefinition(name);
}
-void LayoutWorkletGlobalScopeProxy::Trace(Visitor* visitor) {
+void LayoutWorkletGlobalScopeProxy::Trace(Visitor* visitor) const {
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 de993758af3..97efbd1f814 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(Visitor*) override;
+ void Trace(Visitor*) const 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 b2dbb2d6b09..05f5ff77648 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
@@ -21,12 +21,7 @@ namespace blink {
NGCustomLayoutAlgorithm::NGCustomLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- params_(params),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
- border_scrollbar_padding_(border_padding_ +
- params.fragment_geometry.scrollbar) {
+ : NGLayoutAlgorithm(params), params_(params) {
DCHECK(params.space.IsNewFormattingContext());
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
@@ -58,7 +53,7 @@ MinMaxSizesResult NGCustomLayoutAlgorithm::ComputeMinMaxSizes(
IntrinsicSizesResultOptions* intrinsic_sizes_result_options = nullptr;
if (!instance->IntrinsicSizes(
ConstraintSpace(), document, Node(),
- container_builder_.InitialBorderBoxSize(), border_scrollbar_padding_,
+ container_builder_.InitialBorderBoxSize(), BorderScrollbarPadding(),
input.percentage_resolution_block_size, &scope,
&intrinsic_sizes_result_options, &depends_on_percentage_block_size)) {
// TODO(ikilpatrick): Report this error to the developer.
@@ -104,7 +99,7 @@ scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() {
scoped_refptr<SerializedScriptValue> fragment_result_data;
if (!instance->Layout(ConstraintSpace(), document, Node(),
container_builder_.InitialBorderBoxSize(),
- border_scrollbar_padding_, &scope,
+ BorderScrollbarPadding(), &scope,
fragment_result_options, &fragment_result_data)) {
// TODO(ikilpatrick): Report this error to the developer.
return FallbackLayout();
@@ -156,10 +151,10 @@ scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() {
// Compute the final block-size.
LayoutUnit auto_block_size = std::max(
- border_padding_.BlockSum(),
+ BorderScrollbarPadding().BlockSum(),
LayoutUnit::FromDoubleRound(fragment_result_options->autoBlockSize()));
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_, auto_block_size,
+ ConstraintSpace(), Style(), BorderPadding(), auto_block_size,
container_builder_.InitialBorderBoxSize().inline_size);
if (fragment_result_options->hasBaseline()) {
@@ -170,7 +165,7 @@ scoped_refptr<const NGLayoutResult> NGCustomLayoutAlgorithm::Layout() {
container_builder_.SetCustomLayoutData(std::move(fragment_result_data));
container_builder_.SetIntrinsicBlockSize(auto_block_size);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(
Node(), ConstraintSpace(),
@@ -190,8 +185,7 @@ void NGCustomLayoutAlgorithm::AddAnyOutOfFlowPositionedChildren(
DCHECK(child);
while (*child && child->IsOutOfFlowPositioned()) {
container_builder_.AddOutOfFlowChildCandidate(
- To<NGBlockNode>(*child), {border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start});
+ To<NGBlockNode>(*child), BorderScrollbarPadding().StartOffset());
*child = child->NextSibling();
}
}
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 2ba330632c7..950ce9da4c2 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
@@ -29,8 +29,6 @@ class CORE_EXPORT NGCustomLayoutAlgorithm
scoped_refptr<const NGLayoutResult> FallbackLayout();
const NGLayoutAlgorithmParams& params_;
- const NGBoxStrut border_padding_;
- const NGBoxStrut border_scrollbar_padding_;
};
} // 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 644f324ac9c..8c9b1e0f2c8 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(Visitor* visitor) {
+void PendingLayoutRegistry::Trace(Visitor* visitor) const {
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 8afe379befd..f0c7eb34ce5 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(Visitor*);
+ void Trace(Visitor*) const;
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/flex/ng_flex_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index 6de92e5be92..8a38aba09cc 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -20,6 +20,7 @@
#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/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
@@ -27,10 +28,6 @@ namespace blink {
NGFlexLayoutAlgorithm::NGFlexLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
: NGLayoutAlgorithm(params),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
- border_scrollbar_padding_(border_padding_ +
- params.fragment_geometry.scrollbar),
is_column_(Style().ResolvedIsColumnFlexDirection()),
is_horizontal_flow_(FlexLayoutAlgorithm::IsHorizontalFlow(Style())),
is_cross_size_definite_(IsContainerCrossSizeDefinite()) {
@@ -39,10 +36,8 @@ NGFlexLayoutAlgorithm::NGFlexLayoutAlgorithm(
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_);
+ ConstraintSpace(), Node(), ChildAvailableSize());
algorithm_.emplace(&Style(), MainAxisContentExtent(LayoutUnit::Max()),
child_percentage_size_, &Node().GetDocument());
@@ -57,20 +52,22 @@ bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis(
LayoutUnit NGFlexLayoutAlgorithm::MainAxisContentExtent(
LayoutUnit sum_hypothetical_main_size) const {
if (Style().ResolvedIsColumnFlexDirection()) {
- // Even though we only pass border_padding_ in the third parameter, the
+ // Even though we only pass border_padding in the third parameter, the
// return value includes scrollbar, so subtract scrollbar to get content
// size.
- // We add border_scrollbar_padding to the fourth parameter because
+ // We add |border_scrollbar_padding| to the fourth parameter because
// |content_size| needs to be the size of the border box. We've overloaded
// the term "content".
- return ComputeBlockSizeForFragment(ConstraintSpace(), Style(),
- border_padding_,
- sum_hypothetical_main_size +
- border_scrollbar_padding_.BlockSum(),
- border_box_size_.inline_size) -
- border_scrollbar_padding_.BlockSum();
+ const LayoutUnit border_scrollbar_padding =
+ BorderScrollbarPadding().BlockSum();
+ return ComputeBlockSizeForFragment(
+ ConstraintSpace(), Style(), BorderPadding(),
+ sum_hypothetical_main_size.ClampNegativeToZero() +
+ border_scrollbar_padding,
+ border_box_size_.inline_size) -
+ border_scrollbar_padding;
}
- return content_box_size_.inline_size;
+ return ChildAvailableSize().inline_size;
}
namespace {
@@ -103,15 +100,20 @@ AxisEdge CrossAxisStaticPositionEdge(const ComputedStyle& style,
const ComputedStyle& child_style) {
ItemPosition alignment =
FlexLayoutAlgorithm::AlignmentForChild(style, child_style);
- bool is_wrap_reverse = style.FlexWrap() == EFlexWrap::kWrapReverse;
+ // AlignmentForChild already accounted for wrap-reverse for kFlexStart and
+ // kFlexEnd, but not kStretch. kStretch is supposed to act like kFlexStart.
+ if (style.FlexWrap() == EFlexWrap::kWrapReverse &&
+ alignment == ItemPosition::kStretch) {
+ return AxisEdge::kEnd;
+ }
if (alignment == ItemPosition::kFlexEnd)
- return is_wrap_reverse ? AxisEdge::kStart : AxisEdge::kEnd;
+ return AxisEdge::kEnd;
if (alignment == ItemPosition::kCenter)
return AxisEdge::kCenter;
- return is_wrap_reverse ? AxisEdge::kEnd : AxisEdge::kStart;
+ return AxisEdge::kStart;
}
} // namespace
@@ -129,18 +131,17 @@ void NGFlexLayoutAlgorithm::HandleOutOfFlowPositioned(NGBlockNode child) {
InlineEdge inline_edge;
BlockEdge block_edge;
- LogicalOffset offset(border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start);
+ LogicalOffset offset = BorderScrollbarPadding().StartOffset();
// 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;
+ offset.inline_offset += ChildAvailableSize().inline_size / 2;
} else {
inline_edge = InlineEdge::kInlineEnd;
- offset.inline_offset += content_box_size_.inline_size;
+ offset.inline_offset += ChildAvailableSize().inline_size;
}
// We may not know the final block-size of the fragment yet. This will be
@@ -149,10 +150,10 @@ void NGFlexLayoutAlgorithm::HandleOutOfFlowPositioned(NGBlockNode child) {
block_edge = BlockEdge::kBlockStart;
} else if (block_axis_edge == AxisEdge::kCenter) {
block_edge = BlockEdge::kBlockCenter;
- offset.block_offset -= border_scrollbar_padding_.BlockSum() / 2;
+ offset.block_offset -= BorderScrollbarPadding().BlockSum() / 2;
} else {
block_edge = BlockEdge::kBlockEnd;
- offset.block_offset -= border_scrollbar_padding_.BlockSum();
+ offset.block_offset -= BorderScrollbarPadding().BlockSum();
}
container_builder_.AddOutOfFlowChildCandidate(child, offset, inline_edge,
@@ -329,16 +330,17 @@ double NGFlexLayoutAlgorithm::GetMainOverCrossAspectRatio(
return ratio;
}
-namespace {
-
-LayoutUnit CalculateFixedCrossSize(LayoutUnit available_size,
- const MinMaxSizes& cross_axis_min_max,
- LayoutUnit margin_sum) {
+LayoutUnit NGFlexLayoutAlgorithm::CalculateFixedCrossSize(
+ const MinMaxSizes& cross_axis_min_max,
+ const NGBoxStrut& margins) const {
+ if (!is_column_)
+ DCHECK_NE(ChildAvailableSize().block_size, kIndefiniteSize);
+ LayoutUnit available_size = is_column_ ? ChildAvailableSize().inline_size
+ : ChildAvailableSize().block_size;
+ LayoutUnit margin_sum = is_column_ ? margins.InlineSum() : margins.BlockSum();
return cross_axis_min_max.ClampSizeToMinAndMax(available_size - margin_sum);
}
-} // namespace
-
NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicBlockSize(
const NGBlockNode& flex_item,
const NGPhysicalBoxStrut& physical_margins = NGPhysicalBoxStrut(),
@@ -354,22 +356,19 @@ NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForIntrinsicBlockSize(
NGBoxStrut margins = physical_margins.ConvertToLogical(
ConstraintSpace().GetWritingMode(), Style().Direction());
- LogicalSize child_available_size = content_box_size_;
+ LogicalSize child_available_size = ChildAvailableSize();
if (ShouldItemShrinkToFit(flex_item)) {
space_builder.SetIsShrinkToFit(true);
} else if (cross_axis_min_max.min_size != kIndefiniteSize &&
WillChildCrossSizeBeContainerCrossSize(flex_item)) {
+ LayoutUnit cross_size =
+ CalculateFixedCrossSize(cross_axis_min_max, margins);
if (is_column_) {
space_builder.SetIsFixedInlineSize(true);
- child_available_size.inline_size =
- CalculateFixedCrossSize(child_available_size.inline_size,
- cross_axis_min_max, margins.InlineSum());
+ child_available_size.inline_size = cross_size;
} 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());
+ child_available_size.block_size = cross_size;
}
}
@@ -402,7 +401,7 @@ NGConstraintSpace NGFlexLayoutAlgorithm::BuildSpaceForFlexBasis(
// 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.SetAvailableSize(ChildAvailableSize());
space_builder.SetPercentageResolutionSize(child_percentage_size_);
space_builder.SetReplacedPercentageResolutionSize(child_percentage_size_);
return space_builder.ToConstraintSpace();
@@ -439,20 +438,15 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
: physical_border_padding.HorizontalSum();
base::Optional<MinMaxSizesResult> min_max_sizes;
- auto MinMaxSizesFunc = [&]() -> MinMaxSizesResult {
+ auto MinMaxSizesFunc = [&](MinMaxSizesType type) -> MinMaxSizesResult {
if (!min_max_sizes) {
- NGConstraintSpace child_space = BuildSpaceForIntrinsicBlockSize(child);
- 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(child_space);
- }
// We want the child's intrinsic inline sizes in its writing mode, so
// pass child's writing mode as the first parameter, which is nominally
// |container_writing_mode|.
- min_max_sizes = child.ComputeMinMaxSizes(
- child_style.GetWritingMode(),
- MinMaxSizesInput(content_box_size_.block_size), &child_space);
+ NGConstraintSpace child_space = BuildSpaceForIntrinsicBlockSize(child);
+ MinMaxSizesInput input(ChildAvailableSize().block_size, type);
+ min_max_sizes = child.ComputeMinMaxSizes(child_style.GetWritingMode(),
+ input, &child_space);
}
return *min_max_sizes;
};
@@ -528,27 +522,25 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
// 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 check should use HasAspectRatio() instead of Style().
+ // AspectRatio(), but to avoid introducing a behavior change we only
+ // do this for the aspect-ratio property for now until FlexNG ships.
+ bool use_cross_axis_for_aspect_ratio =
+ child.Style().AspectRatio() &&
+ WillChildCrossSizeBeContainerCrossSize(child);
+ if (use_cross_axis_for_aspect_ratio ||
+ (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)) {
+ if (use_cross_axis_for_aspect_ratio) {
+ NGBoxStrut margins = physical_child_margins.ConvertToLogical(
+ ConstraintSpace().GetWritingMode(), Style().Direction());
+ cross_size = CalculateFixedCrossSize(
+ min_max_sizes_in_cross_axis_direction, margins);
+ } else if (MainAxisIsInlineAxis(child)) {
cross_size = ResolveMainBlockLength(
flex_basis_space, child_style,
border_padding_in_child_writing_mode, cross_axis_length,
@@ -575,12 +567,14 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
// is not exactly correct.
// TODO(dgrogan): Replace with a variant of ComputeReplacedSize that
// ignores min-width, width, max-width.
- MinMaxSizesInput input(child_percentage_size_.block_size);
+ MinMaxSizesInput input(child_percentage_size_.block_size,
+ MinMaxSizesType::kContent);
flex_base_border_box =
ComputeMinAndMaxContentContribution(Style(), child, input)
.sizes.max_size;
} else {
- flex_base_border_box = MinMaxSizesFunc().sizes.max_size;
+ flex_base_border_box =
+ MinMaxSizesFunc(MinMaxSizesType::kContent).sizes.max_size;
}
} else {
// Parts C, D, and E for what are usually column flex containers.
@@ -627,14 +621,16 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
MinMaxSizes table_preferred_widths =
ComputeMinAndMaxContentContribution(
Style(), child,
- MinMaxSizesInput(child_percentage_size_.block_size))
+ MinMaxSizesInput(child_percentage_size_.block_size,
+ MinMaxSizesType::kIntrinsic))
.sizes;
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().sizes.min_size;
+ content_size_suggestion =
+ MinMaxSizesFunc(MinMaxSizesType::kContent).sizes.min_size;
} else {
LayoutUnit intrinsic_block_size;
if (child.IsReplaced()) {
@@ -737,12 +733,13 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
DCHECK_GE(min_max_sizes_in_main_axis_direction.min_size, 0);
DCHECK_GE(min_max_sizes_in_main_axis_direction.max_size, 0);
+ NGBoxStrut scrollbars = ComputeScrollbarsForNonAnonymous(child);
algorithm_
->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, cross_axis_border_padding,
- physical_child_margins)
+ physical_child_margins, scrollbars)
.ng_input_node = child;
}
}
@@ -791,27 +788,49 @@ NGFlexLayoutAlgorithm::AdjustChildSizeForAspectRatioCrossAxisMinAndMax(
}
scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
+ if (auto result = LayoutInternal())
+ return result;
+
+ // We may have aborted layout due to a child changing scrollbars, relayout
+ // with the new scrollbar information.
+ return RelayoutIgnoringChildScrollbarChanges();
+}
+
+scoped_refptr<const NGLayoutResult>
+NGFlexLayoutAlgorithm::RelayoutIgnoringChildScrollbarChanges() {
+ DCHECK(!ignore_child_scrollbar_changes_);
+ // Freezing the scrollbars for the sub-tree shouldn't be strictly necessary,
+ // but we do this just in case we trigger an unstable layout.
+ PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbars;
+ NGLayoutAlgorithmParams params(
+ Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(),
+ BreakToken(), /* early_break */ nullptr);
+ NGFlexLayoutAlgorithm algorithm(params);
+ algorithm.ignore_child_scrollbar_changes_ = true;
+ return algorithm.Layout();
+}
+
+scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::LayoutInternal() {
PaintLayerScrollableArea::DelayScrollOffsetClampScope delay_clamp_scope;
ConstructAndAppendFlexItems();
LayoutUnit main_axis_start_offset;
LayoutUnit main_axis_end_offset;
- LayoutUnit cross_axis_offset = border_scrollbar_padding_.block_start;
+ LayoutUnit cross_axis_offset = BorderScrollbarPadding().block_start;
if (is_column_) {
const bool is_column_reverse =
Style().ResolvedIsColumnReverseFlexDirection();
- main_axis_start_offset = is_column_reverse
- ? LayoutUnit()
- : border_scrollbar_padding_.block_start;
+ main_axis_start_offset =
+ is_column_reverse ? LayoutUnit() : BorderScrollbarPadding().block_start;
main_axis_end_offset =
- is_column_reverse ? LayoutUnit() : border_scrollbar_padding_.block_end;
- cross_axis_offset = border_scrollbar_padding_.inline_start;
+ is_column_reverse ? LayoutUnit() : BorderScrollbarPadding().block_end;
+ cross_axis_offset = BorderScrollbarPadding().inline_start;
} else if (Style().ResolvedIsRowReverseFlexDirection()) {
- main_axis_start_offset = border_scrollbar_padding_.inline_end;
- main_axis_end_offset = border_scrollbar_padding_.inline_start;
+ main_axis_start_offset = BorderScrollbarPadding().inline_end;
+ main_axis_end_offset = BorderScrollbarPadding().inline_start;
} else {
- main_axis_start_offset = border_scrollbar_padding_.inline_start;
- main_axis_end_offset = border_scrollbar_padding_.inline_end;
+ main_axis_start_offset = BorderScrollbarPadding().inline_start;
+ main_axis_end_offset = BorderScrollbarPadding().inline_end;
}
FlexLine* line;
while (
@@ -838,15 +857,14 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
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.inline_size = ChildAvailableSize().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());
+ flex_item.min_max_cross_sizes.value(), margins);
}
// https://drafts.csswg.org/css-flexbox/#definite-sizes
// If the flex container has a definite main size, a flex item's
@@ -859,13 +877,12 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
} else {
available_size.inline_size =
flex_item.flexed_content_size + flex_item.main_axis_border_padding;
- available_size.block_size = content_box_size_.block_size;
+ available_size.block_size = ChildAvailableSize().block_size;
space_builder.SetIsFixedInlineSize(true);
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());
+ flex_item.min_max_cross_sizes.value(), margins);
} else if (DoesItemStretch(flex_item.ng_input_node)) {
// If we are in a row flexbox, and we don't have a fixed block-size
// (yet), use the "measure" cache slot. This will be the first
@@ -913,20 +930,28 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
cross_axis_offset);
}
- LayoutUnit intrinsic_block_size = algorithm_->IntrinsicContentBlockSize() +
- border_scrollbar_padding_.BlockSum();
+ LayoutUnit intrinsic_block_size = BorderScrollbarPadding().BlockSum();
+
+ if (algorithm_->FlexLines().IsEmpty() &&
+ To<LayoutBlock>(Node().GetLayoutBox())->HasLineIfEmpty()) {
+ intrinsic_block_size += Node().GetLayoutBox()->LogicalHeightForEmptyLine();
+ } else {
+ intrinsic_block_size += algorithm_->IntrinsicContentBlockSize();
+ }
intrinsic_block_size =
ClampIntrinsicBlockSize(ConstraintSpace(), Node(),
- border_scrollbar_padding_, intrinsic_block_size);
+ BorderScrollbarPadding(), intrinsic_block_size);
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_, intrinsic_block_size,
+ ConstraintSpace(), Style(), BorderPadding(), intrinsic_block_size,
border_box_size_.inline_size);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
- GiveLinesAndItemsFinalPositionAndSize();
+ bool success = GiveLinesAndItemsFinalPositionAndSize();
+ if (!success)
+ return nullptr;
NGOutOfFlowLayoutPart(
Node(), ConstraintSpace(),
@@ -973,16 +998,16 @@ void NGFlexLayoutAlgorithm::ApplyStretchAlignmentToChild(FlexItem& flex_item) {
flex_item.ng_input_node.Layout(child_space, /* break_token */ nullptr);
}
-void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
+bool NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
Vector<FlexLine>& line_contexts = algorithm_->FlexLines();
const LayoutUnit cross_axis_start_edge =
line_contexts.IsEmpty() ? LayoutUnit()
: line_contexts[0].cross_axis_offset;
LayoutUnit final_content_main_size =
- container_builder_.InlineSize() - border_scrollbar_padding_.InlineSum();
- LayoutUnit final_content_cross_size =
- container_builder_.BlockSize() - border_scrollbar_padding_.BlockSum();
+ container_builder_.InlineSize() - BorderScrollbarPadding().InlineSum();
+ LayoutUnit final_content_cross_size = container_builder_.FragmentBlockSize() -
+ BorderScrollbarPadding().BlockSum();
if (is_column_)
std::swap(final_content_main_size, final_content_cross_size);
@@ -1003,11 +1028,12 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
if (Style().ResolvedIsColumnReverseFlexDirection()) {
algorithm_->LayoutColumnReverse(final_content_main_size,
- border_scrollbar_padding_.block_start);
+ BorderScrollbarPadding().block_start);
}
base::Optional<LayoutUnit> fallback_baseline;
+ bool success = true;
LayoutUnit overflow_block_size;
for (FlexLine& line_context : line_contexts) {
for (wtf_size_t child_number = 0;
@@ -1049,16 +1075,30 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
overflow_block_size =
std::max(overflow_block_size,
location.Y() + fragment.BlockSize() + margin_block_end);
+
+ // Detect if the flex-item had its scrollbar state change. If so we need
+ // to relayout as the input to the flex algorithm is incorrect.
+ if (!ignore_child_scrollbar_changes_) {
+ if (flex_item.scrollbars !=
+ ComputeScrollbarsForNonAnonymous(flex_item.ng_input_node))
+ success = false;
+ } else {
+ DCHECK_EQ(flex_item.scrollbars,
+ ComputeScrollbarsForNonAnonymous(flex_item.ng_input_node));
+ }
}
}
container_builder_.SetOverflowBlockSize(overflow_block_size +
- border_scrollbar_padding_.block_end);
+ BorderScrollbarPadding().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);
+
+ // Signal if we need to relayout with new child scrollbar information.
+ return success;
}
void NGFlexLayoutAlgorithm::PropagateBaselineFromChild(
@@ -1090,7 +1130,7 @@ void NGFlexLayoutAlgorithm::PropagateBaselineFromChild(
MinMaxSizesResult NGFlexLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& child_input) const {
if (auto result = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_))
+ Node(), BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
@@ -1136,7 +1176,7 @@ MinMaxSizesResult NGFlexLayoutAlgorithm::ComputeMinMaxSizes(
// 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();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
index e0e33c2bdc6..8bb19f7da46 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h
@@ -23,11 +23,13 @@ class CORE_EXPORT NGFlexLayoutAlgorithm
public:
NGFlexLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
- scoped_refptr<const NGLayoutResult> Layout() override;
-
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override;
+ scoped_refptr<const NGLayoutResult> Layout() override;
private:
+ scoped_refptr<const NGLayoutResult> RelayoutIgnoringChildScrollbarChanges();
+ scoped_refptr<const NGLayoutResult> LayoutInternal();
+
bool DoesItemCrossSizeComputeToAuto(const NGBlockNode& child) const;
bool IsItemFlexBasisDefinite(const NGBlockNode& child) const;
bool IsItemMainSizeDefinite(const NGBlockNode& child) const;
@@ -51,6 +53,9 @@ class CORE_EXPORT NGFlexLayoutAlgorithm
bool IsColumnContainerMainSizeDefinite() const;
bool IsContainerCrossSizeDefinite() const;
+ LayoutUnit CalculateFixedCrossSize(const MinMaxSizes& cross_axis_min_max,
+ const NGBoxStrut& margins) const;
+
NGConstraintSpace BuildSpaceForFlexBasis(const NGBlockNode& flex_item) const;
NGConstraintSpace BuildSpaceForIntrinsicBlockSize(
const NGBlockNode& flex_item,
@@ -58,7 +63,7 @@ class CORE_EXPORT NGFlexLayoutAlgorithm
const MinMaxSizes& cross_axis) const;
void ConstructAndAppendFlexItems();
void ApplyStretchAlignmentToChild(FlexItem& flex_item);
- void GiveLinesAndItemsFinalPositionAndSize();
+ bool GiveLinesAndItemsFinalPositionAndSize();
void LayoutColumnReverse(LayoutUnit main_axis_content_size);
// This is same method as FlexItem but we need that logic before FlexItem is
@@ -75,13 +80,11 @@ class CORE_EXPORT NGFlexLayoutAlgorithm
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_;
const bool is_cross_size_definite_;
+ bool ignore_child_scrollbar_changes_ = false;
LogicalSize border_box_size_;
- LogicalSize content_box_size_;
LogicalSize child_percentage_size_;
base::Optional<FlexLayoutAlgorithm> algorithm_;
};
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc
index 672f1a4b32c..b4bdc582181 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "third_party/blink/renderer/core/layout/ng/layout_ng_grid.h"
+#include "third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.h b/chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h
index 060835974ba..d7601da5c03 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_grid.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_GRID_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_GRID_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_LAYOUT_NG_GRID_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_LAYOUT_NG_GRID_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/layout_block.h"
@@ -28,4 +28,4 @@ class CORE_EXPORT LayoutNGGrid : public LayoutNGMixin<LayoutBlock> {
} // namespace blink
-#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LAYOUT_NG_GRID_H_
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_LAYOUT_NG_GRID_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.cc
new file mode 100644
index 00000000000..d0791c16dea
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.cc
@@ -0,0 +1,37 @@
+// 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/grid/ng_grid_child_iterator.h"
+
+namespace blink {
+
+NGGridChildIterator::NGGridChildIterator(const NGBlockNode node) {
+ Setup(node);
+}
+
+void NGGridChildIterator::Setup(const NGBlockNode node) {
+ const int initial_order = ComputedStyleInitialValues::InitialOrder();
+ bool needs_sort = false;
+
+ // Collect all our children, and order them by either their order property.
+ for (NGLayoutInputNode child = node.FirstChild(); child;
+ child = child.NextSibling()) {
+ int order = 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 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/grid/ng_grid_child_iterator.h b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h
new file mode 100644
index 00000000000..440e4a8180f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.h
@@ -0,0 +1,54 @@
+// 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_GRID_NG_GRID_CHILD_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_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 which given the current grid node will iterate through its
+// children.
+//
+// TODO(layout-dev): Once LayoutNG 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 NGGridChildIterator {
+ STACK_ALLOCATED();
+
+ public:
+ explicit NGGridChildIterator(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;
+ };
+
+ protected:
+ virtual void Setup(const NGBlockNode node);
+ Vector<ChildWithOrder, 4> children_;
+ Vector<ChildWithOrder, 4>::const_iterator iterator_;
+};
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(
+ blink::NGGridChildIterator::ChildWithOrder)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_CHILD_ITERATOR_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator_test.cc
new file mode 100644
index 00000000000..d5468ce0775
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator_test.cc
@@ -0,0 +1,98 @@
+// 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/grid/ng_grid_child_iterator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+
+namespace blink {
+namespace {
+
+TEST_F(NGLayoutTest, TestNGGridChildIterator) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="parent" style="display: grid">
+ <div id="child1">Child 1</div>
+ <div id="child2">Child 2</div>
+ <div id="child3">Child 3</div>
+ <div id="child4">Child 4</div>
+ </div>
+ )HTML");
+
+ NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
+
+ int index = 0;
+ NGGridChildIterator iterator(parent_block);
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
+ StringBuilder cell_id;
+ cell_id.Append("child");
+ cell_id.Append(AtomicString::Number(++index));
+ NGBlockNode cell_block(ToLayoutBox(
+ GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
+ EXPECT_EQ(child, cell_block);
+ }
+
+ EXPECT_EQ(index, 4);
+}
+
+TEST_F(NGLayoutTest, TestNGGridChildIteratorWithOrderReversed) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="parent" style="display: grid">
+ <div id="child1" style="order: 4">Child 1</div>
+ <div id="child2" style="order: 3">Child 2</div>
+ <div id="child3" style="order: 2">Child 3</div>
+ <div id="child4" style="order: 1">Child 4</div>
+ </div>
+ )HTML");
+
+ NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
+
+ int index = 4;
+ NGGridChildIterator iterator(parent_block);
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
+ StringBuilder cell_id;
+ cell_id.Append("child");
+ cell_id.Append(AtomicString::Number(index));
+ NGBlockNode cell_block(ToLayoutBox(
+ GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
+ EXPECT_EQ(child, cell_block);
+ --index;
+ }
+
+ EXPECT_EQ(index, 0);
+}
+
+TEST_F(NGLayoutTest, TestNGGridChildIteratorWithOrderMixed) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="parent" style="display: grid">
+ <div id="child1" style="order: 3">Child 1</div>
+ <div id="child2" style="order: 3">Child 2</div>
+ <div id="child3" style="order: -1">Child 3</div>
+ <div id="child4" style="order: 0">Child 4</div>
+ </div>
+ )HTML");
+
+ NGBlockNode parent_block(ToLayoutBox(GetLayoutObjectByElementId("parent")));
+ int expected_order[] = {3, 4, 1, 2};
+
+ int index = 0;
+ NGGridChildIterator iterator(parent_block);
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
+ StringBuilder cell_id;
+ cell_id.Append("child");
+ cell_id.Append(AtomicString::Number(expected_order[index]));
+ NGBlockNode cell_block(ToLayoutBox(
+ GetLayoutObjectByElementId(cell_id.ToString().Ascii().c_str())));
+ EXPECT_EQ(child, cell_block);
+ ++index;
+ }
+
+ EXPECT_EQ(index, 4);
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
new file mode 100644
index 00000000000..63c63ee2c72
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -0,0 +1,76 @@
+// 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/grid/ng_grid_layout_algorithm.h"
+
+#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_child_iterator.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"
+
+namespace blink {
+
+NGGridLayoutAlgorithm::NGGridLayoutAlgorithm(
+ const NGLayoutAlgorithmParams& params)
+ : NGLayoutAlgorithm(params),
+ state_(GridLayoutAlgorithmState::kMeasuringItems) {
+ DCHECK(params.space.IsNewFormattingContext());
+ DCHECK(!params.break_token);
+ container_builder_.SetIsNewFormattingContext(true);
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
+
+ child_percentage_size_ = CalculateChildPercentageSize(
+ ConstraintSpace(), Node(), ChildAvailableSize());
+}
+
+scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() {
+ switch (state_) {
+ case GridLayoutAlgorithmState::kMeasuringItems:
+ ConstructAndAppendGridItems();
+ break;
+
+ default:
+ break;
+ }
+
+ return container_builder_.ToBoxFragment();
+}
+
+MinMaxSizesResult NGGridLayoutAlgorithm::ComputeMinMaxSizes(
+ const MinMaxSizesInput& input) const {
+ return {MinMaxSizes(), /* depends_on_percentage_block_size */ true};
+}
+
+void NGGridLayoutAlgorithm::ConstructAndAppendGridItems() {
+ NGGridChildIterator iterator(Node());
+ for (NGBlockNode child = iterator.NextChild(); child;
+ child = iterator.NextChild()) {
+ ConstructAndAppendGridItem(child);
+ }
+}
+
+void NGGridLayoutAlgorithm::ConstructAndAppendGridItem(
+ const NGBlockNode& node) {
+ GridItem item;
+ item.constraint_space = BuildSpaceForMeasure(node);
+ items_.emplace_back(item);
+}
+
+NGConstraintSpace NGGridLayoutAlgorithm::BuildSpaceForMeasure(
+ const NGBlockNode& grid_item) {
+ const ComputedStyle& child_style = grid_item.Style();
+
+ NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
+ child_style.GetWritingMode(),
+ /* is_new_fc */ true);
+ space_builder.SetCacheSlot(NGCacheSlot::kMeasure);
+ space_builder.SetIsPaintedAtomically(true);
+
+ // TODO(kschmi) - do layout/measuring and handle non-fixed sizes here.
+ space_builder.SetAvailableSize(ChildAvailableSize());
+ space_builder.SetPercentageResolutionSize(child_percentage_size_);
+ space_builder.SetTextDirection(child_style.Direction());
+ return space_builder.ToConstraintSpace();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
index 65530019710..e0898325e1a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.h
@@ -2,12 +2,14 @@
// 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_GRID_LAYOUT_ALGORITHM_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_GRID_LAYOUT_ALGORITHM_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_LAYOUT_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_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_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_layout_algorithm.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
@@ -21,8 +23,27 @@ class CORE_EXPORT NGGridLayoutAlgorithm
scoped_refptr<const NGLayoutResult> Layout() override;
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override;
+
+ private:
+ friend class NGGridLayoutAlgorithmTest;
+
+ void ConstructAndAppendGridItems();
+ void ConstructAndAppendGridItem(const NGBlockNode& node);
+ NGConstraintSpace BuildSpaceForMeasure(const NGBlockNode& grid_item);
+
+ enum class GridLayoutAlgorithmState {
+ kMeasuringItems,
+ };
+ GridLayoutAlgorithmState state_;
+
+ struct GridItem {
+ NGConstraintSpace constraint_space;
+ };
+ Vector<GridItem> items_;
+
+ LogicalSize child_percentage_size_;
};
} // namespace blink
-#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_GRID_LAYOUT_ALGORITHM_H_
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_LAYOUT_ALGORITHM_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
new file mode 100644
index 00000000000..0f991d63a5d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm_test.cc
@@ -0,0 +1,106 @@
+// 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/grid/ng_grid_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+class NGGridLayoutAlgorithmTest
+ : public NGBaseLayoutAlgorithmTest,
+ private ScopedLayoutNGGridForTest,
+ private ScopedLayoutNGBlockFragmentationForTest {
+ protected:
+ NGGridLayoutAlgorithmTest()
+ : ScopedLayoutNGGridForTest(true),
+ ScopedLayoutNGBlockFragmentationForTest(true) {}
+ void SetUp() override {
+ NGBaseLayoutAlgorithmTest::SetUp();
+ style_ = ComputedStyle::Create();
+ }
+
+ // Helper methods to access private data on NGGridLayoutAlgorithm. This class
+ // is a friend of NGGridLayoutAlgorithm but the individual tests are not.
+ size_t GridItemSize(NGGridLayoutAlgorithm& algorithm) {
+ return algorithm.items_.size();
+ }
+
+ Vector<NGConstraintSpace> GridItemConstraintSpaces(
+ NGGridLayoutAlgorithm& algorithm) {
+ Vector<NGConstraintSpace> constraint_spaces;
+ for (auto& item : algorithm.items_) {
+ constraint_spaces.push_back(NGConstraintSpace(item.constraint_space));
+ }
+ return constraint_spaces;
+ }
+
+ scoped_refptr<ComputedStyle> style_;
+};
+
+TEST_F(NGGridLayoutAlgorithmTest, NGGridLayoutAlgorithmMeasuring) {
+ if (!RuntimeEnabledFeatures::LayoutNGGridEnabled())
+ return;
+
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #grid1 {
+ display: grid;
+ grid-template-columns: 100px 100px;
+ grid-template-rows: 100px 100px;
+ }
+ #cell1 {
+ grid-column: 1;
+ grid-row: 1;
+ width: 50px;
+ }
+ #cell2 {
+ grid-column: 2;
+ grid-row: 1;
+ width: 50px;
+ }
+ #cell3 {
+ grid-column: 1;
+ grid-row: 2;
+ width: 50px;
+ }
+ #cell4 {
+ grid-column: 2;
+ grid-row: 2;
+ width: 50px;
+ }
+ </style>
+ <div id="grid1">
+ <div id="cell1">Cell 1</div>
+ <div id="cell2">Cell 2</div>
+ <div id="cell3">Cell 3</div>
+ <div id="cell4">Cell 4</div>
+ </div>
+ )HTML");
+
+ NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("grid1")));
+
+ NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
+ WritingMode::kHorizontalTb, TextDirection::kLtr,
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)), false, true);
+
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(space, node);
+
+ NGGridLayoutAlgorithm algorithm({node, fragment_geometry, space});
+ EXPECT_EQ(GridItemSize(algorithm), 0U);
+ algorithm.Layout();
+ EXPECT_EQ(GridItemSize(algorithm), 4U);
+
+ Vector<NGConstraintSpace> constraint_spaces =
+ GridItemConstraintSpaces(algorithm);
+
+ EXPECT_EQ(GridItemSize(algorithm), constraint_spaces.size());
+ for (auto& constraint_space : constraint_spaces) {
+ EXPECT_EQ(constraint_space.AvailableSize().inline_size.ToInt(), 100);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
new file mode 100644
index 00000000000..26c190ad4a7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.cc
@@ -0,0 +1,510 @@
+// 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/grid/ng_grid_track_collection.h"
+#include "base/check.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+constexpr wtf_size_t NGGridTrackCollectionBase::kInvalidRangeIndex;
+
+wtf_size_t NGGridTrackCollectionBase::RangeIndexFromTrackNumber(
+ wtf_size_t track_number) const {
+ wtf_size_t upper = RangeCount();
+ wtf_size_t lower = 0u;
+
+ // We can't look for a range in a collection with no ranges.
+ DCHECK_NE(upper, 0u);
+ // We don't expect a |track_number| outside of the bounds of the collection.
+ DCHECK_NE(track_number, kInvalidRangeIndex);
+ DCHECK_LT(track_number,
+ RangeTrackNumber(upper - 1u) + RangeTrackCount(upper - 1u));
+
+ // Do a binary search on the tracks.
+ wtf_size_t range = upper - lower;
+ while (range > 1) {
+ wtf_size_t center = lower + (range / 2u);
+
+ wtf_size_t center_track_number = RangeTrackNumber(center);
+ wtf_size_t center_track_count = RangeTrackCount(center);
+
+ if (center_track_number <= track_number &&
+ (track_number - center_track_number) < center_track_count) {
+ // We found the track.
+ return center;
+ } else if (center_track_number > track_number) {
+ // This track is too high.
+ upper = center;
+ range = upper - lower;
+ } else {
+ // This track is too low.
+ lower = center + 1u;
+ range = upper - lower;
+ }
+ }
+
+ return lower;
+}
+
+String NGGridTrackCollectionBase::ToString() const {
+ if (RangeCount() == kInvalidRangeIndex)
+ return "NGGridTrackCollection: Empty";
+
+ StringBuilder builder;
+ builder.Append("NGGridTrackCollection: [RangeCount: ");
+ builder.AppendNumber<wtf_size_t>(RangeCount());
+ builder.Append("], Ranges: ");
+ for (wtf_size_t range_index = 0; range_index < RangeCount(); ++range_index) {
+ builder.Append("[Start: ");
+ builder.AppendNumber<wtf_size_t>(RangeTrackNumber(range_index));
+ builder.Append(", Count: ");
+ builder.AppendNumber<wtf_size_t>(RangeTrackCount(range_index));
+ if (IsRangeCollapsed(range_index)) {
+ builder.Append(", Collapsed ");
+ }
+ builder.Append("]");
+ if (range_index + 1 < RangeCount())
+ builder.Append(", ");
+ }
+ return builder.ToString();
+}
+
+NGGridTrackCollectionBase::RangeRepeatIterator::RangeRepeatIterator(
+ const NGGridTrackCollectionBase* collection,
+ wtf_size_t range_index)
+ : collection_(collection) {
+ DCHECK(collection_);
+ range_count_ = collection_->RangeCount();
+ SetRangeIndex(range_index);
+}
+
+bool NGGridTrackCollectionBase::RangeRepeatIterator::MoveToNextRange() {
+ return SetRangeIndex(range_index_ + 1);
+}
+
+wtf_size_t NGGridTrackCollectionBase::RangeRepeatIterator::RepeatCount() const {
+ return range_track_count_;
+}
+
+wtf_size_t NGGridTrackCollectionBase::RangeRepeatIterator::RangeTrackStart()
+ const {
+ return range_track_start_;
+}
+
+wtf_size_t NGGridTrackCollectionBase::RangeRepeatIterator::RangeTrackEnd()
+ const {
+ if (range_index_ == kInvalidRangeIndex)
+ return kInvalidRangeIndex;
+ return range_track_start_ + range_track_count_ - 1;
+}
+
+bool NGGridTrackCollectionBase::RangeRepeatIterator::IsRangeCollapsed() const {
+ DCHECK(range_index_ != kInvalidRangeIndex);
+ DCHECK(collection_);
+ return collection_->IsRangeCollapsed(range_index_);
+}
+
+bool NGGridTrackCollectionBase::RangeRepeatIterator::SetRangeIndex(
+ wtf_size_t range_index) {
+ if (range_index < 0 || range_index >= range_count_) {
+ // Invalid index.
+ range_index_ = kInvalidRangeIndex;
+ range_track_start_ = kInvalidRangeIndex;
+ range_track_count_ = 0;
+ return false;
+ }
+
+ range_index_ = range_index;
+ range_track_start_ = collection_->RangeTrackNumber(range_index_);
+ range_track_count_ = collection_->RangeTrackCount(range_index_);
+ return true;
+}
+
+NGGridTrackRepeater::NGGridTrackRepeater(wtf_size_t track_index,
+ wtf_size_t repeat_size,
+ wtf_size_t repeat_count,
+ RepeatType repeat_type)
+ : track_index(track_index),
+ repeat_size(repeat_size),
+ repeat_count(repeat_count),
+ repeat_type(repeat_type) {}
+
+String NGGridTrackRepeater::ToString() const {
+ StringBuilder builder;
+ builder.Append("Repeater: [Index: ");
+ builder.AppendNumber<wtf_size_t>(track_index);
+ builder.Append("], [RepeatSize: ");
+ builder.AppendNumber<wtf_size_t>(repeat_size);
+ builder.Append("], [RepeatCount: ");
+ switch (repeat_type) {
+ case RepeatType::kCount:
+ builder.AppendNumber<wtf_size_t>(repeat_count);
+ builder.Append("]");
+ break;
+ case RepeatType::kAutoFill:
+ builder.Append("auto-fill]");
+ break;
+ case RepeatType::kAutoFit:
+ builder.Append("auto-fit]");
+ break;
+ }
+ return builder.ToString();
+}
+
+NGGridTrackList::NGGridTrackList() {
+ auto_repeater_index_ = NGGridTrackCollectionBase::kInvalidRangeIndex;
+ total_track_count_ = 0;
+}
+
+wtf_size_t NGGridTrackList::RepeatCount(wtf_size_t index,
+ wtf_size_t auto_value) const {
+ DCHECK_LT(index, RepeaterCount());
+ if (index == auto_repeater_index_)
+ return auto_value;
+ return repeaters_[index].repeat_count;
+}
+
+wtf_size_t NGGridTrackList::RepeatSize(wtf_size_t index) const {
+ DCHECK_LT(index, RepeaterCount());
+ return repeaters_[index].repeat_size;
+}
+
+NGGridTrackRepeater::RepeatType NGGridTrackList::RepeatType(
+ wtf_size_t index) const {
+ DCHECK_LT(index, RepeaterCount());
+ return repeaters_[index].repeat_type;
+}
+
+wtf_size_t NGGridTrackList::RepeaterCount() const {
+ return repeaters_.size();
+}
+
+wtf_size_t NGGridTrackList::TotalTrackCount() const {
+ return total_track_count_;
+}
+
+bool NGGridTrackList::AddRepeater(wtf_size_t track_index,
+ wtf_size_t track_count,
+ wtf_size_t repeat_count) {
+ return AddRepeater(track_index, track_count, repeat_count,
+ NGGridTrackRepeater::RepeatType::kCount);
+}
+
+bool NGGridTrackList::AddAutoRepeater(
+ wtf_size_t track_index,
+ wtf_size_t track_count,
+ NGGridTrackRepeater::RepeatType repeat_type) {
+ return AddRepeater(track_index, track_count, 1u, repeat_type);
+}
+
+bool NGGridTrackList::AddRepeater(wtf_size_t track_index,
+ wtf_size_t track_count,
+ wtf_size_t repeat_count,
+ NGGridTrackRepeater::RepeatType repeat_type) {
+ // Ensure valid track index.
+ DCHECK_NE(NGGridTrackCollectionBase::kInvalidRangeIndex, track_index);
+
+#if DCHECK_IS_ON()
+ // Ensure we do not skip or overlap tracks.
+ DCHECK(IsTrackContiguous(track_index));
+#endif
+
+ // If the repeater is auto, the repeat_count should be 1.
+ DCHECK(repeat_type == NGGridTrackRepeater::RepeatType::kCount ||
+ repeat_count == 1u);
+
+ // Ensure adding tracks will not overflow the total in this track list and
+ // that there is only one auto repeater per track list.
+ switch (repeat_type) {
+ case NGGridTrackRepeater::RepeatType::kCount:
+ if (track_count > AvailableTrackCount() / repeat_count)
+ return false;
+ total_track_count_ += track_count * repeat_count;
+ break;
+ case NGGridTrackRepeater::RepeatType::kAutoFill:
+ case NGGridTrackRepeater::RepeatType::kAutoFit: // Intentional Fallthrough.
+ if (HasAutoRepeater() || track_count > AvailableTrackCount())
+ return false;
+ total_track_count_ += track_count;
+ // Update auto repeater index and append repeater.
+ auto_repeater_index_ = repeaters_.size();
+ break;
+ }
+
+ repeaters_.emplace_back(track_index, track_count, repeat_count, repeat_type);
+
+ return true;
+}
+
+String NGGridTrackList::ToString() const {
+ StringBuilder builder;
+ builder.Append("TrackList: {");
+ for (wtf_size_t i = 0; i < repeaters_.size(); ++i) {
+ builder.Append(" ");
+ builder.Append(repeaters_[i].ToString());
+ if (i + 1 != repeaters_.size())
+ builder.Append(", ");
+ }
+ builder.Append(" } ");
+ return builder.ToString();
+}
+
+bool NGGridTrackList::HasAutoRepeater() {
+ return auto_repeater_index_ != NGGridTrackCollectionBase::kInvalidRangeIndex;
+}
+
+wtf_size_t NGGridTrackList::AvailableTrackCount() const {
+ return NGGridTrackCollectionBase::kMaxRangeIndex - total_track_count_;
+}
+
+#if DCHECK_IS_ON()
+bool NGGridTrackList::IsTrackContiguous(wtf_size_t track_index) const {
+ return repeaters_.IsEmpty() ||
+ (repeaters_.back().track_index + repeaters_.back().repeat_size ==
+ track_index);
+}
+#endif
+
+void NGGridBlockTrackCollection::SetSpecifiedTracks(
+ const NGGridTrackList& specified_tracks,
+ wtf_size_t auto_repeat_count,
+ const NGGridTrackList& implicit_tracks) {
+ // The implicit track list should have only one repeater, if any.
+ DCHECK_LE(implicit_tracks.RepeaterCount(), 1u);
+ specified_tracks_ = specified_tracks;
+ implicit_tracks_ = implicit_tracks;
+ auto_repeat_count_ = auto_repeat_count;
+
+ wtf_size_t repeater_count = specified_tracks_.RepeaterCount();
+ wtf_size_t total_track_count = 0;
+
+ for (wtf_size_t i = 0; i < repeater_count; ++i) {
+ wtf_size_t repeater_track_start = total_track_count + 1;
+ wtf_size_t repeater_track_count =
+ specified_tracks_.RepeatCount(i, auto_repeat_count_) *
+ specified_tracks_.RepeatSize(i);
+ if (repeater_track_count != 0) {
+ starting_tracks_.push_back(repeater_track_start);
+ ending_tracks_.push_back(repeater_track_start + repeater_track_count - 1);
+ }
+ total_track_count += repeater_track_count;
+ }
+}
+
+void NGGridBlockTrackCollection::EnsureTrackCoverage(wtf_size_t track_number,
+ wtf_size_t span_length) {
+ DCHECK_NE(kInvalidRangeIndex, track_number);
+ DCHECK_NE(kInvalidRangeIndex, span_length);
+ track_indices_need_sort_ = true;
+ starting_tracks_.push_back(track_number);
+ ending_tracks_.push_back(track_number + span_length - 1);
+}
+
+void NGGridBlockTrackCollection::FinalizeRanges() {
+ ranges_.clear();
+
+ // Sort start and ending tracks from low to high.
+ if (track_indices_need_sort_) {
+ std::stable_sort(starting_tracks_.begin(), starting_tracks_.end());
+ std::stable_sort(ending_tracks_.begin(), ending_tracks_.end());
+ }
+
+ wtf_size_t current_range_track_start = 1u;
+ if (starting_tracks_.size() > 0 && starting_tracks_[0] == 0)
+ current_range_track_start = 0;
+
+ // Indices into the starting and ending track vectors.
+ wtf_size_t starting_tracks_index = 0;
+ wtf_size_t ending_tracks_index = 0;
+
+ wtf_size_t repeater_index = kInvalidRangeIndex;
+ wtf_size_t repeater_track_start = kInvalidRangeIndex;
+ wtf_size_t next_repeater_track_start = 1u;
+ wtf_size_t current_repeater_track_count = 0;
+
+ wtf_size_t total_repeater_count = specified_tracks_.RepeaterCount();
+ wtf_size_t open_items_or_repeaters = 0;
+ bool is_in_auto_fit_range = false;
+
+ while (true) {
+ // Identify starting tracks index.
+ while (starting_tracks_index < starting_tracks_.size() &&
+ current_range_track_start >=
+ starting_tracks_[starting_tracks_index]) {
+ ++starting_tracks_index;
+ ++open_items_or_repeaters;
+ }
+
+ // Identify ending tracks index.
+ while (ending_tracks_index < ending_tracks_.size() &&
+ current_range_track_start > ending_tracks_[ending_tracks_index]) {
+ ++ending_tracks_index;
+ --open_items_or_repeaters;
+ DCHECK_GE(open_items_or_repeaters, 0u);
+ }
+
+ // Identify ending tracks index.
+ if (ending_tracks_index >= ending_tracks_.size()) {
+ DCHECK_EQ(open_items_or_repeaters, 0u);
+ break;
+ }
+
+ // Determine the next starting and ending track index.
+ wtf_size_t next_starting_track = kInvalidRangeIndex;
+ if (starting_tracks_index < starting_tracks_.size())
+ next_starting_track = starting_tracks_[starting_tracks_index];
+ wtf_size_t next_ending_track = ending_tracks_[ending_tracks_index];
+
+ // Move |next_repeater_track_start| to the start of the next repeater, if
+ // needed.
+ while (current_range_track_start == next_repeater_track_start) {
+ if (++repeater_index == total_repeater_count) {
+ repeater_index = kInvalidRangeIndex;
+ repeater_track_start = next_repeater_track_start;
+ is_in_auto_fit_range = false;
+ break;
+ }
+
+ is_in_auto_fit_range = specified_tracks_.RepeatType(repeater_index) ==
+ NGGridTrackRepeater::RepeatType::kAutoFit;
+ current_repeater_track_count =
+ specified_tracks_.RepeatCount(repeater_index, auto_repeat_count_) *
+ specified_tracks_.RepeatSize(repeater_index);
+ repeater_track_start = next_repeater_track_start;
+ next_repeater_track_start += current_repeater_track_count;
+ }
+
+ // Determine track number and count of the range.
+ Range range;
+ range.starting_track_number = current_range_track_start;
+ if (next_starting_track != kInvalidRangeIndex) {
+ range.track_count =
+ std::min(next_ending_track + 1u, next_starting_track) -
+ current_range_track_start;
+ } else {
+ range.track_count = next_ending_track + 1u - current_range_track_start;
+ }
+
+ // Compute repeater index and offset.
+ if (repeater_index == kInvalidRangeIndex) {
+ range.is_implicit_range = true;
+ if (implicit_tracks_.RepeaterCount() == 0) {
+ // No specified implicit tracks, use auto tracks.
+ range.repeater_index = kInvalidRangeIndex;
+ range.repeater_offset = 0;
+ } else {
+ // Use implicit tracks.
+ wtf_size_t implicit_repeat_size = ImplicitRepeatSize();
+ range.repeater_index = 0;
+ if (range.starting_track_number == 0) {
+ wtf_size_t offset_from_end =
+ (1 - range.starting_track_number) % implicit_repeat_size;
+ if (offset_from_end == implicit_repeat_size) {
+ range.repeater_offset = 0;
+ } else {
+ range.repeater_offset =
+ current_range_track_start - repeater_track_start;
+ }
+ }
+ }
+ } else {
+ range.is_implicit_range = false;
+ range.repeater_index = repeater_index;
+ range.repeater_offset = current_range_track_start - repeater_track_start;
+ }
+ range.is_collapsed = is_in_auto_fit_range && open_items_or_repeaters == 1u;
+
+ current_range_track_start += range.track_count;
+ ranges_.push_back(range);
+ }
+
+#if DCHECK_IS_ON()
+ while (repeater_index != kInvalidRangeIndex &&
+ repeater_index < total_repeater_count - 1u) {
+ ++repeater_index;
+ DCHECK_EQ(0u, specified_tracks_.RepeatSize(repeater_index));
+ }
+#endif
+ DCHECK_EQ(starting_tracks_index, starting_tracks_.size());
+ DCHECK_EQ(ending_tracks_index, starting_tracks_.size());
+ DCHECK(repeater_index == total_repeater_count - 1u ||
+ repeater_index == kInvalidRangeIndex);
+ starting_tracks_.clear();
+ ending_tracks_.clear();
+}
+
+const NGGridBlockTrackCollection::Range&
+NGGridBlockTrackCollection::RangeAtRangeIndex(wtf_size_t range_index) const {
+ DCHECK_NE(range_index, kInvalidRangeIndex);
+ DCHECK_LT(range_index, ranges_.size());
+ return ranges_[range_index];
+}
+const NGGridBlockTrackCollection::Range&
+NGGridBlockTrackCollection::RangeAtTrackNumber(wtf_size_t track_number) const {
+ wtf_size_t range_index = RangeIndexFromTrackNumber(track_number);
+ DCHECK_NE(range_index, kInvalidRangeIndex);
+ DCHECK_LT(range_index, ranges_.size());
+ return ranges_[range_index];
+}
+
+String NGGridBlockTrackCollection::ToString() const {
+ if (ranges_.IsEmpty()) {
+ StringBuilder builder;
+ builder.Append("NGGridTrackCollection: [SpecifiedTracks: ");
+ builder.Append(specified_tracks_.ToString());
+ if (HasImplicitTracks()) {
+ builder.Append("], [ImplicitTracks: ");
+ builder.Append(implicit_tracks_.ToString());
+ }
+
+ builder.Append("], [Starting: {");
+ for (wtf_size_t i = 0; i < starting_tracks_.size(); ++i) {
+ builder.AppendNumber<wtf_size_t>(starting_tracks_[i]);
+ if (i + 1 != starting_tracks_.size())
+ builder.Append(", ");
+ }
+ builder.Append("} ], [Ending: {");
+ for (wtf_size_t i = 0; i < ending_tracks_.size(); ++i) {
+ builder.AppendNumber<wtf_size_t>(ending_tracks_[i]);
+ if (i + 1 != ending_tracks_.size())
+ builder.Append(", ");
+ }
+ builder.Append("} ] ");
+ return builder.ToString();
+ } else {
+ return NGGridTrackCollectionBase::ToString();
+ }
+}
+bool NGGridBlockTrackCollection::HasImplicitTracks() const {
+ return implicit_tracks_.RepeaterCount() != 0;
+}
+wtf_size_t NGGridBlockTrackCollection::ImplicitRepeatSize() const {
+ DCHECK(HasImplicitTracks());
+ return implicit_tracks_.RepeatSize(0);
+}
+
+wtf_size_t NGGridBlockTrackCollection::RangeTrackNumber(
+ wtf_size_t range_index) const {
+ DCHECK_LT(range_index, RangeCount());
+ return ranges_[range_index].starting_track_number;
+}
+
+wtf_size_t NGGridBlockTrackCollection::RangeTrackCount(
+ wtf_size_t range_index) const {
+ DCHECK_LT(range_index, RangeCount());
+ return ranges_[range_index].track_count;
+}
+
+bool NGGridBlockTrackCollection::IsRangeCollapsed(
+ wtf_size_t range_index) const {
+ DCHECK_LT(range_index, RangeCount());
+ return ranges_[range_index].is_collapsed;
+}
+
+wtf_size_t NGGridBlockTrackCollection::RangeCount() const {
+ return ranges_.size();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
new file mode 100644
index 00000000000..ca66e0190f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h
@@ -0,0 +1,207 @@
+// 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_GRID_NG_GRID_TRACK_COLLECTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_TRACK_COLLECTION_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// NGGridTrackCollectionBase provides an implementation for some shared
+// functionality on track range collections, specifically binary search on
+// the collection to get a range index given a track number.
+class CORE_EXPORT NGGridTrackCollectionBase {
+ public:
+ static constexpr wtf_size_t kInvalidRangeIndex = kNotFound;
+ static constexpr wtf_size_t kMaxRangeIndex = kNotFound - 1;
+
+ class CORE_EXPORT RangeRepeatIterator {
+ public:
+ RangeRepeatIterator(const NGGridTrackCollectionBase* collection,
+ wtf_size_t range_index);
+
+ // Moves iterator to next range, skipping over repeats in a range. Return
+ // true if the move was successful.
+ bool MoveToNextRange();
+ wtf_size_t RepeatCount() const;
+ // Returns the track number for the start of the range.
+ wtf_size_t RangeTrackStart() const;
+ // Returns the track number at the end of the range.
+ wtf_size_t RangeTrackEnd() const;
+
+ bool IsRangeCollapsed() const;
+
+ private:
+ bool SetRangeIndex(wtf_size_t range_index);
+ const NGGridTrackCollectionBase* collection_;
+ wtf_size_t range_index_;
+ wtf_size_t range_count_;
+
+ // First track number of a range.
+ wtf_size_t range_track_start_;
+ // Count of repeated tracks in a range.
+ wtf_size_t range_track_count_;
+ };
+
+ // Gets the range index for the range that contains the given track number.
+ wtf_size_t RangeIndexFromTrackNumber(wtf_size_t track_number) const;
+
+ String ToString() const;
+
+ protected:
+ // Returns the first track number of a range.
+ virtual wtf_size_t RangeTrackNumber(wtf_size_t range_index) const = 0;
+
+ // Returns the number of tracks in a range.
+ virtual wtf_size_t RangeTrackCount(wtf_size_t range_index) const = 0;
+
+ // Returns true if the range at the given index is collapsed.
+ virtual bool IsRangeCollapsed(wtf_size_t range_index) const = 0;
+
+ // Returns the number of track ranges in the collection.
+ virtual wtf_size_t RangeCount() const = 0;
+};
+
+// Stores tracks related data by compressing repeated tracks into a single node.
+struct NGGridTrackRepeater {
+ enum class RepeatType { kCount, kAutoFill, kAutoFit };
+ NGGridTrackRepeater(wtf_size_t track_index,
+ wtf_size_t repeat_size,
+ wtf_size_t repeat_count,
+ RepeatType repeat_type);
+ String ToString() const;
+ bool operator==(const NGGridTrackRepeater& rhs) const;
+ // Index of the first track being repeated.
+ wtf_size_t track_index;
+ // Amount of tracks to be repeated.
+ wtf_size_t repeat_size;
+ // Amount of times the group of tracks are repeated.
+ wtf_size_t repeat_count;
+ // Type of repetition.
+ RepeatType repeat_type;
+};
+
+class CORE_EXPORT NGGridTrackList {
+ public:
+ NGGridTrackList();
+ // Returns the repeat count of the repeater at |index|, or |auto_value|
+ // if the repeater is auto.
+ wtf_size_t RepeatCount(wtf_size_t index, wtf_size_t auto_value) const;
+ // Returns the number of tracks in the repeater at |index|.
+ wtf_size_t RepeatSize(wtf_size_t index) const;
+ // Returns the repeat type of the repeater at |index|.
+ NGGridTrackRepeater::RepeatType RepeatType(wtf_size_t index) const;
+ // Returns the count of repeaters.
+ wtf_size_t RepeaterCount() const;
+ // Returns the total count of all the tracks in this list.
+ wtf_size_t TotalTrackCount() const;
+
+ // Adds a non-auto repeater.
+ bool AddRepeater(wtf_size_t track_index,
+ wtf_size_t track_count,
+ wtf_size_t repeat_count);
+ // Adds an auto repeater.
+ bool AddAutoRepeater(wtf_size_t track_index,
+ wtf_size_t track_count,
+ NGGridTrackRepeater::RepeatType repeat_type);
+ // Returns true if this list contains an auto repeater.
+ bool HasAutoRepeater();
+
+ // Clears all data.
+ void Clear();
+
+ String ToString() const;
+
+ private:
+ bool AddRepeater(wtf_size_t track_index,
+ wtf_size_t track_count,
+ wtf_size_t repeat_count,
+ NGGridTrackRepeater::RepeatType repeat_type);
+ // Returns the amount of tracks available before overflow.
+ wtf_size_t AvailableTrackCount() const;
+
+#if DCHECK_IS_ON()
+ // Helper to check if |track_index| does not cause a gap or overlap with the
+ // tracks in this list. Ensures |track_index| is equal to 1 + the last track's
+ // index.
+ bool IsTrackContiguous(wtf_size_t track_index) const;
+#endif
+
+ Vector<NGGridTrackRepeater> repeaters_;
+ // The index of the automatic repeater, if there is one; |kInvalidRangeIndex|
+ // otherwise.
+ wtf_size_t auto_repeater_index_;
+ // Total count of tracks.
+ wtf_size_t total_track_count_;
+};
+
+class CORE_EXPORT NGGridBlockTrackCollection
+ : public NGGridTrackCollectionBase {
+ public:
+ struct Range {
+ wtf_size_t starting_track_number;
+ wtf_size_t track_count;
+ wtf_size_t repeater_index;
+ wtf_size_t repeater_offset;
+ bool is_collapsed;
+ bool is_implicit_range;
+ };
+
+ // Sets the specified, implicit tracks, along with a given auto repeat value.
+ void SetSpecifiedTracks(const NGGridTrackList& specified_tracks,
+ wtf_size_t auto_repeat_count,
+ const NGGridTrackList& implicit_tracks);
+ // Ensures that after FinalizeRanges is called, a range will start at the
+ // |track_number|, and a range will end at |track_number| + |span_length|
+ void EnsureTrackCoverage(wtf_size_t track_number, wtf_size_t span_length);
+
+ // Build the collection of ranges based on information provided by
+ // SetSpecifiedTracks and EnsureTrackCoverage.
+ void FinalizeRanges();
+ // Returns the range at the given range index.
+ const Range& RangeAtRangeIndex(wtf_size_t range_index) const;
+ // Returns the range at the given track.
+ const Range& RangeAtTrackNumber(wtf_size_t track_number) const;
+
+ String ToString() const;
+
+ protected:
+ // Returns the first track number of a range.
+ wtf_size_t RangeTrackNumber(wtf_size_t range_index) const override;
+
+ // Returns the number of tracks in a range.
+ wtf_size_t RangeTrackCount(wtf_size_t range_index) const override;
+
+ // Returns true if the range at |range_index| is collapsed.
+ bool IsRangeCollapsed(wtf_size_t range_index) const override;
+
+ // Returns the number of track ranges in the collection.
+ wtf_size_t RangeCount() const override;
+
+ private:
+ // Returns true if this collection had implicit tracks provided.
+ bool HasImplicitTracks() const;
+ // Returns the repeat size of the implicit tracks.
+ wtf_size_t ImplicitRepeatSize() const;
+
+ bool track_indices_need_sort_ = false;
+ wtf_size_t auto_repeat_count_ = 0;
+
+ // Stores the specified and implicit tracks specified by SetSpecifiedTracks.
+ NGGridTrackList specified_tracks_;
+ NGGridTrackList implicit_tracks_;
+
+ // Starting and ending tracks mark where ranges will start and end.
+ // Once the ranges have been built in FinalizeRanges, these are cleared.
+ Vector<wtf_size_t> starting_tracks_;
+ Vector<wtf_size_t> ending_tracks_;
+ Vector<Range> ranges_;
+};
+
+} // namespace blink
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGGridTrackRepeater)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GRID_NG_GRID_TRACK_COLLECTION_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
new file mode 100644
index 00000000000..e194b80d014
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection_test.cc
@@ -0,0 +1,248 @@
+// 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 <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_track_collection.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+
+namespace blink {
+namespace {
+#define EXPECT_RANGE(expected_start, expected_count, iterator) \
+ EXPECT_EQ(expected_count, iterator.RepeatCount()); \
+ EXPECT_EQ(expected_start, iterator.RangeTrackStart()); \
+ EXPECT_EQ(expected_start + expected_count - 1, iterator.RangeTrackEnd()); \
+ EXPECT_FALSE(iterator.IsRangeCollapsed());
+#define EXPECT_COLLAPSED_RANGE(expected_start, expected_count, iterator) \
+ EXPECT_EQ(expected_start, iterator.RangeTrackStart()); \
+ EXPECT_EQ(expected_count, iterator.RepeatCount()); \
+ EXPECT_EQ(expected_start + expected_count - 1, iterator.RangeTrackEnd()); \
+ EXPECT_TRUE(iterator.IsRangeCollapsed());
+class NGGridTrackCollectionBaseTest : public NGGridTrackCollectionBase {
+ public:
+ struct TestTrackRange {
+ wtf_size_t track_number;
+ wtf_size_t track_count;
+ };
+ explicit NGGridTrackCollectionBaseTest(
+ const std::vector<wtf_size_t>& range_sizes) {
+ wtf_size_t track_number = 0;
+ for (wtf_size_t size : range_sizes) {
+ TestTrackRange range;
+ range.track_number = track_number;
+ range.track_count = size;
+ ranges_.push_back(range);
+ track_number += size;
+ }
+ }
+
+ protected:
+ wtf_size_t RangeTrackNumber(wtf_size_t range_index) const override {
+ return ranges_[range_index].track_number;
+ }
+ wtf_size_t RangeTrackCount(wtf_size_t range_index) const override {
+ return ranges_[range_index].track_count;
+ }
+ bool IsRangeCollapsed(wtf_size_t range_index) const override { return false; }
+
+ wtf_size_t RangeCount() const override { return ranges_.size(); }
+
+ private:
+ Vector<TestTrackRange> ranges_;
+};
+
+using NGGridTrackCollectionTest = NGLayoutTest;
+
+TEST_F(NGGridTrackCollectionTest, TestRangeIndexFromTrackNumber) {
+ // Small case.
+ NGGridTrackCollectionBaseTest track_collection({3, 10u, 5u});
+ EXPECT_EQ(0u, track_collection.RangeIndexFromTrackNumber(0u));
+ EXPECT_EQ(1u, track_collection.RangeIndexFromTrackNumber(4u));
+ EXPECT_EQ(2u, track_collection.RangeIndexFromTrackNumber(15u));
+
+ // Small case with large repeat count.
+ track_collection = NGGridTrackCollectionBaseTest({3000000u, 7u, 10u});
+ EXPECT_EQ(0u, track_collection.RangeIndexFromTrackNumber(600u));
+ EXPECT_EQ(1u, track_collection.RangeIndexFromTrackNumber(3000000u));
+ EXPECT_EQ(1u, track_collection.RangeIndexFromTrackNumber(3000004u));
+
+ // Larger case.
+ track_collection = NGGridTrackCollectionBaseTest({
+ 10u, // 0 - 9
+ 10u, // 10 - 19
+ 10u, // 20 - 29
+ 10u, // 30 - 39
+ 20u, // 40 - 59
+ 20u, // 60 - 79
+ 20u, // 80 - 99
+ 100u, // 100 - 199
+ });
+ EXPECT_EQ(0u, track_collection.RangeIndexFromTrackNumber(0u));
+ EXPECT_EQ(3u, track_collection.RangeIndexFromTrackNumber(35u));
+ EXPECT_EQ(4u, track_collection.RangeIndexFromTrackNumber(40u));
+ EXPECT_EQ(5u, track_collection.RangeIndexFromTrackNumber(79));
+ EXPECT_EQ(7u, track_collection.RangeIndexFromTrackNumber(105u));
+}
+
+TEST_F(NGGridTrackCollectionTest, TestRangeRepeatIteratorMoveNext) {
+ // [1-3] [4-13] [14 -18]
+ NGGridTrackCollectionBaseTest track_collection({3u, 10u, 5u});
+ EXPECT_EQ(0u, track_collection.RangeIndexFromTrackNumber(0u));
+
+ NGGridTrackCollectionBaseTest::RangeRepeatIterator iterator(&track_collection,
+ 0u);
+ EXPECT_RANGE(0u, 3u, iterator);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(3u, 10u, iterator);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(13u, 5u, iterator);
+
+ EXPECT_FALSE(iterator.MoveToNextRange());
+
+ NGGridTrackCollectionBaseTest empty_collection({});
+
+ NGGridTrackCollectionBaseTest::RangeRepeatIterator empty_iterator(
+ &empty_collection, 0u);
+ EXPECT_EQ(NGGridTrackCollectionBase::kInvalidRangeIndex,
+ empty_iterator.RangeTrackStart());
+ EXPECT_EQ(NGGridTrackCollectionBase::kInvalidRangeIndex,
+ empty_iterator.RangeTrackEnd());
+ EXPECT_EQ(0u, empty_iterator.RepeatCount());
+ EXPECT_FALSE(empty_iterator.MoveToNextRange());
+}
+
+TEST_F(NGGridTrackCollectionTest, TestNGGridTrackList) {
+ NGGridTrackList track_list;
+ ASSERT_EQ(0u, track_list.RepeaterCount());
+ EXPECT_FALSE(track_list.HasAutoRepeater());
+
+ EXPECT_TRUE(track_list.AddRepeater(0, 2, 4));
+ ASSERT_EQ(1u, track_list.RepeaterCount());
+ EXPECT_EQ(8u, track_list.TotalTrackCount());
+ EXPECT_EQ(4u, track_list.RepeatCount(0, 77));
+ EXPECT_EQ(2u, track_list.RepeatSize(0));
+ EXPECT_FALSE(track_list.HasAutoRepeater());
+
+ EXPECT_TRUE(track_list.AddAutoRepeater(
+ 2, 3, NGGridTrackRepeater::RepeatType::kAutoFill));
+ ASSERT_EQ(2u, track_list.RepeaterCount());
+ EXPECT_EQ(11u, track_list.TotalTrackCount());
+ EXPECT_EQ(77u, track_list.RepeatCount(1, 77));
+ EXPECT_EQ(3u, track_list.RepeatSize(1));
+ EXPECT_TRUE(track_list.HasAutoRepeater());
+
+ // Can't add more than one auto repeater to a list.
+ EXPECT_FALSE(track_list.AddAutoRepeater(
+ 5, 3, NGGridTrackRepeater::RepeatType::kAutoFill));
+
+ EXPECT_TRUE(track_list.AddRepeater(
+ 5, NGGridTrackCollectionBase::kMaxRangeIndex - 20, 1));
+ ASSERT_EQ(3u, track_list.RepeaterCount());
+ EXPECT_EQ(NGGridTrackCollectionBase::kMaxRangeIndex - 9,
+ track_list.TotalTrackCount());
+ EXPECT_EQ(1u, track_list.RepeatCount(2, 77));
+ EXPECT_EQ(NGGridTrackCollectionBase::kMaxRangeIndex - 20,
+ track_list.RepeatSize(2));
+
+ // Try to add a repeater that would overflow the total track count.
+ EXPECT_FALSE(track_list.AddRepeater(
+ NGGridTrackCollectionBase::kMaxRangeIndex - 15u, 3, 10));
+ ASSERT_EQ(3u, track_list.RepeaterCount());
+
+ // Try to add a repeater that would overflow the track size in a repeater.
+ EXPECT_FALSE(
+ track_list.AddRepeater(NGGridTrackCollectionBase::kMaxRangeIndex - 15u,
+ NGGridTrackCollectionBase::kMaxRangeIndex, 10));
+ ASSERT_EQ(3u, track_list.RepeaterCount());
+}
+
+TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollection) {
+ NGGridTrackList specified_tracks;
+ ASSERT_TRUE(specified_tracks.AddRepeater(1, 2, 4));
+ ASSERT_TRUE(specified_tracks.AddAutoRepeater(
+ 3, 3, NGGridTrackRepeater::RepeatType::kAutoFill));
+ ASSERT_EQ(2u, specified_tracks.RepeaterCount());
+ NGGridBlockTrackCollection block_collection;
+ block_collection.SetSpecifiedTracks(specified_tracks, 3, NGGridTrackList());
+ block_collection.FinalizeRanges();
+
+ NGGridTrackCollectionBase::RangeRepeatIterator iterator(&block_collection,
+ 0u);
+ EXPECT_RANGE(1u, 8u, iterator);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(9u, 9u, iterator);
+
+ EXPECT_FALSE(iterator.MoveToNextRange());
+}
+
+TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollectionCollapsed) {
+ NGGridTrackList specified_tracks;
+ ASSERT_TRUE(specified_tracks.AddRepeater(1, 2, 4));
+ ASSERT_TRUE(specified_tracks.AddAutoRepeater(
+ 3, 3, NGGridTrackRepeater::RepeatType::kAutoFit));
+ ASSERT_TRUE(specified_tracks.AddRepeater(6, 3, 7));
+ ASSERT_EQ(3u, specified_tracks.RepeaterCount());
+ NGGridBlockTrackCollection block_collection;
+ block_collection.SetSpecifiedTracks(specified_tracks, 3, NGGridTrackList());
+ block_collection.FinalizeRanges();
+
+ NGGridTrackCollectionBase::RangeRepeatIterator iterator(&block_collection,
+ 0u);
+ EXPECT_RANGE(1u, 8u, iterator);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_COLLAPSED_RANGE(9u, 9u, iterator);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(18u, 21u, iterator);
+
+ EXPECT_FALSE(iterator.MoveToNextRange());
+}
+
+TEST_F(NGGridTrackCollectionTest, TestNGGridBlockTrackCollectionImplicit) {
+ NGGridTrackList specified_tracks;
+ ASSERT_TRUE(specified_tracks.AddRepeater(1, 2, 4));
+ ASSERT_TRUE(specified_tracks.AddRepeater(3, 3, 3));
+ ASSERT_TRUE(specified_tracks.AddRepeater(6, 3, 7));
+ ASSERT_EQ(3u, specified_tracks.RepeaterCount());
+
+ NGGridTrackList implicit_tracks;
+ ASSERT_TRUE(implicit_tracks.AddRepeater(1, 8, 2));
+
+ NGGridBlockTrackCollection block_collection;
+ block_collection.SetSpecifiedTracks(specified_tracks, 3, implicit_tracks);
+ block_collection.EnsureTrackCoverage(3, 40);
+ block_collection.EnsureTrackCoverage(3, 40);
+ block_collection.FinalizeRanges();
+ NGGridTrackCollectionBase::RangeRepeatIterator iterator(&block_collection,
+ 0u);
+ EXPECT_RANGE(1u, 2u, iterator);
+ EXPECT_FALSE(block_collection.RangeAtTrackNumber(1u).is_implicit_range);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(3u, 6u, iterator);
+ EXPECT_FALSE(block_collection.RangeAtTrackNumber(4).is_implicit_range);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(9u, 9u, iterator);
+ EXPECT_FALSE(block_collection.RangeAtTrackNumber(7).is_implicit_range);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_RANGE(18u, 21u, iterator);
+ EXPECT_FALSE(block_collection.RangeAtTrackNumber(20).is_implicit_range);
+
+ EXPECT_TRUE(iterator.MoveToNextRange());
+ EXPECT_TRUE(block_collection.RangeAtTrackNumber(40).is_implicit_range);
+ EXPECT_RANGE(39u, 4u, iterator);
+
+ EXPECT_FALSE(iterator.MoveToNextRange());
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/README.md b/chromium/third_party/blink/renderer/core/layout/ng/inline/README.md
index 0ddc51ec99d..6c6a762b856 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/README.md
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/README.md
@@ -62,11 +62,12 @@ as in the following.
Inline layout is performed in the following phases:
-1. **Pre-layout** converts LayoutObject tree to a concatenated string
+1. **[Pre-layout]** converts LayoutObject tree to a concatenated string
and a list of [NGInlineItem].
-2. **Line breaking** breaks it into lines and
+2. **[Line breaking]** breaks it into lines and
produces a list of [NGInlineItemResult] for each line.
-3. **Line box construction** produces a fragment tree.
+3. **[Line box construction]** orders and positions items on a line.
+4. **[Generate fragments]** generates physical fragments.
This is similar to [CSS Text Processing Order of Operations],
but not exactly the same,
@@ -74,8 +75,8 @@ because the spec prioritizes the simple description than being accurate.
[CSS Text Processing Order of Operations]: https://drafts.csswg.org/css-text-3/#order
-### Pre-layout ###
-[Pre-layout]: #pre-layout
+### <a name="pre-layout">Pre-layout</a> ###
+[pre-layout]: #pre-layout
For inline layout there is a pre-layout pass that prepares the internal data
structures needed to perform line layout.
@@ -104,7 +105,8 @@ three separate steps or stages that are executed in order:
[text-transform]: https://drafts.csswg.org/css-text-3/#propdef-text-transform
-### Line Breaking ###
+### <a name="line-breaking">Line Breaking</a> ###
+[line breaking]: #line-breaking
[NGLineBreaker] takes a list of [NGInlineItem],
measure them, break into lines, and
@@ -134,53 +136,51 @@ This phase:
[CSS Calculating widths and margins]: https://drafts.csswg.org/css2/visudet.html#Computing_widths_and_margins
-### Line Box Construction ###
+### <a name="create-line">Line Box Construction</a> ###
+[line Box Construction]: #create-line
`NGInlineLayoutAlgorithm::CreateLine()` takes a list of [NGInlineItemResult] and
-produces [NGPhysicalLineBoxFragment] for each line.
-
-Lines are then wrapped in an anonymous [NGPhysicalBoxFragment]
-so that one [NGInlineNode] has one corresponding fragment.
+produces a list of [NGLogicalLineItem].
This phase consists of following sub-phases:
-1. Bidirectional reordering:
- Reorder the list of [NGInlineItemResult]
+1. Create a [NGLogicalLineItem] for each [NGInlineItemResult]
+ and determine the positions.
+
+ The inline size of each item was already determined by [NGLineBreaker],
+ but the inline position is recomputed
+ because [BiDi reordering](#bidi) may change them.
+
+ In block direction,
+ [NGLogicalLineItem] is placed as if the baseline is at 0.
+ This is adjusted later, possibly multiple times,
+ for [vertical-align] and the block offset of the parent inline box.
+
+ An open-tag item pushes a new stack entry of [NGInlineBoxState],
+ and a close-tag item pops a stack entry.
+ This stack is used to determine the size of the inline box,
+ for [vertical-align], and for a few other purposes.
+ Please see [Inline Box Tree] below.
+
+2. Process all pending operations in [Inline Box Tree].
+3. [Bidirectional reordering](#bidi):
+ Reorder the list of [NGLogicalLineItem]
according to [UAX#9 Reordering Resolved Levels].
See [Bidirectional text] below.
- After this point forward, the list of [NGInlineItemResult] is
+ After this point forward, the list of [NGLogicalLineItem] is
in _visual order_; which is from [line-left] to [line-right].
The block direction is still logical,
but the inline direction is physical.
+4. Applies [ellipsizing] if needed.
+5. Applies the CSS [text-align] property.
+6. Moves the baseline to the correct position
+ based on the height of the line box.
-2. Create a [NGPhysicalFragment] for each [NGInlineItemResult]
- in visual ([line-left] to [line-right]) order,
- and place them into [NGPhysicalLineBoxFragment].
-
- 1. A text item produces a [NGPhysicalTextFragment].
- 2. An open-tag item pushes a new stack entry of [NGInlineBoxState],
- and a close-tag item pops a stack entry.
- Performs operations that require the size of the inline box,
- or ancestor boxes.
- See [Inline Box Tree] below.
-
- The inline size of each item was already determined by [NGLineBreaker],
- but the inline position is recomputed
- because BiDi reordering may have changed it.
-
- In block direction, [NGPhysicalFragment] is placed
- as if the baseline is at 0.
- This is adjusted later, possibly multiple times.
- See [Inline Box Tree] and the post-process below.
-
-3. Post-process the constructed line box.
- This includes:
- 1. Process all pending operations in [Inline Box Tree].
- 2. Moves the baseline to the correct position
- based on the height of the line box.
- 3. Applies the CSS [text-align] property.
+Note: There is [a discussion](https://docs.google.com/document/d/1dxzIHl1dwBtgeKgWd2cKcog8AyydN5rduQvXthMOMD0/edit?usp=sharing)
+to merge [NGInlineItemResult] and [NGLogicalLineItem], but this hasn't been done yet.
+[ellipsizing]: https://drafts.csswg.org/css-ui-3/#overflow-ellipsis
[line-left]: https://drafts.csswg.org/css-writing-modes-3/#line-left
[line-right]: https://drafts.csswg.org/css-writing-modes-3/#line-right
[text-align]: https://drafts.csswg.org/css-text-3/#propdef-text-align
@@ -240,6 +240,20 @@ Once all children and their positions and sizes are finalized,
`NGInlineLayoutStateStack::CreateBoxFragments()`
creates [NGPhysicalBoxFragment] and add children to it.
+### <a name="generate-fragments">Generate Fragments</a> ###
+[generate fragments]: #generate-fragments
+
+When all [NGLogicalLineItem]s are ordered and positioned,
+they are converted to fragments.
+
+Without [NGFragmentItem] enabled,
+each [NGLogicalLineItem] produces a [NGPhysicalFragment],
+added to the [NGPhysicalLineBoxFragment].
+
+With [NGFragmentItem] enabled,
+each [NGLogicalLineItem] produces a [NGFragmentItem],
+added to the [NGFragmentItems] in the containing block of the inline formatting context.
+
## Miscellaneous topics ##
### Baseline ###
@@ -355,6 +369,8 @@ positions in the context. See [design doc](https://goo.gl/CJbxky) for details.
[NGBoxFragmentBuilder]: ../ng_box_fragment_builder.h
[NGConstraintSpace]: ../ng_constraint_space_builder.h
[NGConstraintSpaceBuilder]: ../ng_constraint_space_builder.h
+[NGFragmentItem]: ng_fragment_item.h
+[NGFragmentItems]: ng_fragment_items.h
[NGInlineBoxState]: ng_inline_box_state.h
[NGInlineItem]: ng_inline_item.h
[NGInlineItemResult]: ng_inline_item_result.h
@@ -362,6 +378,8 @@ positions in the context. See [design doc](https://goo.gl/CJbxky) for details.
[NGInlineLayoutAlgorithm]: ng_inline_layout_algorithm.h
[NGLayoutInputNode]: ../ng_layout_input_node.h
[NGLineBreaker]: ng_line_breaker.h
+[NGLogicalLineItem]: ng_logical_line_item.h
+[NGLogicalLineItems]: ng_logical_line_items.h
[NGOffsetMapping]: ng_offset_mapping.h
[NGPhysicalBoxFragment]: ../ng_physical_box_fragment.h
[NGPhysicalFragment]: ../ng_physical_fragment.h
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 da28e31bfcf..ed5a6e062c9 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
@@ -142,6 +142,23 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteCollapseWhiteSpaceEnd) {
GetItemsAsString(*text.GetLayoutObject()));
}
+// web_tests/external/wpt/editing/run/delete.html?993-993
+// web_tests/external/wpt/editing/run/forwarddelete.html?1193-1193
+TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteNbspInPreWrap) {
+ if (!RuntimeEnabledFeatures::LayoutNGEnabled())
+ return;
+
+ InsertStyleElement("#target { white-space:pre-wrap; }");
+ SetBodyInnerHTML(u"<p id=target>&nbsp; abc</p>");
+ Text& text = To<Text>(*GetElementById("target")->firstChild());
+ text.deleteData(0, 1, ASSERT_NO_EXCEPTION);
+
+ EXPECT_EQ(
+ "*{' ', ShapeResult=0+1}\n"
+ "*{'abc', ShapeResult=2+3}\n",
+ GetItemsAsString(*text.GetLayoutObject()));
+}
+
TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
return;
@@ -174,6 +191,38 @@ TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteRTL2) {
GetItemsAsString(*text.GetLayoutObject()));
}
+// editing/deleting/delete_ws_fixup.html
+TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteThenNonCollapse) {
+ if (!RuntimeEnabledFeatures::LayoutNGEnabled())
+ return;
+
+ SetBodyInnerHTML(u"<div id=target>abc def<b> </b>ghi</div>");
+ Text& text = To<Text>(*GetElementById("target")->firstChild());
+ text.deleteData(4, 3, ASSERT_NO_EXCEPTION); // remove "def"
+
+ EXPECT_EQ(
+ "*{'abc ', ShapeResult=0+4}\n"
+ "{''}\n"
+ "{'ghi', ShapeResult=4+3}\n",
+ GetItemsAsString(*text.GetLayoutObject()));
+}
+
+// editing/deleting/delete_ws_fixup.html
+TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteThenNonCollapse2) {
+ if (!RuntimeEnabledFeatures::LayoutNGEnabled())
+ return;
+
+ SetBodyInnerHTML(u"<div id=target>abc def<b> X </b>ghi</div>");
+ Text& text = To<Text>(*GetElementById("target")->firstChild());
+ text.deleteData(4, 3, ASSERT_NO_EXCEPTION); // remove "def"
+
+ EXPECT_EQ(
+ "*{'abc ', ShapeResult=0+4}\n"
+ "{'X ', ShapeResult=4+2}\n"
+ "{'ghi', ShapeResult=6+3}\n",
+ GetItemsAsString(*text.GetLayoutObject()));
+}
+
// http://crbug.com/1039143
TEST_F(LayoutNGTextTest, SetTextWithOffsetDeleteWithBidiControl) {
if (!RuntimeEnabledFeatures::LayoutNGEnabled())
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.cc
index 4098a6363f7..08a2a61198f 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.cc
@@ -15,17 +15,23 @@ NGBidiParagraph::~NGBidiParagraph() {
bool NGBidiParagraph::SetParagraph(const String& text,
const ComputedStyle& block_style) {
+ if (UNLIKELY(block_style.GetUnicodeBidi() == UnicodeBidi::kPlaintext))
+ return SetParagraph(text, base::nullopt);
+ return SetParagraph(text, block_style.Direction());
+}
+
+bool NGBidiParagraph::SetParagraph(
+ const String& text,
+ base::Optional<TextDirection> base_direction) {
DCHECK(!ubidi_);
ubidi_ = ubidi_open();
- bool use_heuristic_base_direction =
- block_style.GetUnicodeBidi() == UnicodeBidi::kPlaintext;
UBiDiLevel para_level;
- if (use_heuristic_base_direction) {
- para_level = UBIDI_DEFAULT_LTR;
- } else {
- base_direction_ = block_style.Direction();
+ if (base_direction) {
+ base_direction_ = *base_direction;
para_level = IsLtr(base_direction_) ? UBIDI_LTR : UBIDI_RTL;
+ } else {
+ para_level = UBIDI_DEFAULT_LTR;
}
ICUError error;
@@ -38,7 +44,7 @@ bool NGBidiParagraph::SetParagraph(const String& text,
return false;
}
- if (use_heuristic_base_direction)
+ if (!base_direction)
base_direction_ = DirectionFromLevel(ubidi_getParaLevel(ubidi_));
return true;
@@ -60,6 +66,17 @@ unsigned NGBidiParagraph::GetLogicalRun(unsigned start,
return end;
}
+void NGBidiParagraph::GetLogicalRuns(const String& text, Runs* runs) const {
+ DCHECK(runs->IsEmpty());
+ for (unsigned start = 0; start < text.length();) {
+ UBiDiLevel level;
+ unsigned end = GetLogicalRun(start, &level);
+ DCHECK_GT(end, start);
+ runs->emplace_back(start, end, level);
+ start = end;
+ }
+}
+
void NGBidiParagraph::IndicesInVisualOrder(
const Vector<UBiDiLevel, 32>& levels,
Vector<int32_t, 32>* indices_in_visual_order_out) {
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h
index dc53a487dfa..fc40c73b1df 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h
@@ -5,6 +5,8 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_BIDI_PARAGRAPH_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_BIDI_PARAGRAPH_H_
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -23,7 +25,7 @@ class ComputedStyle;
// UAX#9 and create logical runs.
// http://unicode.org/reports/tr9/
// It can also create visual runs once lines breaks are determined.
-class NGBidiParagraph {
+class CORE_EXPORT NGBidiParagraph {
STACK_ALLOCATED();
public:
@@ -35,6 +37,8 @@ class NGBidiParagraph {
// Returns false on failure. Nothing other than the destructor should be
// called.
bool SetParagraph(const String&, const ComputedStyle&);
+ bool SetParagraph(const String&,
+ base::Optional<TextDirection> base_direction);
// @return the entire text is unidirectional.
bool IsUnidirectional() const {
@@ -52,6 +56,30 @@ class NGBidiParagraph {
// http://unicode.org/reports/tr9/#The_Paragraph_Level
static TextDirection BaseDirectionForString(const StringView&);
+ struct Run {
+ Run(unsigned start, unsigned end, UBiDiLevel level)
+ : start(start), end(end), level(level) {
+ DCHECK_GT(end, start);
+ }
+
+ unsigned Length() const { return end - start; }
+ TextDirection Direction() const { return DirectionFromLevel(level); }
+
+ bool operator==(const Run& other) const {
+ return start == other.start && end == other.end && level == other.level;
+ }
+
+ unsigned start;
+ unsigned end;
+ UBiDiLevel level;
+ };
+ using Runs = Vector<Run, 32>;
+
+ // Get a list of |Run| in the logical order (before bidi reorder.)
+ // |text| must be the same one as |SetParagraph|.
+ // This is higher-level API for |GetLogicalRun|.
+ void GetLogicalRuns(const String& text, Runs* runs) const;
+
// Returns the end offset of a logical run that starts from the |start|
// offset.
unsigned GetLogicalRun(unsigned start, UBiDiLevel*) const;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph_test.cc
new file mode 100644
index 00000000000..15e6591055e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph_test.cc
@@ -0,0 +1,40 @@
+// 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/inline/ng_bidi_paragraph.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+using testing::ElementsAre;
+
+TEST(NGBidiParagraph, SetParagraphHeuristicLtr) {
+ String text(u"abc");
+ NGBidiParagraph bidi;
+ bidi.SetParagraph(text, base::nullopt);
+ EXPECT_EQ(bidi.BaseDirection(), TextDirection::kLtr);
+}
+
+TEST(NGBidiParagraph, SetParagraphHeuristicRtl) {
+ String text(u"\u05D0\u05D1\u05D2");
+ NGBidiParagraph bidi;
+ bidi.SetParagraph(text, base::nullopt);
+ EXPECT_EQ(bidi.BaseDirection(), TextDirection::kRtl);
+}
+
+TEST(NGBidiParagraph, GetLogicalRuns) {
+ String text(u"\u05D0\u05D1\u05D2 abc \u05D3\u05D4\u05D5");
+ NGBidiParagraph bidi;
+ bidi.SetParagraph(text, TextDirection::kRtl);
+ NGBidiParagraph::Runs runs;
+ bidi.GetLogicalRuns(text, &runs);
+ EXPECT_THAT(runs, ElementsAre(NGBidiParagraph::Run(0, 4, 1),
+ NGBidiParagraph::Run(4, 7, 2),
+ NGBidiParagraph::Run(7, 11, 1)));
+}
+
+} // 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 d5075dba7d6..5867968d822 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
@@ -299,7 +299,12 @@ NGCaretPosition ComputeNGCaretPosition(const PositionWithAffinity& position) {
return NGCaretPosition();
const NGOffsetMapping* mapping = NGInlineNode::GetOffsetMapping(context);
- DCHECK(mapping);
+ if (!mapping) {
+ // TODO(yosin): We should find when we reach here[1].
+ // [1] http://crbug.com/1100481
+ NOTREACHED() << context;
+ return NGCaretPosition();
+ }
const base::Optional<unsigned> maybe_offset =
mapping->GetTextContentOffset(position.GetPosition());
if (!maybe_offset.has_value()) {
@@ -322,17 +327,17 @@ PositionWithAffinity NGCaretPosition::ToPositionInDOMTreeWithAffinity() const {
return PositionWithAffinity();
switch (position_type) {
case NGCaretPositionType::kBeforeBox:
- if (cursor.Current().GetNode())
- return PositionWithAffinity();
- return PositionWithAffinity(
- Position::BeforeNode(*cursor.Current().GetNode()),
- TextAffinity::kDownstream);
+ if (const Node* node = cursor.Current().GetNode()) {
+ return PositionWithAffinity(Position::BeforeNode(*node),
+ TextAffinity::kDownstream);
+ }
+ return PositionWithAffinity();
case NGCaretPositionType::kAfterBox:
- if (cursor.Current().GetNode())
- return PositionWithAffinity();
- return PositionWithAffinity(
- Position::AfterNode(*cursor.Current().GetNode()),
- TextAffinity::kUpstreamIfPossible);
+ if (const Node* node = cursor.Current().GetNode()) {
+ return PositionWithAffinity(Position::AfterNode(*node),
+ TextAffinity::kUpstreamIfPossible);
+ }
+ return PositionWithAffinity();
case NGCaretPositionType::kAtTextOffset:
// In case of ::first-letter, |cursor.Current().GetNode()| is null.
DCHECK(text_offset.has_value());
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 bccd83f3551..6bef0e6f0f6 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
@@ -52,6 +52,26 @@ PhysicalRect ComputeLocalCaretRectByBoxSide(const NGInlineCursor& cursor,
return PhysicalRect(caret_location, caret_size);
}
+bool ShouldAlignCaretRight(ETextAlign text_align, TextDirection direction) {
+ switch (text_align) {
+ case ETextAlign::kRight:
+ case ETextAlign::kWebkitRight:
+ return true;
+ case ETextAlign::kLeft:
+ case ETextAlign::kWebkitLeft:
+ case ETextAlign::kCenter:
+ case ETextAlign::kWebkitCenter:
+ return false;
+ case ETextAlign::kJustify:
+ case ETextAlign::kStart:
+ return IsRtl(direction);
+ case ETextAlign::kEnd:
+ return IsLtr(direction);
+ }
+ NOTREACHED();
+ return false;
+}
+
PhysicalRect ComputeLocalCaretRectAtTextOffset(const NGInlineCursor& cursor,
unsigned offset) {
DCHECK(cursor.Current().IsText());
@@ -62,7 +82,8 @@ PhysicalRect ComputeLocalCaretRectAtTextOffset(const NGInlineCursor& cursor,
cursor.Current().GetLayoutObject()->GetDocument().View();
LayoutUnit caret_width = frame_view->CaretWidth();
- const bool is_horizontal = cursor.Current().Style().IsHorizontalWritingMode();
+ const ComputedStyle& style = cursor.Current().Style();
+ const bool is_horizontal = style.IsHorizontalWritingMode();
LayoutUnit caret_height = is_horizontal ? cursor.Current().Size().height
: cursor.Current().Size().width;
@@ -90,15 +111,33 @@ PhysicalRect ComputeLocalCaretRectAtTextOffset(const NGInlineCursor& cursor,
line_box.Current().OffsetInContainerBlock();
const PhysicalRect line_box_rect(line_box_offset, line_box.Current().Size());
+ const NGInlineBreakToken& break_token =
+ *line_box.Current().InlineBreakToken();
+ const bool is_last_line =
+ break_token.IsFinished() || break_token.IsForcedBreak();
+ const ComputedStyle& block_style = fragmentainer.Style();
+ bool should_align_caret_right =
+ ShouldAlignCaretRight(block_style.GetTextAlign(is_last_line),
+ block_style.Direction()) &&
+ (style.GetUnicodeBidi() != UnicodeBidi::kPlaintext ||
+ IsLtr(cursor.Current().ResolvedDirection()));
+
// 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
// then round it to the nearest pixel.
if (is_horizontal) {
- const LayoutUnit min_x = std::min(LayoutUnit(), line_box_offset.left);
- caret_location.left = std::max(caret_location.left, min_x);
- const LayoutUnit max_x =
- std::max(fragmentainer.Size().width, line_box_rect.Right());
- caret_location.left = std::min(caret_location.left, max_x - caret_width);
+ if (should_align_caret_right) {
+ const LayoutUnit left_edge = std::min(LayoutUnit(), line_box_rect.X());
+ caret_location.left = std::max(caret_location.left, left_edge);
+ caret_location.left =
+ std::min(caret_location.left, line_box_rect.Right() - caret_width);
+ } else {
+ const LayoutUnit right_edge =
+ std::max(fragmentainer.Size().width, line_box_rect.Right());
+ caret_location.left =
+ std::min(caret_location.left, right_edge - caret_width);
+ caret_location.left = std::max(caret_location.left, line_box_rect.X());
+ }
caret_location.left = LayoutUnit(caret_location.left.Round());
return PhysicalRect(caret_location, caret_size);
}
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 4aedb519e64..56af2e5c74d 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
@@ -15,6 +15,23 @@
namespace blink {
+namespace {
+
+struct SameSizeAsNGFragmentItem {
+ struct {
+ void* pointer;
+ NGTextOffset text_offset;
+ } type_data;
+ PhysicalRect rect;
+ void* pointers[2];
+ wtf_size_t sizes[2];
+ unsigned flags;
+};
+
+static_assert(sizeof(NGFragmentItem) == sizeof(SameSizeAsNGFragmentItem),
+ "NGFragmentItem should stay small");
+} // namespace
+
NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text)
: layout_object_(text.GetLayoutObject()),
text_({text.TextShapeResult(), text.TextOffset()}),
@@ -26,7 +43,6 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text)
text_direction_(static_cast<unsigned>(text.ResolvedDirection())),
ink_overflow_computed_(false),
is_dirty_(false),
- is_first_for_node_(true),
is_last_for_node_(true) {
#if DCHECK_IS_ON()
if (text_.shape_result) {
@@ -44,19 +60,22 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text)
DCHECK(!IsFormattingContextRoot());
}
-NGFragmentItem::NGFragmentItem(NGInlineItemResult&& item_result,
- const PhysicalSize& size)
- : layout_object_(item_result.item->GetLayoutObject()),
- text_({std::move(item_result.shape_result), item_result.TextOffset()}),
+NGFragmentItem::NGFragmentItem(
+ const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset,
+ const PhysicalSize& size,
+ bool is_hidden_for_paint)
+ : layout_object_(inline_item.GetLayoutObject()),
+ text_({std::move(shape_result), text_offset}),
rect_({PhysicalOffset(), size}),
type_(kText),
- sub_type_(static_cast<unsigned>(item_result.item->TextType())),
- style_variant_(static_cast<unsigned>(item_result.item->StyleVariant())),
- is_hidden_for_paint_(false), // TODO(kojii): not supported yet.
- text_direction_(static_cast<unsigned>(item_result.item->Direction())),
+ sub_type_(static_cast<unsigned>(inline_item.TextType())),
+ style_variant_(static_cast<unsigned>(inline_item.StyleVariant())),
+ is_hidden_for_paint_(is_hidden_for_paint),
+ text_direction_(static_cast<unsigned>(inline_item.Direction())),
ink_overflow_computed_(false),
is_dirty_(false),
- is_first_for_node_(true),
is_last_for_node_(true) {
#if DCHECK_IS_ON()
if (text_.shape_result) {
@@ -64,15 +83,40 @@ NGFragmentItem::NGFragmentItem(NGInlineItemResult&& item_result,
DCHECK_EQ(text_.shape_result->EndIndex(), EndOffset());
}
#endif
- // TODO(kojii): Generated text not supported yet.
DCHECK_NE(TextType(), NGTextType::kLayoutGenerated);
DCHECK(!IsFormattingContextRoot());
}
-NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line,
- wtf_size_t item_count)
+NGFragmentItem::NGFragmentItem(
+ const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const String& text_content,
+ const PhysicalSize& size,
+ bool is_hidden_for_paint)
+ : layout_object_(inline_item.GetLayoutObject()),
+ generated_text_({std::move(shape_result), text_content}),
+ rect_({PhysicalOffset(), size}),
+ type_(kGeneratedText),
+ sub_type_(static_cast<unsigned>(inline_item.TextType())),
+ style_variant_(static_cast<unsigned>(inline_item.StyleVariant())),
+ is_hidden_for_paint_(is_hidden_for_paint),
+ text_direction_(static_cast<unsigned>(inline_item.Direction())),
+ ink_overflow_computed_(false),
+ is_dirty_(false),
+ is_last_for_node_(true) {
+#if DCHECK_IS_ON()
+ if (text_.shape_result) {
+ DCHECK_EQ(text_.shape_result->StartIndex(), StartOffset());
+ DCHECK_EQ(text_.shape_result->EndIndex(), EndOffset());
+ }
+#endif
+ DCHECK_EQ(TextType(), NGTextType::kLayoutGenerated);
+ DCHECK(!IsFormattingContextRoot());
+}
+
+NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line)
: layout_object_(line.ContainerLayoutObject()),
- line_({&line, item_count}),
+ line_({&line, /* descendants_count */ 1}),
rect_({PhysicalOffset(), line.Size()}),
type_(kLine),
sub_type_(static_cast<unsigned>(line.LineBoxType())),
@@ -81,7 +125,6 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line,
text_direction_(static_cast<unsigned>(line.BaseDirection())),
ink_overflow_computed_(false),
is_dirty_(false),
- is_first_for_node_(true),
is_last_for_node_(true) {
DCHECK(!IsFormattingContextRoot());
}
@@ -89,7 +132,7 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalLineBoxFragment& line,
NGFragmentItem::NGFragmentItem(const NGPhysicalBoxFragment& box,
TextDirection resolved_direction)
: layout_object_(box.GetLayoutObject()),
- box_({&box, 1}),
+ box_({&box, /* descendants_count */ 1}),
rect_({PhysicalOffset(), box.Size()}),
type_(kBox),
style_variant_(static_cast<unsigned>(box.StyleVariant())),
@@ -97,65 +140,83 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalBoxFragment& box,
text_direction_(static_cast<unsigned>(resolved_direction)),
ink_overflow_computed_(false),
is_dirty_(false),
- is_first_for_node_(true),
is_last_for_node_(true) {
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_dirty_(false),
- is_first_for_node_(true),
- is_last_for_node_(true) {
- DCHECK_EQ(inline_item.Type(), NGInlineItem::kOpenTag);
- DCHECK(layout_object_);
- DCHECK(layout_object_->IsLayoutInline());
- DCHECK(!IsFormattingContextRoot());
-}
+NGFragmentItem::NGFragmentItem(NGLogicalLineItem&& line_item,
+ WritingMode writing_mode) {
+ DCHECK(line_item.CanCreateFragmentItem());
-// static
-void NGFragmentItem::Create(NGLineBoxFragmentBuilder::ChildList* child_list,
- const String& text_content,
- WritingMode writing_mode) {
- for (auto& child : *child_list) {
- DCHECK(!child.fragment_item);
+ if (line_item.fragment) {
+ new (this) NGFragmentItem(*line_item.fragment);
+ return;
+ }
- if (child.fragment) {
- child.fragment_item = base::AdoptRef(new NGFragmentItem(*child.fragment));
- continue;
+ if (line_item.inline_item) {
+ if (UNLIKELY(line_item.text_content)) {
+ new (this) NGFragmentItem(
+ *line_item.inline_item, std::move(line_item.shape_result),
+ line_item.text_content,
+ ToPhysicalSize(line_item.MarginSize(), writing_mode),
+ line_item.is_hidden_for_paint);
+ return;
}
- if (child.item_result) {
- child.fragment_item = base::AdoptRef(new NGFragmentItem(
- std::move(*child.item_result),
- ToPhysicalSize({child.inline_size, child.rect.size.block_size},
- writing_mode)));
- continue;
- }
+ new (this)
+ NGFragmentItem(*line_item.inline_item,
+ std::move(line_item.shape_result), line_item.text_offset,
+ ToPhysicalSize(line_item.MarginSize(), writing_mode),
+ line_item.is_hidden_for_paint);
+ return;
+ }
- if (child.layout_result) {
- const NGPhysicalBoxFragment& fragment =
- To<NGPhysicalBoxFragment>(child.layout_result->PhysicalFragment());
- child.fragment_item = base::AdoptRef(
- new NGFragmentItem(fragment, child.ResolvedDirection()));
- continue;
- }
+ if (line_item.layout_result) {
+ const NGPhysicalBoxFragment& box_fragment =
+ To<NGPhysicalBoxFragment>(line_item.layout_result->PhysicalFragment());
+ new (this) NGFragmentItem(box_fragment, line_item.ResolvedDirection());
+ return;
+ }
- if (child.inline_item) {
- child.fragment_item = base::AdoptRef(new NGFragmentItem(
- *child.inline_item,
- ToPhysicalSize(child.rect.size,
- child.inline_item->Style()->GetWritingMode())));
- }
+ // CanCreateFragmentItem()
+ NOTREACHED();
+ CHECK(false);
+}
+
+NGFragmentItem::NGFragmentItem(const NGFragmentItem& source)
+ : layout_object_(source.layout_object_),
+ rect_(source.rect_),
+ fragment_id_(source.fragment_id_),
+ delta_to_next_for_same_layout_object_(
+ source.delta_to_next_for_same_layout_object_),
+ type_(source.type_),
+ sub_type_(source.sub_type_),
+ style_variant_(source.style_variant_),
+ is_hidden_for_paint_(source.is_hidden_for_paint_),
+ text_direction_(source.text_direction_),
+ ink_overflow_computed_(source.ink_overflow_computed_),
+ is_dirty_(source.is_dirty_),
+ is_last_for_node_(source.is_last_for_node_) {
+ switch (Type()) {
+ case kText:
+ new (&text_) TextItem(source.text_);
+ break;
+ case kGeneratedText:
+ new (&generated_text_) GeneratedTextItem(source.generated_text_);
+ break;
+ case kLine:
+ new (&line_) LineItem(source.line_);
+ break;
+ case kBox:
+ new (&box_) BoxItem(source.box_);
+ break;
}
+
+ // Copy |ink_overflow_| only for text items, because ink overflow for other
+ // items may be chnaged even in simplified layout or when reusing lines, and
+ // that they need to be re-computed anyway.
+ if (source.ink_overflow_ && ink_overflow_computed_ && IsText())
+ ink_overflow_ = std::make_unique<NGInkOverflow>(*source.ink_overflow_);
}
NGFragmentItem::~NGFragmentItem() {
@@ -179,8 +240,7 @@ bool NGFragmentItem::IsInlineBox() const {
if (Type() == kBox) {
if (const NGPhysicalBoxFragment* box = BoxFragment())
return box->IsInlineBox();
- DCHECK(GetLayoutObject()->IsLayoutInline());
- return true;
+ NOTREACHED();
}
return false;
}
@@ -336,7 +396,7 @@ TextDirection NGFragmentItem::ResolvedDirection() const {
return static_cast<TextDirection>(text_direction_);
}
-String NGFragmentItem::DebugName() const {
+String NGFragmentItem::ToString() 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|.
@@ -368,20 +428,6 @@ String NGFragmentItem::DebugName() const {
return "NGFragmentItem";
}
-IntRect NGFragmentItem::VisualRect() const {
- // TODO(kojii): Need to reconsider the storage of |VisualRect|, to integrate
- // better with |FragmentData| and to avoid dependency to |LayoutObject|.
- DCHECK(GetLayoutObject());
- 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());
@@ -427,6 +473,14 @@ void NGFragmentItem::RecalcInkOverflow(
PhysicalRect* self_and_contents_rect_out) {
DCHECK_EQ(this, cursor->CurrentItem());
+ if (UNLIKELY(IsLayoutObjectDestroyedOrMoved())) {
+ // TODO(crbug.com/1099613): This should not happen, as long as it is really
+ // layout-clean. It looks like there are cases where the layout is dirty.
+ NOTREACHED();
+ cursor->MoveToNextSkippingChildren();
+ return;
+ }
+
if (IsText()) {
cursor->MoveToNext();
@@ -482,7 +536,7 @@ void NGFragmentItem::RecalcInkOverflow(
DCHECK(box_fragment->IsInlineBox());
self_rect = box_fragment->ComputeSelfInkOverflow();
} else {
- self_rect = LocalRect();
+ NOTREACHED();
}
*self_and_contents_rect_out = UnionRect(self_rect, contents_rect);
} else {
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 703564f3ebc..7823ff0bcef 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
@@ -21,16 +21,15 @@ namespace blink {
class NGFragmentItems;
class NGInlineBreakToken;
-class NGInlineItem;
class NGPhysicalTextFragment;
struct NGTextFragmentPaintInfo;
+struct NGLogicalLineItem;
// This class represents a text run or a box in an inline formatting context.
//
// This class consumes less memory than a full fragment, and can be stored in a
// flat list (NGFragmentItems) for easier and faster traversal.
-class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
- public DisplayItemClient {
+class CORE_EXPORT NGFragmentItem {
public:
// Represents regular text that exists in the DOM.
struct TextItem {
@@ -66,23 +65,21 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
enum ItemType { kText, kGeneratedText, kLine, kBox };
+ // Create appropriate type for |line_item|.
+ NGFragmentItem(NGLogicalLineItem&& line_item, WritingMode writing_mode);
// Create a text item.
// TODO(kojii): Should be able to create without once creating fragments.
explicit NGFragmentItem(const NGPhysicalTextFragment& text);
// Create a box item.
NGFragmentItem(const NGPhysicalBoxFragment& box,
TextDirection resolved_direction);
- // Create a culled box item.
- NGFragmentItem(const NGInlineItem& inline_item, const PhysicalSize& size);
// Create a line item.
- NGFragmentItem(const NGPhysicalLineBoxFragment& line, wtf_size_t item_count);
+ explicit NGFragmentItem(const NGPhysicalLineBoxFragment& line);
- // Create |NGFragmentItem| for all items in |child_list|.
- static void Create(NGLineBoxFragmentBuilder::ChildList* child_list,
- const String& text_content,
- WritingMode writing_mode);
+ // The copy constructor.
+ NGFragmentItem(const NGFragmentItem&);
- ~NGFragmentItem() final;
+ ~NGFragmentItem();
ItemType Type() const { return static_cast<ItemType>(type_); }
@@ -95,19 +92,33 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
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);
- return is_first_for_node_;
+ // A sequence number of fragments generated from a |LayoutObject|.
+ // For line boxes, please see |kInitialLineFragmentId|.
+ wtf_size_t FragmentId() const {
+ DCHECK_NE(Type(), kLine);
+ return fragment_id_;
}
+ void SetFragmentId(wtf_size_t id) const {
+ DCHECK_NE(Type(), kLine);
+ fragment_id_ = id;
+ }
+ // The initial framgent_id for line boxes.
+ // TODO(kojii): This is to avoid conflict with multicol because line boxes use
+ // its |LayoutBlockFlow| as their |DisplayItemClient|, but multicol also uses
+ // fragment id for |LayoutBlockFlow| today. The plan is to make |FragmentData|
+ // a |DisplayItemClient| instead.
+ // TODO(kojii): The fragment id for line boxes must be unique across NG block
+ // fragmentation. This is not implemented yet.
+ static constexpr wtf_size_t kInitialLineFragmentId = 0x80000000;
+
+ // Return true if this is the first fragment generated from a node.
+ bool IsFirstForNode() const { return !FragmentId(); }
// Return true if this is the last fragment generated from a node.
bool IsLastForNode() const {
DCHECK(Type() != kLine);
return is_last_for_node_;
}
-
- void SetIsFirstForNode(bool is_first) const { is_first_for_node_ = is_first; }
void SetIsLastForNode(bool is_last) const { is_last_for_node_ = is_last; }
NGStyleVariant StyleVariant() const {
@@ -129,11 +140,17 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
LayoutObject* GetMutableLayoutObject() const {
return const_cast<LayoutObject*>(layout_object_);
}
+ bool IsLayoutObjectDestroyedOrMoved() const { return !layout_object_; }
void LayoutObjectWillBeDestroyed() const;
void LayoutObjectWillBeMoved() const;
Node* GetNode() const { return layout_object_->GetNode(); }
Node* NodeForHitTest() const { return layout_object_->NodeForHitTest(); }
+ // Use |LayoutObject|+|FragmentId()| for |DisplayItem::Id|.
+ const DisplayItemClient* GetDisplayItemClient() const {
+ return GetLayoutObject();
+ }
+
wtf_size_t DeltaToNextForSameLayoutObject() const {
return delta_to_next_for_same_layout_object_;
}
@@ -161,8 +178,15 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
}
bool HasChildren() const { return DescendantsCount() > 1; }
void SetDescendantsCount(wtf_size_t count) {
- CHECK_EQ(Type(), kBox);
- box_.descendants_count = count;
+ if (Type() == kBox) {
+ box_.descendants_count = count;
+ return;
+ }
+ if (Type() == kLine) {
+ line_.descendants_count = count;
+ return;
+ }
+ NOTREACHED();
}
// Returns |NGPhysicalBoxFragment| if one is associated with this item.
@@ -202,11 +226,6 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
return NGLineBoxType::kNormalLineBox;
}
- // DisplayItemClient overrides
- String DebugName() const override;
- IntRect VisualRect() const override;
- IntRect PartialInvalidationVisualRect() const override;
-
static PhysicalRect LocalVisualRectFor(const LayoutObject& layout_object);
// Re-compute the ink overflow for the |cursor| until its end.
@@ -347,9 +366,24 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
// Returns true if this item is reusable.
bool CanReuse() const;
+ const NGFragmentItem* operator->() const { return this; }
+
+ // Get a description of |this| for the debug purposes.
+ String ToString() const;
+
private:
// Create a text item.
- NGFragmentItem(NGInlineItemResult&& item_result, const PhysicalSize& size);
+ NGFragmentItem(const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset,
+ const PhysicalSize& size,
+ bool is_hidden_for_paint);
+ // Create a generated text item.
+ NGFragmentItem(const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const String& text_content,
+ const PhysicalSize& size,
+ bool is_hidden_for_paint);
const LayoutBox* InkOverflowOwnerBox() const;
LayoutBox* MutableInkOverflowOwnerBox();
@@ -375,6 +409,8 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
std::unique_ptr<NGInkOverflow> ink_overflow_;
+ mutable wtf_size_t fragment_id_ = 0;
+
// Item index delta to the next item for the same |LayoutObject|.
mutable wtf_size_t delta_to_next_for_same_layout_object_ = 0;
@@ -392,7 +428,6 @@ class CORE_EXPORT NGFragmentItem : public RefCounted<NGFragmentItem>,
mutable unsigned is_dirty_ : 1;
- mutable unsigned is_first_for_node_ : 1;
mutable unsigned is_last_for_node_ : 1;
};
@@ -405,6 +440,9 @@ inline bool NGFragmentItem::CanReuse() const {
return false;
}
+CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGFragmentItem*);
+CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGFragmentItem&);
+
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEM_H_
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 59d6f53c852..cfe1900729d 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
@@ -12,14 +12,6 @@
namespace blink {
-namespace {
-
-inline bool ShouldSetFirstAndLastForNode() {
- return RuntimeEnabledFeatures::LayoutNGFragmentTraversalEnabled();
-}
-
-} // namespace
-
NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder)
: text_content_(std::move(builder->text_content_)),
first_line_text_content_(std::move(builder->first_line_text_content_)),
@@ -28,16 +20,13 @@ NGFragmentItems::NGFragmentItems(NGFragmentItemsBuilder* builder)
for (unsigned i = 0; i < size_; ++i) {
// Call the move constructor to move without |AddRef|. Items in
// |NGFragmentItemsBuilder| are not used after |this| was constructed.
- DCHECK(source_items[i].item);
- new (&items_[i])
- scoped_refptr<const NGFragmentItem>(std::move(source_items[i].item));
- DCHECK(!source_items[i].item); // Ensure the source was moved.
+ new (&items_[i]) NGFragmentItem(std::move(source_items[i].item));
}
}
NGFragmentItems::~NGFragmentItems() {
for (unsigned i = 0; i < size_; ++i)
- items_[i]->Release();
+ items_[i].~NGFragmentItem();
}
bool NGFragmentItems::IsSubSpan(const Span& span) const {
@@ -47,63 +36,66 @@ bool NGFragmentItems::IsSubSpan(const Span& span) const {
void NGFragmentItems::FinalizeAfterLayout(
const Vector<scoped_refptr<const NGLayoutResult>, 1>& results) {
- HashMap<const LayoutObject*, const NGFragmentItem*> first_and_last;
+ struct LastItem {
+ const NGFragmentItem* item;
+ wtf_size_t fragment_id;
+ wtf_size_t item_index;
+ };
+ HashMap<const LayoutObject*, LastItem> last_items;
for (const auto& result : results) {
const auto& fragment =
To<NGPhysicalBoxFragment>(result->PhysicalFragment());
const NGFragmentItems* current = fragment.Items();
if (UNLIKELY(!current))
continue;
- HashMap<const LayoutObject*, wtf_size_t> last_fragment_map;
+
+ // TODO(layout-dev): Make this work for multiple box fragments (block
+ // fragmentation).
+ const bool create_index_cache = fragment.IsFirstForNode();
+
const Span items = current->Items();
wtf_size_t index = 0;
- for (const scoped_refptr<const NGFragmentItem>& item : items) {
+ for (const NGFragmentItem& item : items) {
++index;
- if (item->Type() == NGFragmentItem::kLine) {
- DCHECK_EQ(item->DeltaToNextForSameLayoutObject(), 0u);
- continue;
- }
- LayoutObject* const layout_object = item->GetMutableLayoutObject();
- if (UNLIKELY(layout_object->IsFloating())) {
- DCHECK_EQ(item->DeltaToNextForSameLayoutObject(), 0u);
+ if (item.Type() == NGFragmentItem::kLine) {
+ DCHECK_EQ(item.DeltaToNextForSameLayoutObject(), 0u);
continue;
}
+ LayoutObject* const layout_object = item.GetMutableLayoutObject();
DCHECK(!layout_object->IsOutOfFlowPositioned());
- DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << *item;
- item->SetDeltaToNextForSameLayoutObject(0);
-
- if (ShouldSetFirstAndLastForNode()) {
- bool is_first_for_node =
- first_and_last.Set(layout_object, item.get()).is_new_entry;
- item->SetIsFirstForNode(is_first_for_node);
- item->SetIsLastForNode(false);
- }
-
- // TODO(layout-dev): Make this work for multiple box fragments (block
- // fragmentation).
- if (!fragment.IsFirstForNode())
+ DCHECK(layout_object->IsInLayoutNGInlineFormattingContext());
+
+ item.SetDeltaToNextForSameLayoutObject(0);
+ item.SetIsLastForNode(false);
+
+ const auto last_item_result =
+ last_items.insert(layout_object, LastItem{&item, 0, index});
+ if (last_item_result.is_new_entry) {
+ item.SetFragmentId(0);
+ if (create_index_cache) {
+ DCHECK_EQ(layout_object->FirstInlineFragmentItemIndex(), 0u);
+ layout_object->SetFirstInlineFragmentItemIndex(index);
+ }
continue;
+ }
- auto insert_result = last_fragment_map.insert(layout_object, index);
- if (insert_result.is_new_entry) {
- DCHECK_EQ(layout_object->FirstInlineFragmentItemIndex(), 0u);
- layout_object->SetFirstInlineFragmentItemIndex(index);
- continue;
+ LastItem* last = &last_item_result.stored_value->value;
+ const NGFragmentItem* last_item = last->item;
+ DCHECK_EQ(last_item->DeltaToNextForSameLayoutObject(), 0u);
+ if (create_index_cache) {
+ const wtf_size_t last_index = last->item_index;
+ DCHECK_GT(last_index, 0u);
+ DCHECK_LT(last_index, items.size());
+ DCHECK_LT(last_index, index);
+ last_item->SetDeltaToNextForSameLayoutObject(index - last_index);
}
- 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);
- DCHECK_EQ(items[last_index - 1]->DeltaToNextForSameLayoutObject(), 0u);
- items[last_index - 1]->SetDeltaToNextForSameLayoutObject(index -
- last_index);
+ item.SetFragmentId(++last->fragment_id);
+ last->item = &item;
+ last->item_index = index;
}
}
- if (!ShouldSetFirstAndLastForNode())
- return;
- for (const auto& iter : first_and_last)
- iter.value->SetIsLastForNode(true);
+ for (const auto& iter : last_items)
+ iter.value.item->SetIsLastForNode(true);
}
void NGFragmentItems::ClearAssociatedFragments(LayoutObject* container) {
@@ -113,7 +105,7 @@ void NGFragmentItems::ClearAssociatedFragments(LayoutObject* container) {
for (LayoutObject* child = container->SlowFirstChild(); child;
child = child->NextSibling()) {
if (UNLIKELY(!child->IsInLayoutNGInlineFormattingContext() ||
- child->IsFloatingOrOutOfFlowPositioned()))
+ child->IsOutOfFlowPositioned()))
continue;
child->ClearFirstInlineFragmentItemIndex();
@@ -183,7 +175,7 @@ bool NGFragmentItems::TryDirtyFirstLineFor(
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
DCHECK(!layout_object.IsFloatingOrOutOfFlowPositioned());
if (wtf_size_t index = layout_object.FirstInlineFragmentItemIndex()) {
- const NGFragmentItem& item = *Items()[index - 1];
+ const NGFragmentItem& item = Items()[index - 1];
DCHECK_EQ(&layout_object, item.GetLayoutObject());
item.SetDirty();
return true;
@@ -251,32 +243,18 @@ void NGFragmentItems::DirtyLinesFromChangedChild(
void NGFragmentItems::DirtyLinesFromNeedsLayout(
const LayoutBlockFlow* container) const {
DCHECK_EQ(this, container->FragmentItems());
- for (LayoutObject* layout_object = container->FirstChild(); layout_object;) {
- if (layout_object->IsText()) {
- if (layout_object->SelfNeedsLayout()) {
- DirtyLinesFromChangedChild(layout_object);
- return;
- }
- } else if (auto* layout_inline = ToLayoutInlineOrNull(layout_object)) {
- if (layout_object->SelfNeedsLayout()) {
- DirtyLinesFromChangedChild(layout_object);
- return;
- }
- if (layout_object->NormalChildNeedsLayout() ||
- layout_object->PosChildNeedsLayout()) {
- if (LayoutObject* child = layout_inline->FirstChild()) {
- layout_object = child;
- continue;
- }
- }
- } else {
- if (layout_object->NeedsLayout()) {
- DirtyLinesFromChangedChild(layout_object);
- return;
- }
+ // Mark dirty for the first top-level child that has |NeedsLayout|.
+ //
+ // TODO(kojii): We could mark first descendant to increase reuse
+ // opportunities. Doing this complicates the logic, especially when culled
+ // inline is involved, and common case is to append to large IFC. Choose
+ // simpler logic and faster to check over more reuse opportunities.
+ for (LayoutObject* child = container->FirstChild(); child;
+ child = child->NextSibling()) {
+ if (child->NeedsLayout()) {
+ DirtyLinesFromChangedChild(child);
+ return;
}
-
- layout_object = layout_object->NextInPreOrderAfterChildren(container);
}
}
@@ -292,8 +270,8 @@ void NGFragmentItems::LayoutObjectWillBeMoved(
*container.GetPhysicalFragment(idx);
DCHECK(fragment.Items());
for (const auto& item : fragment.Items()->Items()) {
- if (item->GetLayoutObject() == &layout_object)
- item->LayoutObjectWillBeMoved();
+ if (item.GetLayoutObject() == &layout_object)
+ item.LayoutObjectWillBeMoved();
}
}
return;
@@ -319,8 +297,8 @@ void NGFragmentItems::LayoutObjectWillBeDestroyed(
*container.GetPhysicalFragment(idx);
DCHECK(fragment.Items());
for (const auto& item : fragment.Items()->Items()) {
- if (item->GetLayoutObject() == &layout_object)
- item->LayoutObjectWillBeDestroyed();
+ if (item.GetLayoutObject() == &layout_object)
+ item.LayoutObjectWillBeDestroyed();
}
}
return;
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 eebdb5f616e..00025571a5a 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
@@ -24,15 +24,21 @@ class CORE_EXPORT NGFragmentItems {
wtf_size_t Size() const { return size_; }
- using Span = base::span<const scoped_refptr<const NGFragmentItem>>;
+ using Span = base::span<const NGFragmentItem>;
Span Items() const { return base::make_span(ItemsData(), size_); }
+ bool Equals(const Span& span) const {
+ return ItemsData() == span.data() && Size() == span.size();
+ }
bool IsSubSpan(const Span& span) const;
const NGFragmentItem& front() const {
CHECK_GE(size_, 1u);
- return *items_[0];
+ return items_[0];
}
+ // Text content for `::first-line`. Available only if `::first-line` has
+ // different style than non-first-line style.
+ const String& FirstLineText() const { return first_line_text_content_; }
const String& Text(bool first_line) const {
return UNLIKELY(first_line) ? first_line_text_content_ : text_content_;
}
@@ -66,9 +72,7 @@ class CORE_EXPORT NGFragmentItems {
wtf_size_t ByteSize() const { return ByteSizeFor(Size()); }
private:
- const scoped_refptr<const NGFragmentItem>* ItemsData() const {
- return reinterpret_cast<const scoped_refptr<const NGFragmentItem>*>(items_);
- }
+ const NGFragmentItem* ItemsData() const { return items_; }
static bool CanReuseAll(NGInlineCursor* cursor);
bool TryDirtyFirstLineFor(const LayoutObject& layout_object) const;
@@ -86,7 +90,7 @@ class CORE_EXPORT NGFragmentItems {
static_assert(
sizeof(NGFragmentItem*) == sizeof(scoped_refptr<const NGFragmentItem>),
"scoped_refptr must be the size of a pointer for |ItemsData()| to work");
- NGFragmentItem* items_[];
+ NGFragmentItem items_[0];
};
} // namespace blink
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 452e1c2f102..74f8d5d2feb 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.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_box_fragment_builder.h"
@@ -11,7 +12,14 @@
namespace blink {
-NGFragmentItemsBuilder::NGFragmentItemsBuilder(const NGInlineNode& node) {
+NGFragmentItemsBuilder::NGFragmentItemsBuilder(
+ WritingDirectionMode writing_direction)
+ : node_(nullptr), writing_direction_(writing_direction) {}
+
+NGFragmentItemsBuilder::NGFragmentItemsBuilder(
+ const NGInlineNode& node,
+ WritingDirectionMode writing_direction)
+ : node_(node), writing_direction_(writing_direction) {
const NGInlineItemsData& items_data = node.ItemsData(false);
text_content_ = items_data.text_content;
const NGInlineItemsData& first_line = node.ItemsData(true);
@@ -30,11 +38,17 @@ NGFragmentItemsBuilder::NGFragmentItemsBuilder(const NGInlineNode& node) {
void NGFragmentItemsBuilder::SetCurrentLine(
const NGPhysicalLineBoxFragment& line,
- ChildList&& children) {
+ NGLogicalLineItems* current_line) {
#if DCHECK_IS_ON()
current_line_fragment_ = &line;
#endif
- current_line_ = std::move(children);
+ DCHECK(current_line);
+ DCHECK(!current_line_); // Check |AddLine| runs after |SetCurrentLine|.
+ current_line_ = current_line;
+}
+
+void NGFragmentItemsBuilder::ClearCurrentLineForTesting() {
+ current_line_ = nullptr;
}
void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line,
@@ -43,79 +57,81 @@ void NGFragmentItemsBuilder::AddLine(const NGPhysicalLineBoxFragment& line,
#if DCHECK_IS_ON()
DCHECK_EQ(current_line_fragment_, &line);
#endif
+ DCHECK(current_line_);
// Reserve the capacity for (children + line box item).
const wtf_size_t size_before = items_.size();
- const wtf_size_t estimated_size = size_before + current_line_.size() + 1;
+ const wtf_size_t estimated_size = size_before + current_line_->size() + 1;
const wtf_size_t old_capacity = items_.capacity();
if (estimated_size > old_capacity)
items_.ReserveCapacity(std::max(estimated_size, old_capacity * 2));
// Add an empty item so that the start of the line can be set later.
const wtf_size_t line_start_index = items_.size();
- items_.emplace_back(offset);
+ items_.emplace_back(offset, line);
- AddItems(current_line_.begin(), current_line_.end());
+ AddItems(current_line_->begin(), current_line_->end());
// All children are added. Create an item for the start of the line.
+ NGFragmentItem& line_item = items_[line_start_index].item;
const wtf_size_t item_count = items_.size() - line_start_index;
- DCHECK(!items_[line_start_index].item);
- items_[line_start_index].item =
- base::MakeRefCounted<NGFragmentItem>(line, item_count);
+ DCHECK_EQ(line_item.DescendantsCount(), 1u);
+ line_item.SetDescendantsCount(item_count);
// Keep children's offsets relative to |line|. They will be adjusted later in
// |ConvertToPhysical()|.
- current_line_.clear();
+ // Clear the current line without releasing the buffer. It is likely to be
+ // reused again.
+ current_line_->Shrink(0);
#if DCHECK_IS_ON()
+ current_line_ = nullptr;
current_line_fragment_ = nullptr;
#endif
DCHECK_LE(items_.size(), estimated_size);
}
-void NGFragmentItemsBuilder::AddItems(Child* child_begin, Child* child_end) {
+void NGFragmentItemsBuilder::AddItems(NGLogicalLineItem* child_begin,
+ NGLogicalLineItem* child_end) {
DCHECK(!is_converted_to_physical_);
- for (Child* child_iter = child_begin; child_iter != child_end;) {
- Child& child = *child_iter;
+ const WritingMode writing_mode = GetWritingMode();
+ for (NGLogicalLineItem* child_iter = child_begin; child_iter != child_end;) {
+ NGLogicalLineItem& child = *child_iter;
// OOF children should have been added to their parent box fragments.
DCHECK(!child.out_of_flow_positioned_box);
- if (!child.fragment_item) {
+ if (!child.CanCreateFragmentItem()) {
++child_iter;
continue;
}
if (child.children_count <= 1) {
- items_.emplace_back(std::move(child.fragment_item), child.rect.offset);
+ items_.emplace_back(child.rect.offset, std::move(child), writing_mode);
++child_iter;
continue;
}
- DCHECK(child.fragment_item->IsContainer());
- DCHECK(!child.fragment_item->IsFloating());
// Children of inline boxes are flattened and added to |items_|, with the
// count of descendant items to preserve the tree structure.
//
// Add an empty item so that the start of the box can be set later.
- wtf_size_t box_start_index = items_.size();
- items_.emplace_back(child.rect.offset);
+ const wtf_size_t box_start_index = items_.size();
+ items_.emplace_back(child.rect.offset, std::move(child), writing_mode);
// Add all children, including their desendants, skipping this item.
CHECK_GE(child.children_count, 1u); // 0 will loop infinitely.
- Child* end_child_iter = child_iter + child.children_count;
+ NGLogicalLineItem* end_child_iter = child_iter + child.children_count;
CHECK_LE(end_child_iter - child_begin, child_end - child_begin);
AddItems(child_iter + 1, end_child_iter);
child_iter = end_child_iter;
// All children are added. Compute how many items are actually added. The
// number of items added maybe different from |child.children_count|.
- wtf_size_t item_count = items_.size() - box_start_index;
-
- // Create an item for the start of the box.
- child.fragment_item->SetDescendantsCount(item_count);
- DCHECK(!items_[box_start_index].item);
- items_[box_start_index].item = std::move(child.fragment_item);
+ const wtf_size_t item_count = items_.size() - box_start_index;
+ NGFragmentItem& box_item = items_[box_start_index].item;
+ DCHECK_EQ(box_item.DescendantsCount(), 1u);
+ box_item.SetDescendantsCount(item_count);
}
}
@@ -127,24 +143,28 @@ void NGFragmentItemsBuilder::AddListMarker(
// Resolved direction matters only for inline items, and outside list markers
// are not inline.
const TextDirection resolved_direction = TextDirection::kLtr;
- items_.emplace_back(
- base::MakeRefCounted<NGFragmentItem>(marker_fragment, resolved_direction),
- offset);
+ items_.emplace_back(offset, marker_fragment, resolved_direction);
}
NGFragmentItemsBuilder::AddPreviousItemsResult
NGFragmentItemsBuilder::AddPreviousItems(
const NGFragmentItems& items,
- WritingMode writing_mode,
- TextDirection direction,
const PhysicalSize& container_size,
NGBoxFragmentBuilder* container_builder,
- bool stop_at_dirty) {
- AddPreviousItemsResult result;
- if (stop_at_dirty) {
+ const NGFragmentItem* end_item) {
+ if (end_item) {
+ DCHECK(node_);
DCHECK(container_builder);
DCHECK(text_content_);
+
+ if (UNLIKELY(items.FirstLineText() && !first_line_text_content_)) {
+ // Don't reuse previous items if they have different `::first-line` style
+ // but |this| doesn't. Reaching here means that computed style doesn't
+ // change, but |NGFragmentItem| has wrong |NGStyleVariant|.
+ return AddPreviousItemsResult();
+ }
} else {
+ DCHECK(!container_builder);
DCHECK(!text_content_);
text_content_ = items.Text(false);
first_line_text_content_ = items.Text(true);
@@ -159,11 +179,13 @@ NGFragmentItemsBuilder::AddPreviousItems(
// This is needed because the container size may be different, in that case,
// the physical offsets are different when `writing-mode: vertial-rl`.
DCHECK(!is_converted_to_physical_);
- const WritingMode line_writing_mode = ToLineWritingMode(writing_mode);
+ const WritingModeConverter converter(GetWritingDirection(), container_size);
+ const WritingMode writing_mode = GetWritingMode();
+ WritingModeConverter line_converter(
+ {ToLineWritingMode(writing_mode), TextDirection::kLtr});
- const NGFragmentItem* const end_item =
- stop_at_dirty ? items.EndOfReusableItems() : nullptr;
- const NGFragmentItem* last_line_start_item = nullptr;
+ const NGInlineBreakToken* last_break_token = nullptr;
+ const NGInlineItemsData* items_data = nullptr;
LayoutUnit used_block_size;
for (NGInlineCursor cursor(items); cursor;) {
@@ -174,74 +196,88 @@ NGFragmentItemsBuilder::AddPreviousItems(
DCHECK(!item.IsDirty());
const LogicalOffset item_offset =
- item.OffsetInContainerBlock().ConvertToLogical(
- writing_mode, direction, container_size, item.Size());
- items_.emplace_back(&item, item_offset);
+ converter.ToLogical(item.OffsetInContainerBlock(), item.Size());
if (item.Type() == NGFragmentItem::kLine) {
+ DCHECK(item.LineBoxFragment());
+ if (end_item) {
+ // Check if this line has valid item_index and offset.
+ const NGPhysicalLineBoxFragment* line_fragment = item.LineBoxFragment();
+ const NGInlineBreakToken* break_token =
+ To<NGInlineBreakToken>(line_fragment->BreakToken());
+ DCHECK(!break_token->IsFinished());
+ const NGInlineItemsData* current_items_data;
+ if (UNLIKELY(break_token->UseFirstLineStyle()))
+ current_items_data = &node_.ItemsData(true);
+ else if (items_data)
+ current_items_data = items_data;
+ else
+ current_items_data = items_data = &node_.ItemsData(false);
+ if (UNLIKELY(!current_items_data->IsValidOffset(
+ break_token->ItemIndex(), break_token->TextOffset()))) {
+ NOTREACHED();
+ break;
+ }
+
+ last_break_token = break_token;
+ container_builder->AddChild(*line_fragment, item_offset);
+ used_block_size +=
+ item.Size().ConvertToLogical(writing_mode).block_size;
+ }
+
+ items_.emplace_back(item_offset, item);
const PhysicalRect line_box_bounds = item.RectInContainerBlock();
+ line_converter.SetOuterSize(line_box_bounds.size);
for (NGInlineCursor line = cursor.CursorForDescendants(); line;
line.MoveToNext()) {
const NGFragmentItem& line_child = *line.Current().Item();
DCHECK(line_child.CanReuse());
items_.emplace_back(
- &line_child,
- (line_child.OffsetInContainerBlock() - line_box_bounds.offset)
- .ConvertToLogical(line_writing_mode, TextDirection::kLtr,
- line_box_bounds.size, line_child.Size()));
+ line_converter.ToLogical(
+ line_child.OffsetInContainerBlock() - line_box_bounds.offset,
+ line_child.Size()),
+ line_child);
}
cursor.MoveToNextSkippingChildren();
- DCHECK(item.LineBoxFragment());
- if (stop_at_dirty) {
- container_builder->AddChild(*item.LineBoxFragment(), item_offset);
- last_line_start_item = &item;
- used_block_size +=
- item.Size().ConvertToLogical(writing_mode).block_size;
- }
continue;
}
DCHECK_NE(item.Type(), NGFragmentItem::kLine);
- DCHECK(!stop_at_dirty);
+ DCHECK(!end_item);
+ items_.emplace_back(item_offset, item);
cursor.MoveToNext();
}
+ DCHECK_LE(items_.size(), estimated_size);
- if (stop_at_dirty && last_line_start_item) {
- result.inline_break_token = last_line_start_item->InlineBreakToken();
- DCHECK(result.inline_break_token);
- DCHECK(!result.inline_break_token->IsFinished());
- result.used_block_size = used_block_size;
- result.succeeded = true;
+ if (end_item && last_break_token) {
+ DCHECK(!last_break_token->IsFinished());
+ return AddPreviousItemsResult{last_break_token, used_block_size, true};
}
-
- DCHECK_LE(items_.size(), estimated_size);
- return result;
+ return AddPreviousItemsResult();
}
const NGFragmentItemsBuilder::ItemWithOffsetList& NGFragmentItemsBuilder::Items(
- WritingMode writing_mode,
- TextDirection direction,
const PhysicalSize& outer_size) {
- ConvertToPhysical(writing_mode, direction, outer_size);
+ ConvertToPhysical(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) {
+void NGFragmentItemsBuilder::ConvertToPhysical(const PhysicalSize& outer_size) {
if (is_converted_to_physical_)
return;
+ const WritingModeConverter converter(GetWritingDirection(), outer_size);
// Children of lines have line-relative offsets. Use line-writing mode to
- // convert their logical offsets.
- const WritingMode line_writing_mode = ToLineWritingMode(writing_mode);
+ // convert their logical offsets. Use `kLtr` because inline items are after
+ // bidi-reoder, and that their offset is visual, not logical.
+ WritingModeConverter line_converter(
+ {ToLineWritingMode(GetWritingMode()), TextDirection::kLtr});
for (ItemWithOffset* iter = items_.begin(); iter != items_.end(); ++iter) {
- NGFragmentItem* item = const_cast<NGFragmentItem*>(iter->item.get());
- item->SetOffset(iter->offset.ConvertToPhysical(writing_mode, direction,
- outer_size, item->Size()));
+ NGFragmentItem* item = &iter->item;
+ item->SetOffset(converter.ToPhysical(iter->offset, item->Size()));
// Transform children of lines separately from children of the block,
// because they may have different directions from the block. To do
@@ -251,16 +287,14 @@ void NGFragmentItemsBuilder::ConvertToPhysical(WritingMode writing_mode,
DCHECK(descendants_count);
if (descendants_count) {
const PhysicalRect line_box_bounds = item->RectInContainerBlock();
+ line_converter.SetOuterSize(line_box_bounds.size);
while (--descendants_count) {
++iter;
DCHECK_NE(iter, items_.end());
- item = const_cast<NGFragmentItem*>(iter->item.get());
- // Use `kLtr` because inline items are after bidi-reoder, and that
- // their offset is visual, not logical.
- item->SetOffset(iter->offset.ConvertToPhysical(
- line_writing_mode, TextDirection::kLtr,
- line_box_bounds.size, item->Size()) +
- line_box_bounds.offset);
+ item = &iter->item;
+ item->SetOffset(
+ line_converter.ToPhysical(iter->offset, item->Size()) +
+ line_box_bounds.offset);
}
}
}
@@ -278,12 +312,10 @@ base::Optional<LogicalOffset> NGFragmentItemsBuilder::LogicalOffsetFor(
return base::nullopt;
}
-void NGFragmentItemsBuilder::ToFragmentItems(WritingMode writing_mode,
- TextDirection direction,
- const PhysicalSize& outer_size,
+void NGFragmentItemsBuilder::ToFragmentItems(const PhysicalSize& outer_size,
void* data) {
DCHECK(text_content_);
- ConvertToPhysical(writing_mode, direction, outer_size);
+ ConvertToPhysical(outer_size);
new (data) NGFragmentItems(this);
}
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 c161eed2aec..081a0286c6a 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
@@ -7,7 +7,8 @@
#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_line_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h"
+#include "third_party/blink/renderer/platform/text/writing_direction_mode.h"
namespace blink {
@@ -22,8 +23,17 @@ class CORE_EXPORT NGFragmentItemsBuilder {
STACK_ALLOCATED();
public:
- NGFragmentItemsBuilder() = default;
- explicit NGFragmentItemsBuilder(const NGInlineNode& node);
+ explicit NGFragmentItemsBuilder(WritingDirectionMode writing_direction);
+ NGFragmentItemsBuilder(const NGInlineNode& node,
+ WritingDirectionMode writing_direction);
+
+ WritingDirectionMode GetWritingDirection() const {
+ return writing_direction_;
+ }
+ WritingMode GetWritingMode() const {
+ return writing_direction_.GetWritingMode();
+ }
+ TextDirection Direction() const { return writing_direction_.Direction(); }
wtf_size_t Size() const { return items_.size(); }
@@ -39,22 +49,20 @@ class CORE_EXPORT NGFragmentItemsBuilder {
: text_content_;
}
- // The caller should create a |ChildList| for a complete line and add to this
- // builder.
+ // The caller should create a |NGLogicalLineItems| for a complete line and add
+ // to this builder.
//
// Adding a line is a two-pass operation, because |NGInlineLayoutAlgorithm|
// creates and positions children within a line box, but its parent algorithm
// positions the line box. |SetCurrentLine| sets the children, and the next
// |AddLine| adds them.
//
- // TODO(kojii): Moving |ChildList| is not cheap because it has inline
- // capacity. Reconsider the ownership.
- using Child = NGLineBoxFragmentBuilder::Child;
- using ChildList = NGLineBoxFragmentBuilder::ChildList;
+ // The caller must keep |children| alive until |AddLine| completes.
void SetCurrentLine(const NGPhysicalLineBoxFragment& line,
- ChildList&& children);
+ NGLogicalLineItems* current_line);
void AddLine(const NGPhysicalLineBoxFragment& line,
const LogicalOffset& offset);
+ void ClearCurrentLineForTesting();
// Add a list marker to the current line.
void AddListMarker(const NGPhysicalBoxFragment& marker_fragment,
@@ -76,25 +84,22 @@ class CORE_EXPORT NGFragmentItemsBuilder {
// items and stops copying before the first dirty line.
AddPreviousItemsResult AddPreviousItems(
const NGFragmentItems& items,
- WritingMode writing_mode,
- TextDirection direction,
const PhysicalSize& container_size,
NGBoxFragmentBuilder* container_builder = nullptr,
- bool stop_at_dirty = false);
+ const NGFragmentItem* end_item = nullptr);
struct ItemWithOffset {
DISALLOW_NEW();
public:
- ItemWithOffset(scoped_refptr<const NGFragmentItem> item,
- const LogicalOffset& offset)
- : item(std::move(item)), offset(offset) {}
- explicit ItemWithOffset(const LogicalOffset& offset) : offset(offset) {}
+ template <class... Args>
+ explicit ItemWithOffset(const LogicalOffset& offset, Args&&... args)
+ : item(std::forward<Args>(args)...), offset(offset) {}
- const NGFragmentItem& operator*() const { return *item; }
- const NGFragmentItem* operator->() const { return item.get(); }
+ const NGFragmentItem& operator*() const { return item; }
+ const NGFragmentItem* operator->() const { return &item; }
- scoped_refptr<const NGFragmentItem> item;
+ NGFragmentItem item;
LogicalOffset offset;
};
@@ -110,30 +115,27 @@ class CORE_EXPORT NGFragmentItemsBuilder {
// containing block geometry for OOF-positioned nodes.
//
// Once this method has been called, new items cannot be added.
- const ItemWithOffsetList& Items(WritingMode,
- TextDirection,
- const PhysicalSize& outer_size);
+ const ItemWithOffsetList& Items(const PhysicalSize& outer_size);
// Build a |NGFragmentItems|. The builder cannot build twice because data set
// to this builder may be cleared.
- void ToFragmentItems(WritingMode,
- TextDirection,
- const PhysicalSize& outer_size,
- void* data);
+ void ToFragmentItems(const PhysicalSize& outer_size, void* data);
private:
- void AddItems(Child* child_begin, Child* child_end);
+ void AddItems(NGLogicalLineItem* child_begin, NGLogicalLineItem* child_end);
- void ConvertToPhysical(WritingMode writing_mode,
- TextDirection direction,
- const PhysicalSize& outer_size);
+ void ConvertToPhysical(const PhysicalSize& outer_size);
ItemWithOffsetList items_;
String text_content_;
String first_line_text_content_;
// Keeps children of a line until the offset is determined. See |AddLine|.
- ChildList current_line_;
+ NGLogicalLineItems* current_line_ = nullptr;
+
+ NGInlineNode node_;
+
+ WritingDirectionMode writing_direction_;
bool has_floating_descendants_for_paint_ = false;
bool is_converted_to_physical_ = false;
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 33ba9243ca0..bb2d2a16de5 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
@@ -106,7 +106,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems(
const ComputedStyle& line_style,
FontBaseline baseline_type,
bool line_height_quirk,
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+ NGLogicalLineItems* line_box) {
if (stack_.IsEmpty()) {
// For the first line, push a box state for the line itself.
stack_.resize(1);
@@ -156,7 +156,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag(
const NGInlineItem& item,
const NGInlineItemResult& item_result,
FontBaseline baseline_type,
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+ NGLogicalLineItems* line_box) {
NGInlineBoxState* box =
OnOpenTag(item, item_result, baseline_type, *line_box);
box->needs_box_fragment = item.ShouldCreateBoxFragment();
@@ -169,7 +169,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag(
const NGInlineItem& item,
const NGInlineItemResult& item_result,
FontBaseline baseline_type,
- const NGLineBoxFragmentBuilder::ChildList& line_box) {
+ const NGLogicalLineItems& line_box) {
DCHECK(item.Style());
const ComputedStyle& style = *item.Style();
stack_.resize(stack_.size() + 1);
@@ -186,7 +186,7 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag(
}
NGInlineBoxState* NGInlineLayoutStateStack::OnCloseTag(
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineBoxState* box,
FontBaseline baseline_type,
bool has_end_edge) {
@@ -200,9 +200,8 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnCloseTag(
return &stack_.back();
}
-void NGInlineLayoutStateStack::OnEndPlaceItems(
- NGLineBoxFragmentBuilder::ChildList* line_box,
- FontBaseline baseline_type) {
+void NGInlineLayoutStateStack::OnEndPlaceItems(NGLogicalLineItems* line_box,
+ FontBaseline baseline_type) {
for (auto it = stack_.rbegin(); it != stack_.rend(); ++it) {
NGInlineBoxState* box = &(*it);
if (!box->has_end_edge && box->needs_box_fragment &&
@@ -215,17 +214,15 @@ void NGInlineLayoutStateStack::OnEndPlaceItems(
// that |ApplyBaselineShift()| can compute offset for both children and boxes.
// Copy the final offset to |box_data_list_|.
for (BoxData& box_data : box_data_list_) {
- const NGLineBoxFragmentBuilder::Child& placeholder =
- (*line_box)[box_data.fragment_start];
+ const NGLogicalLineItem& placeholder = (*line_box)[box_data.fragment_start];
DCHECK(placeholder.IsPlaceholder());
box_data.rect.offset = placeholder.rect.offset;
}
}
-void NGInlineLayoutStateStack::EndBoxState(
- NGInlineBoxState* box,
- NGLineBoxFragmentBuilder::ChildList* line_box,
- FontBaseline baseline_type) {
+void NGInlineLayoutStateStack::EndBoxState(NGInlineBoxState* box,
+ NGLogicalLineItems* line_box,
+ FontBaseline baseline_type) {
if (box->needs_box_fragment)
AddBoxData(box, line_box);
@@ -237,8 +234,6 @@ void NGInlineLayoutStateStack::EndBoxState(
return;
NGInlineBoxState& parent_box = *std::prev(box);
- // Propagate necessary data back to the parent box.
-
// Unite the metrics to the parent box.
if (position_pending == kPositionNotPending)
parent_box.metrics.Unite(box->metrics);
@@ -250,7 +245,7 @@ void NGInlineLayoutStateStack::EndBoxState(
// from placeholders.
void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
NGInlineBoxState* box,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
FontBaseline baseline_type) {
DCHECK(box != stack_.begin() &&
box->item->Type() != NGInlineItem::kAtomicInline);
@@ -278,49 +273,37 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
}
// 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));
+void NGInlineLayoutStateStack::AddBoxData(NGInlineBoxState* box,
+ NGLogicalLineItems* line_box) {
+ DCHECK(box->needs_box_fragment);
DCHECK(box->style);
const ComputedStyle& style = *box->style;
- NGLineBoxFragmentBuilder::Child& placeholder =
- (*line_box)[box->fragment_start];
+ NGLogicalLineItem& 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, 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 {
- DCHECK_EQ(box->margin_inline_start, 0);
- DCHECK_EQ(box->margin_inline_end, 0);
- DCHECK(box->padding.IsEmpty());
- DCHECK(box->borders.IsEmpty());
+ 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);
}
DCHECK((*line_box)[box->fragment_start].IsPlaceholder());
@@ -356,8 +339,7 @@ void NGInlineLayoutStateStack::ChildInserted(unsigned index) {
}
}
-void NGInlineLayoutStateStack::PrepareForReorder(
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+void NGInlineLayoutStateStack::PrepareForReorder(NGLogicalLineItems* line_box) {
// There's nothing to do if no boxes.
if (box_data_list_.IsEmpty())
return;
@@ -368,7 +350,7 @@ void NGInlineLayoutStateStack::PrepareForReorder(
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];
+ NGLogicalLineItem& child = (*line_box)[i];
unsigned child_box_data_index = child.box_data_index;
if (!child_box_data_index) {
child.box_data_index = box_data_index;
@@ -390,7 +372,7 @@ void NGInlineLayoutStateStack::PrepareForReorder(
}
void NGInlineLayoutStateStack::UpdateAfterReorder(
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+ NGLogicalLineItems* line_box) {
// There's nothing to do if no boxes.
if (box_data_list_.IsEmpty())
return;
@@ -416,18 +398,18 @@ void NGInlineLayoutStateStack::UpdateAfterReorder(
DCHECK_GT(box_data.fragment_end, box_data.fragment_start);
}
// Check all |box_data_index| were migrated to BoxData.
- for (const NGLineBoxFragmentBuilder::Child& child : *line_box) {
+ for (const NGLogicalLineItem& child : *line_box) {
DCHECK_EQ(child.box_data_index, 0u);
}
#endif
}
unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
unsigned index) {
// Find the first line box item that should create a box fragment.
for (; index < line_box->size(); index++) {
- NGLineBoxFragmentBuilder::Child* start = &(*line_box)[index];
+ NGLogicalLineItem* start = &(*line_box)[index];
const unsigned box_data_index = start->box_data_index;
if (!box_data_index)
continue;
@@ -444,7 +426,7 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
// Find the end line box item.
const unsigned start_index = index;
for (index++; index < line_box->size(); index++) {
- NGLineBoxFragmentBuilder::Child* end = &(*line_box)[index];
+ NGLogicalLineItem* end = &(*line_box)[index];
// If we found another box that maybe included in this box, update it
// first. Updating will change |end->box_data_index| so that we can
@@ -510,11 +492,11 @@ void NGInlineLayoutStateStack::BoxData::UpdateFragmentEdges(
}
LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions(
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
LayoutUnit position) {
// At this point, children are in the visual order, and they have their
// origins at (0, 0). Accumulate inline offset from left to right.
- for (NGLineBoxFragmentBuilder::Child& child : *line_box) {
+ for (NGLogicalLineItem& child : *line_box) {
child.margin_line_left = child.rect.offset.inline_offset;
child.rect.offset.inline_offset += position;
// Box margins/boders/paddings will be processed later.
@@ -560,7 +542,7 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions(
// border/padding of this box and margin/border/padding of descendants
// boxes, while accumulating its margin/border/padding.
unsigned start = box_data.fragment_start;
- NGLineBoxFragmentBuilder::Child& start_child = (*line_box)[start];
+ NGLogicalLineItem& start_child = (*line_box)[start];
LayoutUnit line_left_offset =
start_child.rect.offset.inline_offset - start_child.margin_line_left;
LinePadding& start_padding = accumulated_padding[start];
@@ -569,7 +551,7 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions(
DCHECK_GT(box_data.fragment_end, start);
unsigned last = box_data.fragment_end - 1;
- NGLineBoxFragmentBuilder::Child& last_child = (*line_box)[last];
+ NGLogicalLineItem& last_child = (*line_box)[last];
LayoutUnit line_right_offset = last_child.rect.offset.inline_offset -
last_child.margin_line_left +
last_child.inline_size;
@@ -585,36 +567,19 @@ LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions(
}
void NGInlineLayoutStateStack::CreateBoxFragments(
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+ NGLogicalLineItems* line_box) {
DCHECK(!box_data_list_.IsEmpty());
for (BoxData& box_data : box_data_list_) {
unsigned start = box_data.fragment_start;
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;
- }
-
- // |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);
- continue;
- }
-
- DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
- DCHECK(box_data.item);
+ NGLogicalLineItem* child = &(*line_box)[start];
+ DCHECK(box_data.item->ShouldCreateBoxFragment());
+ scoped_refptr<const NGLayoutResult> box_fragment =
+ box_data.CreateBoxFragment(line_box);
if (child->IsPlaceholder()) {
- child->inline_item = box_data.item;
+ child->layout_result = std::move(box_fragment);
child->rect = box_data.rect;
child->children_count = end - start;
continue;
@@ -622,7 +587,7 @@ void NGInlineLayoutStateStack::CreateBoxFragments(
// |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,
+ line_box->InsertChild(start, std::move(box_fragment), box_data.rect,
end - start + 1);
ChildInserted(start + 1);
}
@@ -632,7 +597,7 @@ void NGInlineLayoutStateStack::CreateBoxFragments(
scoped_refptr<const NGLayoutResult>
NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
- NGLineBoxFragmentBuilder::ChildList* line_box) {
+ NGLogicalLineItems* line_box) {
DCHECK(item);
DCHECK(item->Style());
const ComputedStyle& style = *item->Style();
@@ -646,7 +611,7 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
// Because children are already in the visual order, use LTR for the
// fragment builder so that it should not transform the coordinates for RTL.
NGBoxFragmentBuilder box(item->GetLayoutObject(), &style,
- style.GetWritingMode(), TextDirection::kLtr);
+ {style.GetWritingMode(), TextDirection::kLtr});
box.SetInitialFragmentGeometry(fragment_geometry);
box.SetBoxType(NGPhysicalFragment::kInlineBox);
box.SetStyleVariant(item->StyleVariant());
@@ -657,7 +622,13 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
box.SetBorderEdges({true, has_line_right_edge, true, has_line_left_edge});
for (unsigned i = fragment_start; i < fragment_end; i++) {
- NGLineBoxFragmentBuilder::Child& child = (*line_box)[i];
+ NGLogicalLineItem& child = (*line_box)[i];
+
+ // If |child| has a fragment created by previous |CreateBoxFragment|, skip
+ // children that were already added to |child|.
+ if (child.children_count)
+ i += child.children_count - 1;
+
if (child.out_of_flow_positioned_box) {
DCHECK(item->GetLayoutObject()->IsLayoutInline());
NGBlockNode oof_box(ToLayoutBox(child.out_of_flow_positioned_box));
@@ -705,10 +676,9 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
}
NGInlineLayoutStateStack::PositionPending
-NGInlineLayoutStateStack::ApplyBaselineShift(
- NGInlineBoxState* box,
- NGLineBoxFragmentBuilder::ChildList* line_box,
- FontBaseline baseline_type) {
+NGInlineLayoutStateStack::ApplyBaselineShift(NGInlineBoxState* box,
+ NGLogicalLineItems* line_box,
+ FontBaseline baseline_type) {
// Some 'vertical-align' values require the size of their parents. Align all
// such descendant boxes that require the size of this box; they are queued in
// |pending_descendants|.
@@ -852,7 +822,7 @@ NGInlineLayoutStateStack::ApplyBaselineShift(
NGLineHeightMetrics NGInlineLayoutStateStack::MetricsForTopAndBottomAlign(
const NGInlineBoxState& box,
- const NGLineBoxFragmentBuilder::ChildList& line_box) const {
+ const NGLogicalLineItems& line_box) const {
DCHECK(!box.pending_descendants.IsEmpty());
// |metrics| is the bounds of "aligned subtree", that is, bounds of
@@ -871,8 +841,7 @@ NGLineHeightMetrics NGInlineLayoutStateStack::MetricsForTopAndBottomAlign(
continue;
// |block_offset| is the top position when the baseline is at 0.
- const NGLineBoxFragmentBuilder::Child& placeholder =
- line_box[box_data.fragment_start];
+ const NGLogicalLineItem& placeholder = line_box[box_data.fragment_start];
DCHECK(placeholder.IsPlaceholder());
LayoutUnit box_ascent = -placeholder.rect.offset.block_offset;
NGLineHeightMetrics box_metrics(box_ascent,
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 6dcee9c380a..6f58b80073a 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
@@ -16,8 +16,9 @@
namespace blink {
class NGInlineItem;
-struct NGInlineItemResult;
+class NGLogicalLineItems;
class ShapeResultView;
+struct NGInlineItemResult;
// Fragments that require the layout position/size of ancestor are packed in
// this struct.
@@ -114,31 +115,30 @@ 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 line_height_quirk,
- NGLineBoxFragmentBuilder::ChildList* line_box);
+ NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle&,
+ FontBaseline,
+ bool line_height_quirk,
+ NGLogicalLineItems* line_box);
// Push a box state stack.
NGInlineBoxState* OnOpenTag(const NGInlineItem&,
const NGInlineItemResult&,
FontBaseline baseline_type,
- const NGLineBoxFragmentBuilder::ChildList&);
+ const NGLogicalLineItems&);
// This variation adds a box placeholder to |line_box|.
NGInlineBoxState* OnOpenTag(const NGInlineItem&,
const NGInlineItemResult&,
FontBaseline baseline_type,
- NGLineBoxFragmentBuilder::ChildList* line_box);
+ NGLogicalLineItems* line_box);
// Pop a box state stack.
- NGInlineBoxState* OnCloseTag(NGLineBoxFragmentBuilder::ChildList*,
+ NGInlineBoxState* OnCloseTag(NGLogicalLineItems*,
NGInlineBoxState*,
FontBaseline,
bool has_end_edge = true);
// Compute all the pending positioning at the end of a line.
- void OnEndPlaceItems(NGLineBoxFragmentBuilder::ChildList*, FontBaseline);
+ void OnEndPlaceItems(NGLogicalLineItems*, FontBaseline);
bool HasBoxFragments() const { return !box_data_list_.IsEmpty(); }
@@ -148,12 +148,12 @@ class CORE_EXPORT NGInlineLayoutStateStack {
// This class keeps indexes to fragments in the line box, and that only
// appending is allowed. Call this function to move all such data to the line
// box, so that outside of this class can reorder fragments in the line box.
- void PrepareForReorder(NGLineBoxFragmentBuilder::ChildList*);
+ void PrepareForReorder(NGLogicalLineItems*);
// When reordering was complete, call this function to re-construct the box
// data from the line box. Callers must call |PrepareForReorder()| before
// reordering.
- void UpdateAfterReorder(NGLineBoxFragmentBuilder::ChildList*);
+ void UpdateAfterReorder(NGLogicalLineItems*);
// Update start/end of the first BoxData found at |index|.
//
@@ -161,19 +161,17 @@ class CORE_EXPORT NGInlineLayoutStateStack {
//
// Returns the index to process next. It should be given to the next call to
// this function.
- unsigned UpdateBoxDataFragmentRange(NGLineBoxFragmentBuilder::ChildList*,
- unsigned index);
+ unsigned UpdateBoxDataFragmentRange(NGLogicalLineItems*, unsigned index);
// Update edges of inline fragmented boxes.
void UpdateFragmentedBoxDataEdges();
// Compute inline positions of fragments and boxes.
- LayoutUnit ComputeInlinePositions(NGLineBoxFragmentBuilder::ChildList*,
- LayoutUnit position);
+ LayoutUnit ComputeInlinePositions(NGLogicalLineItems*, LayoutUnit position);
// Create box fragments. This function turns a flat list of children into
// a box tree.
- void CreateBoxFragments(NGLineBoxFragmentBuilder::ChildList*);
+ void CreateBoxFragments(NGLogicalLineItems*);
#if DCHECK_IS_ON()
void CheckSame(const NGInlineLayoutStateStack&) const;
@@ -182,14 +180,12 @@ class CORE_EXPORT NGInlineLayoutStateStack {
private:
// End of a box state, either explicitly by close tag, or implicitly at the
// end of a line.
- void EndBoxState(NGInlineBoxState*,
- NGLineBoxFragmentBuilder::ChildList*,
- FontBaseline);
+ void EndBoxState(NGInlineBoxState*, NGLogicalLineItems*, FontBaseline);
void AddBoxFragmentPlaceholder(NGInlineBoxState*,
- NGLineBoxFragmentBuilder::ChildList*,
+ NGLogicalLineItems*,
FontBaseline);
- void AddBoxData(NGInlineBoxState*, NGLineBoxFragmentBuilder::ChildList*);
+ void AddBoxData(NGInlineBoxState*, NGLogicalLineItems*);
enum PositionPending { kPositionNotPending, kPositionPending };
@@ -200,14 +196,14 @@ class CORE_EXPORT NGInlineLayoutStateStack {
// https://www.w3.org/TR/CSS22/visudet.html#propdef-vertical-align
// https://www.w3.org/TR/css-inline-3/#propdef-vertical-align
PositionPending ApplyBaselineShift(NGInlineBoxState*,
- NGLineBoxFragmentBuilder::ChildList*,
+ NGLogicalLineItems*,
FontBaseline);
// Compute the metrics for when 'vertical-align' is 'top' and 'bottom' from
// |pending_descendants|.
NGLineHeightMetrics MetricsForTopAndBottomAlign(
const NGInlineBoxState&,
- const NGLineBoxFragmentBuilder::ChildList&) const;
+ const NGLogicalLineItems&) const;
// Data for a box fragment. See AddBoxFragmentPlaceholder().
// This is a transient object only while building a line box.
@@ -253,8 +249,7 @@ class CORE_EXPORT NGInlineLayoutStateStack {
void UpdateFragmentEdges(Vector<BoxData, 4>& list);
- scoped_refptr<const NGLayoutResult> CreateBoxFragment(
- NGLineBoxFragmentBuilder::ChildList*);
+ scoped_refptr<const NGLayoutResult> CreateBoxFragment(NGLogicalLineItems*);
};
Vector<NGInlineBoxState, 4> stack_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.cc
index 888119d970d..0f4293fd07c 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.cc
@@ -9,6 +9,7 @@ namespace blink {
namespace {
struct SameSizeAsNGInlineChildLayoutContext {
+ NGLogicalLineItems line_items_;
base::Optional<NGInlineLayoutStateStack> box_states_;
void* pointers[2];
unsigned number;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h
index 360714b2902..09210c5894f 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h
@@ -7,6 +7,7 @@
#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_box_state.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h"
namespace blink {
@@ -27,6 +28,10 @@ class NGInlineChildLayoutContext {
items_builder_ = builder;
}
+ // Returns an instance of |NGLogicalLineItems|. This is reused when laying out
+ // the next line.
+ NGLogicalLineItems* LogicalLineItems() { return &logical_line_items_; }
+
// Returns the NGInlineLayoutStateStack in this context.
bool HasBoxStates() const { return box_states_.has_value(); }
NGInlineLayoutStateStack* BoxStates() { return &*box_states_; }
@@ -50,6 +55,8 @@ class NGInlineChildLayoutContext {
// transit, allocating separately is easier.
NGFragmentItemsBuilder* items_builder_ = nullptr;
+ NGLogicalLineItems logical_line_items_;
+
base::Optional<NGInlineLayoutStateStack> box_states_;
// The items and its index this context is set up for.
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 88473e916fd..1da02bfd534 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
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
@@ -18,8 +19,11 @@ namespace blink {
inline void NGInlineCursor::MoveToItem(const ItemsSpan::iterator& iter) {
DCHECK(IsItemCursor());
DCHECK(iter >= items_.begin() && iter <= items_.end());
- current_.item_iter_ = iter;
- current_.item_ = iter == items_.end() ? nullptr : iter->get();
+ if (iter != items_.end()) {
+ current_.Set(iter);
+ return;
+ }
+ MakeNull();
}
void NGInlineCursor::SetRoot(const NGFragmentItems& fragment_items,
@@ -117,7 +121,7 @@ const LayoutBlockFlow* NGInlineCursor::GetLayoutBlockFlow() const {
return layout_object->RootInlineFormattingContext();
}
if (IsItemCursor()) {
- const NGFragmentItem& item = *fragment_items_->Items().front();
+ const NGFragmentItem& item = fragment_items_->front();
const LayoutObject* layout_object = item.GetLayoutObject();
if (item.Type() == NGFragmentItem::kLine)
return To<LayoutBlockFlow>(layout_object);
@@ -454,11 +458,18 @@ const DisplayItemClient* NGInlineCursorPosition::GetDisplayItemClient() const {
if (paint_fragment_)
return paint_fragment_;
if (item_)
- return item_;
+ return item_->GetDisplayItemClient();
NOTREACHED();
return nullptr;
}
+wtf_size_t NGInlineCursorPosition::FragmentId() const {
+ if (paint_fragment_)
+ return 0;
+ DCHECK(item_);
+ return item_->FragmentId();
+}
+
const NGInlineBreakToken* NGInlineCursorPosition::InlineBreakToken() const {
DCHECK(IsLineBox());
if (paint_fragment_) {
@@ -652,6 +663,20 @@ PhysicalOffset NGInlineCursorPosition::LineEndPoint() const {
pixel_size);
}
+LogicalRect NGInlineCursorPosition::ConvertChildToLogical(
+ const PhysicalRect& physical_rect) const {
+ return WritingModeConverter(
+ {Style().GetWritingMode(), ResolvedOrBaseDirection()}, Size())
+ .ToLogical(physical_rect);
+}
+
+PhysicalRect NGInlineCursorPosition::ConvertChildToPhysical(
+ const LogicalRect& logical_rect) const {
+ return WritingModeConverter(
+ {Style().GetWritingMode(), ResolvedOrBaseDirection()}, Size())
+ .ToPhysical(logical_rect);
+}
+
PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext(
const PhysicalOffset& point,
const NGPhysicalBoxFragment& container) {
@@ -667,20 +692,26 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext(
PhysicalSize(LayoutUnit(1), LayoutUnit(1)))
.block_offset;
- // Stores the closest line box child above |point| in the block direction.
+ // Stores the closest line box child after |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();
+ NGInlineCursorPosition closest_line_after;
+ LayoutUnit closest_line_after_block_offset = LayoutUnit::Min();
- // Stores the closest line box child below |point| in the block direction.
+ // Stores the closest line box child before |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();
+ NGInlineCursorPosition closest_line_before;
+ LayoutUnit closest_line_before_block_offset = LayoutUnit::Max();
while (*this) {
const NGFragmentItem* child_item = CurrentItem();
DCHECK(child_item);
if (child_item->Type() == NGFragmentItem::kLine) {
+ if (!CursorForDescendants().TryToMoveToFirstInlineLeafChild()) {
+ // editing/selection/last-empty-inline.html requires this to skip
+ // empty <span> with padding.
+ MoveToNextItemSkippingChildren();
+ continue;
+ }
// Try to resolve if |point| falls in a line box in block direction.
const LayoutUnit child_block_offset =
child_item->OffsetInContainerBlock()
@@ -688,9 +719,9 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext(
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();
+ if (child_block_offset < closest_line_before_block_offset) {
+ closest_line_before_block_offset = child_block_offset;
+ closest_line_before = Current();
}
MoveToNextItemSkippingChildren();
continue;
@@ -701,9 +732,9 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext(
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();
+ if (child_block_end_offset > closest_line_after_block_offset) {
+ closest_line_after_block_offset = child_block_end_offset;
+ closest_line_after = Current();
}
MoveToNextItemSkippingChildren();
continue;
@@ -719,18 +750,47 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineFormattingContext(
MoveToNextItem();
}
- if (closest_line_after) {
- MoveTo(closest_line_after);
- if (const PositionWithAffinity child_position =
- PositionForPointInInlineBox(point))
+ // At here, |point| is not inside any line in |this|:
+ // |closest_line_before|
+ // |point|
+ // |closest_line_after|
+ if (closest_line_before) {
+ MoveTo(closest_line_before);
+ // Note: |move_caret_to_boundary| is true for Mac and Unix.
+ const bool move_caret_to_boundary =
+ To<LayoutBlockFlow>(Current().GetLayoutObject())
+ ->ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
+ if (move_caret_to_boundary) {
+ // Tests[1-3] reach here.
+ // [1] editing/selection/click-in-margins-inside-editable-div.html
+ // [2] fast/writing-mode/flipped-blocks-hit-test-line-edges.html
+ // [3] All/LayoutViewHitTestTest.HitTestHorizontal/4
+ if (auto first_position = PositionForStartOfLine())
+ return PositionWithAffinity(first_position.GetPosition());
+ } else 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))
+ if (closest_line_after) {
+ MoveTo(closest_line_after);
+ // Note: |move_caret_to_boundary| is true for Mac and Unix.
+ const bool move_caret_to_boundary =
+ To<LayoutBlockFlow>(Current().GetLayoutObject())
+ ->ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
+ if (move_caret_to_boundary) {
+ // Tests[1-3] reach here.
+ // [1] editing/selection/click-in-margins-inside-editable-div.html
+ // [2] fast/writing-mode/flipped-blocks-hit-test-line-edges.html
+ // [3] All/LayoutViewHitTestTest.HitTestHorizontal/4
+ if (auto last_position = PositionForEndOfLine())
+ return PositionWithAffinity(last_position.GetPosition());
+ } else if (const PositionWithAffinity child_position =
+ PositionForPointInInlineBox(point)) {
+ // Test[1] reaches here.
+ // [1] editing/selection/last-empty-inline.html
return child_position;
+ }
}
return PositionWithAffinity();
@@ -802,14 +862,14 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineBox(
}
if (const PositionWithAffinity child_position =
- descendants.PositionForPointInChild(point, *child_item))
+ descendants.PositionForPointInChild(point))
return child_position;
}
if (closest_child_after) {
descendants.MoveTo(closest_child_after);
if (const PositionWithAffinity child_position =
- descendants.PositionForPointInChild(point, *closest_child_after))
+ descendants.PositionForPointInChild(point))
return child_position;
// TODO(yosin): we should do like "closest_child_before" once we have a
// case.
@@ -818,7 +878,7 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineBox(
if (closest_child_before) {
descendants.MoveTo(closest_child_before);
if (const PositionWithAffinity child_position =
- descendants.PositionForPointInChild(point, *closest_child_before))
+ descendants.PositionForPointInChild(point))
return child_position;
if (closest_child_before->BoxFragment()) {
// LayoutViewHitTest.HitTestHorizontal "Top-right corner (outside) of div"
@@ -827,36 +887,22 @@ PositionWithAffinity NGInlineCursor::PositionForPointInInlineBox(
}
}
- if (container->Type() == NGFragmentItem::kLine) {
- // There are no inline items to hit in this line box, e.g. <span> with
- // size and border. We try in lines before |this| line in the block.
- // See editing/selection/last-empty-inline.html
- NGInlineCursor cursor;
- cursor.MoveTo(*this);
- const PhysicalOffset point_in_line =
- point - Current().OffsetInContainerBlock();
- for (;;) {
- cursor.MoveToPreviousLine();
- if (!cursor)
- break;
- const PhysicalOffset adjusted_point =
- point_in_line + cursor.Current().OffsetInContainerBlock();
- if (auto position = cursor.PositionForPointInInlineBox(adjusted_point))
- return position;
- }
- }
-
return PositionWithAffinity();
}
PositionWithAffinity NGInlineCursor::PositionForPointInChild(
- const PhysicalOffset& point,
- const NGFragmentItem& child_item) const {
- DCHECK_EQ(&child_item, CurrentItem());
+ const PhysicalOffset& point_in_container) const {
+ if (auto* paint_fragment = CurrentPaintFragment()) {
+ const PhysicalOffset point_in_child =
+ point_in_container - paint_fragment->OffsetInContainerBlock();
+ return paint_fragment->PositionForPoint(point_in_child);
+ }
+ DCHECK(CurrentItem());
+ const NGFragmentItem& child_item = *CurrentItem();
switch (child_item.Type()) {
case NGFragmentItem::kText:
return child_item.PositionForPointInText(
- point - child_item.OffsetInContainerBlock(), *this);
+ point_in_container - child_item.OffsetInContainerBlock(), *this);
case NGFragmentItem::kGeneratedText:
break;
case NGFragmentItem::kBox:
@@ -867,9 +913,9 @@ PositionWithAffinity NGInlineCursor::PositionForPointInChild(
// 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()) {
+ if (!box_fragment->IsInlineBox()) {
return child_item.GetLayoutObject()->PositionForPoint(
- point - child_item.OffsetInContainerBlock());
+ point_in_container - child_item.OffsetInContainerBlock());
}
} else {
// |LayoutInline| used to be culled.
@@ -883,6 +929,26 @@ PositionWithAffinity NGInlineCursor::PositionForPointInChild(
return PositionWithAffinity();
}
+PositionWithAffinity NGInlineCursor::PositionForStartOfLine() const {
+ DCHECK(Current().IsLineBox());
+ const PhysicalOffset point_in_line = Current().LineStartPoint();
+ if (IsItemCursor()) {
+ return PositionForPointInInlineBox(point_in_line +
+ Current().OffsetInContainerBlock());
+ }
+ return CurrentPaintFragment()->PositionForPoint(point_in_line);
+}
+
+PositionWithAffinity NGInlineCursor::PositionForEndOfLine() const {
+ DCHECK(Current().IsLineBox());
+ const PhysicalOffset point_in_line = Current().LineEndPoint();
+ if (IsItemCursor()) {
+ return PositionForPointInInlineBox(point_in_line +
+ Current().OffsetInContainerBlock());
+ }
+ return CurrentPaintFragment()->PositionForPoint(point_in_line);
+}
+
void NGInlineCursor::MoveTo(const NGInlineCursorPosition& position) {
CheckValid(position);
current_ = position;
@@ -913,7 +979,7 @@ NGInlineCursor::ItemsSpan::iterator NGInlineCursor::SlowFirstItemIteratorFor(
const LayoutObject& layout_object,
const ItemsSpan& items) {
for (ItemsSpan::iterator iter = items.begin(); iter != items.end(); ++iter) {
- if ((*iter)->GetLayoutObject() == &layout_object)
+ if (iter->GetLayoutObject() == &layout_object)
return iter;
}
return items.end();
@@ -992,7 +1058,7 @@ bool NGInlineCursor::IsAtFirst() const {
if (const NGPaintFragment* paint_fragment = Current().PaintFragment())
return paint_fragment == root_paint_fragment_->FirstChild();
if (const NGFragmentItem* item = Current().Item())
- return item == items_.front().get();
+ return item == &items_.front();
return false;
}
@@ -1022,7 +1088,7 @@ void NGInlineCursor::MoveToFirstLine() {
if (IsItemCursor()) {
auto iter = std::find_if(
items_.begin(), items_.end(),
- [](const auto& item) { return item->Type() == NGFragmentItem::kLine; });
+ [](const auto& item) { return item.Type() == NGFragmentItem::kLine; });
if (iter != items_.end()) {
MoveToItem(iter);
return;
@@ -1058,7 +1124,7 @@ void NGInlineCursor::MoveToLastLine() {
DCHECK(IsItemCursor());
auto iter = std::find_if(
items_.rbegin(), items_.rend(),
- [](const auto& item) { return item->Type() == NGFragmentItem::kLine; });
+ [](const auto& item) { return item.Type() == NGFragmentItem::kLine; });
if (iter != items_.rend())
MoveToItem(std::next(iter).base());
else
@@ -1198,6 +1264,15 @@ bool NGInlineCursor::TryToMoveToFirstChild() {
return true;
}
+bool NGInlineCursor::TryToMoveToFirstInlineLeafChild() {
+ while (IsNotNull()) {
+ if (Current().IsInlineLeaf())
+ return true;
+ MoveToNext();
+ }
+ return false;
+}
+
bool NGInlineCursor::TryToMoveToLastChild() {
if (!Current().HasChildren())
return false;
@@ -1226,7 +1301,7 @@ void NGInlineCursor::MoveToNextItem() {
return;
DCHECK(current_.item_iter_ != items_.end());
if (++current_.item_iter_ != items_.end()) {
- current_.item_ = current_.item_iter_->get();
+ current_.item_ = &*current_.item_iter_;
return;
}
MakeNull();
@@ -1250,7 +1325,7 @@ void NGInlineCursor::MoveToPreviousItem() {
if (current_.item_iter_ == items_.begin())
return MakeNull();
--current_.item_iter_;
- current_.item_ = current_.item_iter_->get();
+ current_.item_ = &*current_.item_iter_;
}
void NGInlineCursor::MoveToParentPaintFragment() {
@@ -1318,46 +1393,79 @@ void NGInlineCursor::MoveToPreviousSiblingPaintFragment() {
void NGInlineCursor::MoveTo(const LayoutObject& layout_object) {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
- DCHECK(!layout_object.IsFloatingOrOutOfFlowPositioned());
- // If this cursor is rootless, find the root of the inline formatting context.
- bool had_root = true;
- if (!HasRoot()) {
- had_root = false;
- const LayoutBlockFlow& root = *layout_object.RootInlineFormattingContext();
- DCHECK(&root);
- SetRoot(root);
+ DCHECK(!layout_object.IsOutOfFlowPositioned());
+
+ if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ // If this cursor is rootless, find the root of the inline formatting
+ // context.
if (!HasRoot()) {
- if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
- MakeNull();
+ const LayoutBlockFlow& root =
+ *layout_object.RootInlineFormattingContext();
+ DCHECK(&root);
+ SetRoot(root);
+ if (!HasRoot()) {
+ const auto fragments =
+ NGPaintFragment::InlineFragmentsFor(&layout_object);
+ if (!fragments.IsInLayoutNGInlineFormattingContext() ||
+ fragments.IsEmpty())
+ return MakeNull();
+ // external/wpt/css/css-scroll-anchoring/text-anchor-in-vertical-rl.html
+ // reaches here.
+ root_paint_fragment_ = fragments.front().Root();
+ }
+ DCHECK(HasRoot());
+ }
+
+ const auto fragments = NGPaintFragment::InlineFragmentsFor(&layout_object);
+ if (!fragments.IsEmpty()) {
+ // If |this| is IFC root, just move to the first fragment.
+ if (!root_paint_fragment_->Parent()) {
+ DCHECK(fragments.front().IsDescendantOfNotSelf(*root_paint_fragment_));
+ MoveTo(fragments.front());
return;
}
- const auto fragments =
- NGPaintFragment::InlineFragmentsFor(&layout_object);
- if (!fragments.IsInLayoutNGInlineFormattingContext() ||
- fragments.IsEmpty())
- return MakeNull();
- // external/wpt/css/css-scroll-anchoring/text-anchor-in-vertical-rl.html
- // reaches here.
- root_paint_fragment_ = fragments.front().Root();
+ // If |this| is limited, find the first fragment in the range.
+ for (const auto* fragment : fragments) {
+ if (fragment->IsDescendantOfNotSelf(*root_paint_fragment_)) {
+ MoveTo(*fragment);
+ return;
+ }
+ }
}
+ return MakeNull();
}
- if (fragment_items_) {
- wtf_size_t item_index = layout_object.FirstInlineFragmentItemIndex();
- if (!item_index) {
- DCHECK_EQ(SlowFirstItemIndexFor(layout_object, fragment_items_->Items()),
- fragment_items_->Size());
+
+ // If this cursor is rootless, find the root of the inline formatting context.
+ if (!HasRoot()) {
+ const LayoutBlockFlow* root = layout_object.RootInlineFormattingContext();
+ DCHECK(root);
+ const NGFragmentItems* fragment_items = root->FragmentItems();
+ if (UNLIKELY(!fragment_items)) {
MakeNull();
return;
}
- // |FirstInlineFragmentItemIndex| is 1-based. Convert to 0-based index.
- --item_index;
+ SetRoot(*fragment_items);
+ DCHECK(HasRoot());
+ }
+
+ wtf_size_t item_index = layout_object.FirstInlineFragmentItemIndex();
+ if (UNLIKELY(!item_index)) {
DCHECK_EQ(SlowFirstItemIndexFor(layout_object, fragment_items_->Items()),
- item_index);
+ fragment_items_->Size());
+ MakeNull();
+ return;
+ }
+
+ // |FirstInlineFragmentItemIndex| is 1-based. Convert to 0-based index.
+ --item_index;
+ DCHECK_EQ(SlowFirstItemIndexFor(layout_object, fragment_items_->Items()),
+ item_index);
- // Skip items before |items_|, in case |this| is part of IFC.
+ // Skip items before |items_|, in case |this| is part of IFC.
+ if (UNLIKELY(!fragment_items_->Equals(items_))) {
const wtf_size_t span_begin_item_index = SpanBeginItemIndex();
- while (item_index < span_begin_item_index) {
- const NGFragmentItem& item = *fragment_items_->Items()[item_index];
+ while (UNLIKELY(item_index < span_begin_item_index)) {
+ const NGFragmentItem& item = fragment_items_->Items()[item_index];
const wtf_size_t next_delta = item.DeltaToNextForSameLayoutObject();
if (!next_delta) {
MakeNull();
@@ -1365,21 +1473,15 @@ void NGInlineCursor::MoveTo(const LayoutObject& layout_object) {
}
item_index += next_delta;
}
- if (item_index >= span_begin_item_index + items_.size()) {
+ if (UNLIKELY(item_index >= span_begin_item_index + items_.size())) {
MakeNull();
return;
}
-
- const wtf_size_t span_index = item_index - span_begin_item_index;
- DCHECK_LT(span_index, items_.size());
- return MoveToItem(items_.begin() + span_index);
- }
- if (root_paint_fragment_) {
- const auto fragments = NGPaintFragment::InlineFragmentsFor(&layout_object);
- if (!fragments.IsInLayoutNGInlineFormattingContext() || fragments.IsEmpty())
- return MakeNull();
- return MoveTo(fragments.front());
+ item_index -= span_begin_item_index;
}
+
+ DCHECK_LT(item_index, items_.size());
+ current_.Set(items_.begin() + item_index);
}
void NGInlineCursor::MoveToIncludingCulledInline(
@@ -1412,18 +1514,34 @@ void NGInlineCursor::MoveToNextForSameLayoutObject() {
if (current_.paint_fragment_) {
if (auto* paint_fragment =
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();
- return MoveTo(*paint_fragment);
+ if (!root_paint_fragment_->Parent()) {
+ // |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();
+ MoveTo(*paint_fragment);
+ return;
+ }
+ // If |this| is limited, make sure the result is in the range.
+ if (paint_fragment->IsDescendantOfNotSelf(*root_paint_fragment_)) {
+ MoveTo(*paint_fragment);
+ return;
+ }
}
return MakeNull();
}
if (current_.item_) {
const wtf_size_t delta = current_.item_->DeltaToNextForSameLayoutObject();
- if (delta == 0u)
- return MakeNull();
- return MoveToItem(current_.item_iter_ + delta);
+ if (delta) {
+ // Check the next item is in |items_| because |delta| can be beyond
+ // |end()| if |this| is limited.
+ const wtf_size_t delta_to_end = items_.end() - current_.item_iter_;
+ if (delta < delta_to_end) {
+ MoveToItem(current_.item_iter_ + delta);
+ return;
+ }
+ DCHECK(!fragment_items_->Equals(items_));
+ }
+ MakeNull();
}
}
@@ -1458,10 +1576,8 @@ NGInlineBackwardCursor::NGInlineBackwardCursor(const NGInlineCursor& cursor)
sibling.MoveToNextSkippingChildren())
sibling_item_iterators_.push_back(sibling.Current().item_iter_);
current_index_ = sibling_item_iterators_.size();
- if (current_index_) {
- current_.item_iter_ = sibling_item_iterators_[--current_index_];
- current_.item_ = current_.item_iter_->get();
- }
+ if (current_index_)
+ current_.Set(sibling_item_iterators_[--current_index_]);
return;
}
DCHECK(!cursor);
@@ -1486,8 +1602,7 @@ void NGInlineBackwardCursor::MoveToPreviousSibling() {
return;
}
if (current_.item_) {
- current_.item_iter_ = sibling_item_iterators_[--current_index_];
- current_.item_ = current_.item_iter_->get();
+ current_.Set(sibling_item_iterators_[--current_index_]);
return;
}
NOTREACHED();
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 6e2b0d842c2..7bd011eb716 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
@@ -10,6 +10,7 @@
#include "base/containers/span.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/forward.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -43,7 +44,7 @@ struct PhysicalSize;
// is faster than moving to |NGFragmentItem|.
class CORE_EXPORT NGInlineCursorPosition {
public:
- using ItemsSpan = base::span<const scoped_refptr<const NGFragmentItem>>;
+ using ItemsSpan = NGFragmentItems::Span;
const NGPaintFragment* PaintFragment() const { return paint_fragment_; }
const NGFragmentItem* Item() const { return item_; }
@@ -109,6 +110,7 @@ class CORE_EXPORT NGInlineCursorPosition {
LayoutObject* GetMutableLayoutObject() const;
const Node* GetNode() const;
const DisplayItemClient* GetDisplayItemClient() const;
+ wtf_size_t FragmentId() const;
// True if fragment at the current position can have children.
bool CanHaveChildren() const;
@@ -154,6 +156,10 @@ class CORE_EXPORT NGInlineCursorPosition {
// line.
TextDirection BaseDirection() const;
+ TextDirection ResolvedOrBaseDirection() const {
+ return IsLineBox() ? BaseDirection() : ResolvedDirection();
+ }
+
// 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,
@@ -164,12 +170,25 @@ class CORE_EXPORT NGInlineCursorPosition {
// other than line.
bool HasSoftWrapToNextLine() const;
- // Returns a point at the visual start/end of the line.
+ // Returns a point at the visual start/end of the line. (0, 0) is left-top of
+ // the line.
// Encapsulates the handling of text direction and writing mode.
PhysicalOffset LineStartPoint() const;
PhysicalOffset LineEndPoint() const;
+ // LogicalRect/PhysicalRect conversions
+ // |logical_rect| and |physical_rect| are converted with |Size()| as
+ // "outer size".
+ LogicalRect ConvertChildToLogical(const PhysicalRect& physical_rect) const;
+ PhysicalRect ConvertChildToPhysical(const LogicalRect& logical_rect) const;
+
private:
+ void Set(const ItemsSpan::iterator& iter) {
+ DCHECK(!paint_fragment_);
+ item_iter_ = iter;
+ item_ = &*iter;
+ }
+
void Clear() {
paint_fragment_ = nullptr;
item_ = nullptr;
@@ -197,7 +216,7 @@ class CORE_EXPORT NGInlineCursor {
STACK_ALLOCATED();
public:
- using ItemsSpan = base::span<const scoped_refptr<const NGFragmentItem>>;
+ using ItemsSpan = NGFragmentItems::Span;
explicit NGInlineCursor(const LayoutBlockFlow& block_flow);
explicit NGInlineCursor(const NGFragmentItems& items);
@@ -296,6 +315,20 @@ class CORE_EXPORT NGInlineCursor {
PositionWithAffinity PositionForPointInInlineBox(
const PhysicalOffset& point) const;
+ // Returns |PositionWithAffinity| in current position at x-coordinate of
+ // |point_in_container| for horizontal writing mode, or y-coordinate of
+ // |point_in_container| for vertical writing mode.
+ // Note: Even if |point_in_container| is outside of an item of current
+ // position, this function returns boundary position of an item.
+ // Note: This function is used for locating caret at same x/y-coordinate as
+ // previous caret after line up/down.
+ PositionWithAffinity PositionForPointInChild(
+ const PhysicalOffset& point_in_container) const;
+
+ // Returns first/last position of |this| line. |this| should be line box.
+ PositionWithAffinity PositionForStartOfLine() const;
+ PositionWithAffinity PositionForEndOfLine() const;
+
//
// Functions to move the current position.
//
@@ -376,6 +409,9 @@ class CORE_EXPORT NGInlineCursor {
// Returns true if the current position moves to first child.
bool TryToMoveToFirstChild();
+ // Returns true if the current position moves to first inline leaf child.
+ bool TryToMoveToFirstInlineLeafChild();
+
// Returns true if the current position moves to last child.
bool TryToMoveToLastChild();
@@ -434,10 +470,6 @@ class CORE_EXPORT NGInlineCursor {
wtf_size_t SpanBeginItemIndex() const;
wtf_size_t SpanIndexFromItemIndex(unsigned index) const;
- PositionWithAffinity PositionForPointInChild(
- const PhysicalOffset& point,
- const NGFragmentItem& child_item) const;
-
NGInlineCursorPosition current_;
ItemsSpan items_;
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 580588369e4..896d6a9e60c 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
@@ -628,6 +628,46 @@ TEST_P(NGInlineCursorTest, NextForSameLayoutObject) {
EXPECT_THAT(list, ElementsAre("abc", "", "def", "", "ghi"));
}
+// Test |NextForSameLayoutObject| with limit range set.
+TEST_P(NGInlineCursorTest, NextForSameLayoutObjectWithRange) {
+ // In this snippet, `<span>` wraps to 3 lines, and that there are 3 fragments
+ // for `<span>`.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ div {
+ font-size: 10px;
+ width: 5ch;
+ }
+ span {
+ background: orange;
+ }
+ </style>
+ <div id="root">
+ <span id="span1">
+ 1111
+ 2222
+ 3333
+ </span>
+ </div>
+ )HTML");
+ LayoutBlockFlow* root =
+ To<LayoutBlockFlow>(GetLayoutObjectByElementId("root"));
+ NGInlineCursor cursor(*root);
+ cursor.MoveToFirstLine();
+ cursor.MoveToNextLine();
+ NGInlineCursor line2 = cursor.CursorForDescendants();
+
+ // Now |line2| is limited to the 2nd line. There should be only one framgnet
+ // for `<span>` if we search using `line2`.
+ LayoutObject* span1 = GetLayoutObjectByElementId("span1");
+ wtf_size_t count = 0;
+ for (line2.MoveTo(*span1); line2; line2.MoveToNextForSameLayoutObject()) {
+ DCHECK_EQ(line2.Current().GetLayoutObject(), span1);
+ ++count;
+ }
+ EXPECT_EQ(count, 1u);
+}
+
TEST_P(NGInlineCursorTest, Sibling) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
@@ -698,6 +738,152 @@ TEST_P(NGInlineCursorTest, EmptyOutOfFlow) {
EXPECT_THAT(list, ElementsAre());
}
+TEST_P(NGInlineCursorTest, PositionForPointInChildHorizontalLTR) {
+ LoadAhem();
+ InsertStyleElement(
+ "p {"
+ "direction: ltr;"
+ "font: 10px/20px Ahem;"
+ "padding: 10px;"
+ "writing-mode: horizontal-tb;"
+ "}");
+ NGInlineCursor cursor = SetupCursor("<p id=root>ab</p>");
+ const auto& text = *To<Text>(GetElementById("root")->firstChild());
+ ASSERT_TRUE(cursor.Current().IsLineBox());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
+ cursor.Current().RectInContainerBlock());
+
+ cursor.MoveTo(*text.GetLayoutObject());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 15), PhysicalSize(20, 10)),
+ cursor.Current().RectInContainerBlock());
+ const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(5, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(10, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(15, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(20, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(25, 0)));
+}
+
+TEST_P(NGInlineCursorTest, PositionForPointInChildHorizontalRTL) {
+ LoadAhem();
+ InsertStyleElement(
+ "p {"
+ "direction: rtl;"
+ "font: 10px/20px Ahem;"
+ "padding: 10px;"
+ "writing-mode: horizontal-tb;"
+ "}");
+ NGInlineCursor cursor = SetupCursor("<p id=root><bdo dir=rtl>AB</bdo></p>");
+ const auto& text =
+ *To<Text>(GetElementById("root")->firstChild()->firstChild());
+ ASSERT_TRUE(cursor.Current().IsLineBox());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 10), PhysicalSize(20, 20)),
+ cursor.Current().RectInContainerBlock());
+
+ cursor.MoveTo(*text.GetLayoutObject());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(754, 15), PhysicalSize(20, 10)),
+ cursor.Current().RectInContainerBlock());
+ const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(-5, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(5, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(10, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(15, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(20, 0)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(25, 0)));
+}
+
+TEST_P(NGInlineCursorTest, PositionForPointInChildVerticalLTR) {
+ LoadAhem();
+ InsertStyleElement(
+ "p {"
+ "direction: ltr;"
+ "font: 10px/20px Ahem;"
+ "padding: 10px;"
+ "writing-mode: vertical-lr;"
+ "}");
+ NGInlineCursor cursor = SetupCursor("<p id=root>ab</p>");
+ const auto& text = *To<Text>(GetElementById("root")->firstChild());
+ ASSERT_TRUE(cursor.Current().IsLineBox());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
+ cursor.Current().RectInContainerBlock());
+
+ cursor.MoveTo(*text.GetLayoutObject());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
+ cursor.Current().RectInContainerBlock());
+ const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 5)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 10)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 15)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 20)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 25)));
+}
+
+TEST_P(NGInlineCursorTest, PositionForPointInChildVerticalRTL) {
+ LoadAhem();
+ InsertStyleElement(
+ "p {"
+ "direction: rtl;"
+ "font: 10px/20px Ahem;"
+ "padding: 10px;"
+ "writing-mode: vertical-rl;"
+ "}");
+ NGInlineCursor cursor = SetupCursor("<p id=root><bdo dir=rtl>AB</bdo></p>");
+ const auto& text =
+ *To<Text>(GetElementById("root")->firstChild()->firstChild());
+ ASSERT_TRUE(cursor.Current().IsLineBox());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(10, 10), PhysicalSize(20, 20)),
+ cursor.Current().RectInContainerBlock());
+
+ cursor.MoveTo(*text.GetLayoutObject());
+ EXPECT_EQ(PhysicalRect(PhysicalOffset(15, 10), PhysicalSize(10, 20)),
+ cursor.Current().RectInContainerBlock());
+ const PhysicalOffset left_top = cursor.Current().OffsetInContainerBlock();
+
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, -5)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 2), TextAffinity::kUpstream),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 5)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 10)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 1)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 15)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 20)));
+ EXPECT_EQ(PositionWithAffinity(Position(text, 0)),
+ cursor.PositionForPointInChild(left_top + PhysicalOffset(0, 25)));
+}
+
TEST_P(NGInlineCursorTest, Previous) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
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 531527710c8..d7aa21969b1 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
@@ -115,6 +115,8 @@ class CORE_EXPORT NGInlineItem {
unsigned EndOffset() const { return end_offset_; }
unsigned Length() const { return end_offset_ - start_offset_; }
+ bool IsValidOffset(unsigned offset) const;
+
TextDirection Direction() const { return DirectionFromLevel(BidiLevel()); }
UBiDiLevel BidiLevel() const { return static_cast<UBiDiLevel>(bidi_level_); }
// Resolved bidi level for the reordering algorithm. Certain items have
@@ -131,6 +133,9 @@ class CORE_EXPORT NGInlineItem {
bool IsImage() const {
return GetLayoutObject() && GetLayoutObject()->IsLayoutImage();
}
+ bool IsRubyRun() const {
+ return GetLayoutObject() && GetLayoutObject()->IsRubyRun();
+ }
void SetOffset(unsigned start, unsigned end) {
DCHECK_GE(end, start);
@@ -184,6 +189,10 @@ class CORE_EXPORT NGInlineItem {
(Type() == NGInlineItem::kControl && type == kCollapsible));
end_collapse_type_ = type;
}
+ bool IsCollapsibleSpaceOnly() const {
+ return Type() == NGInlineItem::kText &&
+ end_collapse_type_ == kCollapsible && Length() == 1u;
+ }
// True if this item was generated (not in DOM).
// NGInlineItemsBuilder may generate break opportunitites to express the
@@ -229,7 +238,7 @@ class CORE_EXPORT NGInlineItem {
unsigned end_offset,
UBiDiLevel);
- void AssertOffset(unsigned offset) const;
+ void AssertOffset(unsigned offset) const { DCHECK(IsValidOffset(offset)); }
void AssertEndOffset(unsigned offset) const;
String ToString() const;
@@ -257,9 +266,9 @@ class CORE_EXPORT NGInlineItem {
friend class NGInlineNodeDataEditor;
};
-inline void NGInlineItem::AssertOffset(unsigned offset) const {
- DCHECK((offset >= start_offset_ && offset < end_offset_) ||
- (offset == start_offset_ && start_offset_ == end_offset_));
+inline bool NGInlineItem::IsValidOffset(unsigned offset) const {
+ return (offset >= start_offset_ && offset < end_offset_) ||
+ (start_offset_ == end_offset_ && offset == start_offset_);
}
inline void NGInlineItem::AssertEndOffset(unsigned offset) const {
@@ -287,6 +296,10 @@ struct CORE_EXPORT NGInlineItemsData {
// The DOM to text content offset mapping of this inline node.
std::unique_ptr<NGOffsetMapping> offset_mapping;
+ bool IsValidOffset(unsigned index, unsigned offset) const {
+ return index < items.size() && items[index].IsValidOffset(offset);
+ }
+
void AssertOffset(unsigned index, unsigned offset) const {
items[index].AssertOffset(offset);
}
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 4fa2ff96298..592f46b652d 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
@@ -10,20 +10,17 @@
namespace blink {
-NGInlineItemResult::NGInlineItemResult()
- : item(nullptr), item_index(0), start_offset(0), end_offset(0) {}
+NGInlineItemResult::NGInlineItemResult() : item(nullptr), item_index(0) {}
NGInlineItemResult::NGInlineItemResult(const NGInlineItem* item,
unsigned index,
- unsigned start,
- unsigned end,
+ const NGTextOffset& text_offset,
bool break_anywhere_if_overflow,
bool should_create_line_box,
bool has_unpositioned_floats)
: item(item),
item_index(index),
- start_offset(start),
- end_offset(end),
+ text_offset(text_offset),
break_anywhere_if_overflow(break_anywhere_if_overflow),
should_create_line_box(should_create_line_box),
has_unpositioned_floats(has_unpositioned_floats) {}
@@ -33,16 +30,31 @@ void NGLineInfo::SetLineStyle(const NGInlineNode& node,
bool use_first_line_style) {
use_first_line_style_ = use_first_line_style;
items_data_ = &items_data;
- line_style_ = node.GetLayoutBox()->Style(use_first_line_style_);
+ const LayoutBox* box = node.GetLayoutBox();
+ line_style_ = box->Style(use_first_line_style_);
needs_accurate_end_position_ = ComputeNeedsAccurateEndPosition();
+ is_ruby_base_ = box->IsRubyBase();
+ is_ruby_text_ = box->IsRubyText();
+}
+
+ETextAlign NGLineInfo::GetTextAlign(bool is_last_line) const {
+ // See LayoutRubyBase::TextAlignmentForLine().
+ if (is_ruby_base_)
+ return ETextAlign::kJustify;
+
+ // See LayoutRubyText::TextAlignmentForLine().
+ if (is_ruby_text_ && LineStyle().GetTextAlign() ==
+ ComputedStyleInitialValues::InitialTextAlign())
+ return ETextAlign::kJustify;
+
+ return LineStyle().GetTextAlign(is_last_line);
}
bool NGLineInfo::ComputeNeedsAccurateEndPosition() const {
// Some 'text-align' values need accurate end position. At this point, we
// don't know if this is the last line or not, and thus we don't know whether
// 'text-align' is used or 'text-align-last' is used.
- const ComputedStyle& line_style = LineStyle();
- switch (line_style.GetTextAlign()) {
+ switch (GetTextAlign()) {
case ETextAlign::kStart:
break;
case ETextAlign::kEnd:
@@ -61,7 +73,16 @@ bool NGLineInfo::ComputeNeedsAccurateEndPosition() const {
return true;
break;
}
- switch (line_style.TextAlignLast()) {
+ ETextAlignLast align_last = LineStyle().TextAlignLast();
+ if (is_ruby_base_) {
+ // See LayoutRubyBase::TextAlignmentForLine().
+ align_last = ETextAlignLast::kJustify;
+ } else if (is_ruby_text_ &&
+ align_last == ComputedStyleInitialValues::InitialTextAlignLast()) {
+ // See LayoutRubyText::TextAlignmentForLine().
+ align_last = ETextAlignLast::kJustify;
+ }
+ switch (align_last) {
case ETextAlignLast::kStart:
case ETextAlignLast::kAuto:
return false;
@@ -85,13 +106,13 @@ bool NGLineInfo::ComputeNeedsAccurateEndPosition() const {
void NGInlineItemResult::CheckConsistency(bool allow_null_shape_result) const {
DCHECK(item);
if (item->Type() == NGInlineItem::kText) {
- DCHECK_LT(start_offset, end_offset);
+ text_offset.AssertNotEmpty();
if (allow_null_shape_result && !shape_result)
return;
DCHECK(shape_result);
- DCHECK_EQ(end_offset - start_offset, shape_result->NumCharacters());
- DCHECK_EQ(start_offset, shape_result->StartIndex());
- DCHECK_EQ(end_offset, shape_result->EndIndex());
+ DCHECK_EQ(Length(), shape_result->NumCharacters());
+ DCHECK_EQ(StartOffset(), shape_result->StartIndex());
+ DCHECK_EQ(EndOffset(), shape_result->EndIndex());
}
}
#endif
@@ -105,7 +126,7 @@ unsigned NGLineInfo::InflowEndOffset() const {
if (item.Type() == NGInlineItem::kText ||
item.Type() == NGInlineItem::kControl ||
item.Type() == NGInlineItem::kAtomicInline)
- return item_result.end_offset;
+ return item_result.EndOffset();
}
return StartOffset();
}
@@ -133,7 +154,7 @@ bool NGLineInfo::ShouldHangTrailingSpaces() const {
}
void NGLineInfo::UpdateTextAlign() {
- text_align_ = line_style_->GetTextAlign(IsLastLine());
+ text_align_ = GetTextAlign(IsLastLine());
if (HasTrailingSpaces() && ShouldHangTrailingSpaces()) {
hang_width_ = ComputeTrailingSpaceWidth(&end_offset_for_justify_);
@@ -175,18 +196,18 @@ LayoutUnit NGLineInfo::ComputeTrailingSpaceWidth(
// The last text item may contain trailing spaces if this is a last line,
// has a forced break, or is 'white-space: pre'.
- unsigned end_offset = item_result.end_offset;
+ unsigned end_offset = item_result.EndOffset();
DCHECK(end_offset);
if (item.Type() == NGInlineItem::kText) {
const String& text = items_data_->text_content;
if (end_offset && text[end_offset - 1] == kSpaceCharacter) {
do {
--end_offset;
- } while (end_offset > item_result.start_offset &&
+ } while (end_offset > item_result.StartOffset() &&
text[end_offset - 1] == kSpaceCharacter);
// If all characters in this item_result are spaces, check next item.
- if (end_offset == item_result.start_offset) {
+ if (end_offset == item_result.StartOffset()) {
trailing_spaces_width += item_result.inline_size;
continue;
}
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 66adbd9e850..eb31318da41 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
@@ -32,11 +32,10 @@ struct CORE_EXPORT NGInlineItemResult {
DISALLOW_NEW();
public:
- NGTextOffset TextOffset() const { return {start_offset, end_offset}; }
- unsigned Length() const {
- DCHECK_GT(end_offset, start_offset);
- return end_offset - start_offset;
- }
+ const NGTextOffset& TextOffset() const { return text_offset; }
+ unsigned StartOffset() const { return text_offset.start; }
+ unsigned EndOffset() const { return text_offset.end; }
+ unsigned Length() const { return text_offset.Length(); }
LayoutUnit HyphenInlineSize() const {
return hyphen_shape_result->SnappedWidth().ClampNegativeToZero();
@@ -52,12 +51,15 @@ struct CORE_EXPORT NGInlineItemResult {
unsigned item_index;
// The range of text content for this item.
- unsigned start_offset;
- unsigned end_offset;
+ NGTextOffset text_offset;
// Inline size of this item.
LayoutUnit inline_size;
+ // Pending inline-end overhang amount for RubyRun.
+ // This is committed if a following item meets conditions.
+ LayoutUnit pending_end_overhang;
+
// ShapeResult for text items. Maybe different from NGInlineItem if re-shape
// is needed in the line breaker.
scoped_refptr<const ShapeResultView> shape_result;
@@ -129,8 +131,7 @@ struct CORE_EXPORT NGInlineItemResult {
NGInlineItemResult();
NGInlineItemResult(const NGInlineItem*,
unsigned index,
- unsigned start,
- unsigned end,
+ const NGTextOffset& text_offset,
bool break_anywhere_if_overflow,
bool should_create_line_box,
bool has_unpositioned_floats);
@@ -253,6 +254,7 @@ class CORE_EXPORT NGLineInfo {
bool NeedsAccurateEndPosition() const { return needs_accurate_end_position_; }
private:
+ ETextAlign GetTextAlign(bool is_last_line = false) const;
bool ComputeNeedsAccurateEndPosition() const;
// The width of preserved trailing spaces.
@@ -283,6 +285,8 @@ class CORE_EXPORT NGLineInfo {
bool has_overflow_ = false;
bool has_trailing_spaces_ = false;
bool needs_accurate_end_position_ = false;
+ bool is_ruby_base_ = false;
+ bool is_ruby_text_ = false;
};
} // namespace blink
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 6e66ff045ba..69eaf05758d 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
@@ -894,6 +894,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendAtomicInline(
RestoreTrailingCollapsibleSpaceIfRemoved();
Append(NGInlineItem::kAtomicInline, kObjectReplacementCharacter,
layout_object);
+ has_ruby_ = has_ruby_ || layout_object->IsRubyRun();
// When this atomic inline is inside of an inline box, the height of the
// inline box can be different from the height of the atomic inline. Ensure
@@ -980,7 +981,7 @@ void NGInlineItemsBuilderTemplate<
// Keep the item even if the length became zero. This is not needed for
// the layout purposes, but needed to maintain LayoutObject states. See
- // |AddEmptyTextItem()|.
+ // |AppendEmptyTextItem()|.
item->SetEndOffset(item->EndOffset() - 1);
item->SetEndCollapseType(NGInlineItem::kCollapsed);
@@ -1188,8 +1189,20 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::ExitInline(
break;
}
DCHECK_GT(i, current_box->item_index);
- if (!item.IsEmptyItem())
- break;
+ if (item.IsEmptyItem()) {
+ // float, abspos, collapsed space(<div>ab <span> </span>).
+ // See editing/caret/empty_inlines.html
+ // See also [1] for empty line box.
+ // [1] https://drafts.csswg.org/css2/visuren.html#phantom-line-box
+ continue;
+ }
+ if (item.IsCollapsibleSpaceOnly()) {
+ // Because we can't collapse trailing spaces until next node, we
+ // create box fragment for it: <div>ab<span> </span></div>
+ // See editing/selection/mixed-editability-10.html
+ continue;
+ }
+ break;
}
}
@@ -1226,6 +1239,11 @@ void NGInlineItemsBuilderTemplate<
// |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it
// doesn't contain any RTL characters.
data->is_bidi_enabled_ = MayBeBidiEnabled();
+ // Note: Even if |IsEmptyInline()| is true, |text_| isn't empty, e.g. it
+ // holds U+FFFC(ORC) for float or abspos.
+ data->has_line_even_if_empty_ =
+ IsEmptyInline() && block_flow_->HasLineIfEmpty();
+ data->has_ruby_ = has_ruby_;
data->is_empty_inline_ = IsEmptyInline();
data->is_block_level_ = IsBlockLevel();
data->changes_may_affect_earlier_lines_ = ChangesMayAffectEarlierLines();
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 a8e6d3f5907..5178c1ebc01 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
@@ -44,10 +44,13 @@ class NGInlineItemsBuilderTemplate {
public:
// Create a builder that appends items to |items|.
- explicit NGInlineItemsBuilderTemplate(Vector<NGInlineItem>* items)
- : items_(items) {}
+ NGInlineItemsBuilderTemplate(LayoutBlockFlow* block_flow,
+ Vector<NGInlineItem>* items)
+ : block_flow_(block_flow), items_(items) {}
~NGInlineItemsBuilderTemplate();
+ LayoutBlockFlow* GetLayoutBlockFlow() const { return block_flow_; }
+
String ToString();
// Returns whether the items contain any Bidi controls.
@@ -146,6 +149,7 @@ class NGInlineItemsBuilderTemplate {
private:
static bool NeedsBoxInfo();
+ LayoutBlockFlow* const block_flow_;
Vector<NGInlineItem>* items_;
StringBuilder text_;
@@ -177,6 +181,7 @@ class NGInlineItemsBuilderTemplate {
Vector<BidiContext> bidi_context_;
bool has_bidi_controls_ = false;
+ bool has_ruby_ = false;
bool is_empty_inline_ = true;
bool is_block_level_ = true;
bool changes_may_affect_earlier_lines_ = false;
@@ -226,6 +231,8 @@ class NGInlineItemsBuilderTemplate {
const ComputedStyle&,
LayoutText*,
unsigned* start);
+
+ friend class NGInlineItemsBuilderTest;
};
template <>
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 77aff76eecc..912b017a631 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
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
+#include "third_party/blink/renderer/core/layout/ng/layout_ng_ruby_run.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -28,6 +29,9 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
void SetUp() override {
NGLayoutTest::SetUp();
style_ = ComputedStyle::Create();
+ block_flow_ = LayoutBlockFlow::CreateAnonymous(&GetDocument(), style_,
+ LegacyLayout::kAuto);
+ anonymous_objects_.push_back(block_flow_);
}
void TearDown() override {
@@ -36,6 +40,8 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
NGLayoutTest::TearDown();
}
+ LayoutBlockFlow* GetLayoutBlockFlow() const { return block_flow_; }
+
void SetWhiteSpace(EWhiteSpace whitespace) {
style_->SetWhiteSpace(whitespace);
}
@@ -48,6 +54,10 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
return style;
}
+ bool HasRuby(const NGInlineItemsBuilder& builder) const {
+ return builder.has_ruby_;
+ }
+
void AppendText(const String& text, NGInlineItemsBuilder* builder) {
LayoutText* layout_text = LayoutText::CreateEmptyAnonymous(
GetDocument(), style_.get(), LegacyLayout::kAuto);
@@ -62,6 +72,14 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
builder->AppendAtomicInline(layout_block_flow);
}
+ void AppendRubyRun(NGInlineItemsBuilder* builder) {
+ LayoutNGRubyRun* ruby_run = new LayoutNGRubyRun();
+ ruby_run->SetDocumentForAnonymous(&GetDocument());
+ ruby_run->SetStyle(style_);
+ anonymous_objects_.push_back(ruby_run);
+ builder->AppendAtomicInline(ruby_run);
+ }
+
struct Input {
const String text;
EWhiteSpace whitespace = EWhiteSpace::kNormal;
@@ -71,7 +89,7 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
const String& TestAppend(Vector<Input> inputs) {
items_.clear();
Vector<LayoutText*> anonymous_objects;
- NGInlineItemsBuilder builder(&items_);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items_);
for (Input& input : inputs) {
if (!input.layout_text) {
input.layout_text = LayoutText::CreateEmptyAnonymous(
@@ -122,7 +140,7 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
fake_data.is_bidi_enabled_ = has_bidi_controls;
Vector<NGInlineItem> reuse_items;
- NGInlineItemsBuilder reuse_builder(&reuse_items);
+ NGInlineItemsBuilder reuse_builder(GetLayoutBlockFlow(), &reuse_items);
for (Input& input : inputs) {
// Collect items for this LayoutObject.
DCHECK(input.layout_text);
@@ -153,6 +171,7 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
EXPECT_EQ(text_, reuse_text);
}
+ LayoutBlockFlow* block_flow_ = nullptr;
Vector<NGInlineItem> items_;
String text_;
scoped_refptr<ComputedStyle> style_;
@@ -317,7 +336,7 @@ TEST_F(NGInlineItemsBuilderTest, CollapseEastAsianWidth) {
#endif
TEST_F(NGInlineItemsBuilderTest, OpaqueToSpaceCollapsing) {
- NGInlineItemsBuilder builder(&items_);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items_);
AppendText("Hello ", &builder);
builder.AppendOpaque(NGInlineItem::kBidiControl,
kFirstStrongIsolateCharacter);
@@ -329,7 +348,7 @@ TEST_F(NGInlineItemsBuilderTest, OpaqueToSpaceCollapsing) {
}
TEST_F(NGInlineItemsBuilderTest, CollapseAroundReplacedElement) {
- NGInlineItemsBuilder builder(&items_);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items_);
AppendText("Hello ", &builder);
AppendAtomicInline(&builder);
AppendText(" World", &builder);
@@ -337,7 +356,7 @@ TEST_F(NGInlineItemsBuilderTest, CollapseAroundReplacedElement) {
}
TEST_F(NGInlineItemsBuilderTest, CollapseNewlineAfterObject) {
- NGInlineItemsBuilder builder(&items_);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items_);
AppendAtomicInline(&builder);
AppendText("\n", &builder);
AppendAtomicInline(&builder);
@@ -389,7 +408,7 @@ TEST_F(NGInlineItemsBuilderTest, IgnorablePre) {
TEST_F(NGInlineItemsBuilderTest, Empty) {
Vector<NGInlineItem> items;
- NGInlineItemsBuilder builder(&items);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
scoped_refptr<ComputedStyle> block_style(ComputedStyle::Create());
builder.EnterBlock(block_style.get());
builder.ExitBlock();
@@ -431,7 +450,7 @@ TEST_F(NGInlineItemsBuilderTest, GenerateBreakOpportunityAfterLeadingSpaces) {
TEST_F(NGInlineItemsBuilderTest, BidiBlockOverride) {
Vector<NGInlineItem> items;
- NGInlineItemsBuilder builder(&items);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
scoped_refptr<ComputedStyle> block_style(ComputedStyle::Create());
block_style->SetUnicodeBidi(UnicodeBidi::kBidiOverride);
block_style->SetDirection(TextDirection::kRtl);
@@ -461,7 +480,7 @@ static LayoutInline* CreateLayoutInline(
TEST_F(NGInlineItemsBuilderTest, BidiIsolate) {
Vector<NGInlineItem> items;
- NGInlineItemsBuilder builder(&items);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
AppendText("Hello ", &builder);
LayoutInline* const isolate_rtl =
CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) {
@@ -486,7 +505,7 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolate) {
TEST_F(NGInlineItemsBuilderTest, BidiIsolateOverride) {
Vector<NGInlineItem> items;
- NGInlineItemsBuilder builder(&items);
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
AppendText("Hello ", &builder);
LayoutInline* const isolate_override_rtl =
CreateLayoutInline(&GetDocument(), [](ComputedStyle* style) {
@@ -509,4 +528,26 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolateOverride) {
isolate_override_rtl->Destroy();
}
+TEST_F(NGInlineItemsBuilderTest, HasRuby) {
+ Vector<NGInlineItem> items;
+ NGInlineItemsBuilder builder(GetLayoutBlockFlow(), &items);
+ EXPECT_FALSE(HasRuby(builder)) << "has_ruby_ should be false initially.";
+
+ AppendText("Hello ", &builder);
+ EXPECT_FALSE(HasRuby(builder))
+ << "Adding non-AtomicInline should not affect it.";
+
+ AppendAtomicInline(&builder);
+ EXPECT_FALSE(HasRuby(builder))
+ << "Adding non-ruby AtomicInline should not affect it.";
+
+ AppendRubyRun(&builder);
+ EXPECT_TRUE(HasRuby(builder))
+ << "Adding a ruby AtomicInline should set it to true.";
+
+ AppendAtomicInline(&builder);
+ EXPECT_TRUE(HasRuby(builder))
+ << "Adding non-ruby AtomicInline should not clear it.";
+}
+
} // 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 0ab10f3f371..81ead292e07 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
@@ -19,6 +19,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h"
#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_ruby_utils.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_outside_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
@@ -63,12 +64,14 @@ NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm(
// lays out in visual order.
TextDirection::kLtr,
break_token),
+ line_box_(*context->LogicalLineItems()),
box_states_(nullptr),
context_(context),
baseline_type_(container_builder_.Style().GetFontBaseline()),
is_horizontal_writing_mode_(
blink::IsHorizontalWritingMode(space.GetWritingMode())) {
DCHECK(context);
+ DCHECK(&line_box_);
quirks_mode_ = inline_node.InLineHeightQuirksMode();
}
@@ -79,7 +82,7 @@ NGInlineLayoutAlgorithm::~NGInlineLayoutAlgorithm() = default;
NGInlineBoxState* NGInlineLayoutAlgorithm::HandleOpenTag(
const NGInlineItem& item,
const NGInlineItemResult& item_result,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineLayoutStateStack* box_states) const {
NGInlineBoxState* box =
box_states->OnOpenTag(item, item_result, baseline_type_, line_box);
@@ -164,7 +167,7 @@ void NGInlineLayoutAlgorithm::RebuildBoxStates(
}
// Create box states for tags that are not closed yet.
- NGLineBoxFragmentBuilder::ChildList line_box;
+ NGLogicalLineItems line_box;
box_states->OnBeginPlaceItems(line_info.LineStyle(), baseline_type_,
quirks_mode_, &line_box);
for (const NGInlineItem* item : open_items) {
@@ -180,7 +183,7 @@ void NGInlineLayoutAlgorithm::CheckBoxStates(
const NGInlineBreakToken* break_token) const {
NGInlineLayoutStateStack rebuilt;
RebuildBoxStates(line_info, break_token, &rebuilt);
- NGLineBoxFragmentBuilder::ChildList line_box;
+ NGLogicalLineItems line_box;
rebuilt.OnBeginPlaceItems(line_info.LineStyle(), baseline_type_, quirks_mode_,
&line_box);
DCHECK(box_states_);
@@ -194,7 +197,8 @@ void NGInlineLayoutAlgorithm::CreateLine(
NGExclusionSpace* exclusion_space) {
// Needs MutableResults to move ShapeResult out of the NGLineInfo.
NGInlineItemResults* line_items = line_info->MutableResults();
- line_box_.resize(0);
+ // Clear the current line without releasing the buffer.
+ line_box_.Shrink(0);
// Apply justification before placing items, because it affects size/position
// of items, which are needed to compute inline static positions.
@@ -244,13 +248,16 @@ void NGInlineLayoutAlgorithm::CreateLine(
item.TextType() == NGTextType::kSymbolMarker);
if (UNLIKELY(item_result.hyphen_shape_result)) {
LayoutUnit hyphen_inline_size = item_result.HyphenInlineSize();
- line_box_.AddChild(&item_result, box->text_top,
+ line_box_.AddChild(item, std::move(item_result.shape_result),
+ item_result.TextOffset(), box->text_top,
item_result.inline_size - hyphen_inline_size,
box->text_height, item.BidiLevel());
PlaceHyphen(item_result, hyphen_inline_size, box);
} else {
- line_box_.AddChild(&item_result, box->text_top, item_result.inline_size,
- box->text_height, item.BidiLevel());
+ line_box_.AddChild(item, std::move(item_result.shape_result),
+ item_result.TextOffset(), box->text_top,
+ item_result.inline_size, box->text_height,
+ item.BidiLevel());
}
has_logical_text_items = true;
@@ -259,6 +266,7 @@ void NGInlineLayoutAlgorithm::CreateLine(
} else if (item.Type() == NGInlineItem::kControl) {
PlaceControlItem(item, *line_info, &item_result, box);
+ has_logical_text_items = true;
} else if (item.Type() == NGInlineItem::kOpenTag) {
box = HandleOpenTag(item, item_result, &line_box_, box_states_);
} else if (item.Type() == NGInlineItem::kCloseTag) {
@@ -319,13 +327,6 @@ void NGInlineLayoutAlgorithm::CreateLine(
line_info->AvailableWidth() - line_info->TextIndent() &&
node_.GetLayoutBlockFlow()->ShouldTruncateOverflowingText()) ||
ConstraintSpace().LinesUntilClamp() == 1)) {
- // TODO(kojii): |NGLineTruncator| does not support |Child|-based truncation
- // yet, so create |NGPhysicalTextFragment| first.
- if (has_logical_text_items) {
- line_box_.CreateTextFragments(ConstraintSpace().GetWritingMode(),
- line_info->ItemsData().text_content);
- has_logical_text_items = false;
- }
NGLineTruncator truncator(*line_info);
auto* input =
DynamicTo<HTMLInputElement>(node_.GetLayoutBlockFlow()->GetNode());
@@ -359,16 +360,18 @@ void NGInlineLayoutAlgorithm::CreateLine(
container_builder_.SetBfcLineOffset(bfc_line_offset);
const NGLineHeightMetrics& line_box_metrics =
- box_states_->LineBoxState().metrics;
+ UNLIKELY(Node().HasLineEvenIfEmpty())
+ ? NGLineHeightMetrics(line_info->LineStyle())
+ : box_states_->LineBoxState().metrics;
// Place out-of-flow positioned objects.
- // This adjusts the NGLineBoxFragmentBuilder::Child::offset member to contain
+ // This adjusts the NGLogicalLineItem::offset member to contain
// the static position of the OOF positioned children relative to the linebox.
if (has_out_of_flow_positioned_items)
PlaceOutOfFlowObjects(*line_info, line_box_metrics);
// Place floating objects.
- // This adjusts the NGLineBoxFragmentBuilder::Child::offset member to
+ // This adjusts the NGLogicalLineItem::offset member to
// contain the position of the float relative to the linebox.
// Additionally it will perform layout on any unpositioned floats which
// needed the line height to correctly determine their final position.
@@ -377,6 +380,14 @@ void NGInlineLayoutAlgorithm::CreateLine(
exclusion_space);
}
+ NGAnnotationMetrics annotation_metrics;
+ if (Node().HasRuby() &&
+ !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ annotation_metrics = ComputeAnnotationOverflow(line_box_, line_box_metrics,
+ -line_box_metrics.ascent,
+ line_info->LineStyle());
+ }
+
// Create box fragments if needed. After this point forward, |line_box_| is a
// tree structure.
// The individual children don't move position within the |line_box_|, rather
@@ -389,11 +400,6 @@ void NGInlineLayoutAlgorithm::CreateLine(
context_->SetItemIndex(line_info->ItemsData().items,
line_info->EndItemIndex());
- if (UNLIKELY(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())) {
- NGFragmentItem::Create(&line_box_, line_info->ItemsData().text_content,
- ConstraintSpace().GetWritingMode());
- }
-
// Even if we have something in-flow, it may just be empty items that
// shouldn't trigger creation of a line. Exit now if that's the case.
if (line_info->IsEmptyLine()) {
@@ -410,12 +416,57 @@ void NGInlineLayoutAlgorithm::CreateLine(
// the line box to the line top.
line_box_.MoveInBlockDirection(line_box_metrics.ascent);
+ if (Node().HasRuby() &&
+ RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ annotation_metrics = ComputeAnnotationOverflow(
+ line_box_, line_box_metrics, LayoutUnit(), line_info->LineStyle());
+ }
+ LayoutUnit annotation_overflow_block_start;
+ LayoutUnit annotation_overflow_block_end;
+ LayoutUnit annotation_space_block_start;
+ LayoutUnit annotation_space_block_end;
+ if (!IsFlippedLinesWritingMode(line_info->LineStyle().GetWritingMode())) {
+ annotation_overflow_block_start = annotation_metrics.overflow_over;
+ annotation_overflow_block_end = annotation_metrics.overflow_under;
+ annotation_space_block_start = annotation_metrics.space_over;
+ annotation_space_block_end = annotation_metrics.space_under;
+ } else {
+ annotation_overflow_block_start = annotation_metrics.overflow_under;
+ annotation_overflow_block_end = annotation_metrics.overflow_over;
+ annotation_space_block_start = annotation_metrics.space_under;
+ annotation_space_block_end = annotation_metrics.space_over;
+ }
+
+ LayoutUnit block_offset_shift = annotation_overflow_block_start;
+ // If the previous line has block-end annotation overflow and this line has
+ // block-start annotation space, shift up the block offset of this line.
+ if (ConstraintSpace().BlockStartAnnotationSpace() < LayoutUnit() &&
+ annotation_space_block_start) {
+ const LayoutUnit overflow = -ConstraintSpace().BlockStartAnnotationSpace();
+ block_offset_shift = -std::min(annotation_space_block_start, overflow);
+ }
+
+ // If this line has block-start annotation overflow and the previous line has
+ // block-end annotation space, borrow the block-end space of the previous line
+ // and shift down the block offset by |overflow - space|.
+ if (annotation_overflow_block_start &&
+ ConstraintSpace().BlockStartAnnotationSpace() > LayoutUnit()) {
+ block_offset_shift = (annotation_overflow_block_start -
+ ConstraintSpace().BlockStartAnnotationSpace())
+ .ClampNegativeToZero();
+ }
+
if (line_info->UseFirstLineStyle())
container_builder_.SetStyleVariant(NGStyleVariant::kFirstLine);
container_builder_.SetBaseDirection(line_info->BaseDirection());
container_builder_.SetInlineSize(inline_size);
container_builder_.SetMetrics(line_box_metrics);
- container_builder_.SetBfcBlockOffset(line_info->BfcOffset().block_offset);
+ container_builder_.SetBfcBlockOffset(line_info->BfcOffset().block_offset +
+ block_offset_shift);
+ if (annotation_overflow_block_end)
+ container_builder_.SetAnnotationOverflow(annotation_overflow_block_end);
+ else if (annotation_space_block_end)
+ container_builder_.SetBlockEndAnnotationSpace(annotation_space_block_end);
}
void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item,
@@ -457,11 +508,10 @@ void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item,
if (UNLIKELY(quirks_mode_ && !box->HasMetrics()))
box->EnsureTextMetrics(*item.Style(), baseline_type_);
- NGTextFragmentBuilder text_builder(ConstraintSpace().GetWritingMode());
- text_builder.SetItem(line_info.ItemsData().text_content, item_result,
- box->text_height);
- line_box_.AddChild(text_builder.ToTextFragment(), box->text_top,
- item_result->inline_size, item.BidiLevel());
+ line_box_.AddChild(item, std::move(item_result->shape_result),
+ item_result->TextOffset(), box->text_top,
+ item_result->inline_size, box->text_height,
+ item.BidiLevel());
}
void NGInlineLayoutAlgorithm::PlaceHyphen(const NGInlineItemResult& item_result,
@@ -472,15 +522,10 @@ void NGInlineLayoutAlgorithm::PlaceHyphen(const NGInlineItemResult& item_result,
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());
+ line_box_.AddChild(
+ item, ShapeResultView::Create(item_result.hyphen_shape_result.get()),
+ item_result.hyphen_string, box->text_top, hyphen_inline_size,
+ box->text_height, item.BidiLevel());
}
NGInlineBoxState* NGInlineLayoutAlgorithm::PlaceAtomicInline(
@@ -568,7 +613,7 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
bool has_rtl_block_level_out_of_flow_objects = false;
bool is_ltr = IsLtr(line_info.BaseDirection());
- for (NGLineBoxFragmentBuilder::Child& child : line_box_) {
+ for (NGLogicalLineItem& child : line_box_) {
has_preceding_inline_level_content |= child.HasInFlowFragment();
const LayoutObject* box = child.out_of_flow_positioned_box;
@@ -608,7 +653,7 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
if (UNLIKELY(has_rtl_block_level_out_of_flow_objects)) {
has_preceding_inline_level_content = false;
- for (NGLineBoxFragmentBuilder::Child& child : base::Reversed(line_box_)) {
+ for (NGLogicalLineItem& child : base::Reversed(line_box_)) {
const LayoutObject* box = child.out_of_flow_positioned_box;
if (!box) {
has_preceding_inline_level_content |= child.HasInFlowFragment();
@@ -648,7 +693,7 @@ void NGInlineLayoutAlgorithm::PlaceFloatingObjects(
? ConstraintSpace().ExpectedBfcBlockOffset()
: line_info.BfcOffset().block_offset;
- for (NGLineBoxFragmentBuilder::Child& child : line_box_) {
+ for (NGLogicalLineItem& child : line_box_) {
// We need to position any floats which should be on the "next" line now.
// If this is an empty inline, all floats are positioned during the
// PositionLeadingFloats step.
@@ -697,22 +742,23 @@ void NGInlineLayoutAlgorithm::PlaceListMarker(const NGInlineItem& item,
// Justify the line. This changes the size of items by adding spacing.
// Returns false if justification failed and should fall back to start-aligned.
-bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
- NGLineInfo* line_info) {
+base::Optional<LayoutUnit> NGInlineLayoutAlgorithm::ApplyJustify(
+ LayoutUnit space,
+ NGLineInfo* line_info) {
// Empty lines should align to start.
if (line_info->IsEmptyLine())
- return false;
+ return base::nullopt;
// Justify the end of visible text, ignoring preserved trailing spaces.
unsigned end_offset = line_info->EndOffsetForJustify();
// If this line overflows, fallback to 'text-align: start'.
if (space <= 0)
- return false;
+ return base::nullopt;
// Can't justify an empty string.
if (end_offset == line_info->StartOffset())
- return false;
+ return base::nullopt;
// Construct the line text to compute spacing for.
StringBuilder line_text_builder;
@@ -735,8 +781,32 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
ShapeResultSpacing<String> spacing(line_text);
spacing.SetExpansion(space, line_info->BaseDirection(),
line_info->LineStyle().GetTextJustify());
- if (!spacing.HasExpansion())
- return false; // no expansion opportunities exist.
+ const LayoutObject* box = Node().GetLayoutBox();
+ if (!spacing.HasExpansion()) {
+ // See AdjustInlineDirectionLineBounds() of LayoutRubyBase and
+ // LayoutRubyText.
+ if (box && (box->IsRubyText() || box->IsRubyBase()))
+ return space / 2;
+ return base::nullopt;
+ }
+
+ LayoutUnit inset;
+ // See AdjustInlineDirectionLineBounds() of LayoutRubyBase and
+ // LayoutRubyText.
+ if (box && (box->IsRubyText() || box->IsRubyBase())) {
+ unsigned count = std::min(spacing.ExpansionOppotunityCount(),
+ static_cast<unsigned>(LayoutUnit::Max().Floor()));
+ // Inset the ruby base/text by half the inter-ideograph expansion amount.
+ inset = space / (count + 1);
+ // For ruby text, inset it by no more than a full-width ruby character on
+ // each side.
+ if (box->IsRubyText()) {
+ inset =
+ std::min(LayoutUnit(2 * line_info->LineStyle().FontSize()), inset);
+ }
+ spacing.SetExpansion(space - inset, line_info->BaseDirection(),
+ line_info->LineStyle().GetTextJustify());
+ }
for (NGInlineItemResult& item_result : *line_info->MutableResults()) {
if (item_result.has_only_trailing_spaces)
@@ -744,10 +814,9 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
if (item_result.shape_result) {
scoped_refptr<ShapeResult> shape_result =
item_result.shape_result->CreateShapeResult();
- DCHECK_GE(item_result.start_offset, line_info->StartOffset());
- DCHECK_EQ(shape_result->NumCharacters(),
- item_result.end_offset - item_result.start_offset);
- shape_result->ApplySpacing(spacing, item_result.start_offset -
+ DCHECK_GE(item_result.StartOffset(), line_info->StartOffset());
+ DCHECK_EQ(shape_result->NumCharacters(), item_result.Length());
+ shape_result->ApplySpacing(spacing, item_result.StartOffset() -
line_info->StartOffset() -
shape_result->StartIndex());
item_result.inline_size = shape_result->SnappedWidth();
@@ -756,9 +825,9 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
item_result.shape_result = ShapeResultView::Create(shape_result.get());
} else if (item_result.item->Type() == NGInlineItem::kAtomicInline) {
float offset = 0.f;
- DCHECK_LE(line_info->StartOffset(), item_result.start_offset);
+ DCHECK_LE(line_info->StartOffset(), item_result.StartOffset());
unsigned line_text_offset =
- item_result.start_offset - line_info->StartOffset();
+ item_result.StartOffset() - line_info->StartOffset();
DCHECK_EQ(kObjectReplacementCharacter, line_text[line_text_offset]);
float space = spacing.ComputeSpacing(line_text_offset, offset);
item_result.inline_size += space;
@@ -766,7 +835,7 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
DCHECK_EQ(offset, 0.f);
}
}
- return true;
+ return inset / 2;
}
// Apply the 'text-align' property to |line_info|. Returns the amount to move
@@ -780,10 +849,9 @@ LayoutUnit NGInlineLayoutAlgorithm::ApplyTextAlign(NGLineInfo* line_info) {
ETextAlign text_align = line_info->TextAlign();
if (text_align == ETextAlign::kJustify) {
- // If justification succeeds, no offset is needed. Expansions are set to
- // each |NGInlineItemResult| in |line_info|.
- if (ApplyJustify(space, line_info))
- return LayoutUnit();
+ base::Optional<LayoutUnit> offset = ApplyJustify(space, line_info);
+ if (offset)
+ return *offset;
// If justification fails, fallback to 'text-align: start'.
text_align = ETextAlign::kStart;
@@ -826,7 +894,7 @@ LayoutUnit NGInlineLayoutAlgorithm::ComputeContentSize(
scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
NGExclusionSpace initial_exclusion_space(ConstraintSpace().ExclusionSpace());
- bool is_empty_inline = Node().IsEmptyInline();
+ const bool is_empty_inline = Node().IsEmptyInline();
if (is_empty_inline) {
// Margins should collapse across "certain zero-height line boxes".
@@ -1018,7 +1086,7 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
container_builder_.ToLineBoxFragment();
items_builder->SetCurrentLine(
To<NGPhysicalLineBoxFragment>(layout_result->PhysicalFragment()),
- std::move(line_box_));
+ &line_box_);
return layout_result;
}
@@ -1109,7 +1177,7 @@ void NGInlineLayoutAlgorithm::BidiReorder(TextDirection base_direction) {
Vector<UBiDiLevel, 32> levels;
levels.ReserveInitialCapacity(line_box_.size());
bool has_opaque_items = false;
- for (NGLineBoxFragmentBuilder::Child& item : line_box_) {
+ for (NGLogicalLineItem& item : line_box_) {
if (item.IsOpaqueToBidiReordering()) {
levels.push_back(kOpaqueBidiLevel);
has_opaque_items = true;
@@ -1136,13 +1204,13 @@ void NGInlineLayoutAlgorithm::BidiReorder(TextDirection base_direction) {
NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order);
// Reorder to the visual order.
- NGLineBoxFragmentBuilder::ChildList visual_items;
+ NGLogicalLineItems visual_items;
visual_items.ReserveInitialCapacity(line_box_.size());
for (unsigned logical_index : indices_in_visual_order) {
visual_items.AddChild(std::move(line_box_[logical_index]));
DCHECK(!line_box_[logical_index].HasInFlowFragment() ||
- // |item_result| will not be null by moving.
- line_box_[logical_index].item_result);
+ // |inline_item| will not be null by moving.
+ line_box_[logical_index].inline_item);
}
DCHECK_EQ(line_box_.size(), visual_items.size());
line_box_ = std::move(visual_items);
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 23722eca460..05b15a0147e 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
@@ -7,7 +7,7 @@
#include "third_party/blink/renderer/core/core_export.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_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.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_algorithm.h"
@@ -74,7 +74,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
NGInlineBoxState* HandleOpenTag(const NGInlineItem&,
const NGInlineItemResult&,
- NGLineBoxFragmentBuilder::ChildList*,
+ NGLogicalLineItems*,
NGInlineLayoutStateStack*) const;
NGInlineBoxState* HandleCloseTag(const NGInlineItem&,
const NGInlineItemResult&,
@@ -105,13 +105,13 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
const NGLineInfo&);
LayoutUnit ApplyTextAlign(NGLineInfo*);
- bool ApplyJustify(LayoutUnit space, NGLineInfo*);
+ base::Optional<LayoutUnit> ApplyJustify(LayoutUnit space, NGLineInfo*);
LayoutUnit ComputeContentSize(const NGLineInfo&,
const NGExclusionSpace&,
LayoutUnit line_height);
- NGLineBoxFragmentBuilder::ChildList line_box_;
+ NGLogicalLineItems& line_box_;
NGInlineLayoutStateStack* box_states_;
NGInlineChildLayoutContext* context_;
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 9bd25a79215..2157a1e6ee2 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
@@ -52,10 +52,11 @@ 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(inline_node);
+ NGBoxFragmentBuilder container_builder(
+ block_flow, block_flow->Style(),
+ block_flow->Style()->GetWritingDirection());
+ NGFragmentItemsBuilder items_builder(inline_node,
+ container_builder.GetWritingDirection());
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
container_builder.SetItemsBuilder(&items_builder);
context.SetItemsBuilder(&items_builder);
@@ -66,12 +67,14 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) {
EXPECT_FALSE(line1.BreakToken()->IsFinished());
// Perform 2nd layout with the break token from the 1st line.
+ items_builder.ClearCurrentLineForTesting();
scoped_refptr<const NGLayoutResult> layout_result2 =
inline_node.Layout(constraint_space, line1.BreakToken(), &context);
const auto& line2 = layout_result2->PhysicalFragment();
EXPECT_FALSE(line2.BreakToken()->IsFinished());
// Perform 3rd layout with the break token from the 2nd line.
+ items_builder.ClearCurrentLineForTesting();
scoped_refptr<const NGLayoutResult> layout_result3 =
inline_node.Layout(constraint_space, line2.BreakToken(), &context);
const auto& line3 = layout_result3->PhysicalFragment();
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 075cd9ebf3b..0518f8b6d7b 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
@@ -7,12 +7,13 @@
#include <algorithm>
#include <memory>
+#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#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/layout_list_marker.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
+#include "third_party/blink/renderer/core/layout/list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.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_break_token.h"
@@ -94,14 +95,17 @@ class ReusingTextShaper final {
ShapeResult::CreateEmpty(*reusable_shape_results.front());
unsigned offset = start_offset;
for (const ShapeResult* reusable_shape_result : reusable_shape_results) {
- DCHECK_LE(offset, reusable_shape_result->StartIndex());
+ // In case of pre-wrap having break opportunity after leading space,
+ // |offset| can be greater than |reusable_shape_result->StartIndex()|.
+ // e.g. <div style="white-space:pre">&nbsp; abc</div>, deleteChar(0, 1)
+ // See xternal/wpt/editing/run/delete.html?993-993
if (offset < reusable_shape_result->StartIndex()) {
AppendShapeResult(
*Reshape(start_item, offset, reusable_shape_result->StartIndex()),
shape_result.get());
offset = shape_result->EndIndex();
}
- DCHECK_EQ(offset, reusable_shape_result->StartIndex());
+ DCHECK_LT(offset, reusable_shape_result->EndIndex());
DCHECK(shape_result->NumCharacters() == 0 ||
shape_result->EndIndex() == offset);
reusable_shape_result->CopyRange(
@@ -183,9 +187,9 @@ class ReusingTextShaper final {
// There are also performance considerations, since template saves the overhead
// for condition checking and branching.
template <typename ItemsBuilder>
-void CollectInlinesInternal(LayoutBlockFlow* block,
- ItemsBuilder* builder,
+void CollectInlinesInternal(ItemsBuilder* builder,
const NGInlineNodeData* previous_data) {
+ LayoutBlockFlow* const block = builder->GetLayoutBlockFlow();
builder->EnterBlock(block->Style());
LayoutObject* node = GetLayoutObjectForFirstChildNode(block);
@@ -215,7 +219,7 @@ void CollectInlinesInternal(LayoutBlockFlow* block,
builder->ClearInlineFragment(node);
} else if (node->IsAtomicInlineLevel()) {
- if (node->IsListMarkerIncludingNGOutside()) {
+ if (node->IsBoxListMarkerIncludingNG()) {
// LayoutNGListItem produces the 'outside' list marker as an inline
// block. This is an out-of-flow item whose position is computed
// automatically.
@@ -373,7 +377,7 @@ bool NGInlineNode::IsPrepareLayoutFinished() const {
return data && !data->text_content.IsNull();
}
-void NGInlineNode::PrepareLayoutIfNeeded() {
+void NGInlineNode::PrepareLayoutIfNeeded() const {
std::unique_ptr<NGInlineNodeData> previous_data;
LayoutBlockFlow* block_flow = GetLayoutBlockFlow();
if (IsPrepareLayoutFinished()) {
@@ -388,7 +392,7 @@ void NGInlineNode::PrepareLayoutIfNeeded() {
}
void NGInlineNode::PrepareLayout(
- std::unique_ptr<NGInlineNodeData> previous_data) {
+ std::unique_ptr<NGInlineNodeData> previous_data) const {
// Scan list of siblings collecting all in-flow non-atomic inlines. A single
// NGInlineNode represent a collection of adjacent non-atomic inlines.
NGInlineNodeData* data = MutableData();
@@ -516,16 +520,19 @@ class NGInlineNodeDataEditor final {
// Skip items in replaced range.
while (it->end_offset_ < end_offset)
++it;
- DCHECK_EQ(it->layout_object_, layout_text_);
// Inserted text
- if (inserted_text_length > 0) {
- const unsigned inserted_start_offset =
- items.IsEmpty() ? 0 : items.back().end_offset_;
- const unsigned inserted_end_offset =
- inserted_start_offset + inserted_text_length;
- items.push_back(NGInlineItem(*it, inserted_start_offset,
- inserted_end_offset, nullptr));
+ if (it->layout_object_ == layout_text_) {
+ if (inserted_text_length > 0) {
+ const unsigned inserted_start_offset =
+ items.IsEmpty() ? 0 : items.back().end_offset_;
+ const unsigned inserted_end_offset =
+ inserted_start_offset + inserted_text_length;
+ items.push_back(NGInlineItem(*it, inserted_start_offset,
+ inserted_end_offset, nullptr));
+ }
+ } else {
+ DCHECK_LE(inserted_text_length, 0);
}
// Copy part of item after replaced range.
@@ -571,7 +578,6 @@ class NGInlineNodeDataEditor final {
unsigned start_offset) const {
DCHECK_LE(item.start_offset_, start_offset);
DCHECK_LT(start_offset, item.end_offset_);
- DCHECK_EQ(item.layout_object_, layout_text_);
if (item.start_offset_ == start_offset)
return item;
const unsigned end_offset = item.end_offset_;
@@ -688,11 +694,11 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text,
NGInlineNode node(editor.GetLayoutBlockFlow());
NGInlineNodeData* data = node.MutableData();
data->items.ReserveCapacity(previous_data->items.size());
- NGInlineItemsBuilder builder(&data->items);
+ NGInlineItemsBuilder builder(editor.GetLayoutBlockFlow(), &data->items);
// TODO(yosin): We should reuse before/after |layout_text| during collecting
// inline items.
layout_text->ClearInlineItems();
- CollectInlinesInternal(node.GetLayoutBlockFlow(), &builder, previous_data);
+ CollectInlinesInternal(&builder, previous_data);
builder.DidFinishCollectInlines(data);
// Relocates |ShapeResult| in |previous_data| after |offset|+|length|
editor.Run();
@@ -703,12 +709,12 @@ bool NGInlineNode::SetTextWithOffset(LayoutText* layout_text,
return true;
}
-const NGInlineNodeData& NGInlineNode::EnsureData() {
+const NGInlineNodeData& NGInlineNode::EnsureData() const {
PrepareLayoutIfNeeded();
return Data();
}
-const NGOffsetMapping* NGInlineNode::ComputeOffsetMappingIfNeeded() {
+const NGOffsetMapping* NGInlineNode::ComputeOffsetMappingIfNeeded() const {
DCHECK(!GetLayoutBlockFlow()->GetDocument().NeedsLayoutTreeUpdate());
NGInlineNodeData* data = MutableData();
@@ -732,10 +738,10 @@ void NGInlineNode::ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow,
// |builder| not construct items and text content.
Vector<NGInlineItem> items;
items.ReserveCapacity(EstimateInlineItemsCount(*layout_block_flow));
- NGInlineItemsBuilderForOffsetMapping builder(&items);
+ NGInlineItemsBuilderForOffsetMapping builder(layout_block_flow, &items);
builder.GetOffsetMappingBuilder().ReserveCapacity(
EstimateOffsetMappingItemsCount(*layout_block_flow));
- CollectInlinesInternal(layout_block_flow, &builder, nullptr);
+ CollectInlinesInternal(&builder, nullptr);
// For non-NG object, we need the text, and also the inline items to resolve
// bidi levels. Otherwise |data| already has the text from the pre-layout
@@ -791,19 +797,19 @@ const NGOffsetMapping* NGInlineNode::GetOffsetMapping(
// parent LayoutInline where possible, and joining all text content in a single
// string to allow bidi resolution and shaping of the entire block.
void NGInlineNode::CollectInlines(NGInlineNodeData* data,
- NGInlineNodeData* previous_data) {
+ NGInlineNodeData* previous_data) const {
DCHECK(data->text_content.IsNull());
DCHECK(data->items.IsEmpty());
LayoutBlockFlow* block = GetLayoutBlockFlow();
block->WillCollectInlines();
data->items.ReserveCapacity(EstimateInlineItemsCount(*block));
- NGInlineItemsBuilder builder(&data->items);
- CollectInlinesInternal(block, &builder, previous_data);
+ NGInlineItemsBuilder builder(block, &data->items);
+ CollectInlinesInternal(&builder, previous_data);
builder.DidFinishCollectInlines(data);
}
-void NGInlineNode::SegmentText(NGInlineNodeData* data) {
+void NGInlineNode::SegmentText(NGInlineNodeData* data) const {
SegmentBidiRuns(data);
SegmentScriptRuns(data);
SegmentFontOrientation(data);
@@ -812,8 +818,8 @@ void NGInlineNode::SegmentText(NGInlineNodeData* data) {
}
// Segment NGInlineItem by script, Emoji, and orientation using RunSegmenter.
-void NGInlineNode::SegmentScriptRuns(NGInlineNodeData* data) {
- DCHECK_EQ(data->segments, nullptr);
+void NGInlineNode::SegmentScriptRuns(NGInlineNodeData* data) const {
+ DCHECK_EQ(data->segments.get(), nullptr);
String& text_content = data->text_content;
if (text_content.IsEmpty()) {
@@ -895,7 +901,7 @@ void NGInlineNode::SegmentScriptRuns(NGInlineNodeData* data) {
DCHECK_EQ(range.end, text_content.length());
}
-void NGInlineNode::SegmentFontOrientation(NGInlineNodeData* data) {
+void NGInlineNode::SegmentFontOrientation(NGInlineNodeData* data) const {
// Segment by orientation, only if vertical writing mode and items with
// 'text-orientation: mixed'.
if (GetLayoutBlockFlow()->IsHorizontalWritingMode())
@@ -937,7 +943,7 @@ void NGInlineNode::SegmentFontOrientation(NGInlineNodeData* data) {
// Segment bidi runs by resolving bidi embedding levels.
// http://unicode.org/reports/tr9/#Resolving_Embedding_Levels
-void NGInlineNode::SegmentBidiRuns(NGInlineNodeData* data) {
+void NGInlineNode::SegmentBidiRuns(NGInlineNodeData* data) const {
if (!data->is_bidi_enabled_) {
data->SetBaseDirection(TextDirection::kLtr);
return;
@@ -982,7 +988,8 @@ void NGInlineNode::SegmentBidiRuns(NGInlineNodeData* data) {
void NGInlineNode::ShapeText(NGInlineItemsData* data,
const String* previous_text,
- const Vector<NGInlineItem>* previous_items) {
+ const Vector<NGInlineItem>* previous_items) const {
+ TRACE_EVENT0("blink", "NGInlineNode::ShapeText");
const String& text_content = data->text_content;
Vector<NGInlineItem>* items = &data->items;
@@ -1009,7 +1016,7 @@ void NGInlineNode::ShapeText(NGInlineItemsData* data,
// Symbol marker is painted as graphics. Create a ShapeResult of space
// glyphs with the desired size to make it less special for line breaker.
if (UNLIKELY(start_item.IsSymbolMarker())) {
- LayoutUnit symbol_width = LayoutListMarker::WidthOfSymbol(start_style);
+ LayoutUnit symbol_width = ListMarker::WidthOfSymbol(start_style);
DCHECK_GT(symbol_width, 0);
start_item.shape_result_ = ShapeResult::CreateForSpaces(
&font, direction, start_item.StartOffset(), start_item.Length(),
@@ -1155,7 +1162,7 @@ void NGInlineNode::ShapeText(NGInlineItemsData* data,
}
// Create Vector<NGInlineItem> with :first-line rules applied if needed.
-void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) {
+void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) const {
// First check if the document has any :first-line rules.
DCHECK(!data->first_line_items_);
LayoutObject* layout_object = GetLayoutBox();
@@ -1200,7 +1207,7 @@ void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) {
data->first_line_items_ = std::move(first_line_items);
}
-void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) {
+void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) const {
#if DCHECK_IS_ON()
HashSet<LayoutObject*> associated_objects;
#endif
@@ -1283,7 +1290,7 @@ void NGInlineNode::ClearAssociatedFragments(
scoped_refptr<const NGLayoutResult> NGInlineNode::Layout(
const NGConstraintSpace& constraint_space,
const NGBreakToken* break_token,
- NGInlineChildLayoutContext* context) {
+ NGInlineChildLayoutContext* context) const {
PrepareLayoutIfNeeded();
const auto* inline_break_token = To<NGInlineBreakToken>(break_token);
@@ -1292,16 +1299,20 @@ scoped_refptr<const NGLayoutResult> NGInlineNode::Layout(
auto layout_result = algorithm.Layout();
#if defined(OS_ANDROID)
- // Cached position data is crucial for line breaking performance and is
- // preserved across layouts to speed up subsequent layout passes due to
- // reflow, page zoom, window resize, etc. On Android though reflows are less
- // common, page zoom isn't used (instead uses pinch-zoom), and the window
- // typically can't be resized (apart from rotation). To reduce memory usage
- // discard the cached position data after layout.
- NGInlineNodeData* data = MutableData();
- for (auto& item : data->items) {
- if (item.shape_result_)
- item.shape_result_->DiscardPositionData();
+ if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ // Cached position data is crucial for line breaking performance and is
+ // preserved across layouts to speed up subsequent layout passes due to
+ // reflow, page zoom, window resize, etc. On Android though reflows are less
+ // common, page zoom isn't used (instead uses pinch-zoom), and the window
+ // typically can't be resized (apart from rotation). To reduce memory usage
+ // discard the cached position data after layout.
+ // TODO(crbug.com/1042604): FragmentItem should save memory enough to re-
+ // enable the position cache.
+ NGInlineNodeData* data = MutableData();
+ for (auto& item : data->items) {
+ if (item.shape_result_)
+ item.shape_result_->DiscardPositionData();
+ }
}
#endif // defined(OS_ANDROID)
@@ -1587,7 +1598,8 @@ static LayoutUnit ComputeContentSize(
const ComputedStyle& float_style = float_node.Style();
// Floats don't intrude into floats.
- MinMaxSizesInput float_input(input.percentage_resolution_block_size);
+ MinMaxSizesInput float_input(input.percentage_resolution_block_size,
+ MinMaxSizesType::kContent);
MinMaxSizesResult child_result =
ComputeMinAndMaxContentContribution(style, float_node, float_input);
LayoutUnit child_inline_margins =
@@ -1637,7 +1649,7 @@ static LayoutUnit ComputeContentSize(
MinMaxSizesResult NGInlineNode::ComputeMinMaxSizes(
WritingMode container_writing_mode,
const MinMaxSizesInput& input,
- const NGConstraintSpace* constraint_space) {
+ const NGConstraintSpace* constraint_space) const {
PrepareLayoutIfNeeded();
// Compute the max of inline sizes of all line boxes with 0 available inline
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 c14ef2b3644..af75b0b6a9f 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
@@ -27,7 +27,8 @@ struct NGInlineItemsData;
// inline nodes and their descendants.
class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
public:
- NGInlineNode(LayoutBlockFlow*);
+ explicit NGInlineNode(LayoutBlockFlow*);
+ explicit NGInlineNode(std::nullptr_t) : NGLayoutInputNode(nullptr) {}
LayoutBlockFlow* GetLayoutBlockFlow() const {
return To<LayoutBlockFlow>(box_);
@@ -44,14 +45,15 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
scoped_refptr<const NGLayoutResult> Layout(
const NGConstraintSpace&,
const NGBreakToken*,
- NGInlineChildLayoutContext* context);
+ NGInlineChildLayoutContext* context) const;
// 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.
- MinMaxSizesResult ComputeMinMaxSizes(WritingMode container_writing_mode,
- const MinMaxSizesInput&,
- const NGConstraintSpace* = nullptr);
+ MinMaxSizesResult ComputeMinMaxSizes(
+ WritingMode container_writing_mode,
+ const MinMaxSizesInput&,
+ const NGConstraintSpace* = nullptr) const;
// Instruct to re-compute |PrepareLayout| on the next layout.
void InvalidatePrepareLayoutForTest() {
@@ -98,7 +100,7 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
// Returns the DOM to text content offset mapping of this block. If it is not
// computed before, compute and store it in NGInlineNodeData.
// This funciton must be called with clean layout.
- const NGOffsetMapping* ComputeOffsetMappingIfNeeded();
+ const NGOffsetMapping* ComputeOffsetMappingIfNeeded() const;
// Get |NGOffsetMapping| for the |layout_block_flow|. |layout_block_flow|
// should be laid out. This function works for both new and legacy layout.
@@ -108,6 +110,9 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
bool IsBidiEnabled() const { return Data().is_bidi_enabled_; }
TextDirection BaseDirection() const { return Data().BaseDirection(); }
+ bool HasLineEvenIfEmpty() { return EnsureData().has_line_even_if_empty_; }
+ bool HasRuby() const { return Data().has_ruby_; }
+
bool IsEmptyInline() { return EnsureData().is_empty_inline_; }
bool IsBlockLevel() { return EnsureData().is_block_level_; }
@@ -127,7 +132,7 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
struct FloatingObject {
DISALLOW_NEW();
- void Trace(Visitor* visitor) {}
+ void Trace(Visitor* visitor) const {}
const ComputedStyle& float_style;
const ComputedStyle& style;
@@ -139,22 +144,22 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
// Prepare inline and text content for layout. Must be called before
// calling the Layout method.
- void PrepareLayoutIfNeeded();
- void PrepareLayout(std::unique_ptr<NGInlineNodeData> previous_data);
+ void PrepareLayoutIfNeeded() const;
+ void PrepareLayout(std::unique_ptr<NGInlineNodeData> previous_data) const;
void CollectInlines(NGInlineNodeData*,
- NGInlineNodeData* previous_data = nullptr);
- void SegmentText(NGInlineNodeData*);
- void SegmentScriptRuns(NGInlineNodeData*);
- void SegmentFontOrientation(NGInlineNodeData*);
- void SegmentBidiRuns(NGInlineNodeData*);
+ NGInlineNodeData* previous_data = nullptr) const;
+ void SegmentText(NGInlineNodeData*) const;
+ void SegmentScriptRuns(NGInlineNodeData*) const;
+ void SegmentFontOrientation(NGInlineNodeData*) const;
+ void SegmentBidiRuns(NGInlineNodeData*) const;
void ShapeText(NGInlineItemsData*,
const String* previous_text = nullptr,
- const Vector<NGInlineItem>* previous_items = nullptr);
- void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*);
- void AssociateItemsWithInlines(NGInlineNodeData*);
+ const Vector<NGInlineItem>* previous_items = nullptr) const;
+ void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*) const;
+ void AssociateItemsWithInlines(NGInlineNodeData*) const;
- NGInlineNodeData* MutableData() {
+ NGInlineNodeData* MutableData() const {
return To<LayoutBlockFlow>(box_)->GetNGInlineNodeData();
}
const NGInlineNodeData& Data() const {
@@ -167,7 +172,7 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
DCHECK(IsPrepareLayoutFinished());
return *To<LayoutBlockFlow>(box_)->GetNGInlineNodeData();
}
- const NGInlineNodeData& EnsureData();
+ const NGInlineNodeData& EnsureData() const;
static void ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow,
NGInlineNodeData* data);
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 5dda66cc9f2..0636afc4b3d 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
@@ -22,6 +22,8 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
return static_cast<TextDirection>(base_direction_);
}
+ bool HasLineEvenIfEmpty() const { return has_line_even_if_empty_; }
+ bool HasRuby() const { return has_ruby_; }
bool IsEmptyInline() const { return is_empty_inline_; }
bool IsBlockLevel() const { return is_block_level_; }
@@ -56,6 +58,14 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
unsigned is_bidi_enabled_ : 1;
unsigned base_direction_ : 1; // TextDirection
+ // True if there are no inline item items and the associated block is root
+ // editable element or having "-internal-empty-line-height:fabricated",
+ // e.g. <div contenteditable></div>, <input type=button value="">
+ unsigned has_line_even_if_empty_ : 1;
+
+ // The node contains <ruby>.
+ unsigned has_ruby_ : 1;
+
// We use this flag to determine if the inline node is empty, and will
// produce a single zero block-size line box. If the node has text, atomic
// inlines, open/close tags with margins/border/padding this will be false.
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 92ef98f42be..511fc704621 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
@@ -114,7 +114,8 @@ class NGInlineNodeTest : public NGLayoutTest {
.ComputeMinMaxSizes(
node.Style().GetWritingMode(),
MinMaxSizesInput(
- /* percentage_resolution_block_size */ LayoutUnit()))
+ /* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent))
.sizes;
}
@@ -140,13 +141,16 @@ class NGInlineNodeTest : public NGLayoutTest {
return end_offsets;
}
- void TestFirstLineIsDirty(LayoutBlockFlow* block_flow, bool expected) {
+ void TestAnyItrermsAreDirty(LayoutBlockFlow* block_flow, bool expected) {
const NGFragmentItems* items = block_flow->FragmentItems();
items->DirtyLinesFromNeedsLayout(block_flow);
- const NGFragmentItem* end_reusable_item = items->EndOfReusableItems();
- NGInlineCursor cursor(*items);
- cursor.MoveToFirstLine();
- EXPECT_EQ(cursor.Current().Item() == end_reusable_item, expected);
+ // Check |NGFragmentItem::IsDirty| directly without using
+ // |EndOfReusableItems|. This is different from the line cache logic, but
+ // some items may not be reusable even if |!IsDirty()|.
+ const bool is_any_items_dirty =
+ std::any_of(items->Items().begin(), items->Items().end(),
+ [](const NGFragmentItem& item) { return item.IsDirty(); });
+ EXPECT_EQ(is_any_items_dirty, expected);
}
scoped_refptr<const ComputedStyle> style_;
@@ -636,8 +640,8 @@ TEST_P(StyleChangeTest, NeedsCollectInlinesOnStyle) {
if (data.is_line_dirty &&
RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
- TestFirstLineIsDirty(To<LayoutBlockFlow>(container->GetLayoutObject()),
- *data.is_line_dirty);
+ TestAnyItrermsAreDirty(To<LayoutBlockFlow>(container->GetLayoutObject()),
+ *data.is_line_dirty);
}
ForceLayout(); // Ensure running layout does not crash.
@@ -1035,9 +1039,11 @@ 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 before_split;
+ before_split.MoveTo(*text->GetLayoutObject());
+ EXPECT_TRUE(before_split);
+ scoped_refptr<const NGPaintFragment> text_fragment_before_split =
+ before_split.Current().PaintFragment();
// Append <div> to <span>. causing SplitFlow().
Element* outer_span = GetElementById("outer_span");
@@ -1053,23 +1059,32 @@ TEST_F(NGInlineNodeTest, ClearFirstInlineFragmentOnSplitFlow) {
// destroyed, and should not be accessible.
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(text->GetLayoutObject()->IsInLayoutNGInlineFormattingContext());
- scoped_refptr<NGPaintFragment> text_fragment_before_layout =
- text->GetLayoutObject()->FirstInlineFragment();
- EXPECT_EQ(text_fragment_before_layout, nullptr);
+ EXPECT_FALSE(text->GetLayoutObject()->HasInlineFragments());
// 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 after_layout;
+ after_layout.MoveTo(*text->GetLayoutObject());
+ EXPECT_TRUE(after_layout);
+ if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ EXPECT_NE(text_fragment_before_split.get(),
+ after_layout.Current().PaintFragment());
+ }
// 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);
+ NGInlineCursor anonymous_block_cursor(*To<LayoutBlockFlow>(anonymous_block));
+ anonymous_block_cursor.MoveToFirstLine();
+ anonymous_block_cursor.MoveToFirstChild();
+ EXPECT_TRUE(anonymous_block_cursor);
+ EXPECT_EQ(anonymous_block_cursor.Current().GetLayoutObject(),
+ text->GetLayoutObject());
+ if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ EXPECT_EQ(anonymous_block_cursor.Current().PaintFragment(),
+ after_layout.Current().PaintFragment());
+ }
}
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 3ec775afee2..2cb44b83e61 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
@@ -8,6 +8,7 @@
#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_result.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.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_text_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
@@ -40,90 +41,7 @@ void NGLineBoxFragmentBuilder::SetIsEmptyLineBox() {
line_box_type_ = NGPhysicalLineBoxFragment::kEmptyLineBox;
}
-void NGLineBoxFragmentBuilder::ChildList::CreateTextFragments(
- WritingMode writing_mode,
- const String& text_content) {
- NGTextFragmentBuilder text_builder(writing_mode);
- for (auto& child : *this) {
- if (NGInlineItemResult* item_result = child.item_result) {
- DCHECK(item_result->item);
- const NGInlineItem& item = *item_result->item;
- DCHECK(item.Type() == NGInlineItem::kText ||
- item.Type() == NGInlineItem::kControl);
- DCHECK(item.TextType() == NGTextType::kNormal ||
- item.TextType() == NGTextType::kSymbolMarker);
- text_builder.SetItem(text_content, item_result,
- child.rect.size.block_size);
- DCHECK(!child.fragment);
- child.fragment = text_builder.ToTextFragment();
- }
- }
-}
-
-NGLineBoxFragmentBuilder::Child*
-NGLineBoxFragmentBuilder::ChildList::FirstInFlowChild() {
- for (auto& child : *this) {
- if (child.HasInFlowFragment())
- return &child;
- }
- return nullptr;
-}
-
-NGLineBoxFragmentBuilder::Child*
-NGLineBoxFragmentBuilder::ChildList::LastInFlowChild() {
- for (auto it = rbegin(); it != rend(); it++) {
- auto& child = *it;
- if (child.HasInFlowFragment())
- return &child;
- }
- 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.rect.offset.inline_offset += delta;
-}
-
-void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection(
- LayoutUnit delta,
- unsigned start,
- unsigned end) {
- for (unsigned index = start; index < end; index++)
- children_[index].rect.offset.inline_offset += delta;
-}
-
-void NGLineBoxFragmentBuilder::ChildList::MoveInBlockDirection(
- LayoutUnit delta) {
- for (auto& child : children_)
- 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].rect.offset.block_offset += delta;
-}
-
-void NGLineBoxFragmentBuilder::AddChildren(ChildList& children) {
+void NGLineBoxFragmentBuilder::AddChildren(NGLogicalLineItems& children) {
children_.ReserveCapacity(children.size());
for (auto& child : children) {
@@ -143,7 +61,8 @@ void NGLineBoxFragmentBuilder::AddChildren(ChildList& children) {
}
}
-void NGLineBoxFragmentBuilder::PropagateChildrenData(ChildList& children) {
+void NGLineBoxFragmentBuilder::PropagateChildrenData(
+ NGLogicalLineItems& children) {
for (unsigned index = 0; index < children.size(); ++index) {
auto& child = children[index];
if (child.layout_result) {
@@ -172,7 +91,7 @@ void NGLineBoxFragmentBuilder::PropagateChildrenData(ChildList& children) {
scoped_refptr<const NGLayoutResult>
NGLineBoxFragmentBuilder::ToLineBoxFragment() {
- writing_mode_ = ToLineWritingMode(writing_mode_);
+ writing_direction_.SetWritingMode(ToLineWritingMode(GetWritingMode()));
if (!break_token_)
break_token_ = NGInlineBreakToken::Create(node_);
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 8c3dc2cd273..47540975fc5 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
@@ -21,7 +21,7 @@ namespace blink {
class ComputedStyle;
class NGInlineBreakToken;
-struct NGInlineItemResult;
+class NGLogicalLineItems;
class CORE_EXPORT NGLineBoxFragmentBuilder final
: public NGContainerFragmentBuilder {
@@ -31,13 +31,13 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
NGLineBoxFragmentBuilder(NGInlineNode node,
scoped_refptr<const ComputedStyle> style,
const NGConstraintSpace* space,
- WritingMode writing_mode,
- TextDirection)
- : NGContainerFragmentBuilder(node,
- style,
- space,
- writing_mode,
- TextDirection::kLtr),
+ WritingDirectionMode writing_direction)
+ : NGContainerFragmentBuilder(
+ node,
+ style,
+ space,
+ // Always use LTR because line items are in visual order.
+ {writing_direction.GetWritingMode(), TextDirection::kLtr}),
line_box_type_(NGPhysicalLineBoxFragment::kNormalLineBox),
base_direction_(TextDirection::kLtr) {}
@@ -71,248 +71,13 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
break_token_ = std::move(break_token);
}
- // A data struct to keep NGLayoutResult or fragment until the box tree
- // structures and child offsets are finalized.
- struct Child {
- DISALLOW_NEW();
-
- scoped_refptr<NGFragmentItem> fragment_item;
- scoped_refptr<const NGLayoutResult> layout_result;
- scoped_refptr<const NGPhysicalTextFragment> fragment;
- const NGInlineItem* inline_item = nullptr;
- // |NGInlineItemResult| to create a text fragment from.
- NGInlineItemResult* item_result = 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.
- LogicalRect rect;
- // The offset of a positioned float wrt. the root BFC. This should only be
- // set for positioned floats.
- NGBfcOffset bfc_offset;
- // The inline size of the margin box.
- LayoutUnit inline_size;
- LayoutUnit margin_line_left;
- // The index of |box_data_list_|, used in |PrepareForReorder()| and
- // |UpdateAfterReorder()| to track children of boxes across BiDi reorder.
- unsigned box_data_index = 0;
- // For an inline box, shows the number of descendant |Child|ren, including
- // empty ones. Includes itself, so 1 means no descendants. 0 if not an
- // inline box. Available only after |CreateBoxFragments()|.
- unsigned children_count = 0;
- UBiDiLevel bidi_level = 0xff;
- // The current text direction for OOF positioned items.
- TextDirection container_direction = TextDirection::kLtr;
-
- // Empty constructor needed for |resize()|.
- Child() = default;
- // Create a placeholder. A placeholder does not have a fragment nor a bidi
- // level.
- 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)),
- rect(offset, LogicalSize()),
- inline_size(inline_size),
- children_count(children_count),
- bidi_level(bidi_level) {}
- // Create an in-flow text fragment.
- Child(NGInlineItemResult* item_result,
- LayoutUnit block_offset,
- LayoutUnit inline_size,
- LayoutUnit text_height,
- UBiDiLevel bidi_level)
- : item_result(item_result),
- rect(LayoutUnit(), block_offset, LayoutUnit(), text_height),
- inline_size(inline_size),
- bidi_level(bidi_level) {}
- Child(scoped_refptr<const NGPhysicalTextFragment> fragment,
- LogicalOffset offset,
- LayoutUnit inline_size,
- UBiDiLevel bidi_level)
- : fragment(std::move(fragment)),
- rect(offset, LogicalSize()),
- inline_size(inline_size),
- bidi_level(bidi_level) {}
- Child(scoped_refptr<const NGPhysicalTextFragment> fragment,
- LayoutUnit block_offset,
- LayoutUnit inline_size,
- UBiDiLevel bidi_level)
- : fragment(std::move(fragment)),
- rect(LayoutUnit(), block_offset, LayoutUnit(), LayoutUnit()),
- inline_size(inline_size),
- bidi_level(bidi_level) {}
- // Create an out-of-flow positioned object.
- Child(LayoutObject* out_of_flow_positioned_box,
- UBiDiLevel bidi_level,
- TextDirection container_direction)
- : out_of_flow_positioned_box(out_of_flow_positioned_box),
- bidi_level(bidi_level),
- container_direction(container_direction) {}
- // Create an unpositioned float.
- Child(LayoutObject* unpositioned_float, UBiDiLevel bidi_level)
- : unpositioned_float(unpositioned_float), bidi_level(bidi_level) {}
- // Create a positioned float.
- Child(scoped_refptr<const NGLayoutResult> layout_result,
- NGBfcOffset bfc_offset,
- UBiDiLevel bidi_level)
- : layout_result(std::move(layout_result)),
- bfc_offset(bfc_offset),
- bidi_level(bidi_level) {}
-
- bool HasInFlowFragment() const {
- if (fragment_item)
- return true;
- if (fragment)
- return true;
- if (item_result)
- return true;
- if (layout_result && !layout_result->PhysicalFragment().IsFloating())
- return true;
-
- return false;
- }
- bool HasOutOfFlowFragment() const { return out_of_flow_positioned_box; }
- bool HasFragment() const {
- return HasInFlowFragment() || HasOutOfFlowFragment();
- }
- bool HasBidiLevel() const { return bidi_level != 0xff; }
- bool IsPlaceholder() const { return !HasFragment() && !HasBidiLevel(); }
- bool IsOpaqueToBidiReordering() const {
- if (IsPlaceholder())
- return true;
- // Skip all inline boxes. Fragments for inline boxes maybe created earlier
- // if they have no children.
- if (layout_result) {
- const LayoutObject* layout_object =
- layout_result->PhysicalFragment().GetLayoutObject();
- DCHECK(layout_object);
- if (layout_object->IsLayoutInline())
- return true;
- }
- 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.
- // Unlike the fragment builder, chlidren are mutable.
- // Callers can add to the fragment builder in a batch once finalized.
- class ChildList {
- STACK_ALLOCATED();
-
- public:
- ChildList() = default;
- void operator=(ChildList&& other) {
- children_ = std::move(other.children_);
- }
-
- Child& operator[](wtf_size_t i) { return children_[i]; }
- const Child& operator[](wtf_size_t i) const { return children_[i]; }
-
- wtf_size_t size() const { return children_.size(); }
- bool IsEmpty() const { return children_.IsEmpty(); }
- void ReserveInitialCapacity(unsigned capacity) {
- children_.ReserveInitialCapacity(capacity);
- }
- void clear() { children_.resize(0); }
- void resize(wtf_size_t size) { children_.resize(size); }
-
- using iterator = Vector<Child, 16>::iterator;
- iterator begin() { return children_.begin(); }
- iterator end() { return children_.end(); }
- using const_iterator = Vector<Child, 16>::const_iterator;
- const_iterator begin() const { return children_.begin(); }
- const_iterator end() const { return children_.end(); }
- using reverse_iterator = Vector<Child, 16>::reverse_iterator;
- reverse_iterator rbegin() { return children_.rbegin(); }
- reverse_iterator rend() { return children_.rend(); }
- using const_reverse_iterator = Vector<Child, 16>::const_reverse_iterator;
- const_reverse_iterator rbegin() const { return children_.rbegin(); }
- const_reverse_iterator rend() const { return children_.rend(); }
-
- Child* FirstInFlowChild();
- Child* LastInFlowChild();
-
- // Add a child. Accepts all constructor arguments for |Child|.
- template <class... Args>
- void AddChild(Args&&... args) {
- children_.emplace_back(std::forward<Args>(args)...);
- }
- void InsertChild(unsigned index);
- void InsertChild(unsigned index,
- scoped_refptr<const NGLayoutResult> layout_result,
- 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);
- void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end);
- void MoveInBlockDirection(LayoutUnit);
- void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end);
-
- // Create |NGPhysicalTextFragment| for all text children.
- void CreateTextFragments(WritingMode writing_mode,
- const String& text_content);
-
- private:
- void WillInsertChild(unsigned index);
-
- Vector<Child, 16> children_;
- };
-
// Add all items in ChildList. Skips null Child if any.
- void AddChildren(ChildList&);
+ void AddChildren(NGLogicalLineItems&);
// Propagate data in |ChildList| without adding them to this builder. When
// adding children as fragment items, they appear in the container, but there
// are some data that should be propagated through line box fragments.
- void PropagateChildrenData(ChildList&);
+ void PropagateChildrenData(NGLogicalLineItems&);
// Creates the fragment. Can only be called once.
scoped_refptr<const NGLayoutResult> ToLineBoxFragment();
@@ -331,7 +96,4 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
} // namespace blink
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
- blink::NGLineBoxFragmentBuilder::Child)
-
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LINE_BOX_FRAGMENT_BUILDER_H_
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 1964973a226..45a4869ac14 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
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.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_ruby_utils.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.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"
@@ -93,7 +94,7 @@ inline void ComputeCanBreakAfter(NGInlineItemResult* item_result,
bool auto_wrap,
const LazyLineBreakIterator& break_iterator) {
item_result->can_break_after =
- auto_wrap && break_iterator.IsBreakable(item_result->end_offset);
+ auto_wrap && break_iterator.IsBreakable(item_result->EndOffset());
}
inline void RemoveLastItem(NGLineInfo* line_info) {
@@ -235,8 +236,9 @@ inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
DCHECK_LE(end_offset, item.EndOffset());
NGInlineItemResults* item_results = line_info->MutableResults();
return &item_results->emplace_back(
- &item, item_index_, offset_, end_offset, break_anywhere_if_overflow_,
- ShouldCreateLineBox(*item_results), HasUnpositionedFloats(*item_results));
+ &item, item_index_, NGTextOffset(offset_, end_offset),
+ break_anywhere_if_overflow_, ShouldCreateLineBox(*item_results),
+ HasUnpositionedFloats(*item_results));
}
inline NGInlineItemResult* NGLineBreaker::AddItem(const NGInlineItem& item,
@@ -350,7 +352,8 @@ void NGLineBreaker::NextLine(
// line boxes. These cases need to be reviewed.
bool should_create_line_box = ShouldCreateLineBox(item_results) ||
(has_list_marker_ && line_info->IsLastLine()) ||
- mode_ != NGLineBreakerMode::kContent;
+ mode_ != NGLineBreakerMode::kContent ||
+ node_.HasLineEvenIfEmpty();
if (!should_create_line_box)
line_info->SetIsEmptyLine();
@@ -443,7 +446,7 @@ void NGLineBreaker::BreakLine(
// determine the break opportunity.
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->can_break_after =
- break_iterator_.IsBreakable(item_result->end_offset);
+ break_iterator_.IsBreakable(item_result->EndOffset());
MoveToNextOf(item);
} else if (item.Type() == NGInlineItem::kListMarker) {
NGInlineItemResult* item_result = AddItem(item, line_info);
@@ -480,23 +483,23 @@ bool NGLineBreaker::ShouldForceCanBreakAfter(
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.length() <= item_result.end_offset ||
- text[item_result.end_offset] != kObjectReplacementCharacter)
+ DCHECK_GE(text.length(), item_result.EndOffset());
+ if (text.length() <= item_result.EndOffset() ||
+ text[item_result.EndOffset()] != kObjectReplacementCharacter)
return false;
// This kObjectReplacementCharacter can be any objects, such as a floating or
// an OOF object. Check if it's really an atomic inline.
const Vector<NGInlineItem>& items = Items();
for (const NGInlineItem* item = std::next(item_result.item);
item != items.end(); ++item) {
- DCHECK_EQ(item->StartOffset(), item_result.end_offset);
+ DCHECK_EQ(item->StartOffset(), item_result.EndOffset());
if (item->Type() == NGInlineItem::kAtomicInline) {
// Except when sticky images quirk was applied.
if (UNLIKELY(text[item->StartOffset()] == kNoBreakSpaceCharacter))
return false;
- return true;
+ return !item->IsRubyRun();
}
- if (item->EndOffset() > item_result.end_offset)
+ if (item->EndOffset() > item_result.EndOffset())
break;
}
return false;
@@ -556,6 +559,12 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
NGInlineItemResult* item_result = AddItem(item, line_info);
item_result->should_create_line_box = true;
+ // Try to commit |pending_end_overhang_| of a prior NGInlineItemResult.
+ // |pending_end_overhang_| doesn't work well with bidi reordering. It's
+ // difficult to compute overhang after bidi reordering because it affect
+ // line breaking.
+ if (maybe_have_end_overhang_)
+ position_ -= CommitPendingEndOverhang(line_info);
if (auto_wrap_) {
if (mode_ == NGLineBreakerMode::kMinContent &&
@@ -582,7 +591,7 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// If the break is at the middle of a text item, we know no trailable
// items follow, only trailable spaces if any. This is very common that
// shortcut to handling trailing spaces.
- if (item_result->end_offset < item.EndOffset())
+ if (item_result->EndOffset() < item.EndOffset())
return HandleTrailingSpaces(item, shape_result, line_info);
// The break point found at the end of this text item. Continue looking
@@ -608,8 +617,8 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// If this is all trailable spaces, this item is trailable, and next item
// maybe too. Don't go to |HandleOverflow()| yet.
- if (IsAllBreakableSpaces(Text(), item_result->start_offset,
- item_result->end_offset))
+ if (IsAllBreakableSpaces(Text(), item_result->StartOffset(),
+ item_result->EndOffset()))
return;
HandleOverflow(line_info);
@@ -618,8 +627,8 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// Add until the end of the item if !auto_wrap. In most cases, it's the whole
// item.
- DCHECK_EQ(item_result->end_offset, item.EndOffset());
- if (item_result->start_offset == item.StartOffset()) {
+ DCHECK_EQ(item_result->EndOffset(), item.EndOffset());
+ if (item_result->StartOffset() == item.StartOffset()) {
item_result->inline_size =
shape_result.SnappedWidth().ClampNegativeToZero();
item_result->shape_result = ShapeResultView::Create(&shape_result);
@@ -627,9 +636,9 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// <wbr> can wrap even if !auto_wrap. Spaces after that will be leading
// spaces and thus be collapsed.
DCHECK(trailing_whitespace_ == WhitespaceState::kLeading &&
- item_result->start_offset >= item.StartOffset());
+ item_result->StartOffset() >= item.StartOffset());
item_result->shape_result = ShapeResultView::Create(
- &shape_result, item_result->start_offset, item_result->end_offset);
+ &shape_result, item_result->StartOffset(), item_result->EndOffset());
item_result->inline_size =
item_result->shape_result->SnappedWidth().ClampNegativeToZero();
}
@@ -652,7 +661,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
(item.Type() == NGInlineItem::kControl &&
Text()[item.StartOffset()] == kTabulationCharacter));
DCHECK(&item_shape_result);
- item.AssertOffset(item_result->start_offset);
+ item.AssertOffset(item_result->StartOffset());
DCHECK_EQ(item_shape_result.StartIndex(), item.StartOffset());
DCHECK_EQ(item_shape_result.EndIndex(), item.EndOffset());
@@ -676,7 +685,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
// Use kStartShouldBeSafe if at the beginning of a line.
unsigned options = ShapingLineBreaker::kDefaultOptions;
- if (item_result->start_offset != line_info->StartOffset())
+ if (item_result->StartOffset() != line_info->StartOffset())
options |= ShapingLineBreaker::kDontReshapeStart;
// Reshaping between the last character and trailing spaces is needed only
@@ -702,7 +711,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
DCHECK_LE(try_count, 2u);
#endif
scoped_refptr<const ShapeResultView> shape_result = breaker.ShapeLine(
- item_result->start_offset, available_width.ClampNegativeToZero(),
+ item_result->StartOffset(), available_width.ClampNegativeToZero(),
options, &result);
// If this item overflows and 'break-word' is set, this line will be
@@ -710,14 +719,15 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
if (!shape_result) {
DCHECK(options & ShapingLineBreaker::kNoResultIfOverflow);
item_result->inline_size = available_width_with_hyphens + 1;
- item_result->end_offset = item.EndOffset();
+ item_result->text_offset.end = item.EndOffset();
+ item_result->text_offset.AssertNotEmpty();
return kOverflow;
}
DCHECK_EQ(shape_result->NumCharacters(),
- result.break_offset - item_result->start_offset);
+ result.break_offset - item_result->StartOffset());
// It is critical to move the offset forward, or NGLineBreaker may keep
// adding NGInlineItemResult until all the memory is consumed.
- CHECK_GT(result.break_offset, item_result->start_offset);
+ CHECK_GT(result.break_offset, item_result->StartOffset());
inline_size = shape_result->SnappedWidth().ClampNegativeToZero();
if (UNLIKELY(result.is_hyphenated)) {
@@ -741,7 +751,8 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
item_result->hyphen_string = String();
}
item_result->inline_size = inline_size;
- item_result->end_offset = result.break_offset;
+ item_result->text_offset.end = result.break_offset;
+ item_result->text_offset.AssertNotEmpty();
item_result->shape_result = std::move(shape_result);
break;
}
@@ -754,7 +765,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
// * If width > available_width: The first break opportunity does not fit.
// offset is the first break opportunity, either inside, at the end, or
// beyond the end.
- if (item_result->end_offset < item.EndOffset()) {
+ if (item_result->EndOffset() < item.EndOffset()) {
item_result->can_break_after = true;
if (UNLIKELY(break_iterator_.BreakType() ==
@@ -764,9 +775,9 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
trailing_whitespace_ = WhitespaceState::kNone;
}
} else {
- DCHECK_EQ(item_result->end_offset, item.EndOffset());
+ DCHECK_EQ(item_result->EndOffset(), item.EndOffset());
item_result->can_break_after =
- break_iterator_.IsBreakable(item_result->end_offset);
+ break_iterator_.IsBreakable(item_result->EndOffset());
if (!item_result->can_break_after && item.Type() == NGInlineItem::kText &&
ShouldForceCanBreakAfter(*item_result))
item_result->can_break_after = true;
@@ -783,7 +794,7 @@ NGLineBreaker::BreakResult NGLineBreaker::BreakText(
}
// Breaks the text item at the previous break opportunity from
-// |item_result->end_offset|. Returns false if there were no previous break
+// |item_result->text_offset.end|. Returns false if there were no previous break
// opportunities.
bool NGLineBreaker::BreakTextAtPreviousBreakOpportunity(
NGInlineItemResult* item_result) {
@@ -794,13 +805,14 @@ bool NGLineBreaker::BreakTextAtPreviousBreakOpportunity(
DCHECK(item.Style() && item.Style()->AutoWrap());
unsigned break_opportunity = break_iterator_.PreviousBreakOpportunity(
- item_result->end_offset - 1, item_result->start_offset);
- if (break_opportunity <= item_result->start_offset)
+ item_result->EndOffset() - 1, item_result->StartOffset());
+ if (break_opportunity <= item_result->StartOffset())
return false;
- item_result->end_offset = break_opportunity;
- item_result->shape_result =
- ShapeResultView::Create(item.TextShapeResult(), item_result->start_offset,
- item_result->end_offset);
+ item_result->text_offset.end = break_opportunity;
+ item_result->text_offset.AssertNotEmpty();
+ item_result->shape_result = ShapeResultView::Create(
+ item.TextShapeResult(), item_result->StartOffset(),
+ item_result->EndOffset());
item_result->inline_size =
item_result->shape_result->SnappedWidth().ClampNegativeToZero();
item_result->can_break_after = true;
@@ -832,7 +844,7 @@ bool NGLineBreaker::HandleTextForFastMinContent(NGInlineItemResult* item_result,
// If this is the first part of the text, it may form a word with the previous
// item. Fallback to |HandleText()|.
- unsigned start_offset = item_result->start_offset;
+ unsigned start_offset = item_result->StartOffset();
DCHECK_LT(start_offset, item.EndOffset());
if (start_offset != line_info->StartOffset() &&
start_offset == item.StartOffset())
@@ -895,7 +907,8 @@ bool NGLineBreaker::HandleTextForFastMinContent(NGInlineItemResult* item_result,
return false;
// Create an NGInlineItemResult that has the max of widths of all words.
- item_result->end_offset = last_end_offset;
+ item_result->text_offset.end = last_end_offset;
+ item_result->text_offset.AssertNotEmpty();
item_result->inline_size = LayoutUnit::FromFloatCeil(min_width);
item_result->can_break_after = true;
@@ -945,7 +958,7 @@ scoped_refptr<ShapeResultView> NGLineBreaker::TruncateLineEndResult(
const NGInlineItem& item = *item_result.item;
// Check given offsets require to truncate |item_result.shape_result|.
- const unsigned start_offset = item_result.start_offset;
+ const unsigned start_offset = item_result.StartOffset();
const ShapeResultView* source_result = item_result.shape_result.get();
DCHECK(source_result);
DCHECK_GE(start_offset, source_result->StartIndex());
@@ -978,7 +991,7 @@ void NGLineBreaker::UpdateShapeResult(const NGLineInfo& line_info,
NGInlineItemResult* item_result) {
DCHECK(item_result);
item_result->shape_result =
- TruncateLineEndResult(line_info, *item_result, item_result->end_offset);
+ TruncateLineEndResult(line_info, *item_result, item_result->EndOffset());
DCHECK(item_result->shape_result);
item_result->inline_size = item_result->shape_result->SnappedWidth();
}
@@ -1038,8 +1051,8 @@ void NGLineBreaker::HandleTrailingSpaces(const NGInlineItem& item,
NGInlineItemResult* item_result = AddItem(item, end, line_info);
item_result->has_only_trailing_spaces = true;
item_result->shape_result = ShapeResultView::Create(&shape_result);
- if (item_result->start_offset == item.StartOffset() &&
- item_result->end_offset == item.EndOffset())
+ if (item_result->StartOffset() == item.StartOffset() &&
+ item_result->EndOffset() == item.EndOffset())
item_result->inline_size = item_result->shape_result->SnappedWidth();
else
UpdateShapeResult(*line_info, item_result);
@@ -1079,7 +1092,7 @@ void NGLineBreaker::RemoveTrailingCollapsibleSpace(NGLineInfo* line_info) {
if (end_index < item_results.size()) {
const NGInlineItemResult& end_item_result = item_results[end_index];
unsigned end_item_index = end_item_result.item_index;
- unsigned end_offset = end_item_result.start_offset;
+ unsigned end_offset = end_item_result.StartOffset();
ResetRewindLoopDetector();
Rewind(end_index, line_info);
item_index_ = end_item_index;
@@ -1100,8 +1113,8 @@ void NGLineBreaker::RemoveTrailingCollapsibleSpace(NGLineInfo* line_info) {
position_ -= item_result->inline_size;
if (scoped_refptr<const ShapeResultView>& collapsed_shape_result =
trailing_collapsible_space_->collapsed_shape_result) {
- DCHECK_GE(item_result->end_offset, item_result->start_offset + 2);
- --item_result->end_offset;
+ --item_result->text_offset.end;
+ item_result->text_offset.AssertNotEmpty();
item_result->shape_result = collapsed_shape_result;
item_result->inline_size = item_result->shape_result->SnappedWidth();
position_ += item_result->inline_size;
@@ -1152,9 +1165,9 @@ void NGLineBreaker::ComputeTrailingCollapsibleSpace(NGLineInfo* line_info) {
if (item.EndCollapseType() == NGInlineItem::kOpaqueToCollapsing)
continue;
if (item.Type() == NGInlineItem::kText) {
- DCHECK_GT(item_result.end_offset, 0u);
+ DCHECK_GT(item_result.EndOffset(), 0u);
DCHECK(item.Style());
- if (!IsBreakableSpace(text[item_result.end_offset - 1]))
+ if (!IsBreakableSpace(text[item_result.EndOffset() - 1]))
break;
if (!item.Style()->CollapseWhiteSpace()) {
trailing_whitespace_ = WhitespaceState::kPreserved;
@@ -1169,10 +1182,10 @@ void NGLineBreaker::ComputeTrailingCollapsibleSpace(NGLineInfo* line_info) {
trailing_collapsible_space_->item_result != &item_result) {
trailing_collapsible_space_.emplace();
trailing_collapsible_space_->item_result = &item_result;
- if (item_result.end_offset - 1 > item_result.start_offset) {
+ if (item_result.EndOffset() - 1 > item_result.StartOffset()) {
trailing_collapsible_space_->collapsed_shape_result =
TruncateLineEndResult(*line_info, item_result,
- item_result.end_offset - 1);
+ item_result.EndOffset() - 1);
}
}
trailing_whitespace_ = WhitespaceState::kCollapsible;
@@ -1377,7 +1390,8 @@ void NGLineBreaker::HandleAtomicInline(
} else {
DCHECK(mode_ == NGLineBreakerMode::kMinContent || !max_size_cache_);
NGBlockNode child(ToLayoutBox(item.GetLayoutObject()));
- MinMaxSizesInput input(percentage_resolution_block_size_for_min_max);
+ MinMaxSizesInput input(percentage_resolution_block_size_for_min_max,
+ MinMaxSizesType::kContent);
MinMaxSizesResult result =
ComputeMinAndMaxContentContribution(node_.Style(), child, input);
if (mode_ == NGLineBreakerMode::kMinContent) {
@@ -1405,6 +1419,25 @@ void NGLineBreaker::HandleAtomicInline(
auto_wrap_ && !(sticky_images_quirk_ && item.IsImage());
position_ += item_result->inline_size;
+
+ if (item.IsRubyRun()) {
+ // Overrides can_break_after.
+ ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
+
+ NGAnnotationOverhang overhang = GetOverhang(*item_result);
+ if (overhang.end > LayoutUnit()) {
+ item_result->pending_end_overhang = overhang.end;
+ maybe_have_end_overhang_ = true;
+ }
+
+ if (CanApplyStartOverhang(*line_info, overhang.start)) {
+ DCHECK_EQ(item_result->margins.inline_start, LayoutUnit());
+ item_result->margins.inline_start = -overhang.start;
+ item_result->inline_size -= overhang.start;
+ position_ -= overhang.start;
+ }
+ }
+
trailing_whitespace_ = WhitespaceState::kNone;
MoveToNextOf(item);
}
@@ -1640,8 +1673,8 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
// iterator cannot compute this because it considers break opportunities are
// before a run of spaces.
const String& text = Text();
- if (item_result->end_offset < text.length() &&
- IsBreakableSpace(text[item_result->end_offset])) {
+ if (item_result->EndOffset() < text.length() &&
+ IsBreakableSpace(text[item_result->EndOffset()])) {
item_result->can_break_after = true;
return;
}
@@ -1720,7 +1753,7 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
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);
+ DCHECK_LE(item_result->EndOffset(), item_result_before.EndOffset());
#if DCHECK_IS_ON()
item_result->CheckConsistency(true);
#endif
@@ -1728,8 +1761,8 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
// If BreakText() changed this item small enough to fit, break here.
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());
+ item_result->EndOffset() < item_result_before.EndOffset()) {
+ DCHECK_LT(item_result->EndOffset(), item.EndOffset());
// If this is the last item, adjust it to accommodate the change.
const unsigned new_end = i + 1;
@@ -1739,7 +1772,7 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
available_width + width_to_rewind + item_result->inline_size;
DCHECK_EQ(position_, line_info->ComputeWidth());
item_index_ = item_result->item_index;
- offset_ = item_result->end_offset;
+ offset_ = item_result->EndOffset();
items_data_.AssertOffset(item_index_, offset_);
HandleTrailingSpaces(item, line_info);
return;
@@ -1818,11 +1851,11 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) {
const EWhiteSpace white_space = item.Style()->WhiteSpace();
if (ComputedStyle::AutoWrap(white_space) &&
white_space != EWhiteSpace::kBreakSpaces &&
- IsBreakableSpace(text[item_result.start_offset])) {
+ IsBreakableSpace(text[item_result.StartOffset()])) {
// If all characters are trailable spaces, check the next item.
if (item_result.shape_result &&
- IsAllBreakableSpaces(text, item_result.start_offset + 1,
- item_result.end_offset)) {
+ IsAllBreakableSpaces(text, item_result.StartOffset() + 1,
+ item_result.EndOffset())) {
continue;
}
// If this item starts with spaces followed by non-space characters,
@@ -1842,7 +1875,7 @@ void NGLineBreaker::RewindOverflow(unsigned new_end, NGLineInfo* line_info) {
// All control characters except newline are trailable if auto_wrap. We
// should not have rewound if there was a newline, so safe to assume all
// controls are trailable.
- DCHECK_NE(text[item_result.start_offset], kNewlineCharacter);
+ DCHECK_NE(text[item_result.StartOffset()], kNewlineCharacter);
DCHECK(item.Style());
EWhiteSpace white_space = item.Style()->WhiteSpace();
if (ComputedStyle::AutoWrap(white_space) &&
@@ -1953,8 +1986,9 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) {
// When rewinding all items, use |results[0].start_offset|.
const NGInlineItemResult& first_remove = item_results[new_end];
item_index_ = first_remove.item_index;
- offset_ = first_remove.start_offset;
+ offset_ = first_remove.StartOffset();
trailing_whitespace_ = WhitespaceState::kLeading;
+ maybe_have_end_overhang_ = false;
}
SetCurrentStyle(ComputeCurrentStyle(new_end, line_info));
@@ -2077,7 +2111,7 @@ void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) {
}
void NGLineBreaker::MoveToNextOf(const NGInlineItemResult& item_result) {
- offset_ = item_result.end_offset;
+ offset_ = item_result.EndOffset();
item_index_ = item_result.item_index;
DCHECK(item_result.item);
if (offset_ == item_result.item->EndOffset())
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 05481028298..32b872488dc 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
@@ -261,6 +261,9 @@ class CORE_EXPORT NGLineBreaker {
// between images, and between text and images.
bool sticky_images_quirk_ = false;
+ // True if the resultant line contains a RubyRun with inline-end overhang.
+ bool maybe_have_end_overhang_ = 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 38160e6e800..54b6f646a29 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
@@ -22,8 +22,7 @@ String ToString(NGInlineItemResults line, NGInlineNode node) {
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));
+ StringView(text, item_result.StartOffset(), item_result.Length()));
}
return builder.ToString();
}
@@ -535,11 +534,12 @@ TEST_F(NGLineBreakerTest, MinMaxWithTrailingSpaces) {
<div id=container>12345 6789 </div>
)HTML");
- auto sizes = node.ComputeMinMaxSizes(
- WritingMode::kHorizontalTb,
- MinMaxSizesInput(/* percentage_resolution_block_size */ (
- LayoutUnit())))
- .sizes;
+ auto sizes =
+ node.ComputeMinMaxSizes(
+ WritingMode::kHorizontalTb,
+ MinMaxSizesInput(/* percentage_resolution_block_size */
+ LayoutUnit(), MinMaxSizesType::kContent))
+ .sizes;
EXPECT_EQ(sizes.min_size, LayoutUnit(60));
EXPECT_EQ(sizes.max_size, LayoutUnit(110));
}
@@ -563,10 +563,38 @@ TEST_F(NGLineBreakerTest, TableCellWidthCalculationQuirkOutOfFlow) {
node.ComputeMinMaxSizes(
WritingMode::kHorizontalTb,
- MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit()));
+ MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent));
// Pass if |ComputeMinMaxSize| doesn't hit DCHECK failures.
}
+// crbug.com/1091359
+TEST_F(NGLineBreakerTest, RewindRubyRun) {
+ NGInlineNode node = CreateInlineNode(R"HTML(
+<div id="container">
+<style>
+* {
+ -webkit-text-security:square;
+ font-size:16px;
+}
+</style>
+<big style="word-wrap: break-word">a
+<ruby dir="rtl">
+<rt>
+B AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+<svg></svg>
+<b>
+</rt>
+</ruby>
+ )HTML");
+
+ node.ComputeMinMaxSizes(
+ WritingMode::kHorizontalTb,
+ MinMaxSizesInput(/* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent));
+ // This test passes if no CHECK failures.
+}
+
#undef MAYBE_OverflowAtomicInline
} // namespace
} // namespace blink
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 0306094c1f4..98b3cf7d46b 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
@@ -6,6 +6,7 @@
#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_item_result.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/platform/fonts/font_baseline.h"
@@ -38,6 +39,7 @@ NGLineTruncator::NGLineTruncator(const NGLineInfo& line_info)
const ComputedStyle& NGLineTruncator::EllipsisStyle() const {
// The ellipsis is styled according to the line style.
// https://drafts.csswg.org/css-ui/#ellipsing-details
+ DCHECK(line_style_);
return *line_style_;
}
@@ -57,20 +59,16 @@ void NGLineTruncator::SetupEllipsis() {
}
LayoutUnit NGLineTruncator::PlaceEllipsisNextTo(
- NGLineBoxFragmentBuilder::ChildList* line_box,
- NGLineBoxFragmentBuilder::Child* ellipsized_child) {
+ NGLogicalLineItems* line_box,
+ NGLogicalLineItem* ellipsized_child) {
// Create the ellipsis, associating it with the ellipsized child.
DCHECK(ellipsized_child->HasInFlowFragment());
LayoutObject* ellipsized_layout_object =
- ellipsized_child->PhysicalFragment()->GetMutableLayoutObject();
+ ellipsized_child->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.
@@ -78,17 +76,23 @@ LayoutUnit NGLineTruncator::PlaceEllipsisNextTo(
IsLtr(line_direction_)
? ellipsized_child->InlineOffset() + ellipsized_child->inline_size
: ellipsized_child->InlineOffset() - ellipsis_width_;
- LayoutUnit ellpisis_ascent;
+ NGLineHeightMetrics ellipsis_metrics;
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;
+ ellipsis_metrics = NGLineHeightMetrics(
+ ellipsis_font_data_->GetFontMetrics(), line_style_->GetFontBaseline());
}
- line_box->AddChild(builder.ToTextFragment(),
- LogicalOffset{ellipsis_inline_offset, -ellpisis_ascent},
- ellipsis_width_, 0);
+
+ DCHECK(ellipsis_text_);
+ DCHECK(ellipsis_shape_result_.get());
+ NGTextFragmentBuilder builder(line_style_->GetWritingMode());
+ builder.SetText(ellipsized_layout_object, ellipsis_text_, &EllipsisStyle(),
+ NGStyleVariant::kEllipsis, std::move(ellipsis_shape_result_),
+ {ellipsis_width_, ellipsis_metrics.LineHeight()});
+ line_box->AddChild(
+ builder.ToTextFragment(),
+ LogicalOffset{ellipsis_inline_offset, -ellipsis_metrics.ascent},
+ ellipsis_width_, 0);
return ellipsis_inline_offset;
}
@@ -97,12 +101,13 @@ wtf_size_t NGLineTruncator::AddTruncatedChild(
bool leave_one_character,
LayoutUnit position,
TextDirection edge,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineLayoutStateStack* box_states) {
- NGLineBoxFragmentBuilder::ChildList& line = *line_box;
-
+ NGLogicalLineItems& line = *line_box;
+ const NGLogicalLineItem& source_item = line[source_index];
+ DCHECK(source_item.shape_result);
scoped_refptr<ShapeResult> shape_result =
- line[source_index].fragment->TextShapeResult()->CreateShapeResult();
+ source_item.shape_result->CreateShapeResult();
unsigned text_offset = shape_result->OffsetToFit(position, edge);
if (IsLtr(edge) ? IsLeftMostOffset(*shape_result, text_offset)
: IsRightMostOffset(*shape_result, text_offset)) {
@@ -116,51 +121,41 @@ wtf_size_t NGLineTruncator::AddTruncatedChild(
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();
+ const wtf_size_t new_index = line.size();
+ line.AddChild(TruncateText(source_item, *shape_result, text_offset, edge));
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) {
+LayoutUnit NGLineTruncator::TruncateLine(LayoutUnit line_width,
+ NGLogicalLineItems* line_box,
+ NGInlineLayoutStateStack* box_states) {
+ DCHECK(std::all_of(line_box->begin(), line_box->end(),
+ [](const auto& item) { return !item.fragment; }));
+
// 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* ellipsized_child = nullptr;
- scoped_refptr<const NGPhysicalTextFragment> truncated_fragment;
+ NGLogicalLineItem* ellipsized_child = nullptr;
+ base::Optional<NGLogicalLineItem> truncated_child;
if (IsLtr(line_direction_)) {
- NGLineBoxFragmentBuilder::Child* first_child = line_box->FirstInFlowChild();
+ NGLogicalLineItem* 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,
- &child, &truncated_fragment)) {
+ &child, &truncated_child)) {
ellipsized_child = &child;
break;
}
}
} else {
- NGLineBoxFragmentBuilder::Child* first_child = line_box->LastInFlowChild();
+ NGLogicalLineItem* first_child = line_box->LastInFlowChild();
for (auto& child : *line_box) {
if (EllipsizeChild(line_width, ellipsis_width_, &child == first_child,
- &child, &truncated_fragment)) {
+ &child, &truncated_child)) {
ellipsized_child = &child;
break;
}
@@ -172,28 +167,23 @@ LayoutUnit NGLineTruncator::TruncateLine(
return line_width;
// Truncate the text fragment if needed.
- if (truncated_fragment) {
- DCHECK(ellipsized_child->fragment);
+ if (truncated_child) {
// In order to preserve layout information before truncated, hide the
// original fragment and insert a truncated one.
size_t child_index_to_truncate = ellipsized_child - line_box->begin();
- line_box->InsertChild(child_index_to_truncate + 1);
+ line_box->InsertChild(child_index_to_truncate + 1,
+ std::move(*truncated_child));
box_states->ChildInserted(child_index_to_truncate + 1);
- NGLineBoxFragmentBuilder::Child* child_to_truncate =
+ NGLogicalLineItem* child_to_truncate =
&(*line_box)[child_index_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, ellipsized_child->inline_size);
+ DCHECK_LE(ellipsized_child->inline_size, child_to_truncate->inline_size);
if (UNLIKELY(IsRtl(line_direction_))) {
ellipsized_child->rect.offset.inline_offset +=
- ellipsized_child->inline_size - new_inline_size;
+ child_to_truncate->inline_size - ellipsized_child->inline_size;
}
- ellipsized_child->inline_size = new_inline_size;
- ellipsized_child->fragment = std::move(truncated_fragment);
}
// Create the ellipsis, associating it with the ellipsized child.
@@ -212,24 +202,27 @@ LayoutUnit NGLineTruncator::TruncateLine(
// Children with IsPlaceholder() can appear anywhere.
LayoutUnit NGLineTruncator::TruncateLineInTheMiddle(
LayoutUnit line_width,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineLayoutStateStack* box_states) {
// Shape the ellipsis and compute its inline size.
SetupEllipsis();
- NGLineBoxFragmentBuilder::ChildList& line = *line_box;
+ NGLogicalLineItems& 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())
+ if (child.IsPlaceholder())
continue;
- if (child.HasOutOfFlowFragment() || !child.fragment ||
- !child.fragment->TextShapeResult()) {
+ if (!child.shape_result) {
if (initial_index_right != kNotFound)
break;
continue;
}
+ // Skip pseudo elements like ::before.
+ if (!child.GetNode())
+ continue;
+
if (initial_index_left == kNotFound)
initial_index_left = i;
initial_index_right = i;
@@ -378,7 +371,7 @@ LayoutUnit NGLineTruncator::TruncateLineInTheMiddle(
// Hide this child from being painted. Leaves a hidden fragment so that layout
// queries such as |offsetWidth| work as if it is not truncated.
-void NGLineTruncator::HideChild(NGLineBoxFragmentBuilder::Child* child) {
+void NGLineTruncator::HideChild(NGLogicalLineItem* child) {
DCHECK(child->HasInFlowFragment());
if (const NGPhysicalTextFragment* text = child->fragment.get()) {
@@ -393,22 +386,15 @@ void NGLineTruncator::HideChild(NGLineBoxFragmentBuilder::Child* child) {
if (fragment.HasOutOfFlowPositionedDescendants())
return;
- // If this child has self painting layer, not producing fragments will not
- // suppress painting because layers are painted separately. Move it out of
- // the clipping area.
- if (fragment.HasSelfPaintingLayer()) {
- // |available_width_| may not be enough when the containing block has
- // 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->rect.offset.inline_offset = LayoutUnit::NearlyMax();
- return;
- }
-
child->layout_result = fragment.CloneAsHiddenForPaint();
return;
}
+ if (child->inline_item) {
+ child->is_hidden_for_paint = true;
+ return;
+ }
+
NOTREACHED();
}
@@ -419,9 +405,9 @@ bool NGLineTruncator::EllipsizeChild(
LayoutUnit line_width,
LayoutUnit ellipsis_width,
bool is_first_child,
- NGLineBoxFragmentBuilder::Child* child,
- scoped_refptr<const NGPhysicalTextFragment>* truncated_fragment) {
- DCHECK(truncated_fragment && !*truncated_fragment);
+ NGLogicalLineItem* child,
+ base::Optional<NGLogicalLineItem>* truncated_child) {
+ DCHECK(truncated_child && !*truncated_child);
// Leave out-of-flow children as is.
if (!child->HasInFlowFragment())
@@ -429,8 +415,7 @@ bool NGLineTruncator::EllipsizeChild(
// Inline boxes should not be ellipsized. Usually they will be created in the
// later phase, but empty inline box are already created.
- if (child->layout_result &&
- child->layout_result->PhysicalFragment().IsInlineBox())
+ if (child->IsInlineBox())
return false;
// Can't place ellipsis if this child is completely outside of the box.
@@ -449,19 +434,20 @@ bool NGLineTruncator::EllipsizeChild(
}
// At least part of this child is in the box.
- // If not all of this child can fit, try to truncate.
+ // If |child| can fit in the space, truncate this line at the end of |child|.
space_for_child -= ellipsis_width;
- if (space_for_child < child->inline_size &&
- !TruncateChild(space_for_child, is_first_child, *child,
- truncated_fragment)) {
- // This child is partially in the box, but it should not be visible because
- // earlier sibling will be truncated and ellipsized.
- if (!is_first_child)
- HideChild(child);
- return false;
- }
+ if (space_for_child >= child->inline_size)
+ return true;
- return true;
+ // If not all of this child can fit, try to truncate.
+ if (TruncateChild(space_for_child, is_first_child, *child, truncated_child))
+ return true;
+
+ // This child is partially in the box, but it can't be truncated to fit. It
+ // should not be visible because earlier sibling will be truncated.
+ if (!is_first_child)
+ HideChild(child);
+ return false;
}
// Truncate the specified child. Returns true if truncated successfully, false
@@ -474,50 +460,53 @@ bool NGLineTruncator::EllipsizeChild(
bool NGLineTruncator::TruncateChild(
LayoutUnit space_for_child,
bool is_first_child,
- const NGLineBoxFragmentBuilder::Child& child,
- scoped_refptr<const NGPhysicalTextFragment>* truncated_fragment) {
- DCHECK(truncated_fragment && !*truncated_fragment);
+ const NGLogicalLineItem& child,
+ base::Optional<NGLogicalLineItem>* truncated_child) {
+ DCHECK(truncated_child && !*truncated_child);
+ DCHECK(!child.fragment);
// If the space is not enough, try the next child.
if (space_for_child <= 0 && !is_first_child)
return false;
// Only text fragments can be truncated.
- if (!child.fragment)
- return is_first_child;
- auto& fragment = To<NGPhysicalTextFragment>(*child.fragment);
-
- // No need to truncate empty results.
- if (!fragment.TextShapeResult())
+ if (!child.shape_result)
return is_first_child;
// TODO(layout-dev): Add support for OffsetToFit to ShapeResultView to avoid
// this copy.
- scoped_refptr<blink::ShapeResult> shape_result =
- fragment.TextShapeResult()->CreateShapeResult();
- if (!shape_result)
- return is_first_child;
-
+ scoped_refptr<ShapeResult> shape_result =
+ child.shape_result->CreateShapeResult();
+ DCHECK(shape_result);
+ const NGTextOffset original_offset = child.text_offset;
// Compute the offset to truncate.
- unsigned new_length = shape_result->OffsetToFit(
+ unsigned offset_to_fit = shape_result->OffsetToFit(
IsLtr(line_direction_) ? space_for_child
: shape_result->Width() - space_for_child,
line_direction_);
- DCHECK_LE(new_length, fragment.TextLength());
- if (!new_length || new_length == fragment.TextLength()) {
+ DCHECK_LE(offset_to_fit, original_offset.Length());
+ if (!offset_to_fit || offset_to_fit == original_offset.Length()) {
if (!is_first_child)
return false;
- new_length = !new_length ? 1 : new_length - 1;
+ offset_to_fit = !offset_to_fit ? 1 : offset_to_fit - 1;
}
-
- // Truncate the text fragment.
- *truncated_fragment =
- line_direction_ == shape_result->Direction()
- ? fragment.TrimText(fragment.StartOffset(),
- fragment.StartOffset() + new_length)
- : fragment.TrimText(fragment.StartOffset() + new_length,
- fragment.EndOffset());
+ *truncated_child =
+ TruncateText(child, *shape_result, offset_to_fit, line_direction_);
return true;
}
+NGLogicalLineItem NGLineTruncator::TruncateText(const NGLogicalLineItem& item,
+ const ShapeResult& shape_result,
+ unsigned offset_to_fit,
+ TextDirection direction) {
+ const NGTextOffset new_text_offset =
+ direction == shape_result.Direction()
+ ? NGTextOffset(item.StartOffset(), item.StartOffset() + offset_to_fit)
+ : NGTextOffset(item.StartOffset() + offset_to_fit, item.EndOffset());
+ scoped_refptr<ShapeResultView> new_shape_result = ShapeResultView::Create(
+ &shape_result, new_text_offset.start, new_text_offset.end);
+ DCHECK(item.inline_item);
+ return NGLogicalLineItem(item, std::move(new_shape_result), new_text_offset);
+}
+
} // namespace blink
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 c65fa3ce5a4..024e2d3cfec 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
@@ -14,6 +14,8 @@ namespace blink {
class NGInlineLayoutStateStack;
class NGLineInfo;
+class NGLogicalLineItems;
+struct NGLogicalLineItem;
// A class to truncate lines and place ellipsis, invoked by the CSS
// 'text-overflow: ellipsis' property.
@@ -30,13 +32,12 @@ class CORE_EXPORT NGLineTruncator final {
// |line_box| should be after bidi reorder, but before box fragments are
// created.
LayoutUnit TruncateLine(LayoutUnit line_width,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineLayoutStateStack* box_states);
- LayoutUnit TruncateLineInTheMiddle(
- LayoutUnit line_width,
- NGLineBoxFragmentBuilder::ChildList* line_box,
- NGInlineLayoutStateStack* box_states);
+ LayoutUnit TruncateLineInTheMiddle(LayoutUnit line_width,
+ NGLogicalLineItems* line_box,
+ NGInlineLayoutStateStack* box_states);
private:
const ComputedStyle& EllipsisStyle() const;
@@ -45,9 +46,8 @@ class CORE_EXPORT NGLineTruncator final {
void SetupEllipsis();
// Add a child for ellipsis next to |ellipsized_child|.
- LayoutUnit PlaceEllipsisNextTo(
- NGLineBoxFragmentBuilder::ChildList* line_box,
- NGLineBoxFragmentBuilder::Child* ellipsized_child);
+ LayoutUnit PlaceEllipsisNextTo(NGLogicalLineItems* line_box,
+ NGLogicalLineItem* ellipsized_child);
static constexpr wtf_size_t kDidNotAddChild = WTF::kNotFound;
// Add a child with truncated text of (*line_box)[source_index].
@@ -65,20 +65,25 @@ class CORE_EXPORT NGLineTruncator final {
bool leave_one_character,
LayoutUnit position,
TextDirection edge,
- NGLineBoxFragmentBuilder::ChildList* line_box,
+ NGLogicalLineItems* line_box,
NGInlineLayoutStateStack* box_states);
- bool EllipsizeChild(
- LayoutUnit line_width,
- LayoutUnit ellipsis_width,
- bool is_first_child,
- NGLineBoxFragmentBuilder::Child*,
- scoped_refptr<const NGPhysicalTextFragment>* truncated_fragment);
- bool TruncateChild(
- LayoutUnit space_for_this_child,
- bool is_first_child,
- const NGLineBoxFragmentBuilder::Child& child,
- scoped_refptr<const NGPhysicalTextFragment>* truncated_fragment);
- void HideChild(NGLineBoxFragmentBuilder::Child* child);
+ bool EllipsizeChild(LayoutUnit line_width,
+ LayoutUnit ellipsis_width,
+ bool is_first_child,
+ NGLogicalLineItem*,
+ base::Optional<NGLogicalLineItem>* truncated_child);
+ bool TruncateChild(LayoutUnit space_for_this_child,
+ bool is_first_child,
+ const NGLogicalLineItem& child,
+ base::Optional<NGLogicalLineItem>* truncated_child);
+ // Create |NGLogicalLineItem| by truncating text |item| at |offset_to_fit|.
+ // |direction| specifies which side of the text is trimmed; if |kLtr|, it
+ // keeps the left end and trims the right end.
+ NGLogicalLineItem TruncateText(const NGLogicalLineItem& item,
+ const ShapeResult& shape_result,
+ unsigned offset_to_fit,
+ TextDirection direction);
+ void HideChild(NGLogicalLineItem* child);
scoped_refptr<const ComputedStyle> line_style_;
LayoutUnit available_width_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc
new file mode 100644
index 00000000000..246b6c54f1b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.cc
@@ -0,0 +1,121 @@
+// 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_logical_line_item.h"
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
+
+namespace blink {
+
+const LayoutObject* NGLogicalLineItem::GetLayoutObject() const {
+ if (inline_item)
+ return inline_item->GetLayoutObject();
+ if (const NGPhysicalFragment* fragment = PhysicalFragment())
+ return fragment->GetLayoutObject();
+ return nullptr;
+}
+
+LayoutObject* NGLogicalLineItem::GetMutableLayoutObject() const {
+ if (inline_item)
+ return inline_item->GetLayoutObject();
+ if (const NGPhysicalFragment* fragment = PhysicalFragment())
+ return fragment->GetMutableLayoutObject();
+ return nullptr;
+}
+
+const Node* NGLogicalLineItem::GetNode() const {
+ if (const LayoutObject* layout_object = GetLayoutObject())
+ return layout_object->GetNode();
+ return nullptr;
+}
+
+const ComputedStyle* NGLogicalLineItem::Style() const {
+ if (const auto* fragment = PhysicalFragment())
+ return &fragment->Style();
+ if (inline_item)
+ return inline_item->Style();
+ return nullptr;
+}
+
+void NGLogicalLineItems::CreateTextFragments(WritingMode writing_mode,
+ const String& text_content) {
+ NGTextFragmentBuilder text_builder(writing_mode);
+ for (auto& child : *this) {
+ if (const NGInlineItem* inline_item = child.inline_item) {
+ if (UNLIKELY(child.text_content)) {
+ // Create a generated text fragmment.
+ text_builder.SetText(inline_item->GetLayoutObject(), child.text_content,
+ inline_item->Style(), inline_item->StyleVariant(),
+ std::move(child.shape_result), child.MarginSize());
+ } else {
+ // Create a regular text fragmment.
+ DCHECK((inline_item->Type() == NGInlineItem::kText &&
+ (inline_item->TextType() == NGTextType::kNormal ||
+ inline_item->TextType() == NGTextType::kSymbolMarker)) ||
+ inline_item->Type() == NGInlineItem::kControl);
+ text_builder.SetItem(text_content, *inline_item,
+ std::move(child.shape_result), child.text_offset,
+ child.MarginSize());
+ }
+ text_builder.SetIsHiddenForPaint(child.is_hidden_for_paint);
+ DCHECK(!child.fragment);
+ child.fragment = text_builder.ToTextFragment();
+ }
+ }
+}
+
+NGLogicalLineItem* NGLogicalLineItems::FirstInFlowChild() {
+ for (auto& child : *this) {
+ if (child.HasInFlowFragment())
+ return &child;
+ }
+ return nullptr;
+}
+
+NGLogicalLineItem* NGLogicalLineItems::LastInFlowChild() {
+ for (auto it = rbegin(); it != rend(); it++) {
+ auto& child = *it;
+ if (child.HasInFlowFragment())
+ return &child;
+ }
+ return nullptr;
+}
+
+void NGLogicalLineItems::WillInsertChild(unsigned insert_before) {
+ unsigned index = 0;
+ for (NGLogicalLineItem& child : children_) {
+ if (index >= insert_before)
+ break;
+ if (child.children_count && index + child.children_count > insert_before)
+ ++child.children_count;
+ ++index;
+ }
+}
+
+void NGLogicalLineItems::MoveInInlineDirection(LayoutUnit delta) {
+ for (auto& child : children_)
+ child.rect.offset.inline_offset += delta;
+}
+
+void NGLogicalLineItems::MoveInInlineDirection(LayoutUnit delta,
+ unsigned start,
+ unsigned end) {
+ for (unsigned index = start; index < end; index++)
+ children_[index].rect.offset.inline_offset += delta;
+}
+
+void NGLogicalLineItems::MoveInBlockDirection(LayoutUnit delta) {
+ for (auto& child : children_)
+ child.rect.offset.block_offset += delta;
+}
+
+void NGLogicalLineItems::MoveInBlockDirection(LayoutUnit delta,
+ unsigned start,
+ unsigned end) {
+ for (unsigned index = start; index < end; index++)
+ children_[index].rect.offset.block_offset += delta;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
new file mode 100644
index 00000000000..92eaa4b0eaa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h
@@ -0,0 +1,301 @@
+// 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_LOGICAL_LINE_ITEM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LOGICAL_LINE_ITEM_H_
+
+#include "third_party/blink/renderer/core/layout/geometry/logical_rect.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_text_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+class LayoutObject;
+
+// This class represents an item in a line, after line break, but still mutable
+// and in the logical coordinate system.
+struct NGLogicalLineItem {
+ DISALLOW_NEW();
+
+ // Empty constructor needed for |resize()|.
+ NGLogicalLineItem() = default;
+ // Create a placeholder. A placeholder does not have a fragment nor a bidi
+ // level.
+ NGLogicalLineItem(LayoutUnit block_offset, LayoutUnit block_size)
+ : rect(LayoutUnit(), block_offset, LayoutUnit(), block_size) {}
+ // Crete a bidi control. A bidi control does not have a fragment, but has
+ // bidi level and affects bidi reordering.
+ explicit NGLogicalLineItem(UBiDiLevel bidi_level) : bidi_level(bidi_level) {}
+ // Create an in-flow |NGLayoutResult|.
+ NGLogicalLineItem(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) {}
+ NGLogicalLineItem(scoped_refptr<const NGLayoutResult> layout_result,
+ LogicalOffset offset,
+ LayoutUnit inline_size,
+ unsigned children_count,
+ UBiDiLevel bidi_level)
+ : layout_result(std::move(layout_result)),
+ rect(offset, LogicalSize()),
+ inline_size(inline_size),
+ children_count(children_count),
+ bidi_level(bidi_level) {}
+ // Create an in-flow text fragment.
+ NGLogicalLineItem(const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset,
+ LayoutUnit block_offset,
+ LayoutUnit inline_size,
+ LayoutUnit text_height,
+ UBiDiLevel bidi_level)
+ : inline_item(&inline_item),
+ shape_result(std::move(shape_result)),
+ text_offset(text_offset),
+ rect(LayoutUnit(), block_offset, LayoutUnit(), text_height),
+ inline_size(inline_size),
+ bidi_level(bidi_level) {}
+ NGLogicalLineItem(const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const String& text_content,
+ LayoutUnit block_offset,
+ LayoutUnit inline_size,
+ LayoutUnit text_height,
+ UBiDiLevel bidi_level)
+ : inline_item(&inline_item),
+ shape_result(std::move(shape_result)),
+ text_offset(
+ {this->shape_result->StartIndex(), this->shape_result->EndIndex()}),
+ text_content(text_content),
+ rect(LayoutUnit(), block_offset, LayoutUnit(), text_height),
+ inline_size(inline_size),
+ bidi_level(bidi_level) {}
+ NGLogicalLineItem(const NGLogicalLineItem& source_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset)
+ : inline_item(source_item.inline_item),
+ shape_result(std::move(shape_result)),
+ text_offset(text_offset),
+ text_content(source_item.text_content),
+ rect(source_item.rect),
+ inline_size(this->shape_result->SnappedWidth()),
+ bidi_level(source_item.bidi_level) {}
+ NGLogicalLineItem(scoped_refptr<const NGPhysicalTextFragment> fragment,
+ LogicalOffset offset,
+ LayoutUnit inline_size,
+ UBiDiLevel bidi_level)
+ : fragment(std::move(fragment)),
+ rect(offset, LogicalSize()),
+ inline_size(inline_size),
+ bidi_level(bidi_level) {}
+ NGLogicalLineItem(scoped_refptr<const NGPhysicalTextFragment> fragment,
+ LayoutUnit block_offset,
+ LayoutUnit inline_size,
+ UBiDiLevel bidi_level)
+ : fragment(std::move(fragment)),
+ rect(LayoutUnit(), block_offset, LayoutUnit(), LayoutUnit()),
+ inline_size(inline_size),
+ bidi_level(bidi_level) {}
+ // Create an out-of-flow positioned object.
+ NGLogicalLineItem(LayoutObject* out_of_flow_positioned_box,
+ UBiDiLevel bidi_level,
+ TextDirection container_direction)
+ : out_of_flow_positioned_box(out_of_flow_positioned_box),
+ bidi_level(bidi_level),
+ container_direction(container_direction) {}
+ // Create an unpositioned float.
+ NGLogicalLineItem(LayoutObject* unpositioned_float, UBiDiLevel bidi_level)
+ : unpositioned_float(unpositioned_float), bidi_level(bidi_level) {}
+ // Create a positioned float.
+ NGLogicalLineItem(scoped_refptr<const NGLayoutResult> layout_result,
+ NGBfcOffset bfc_offset,
+ UBiDiLevel bidi_level)
+ : layout_result(std::move(layout_result)),
+ bfc_offset(bfc_offset),
+ bidi_level(bidi_level) {}
+
+ bool IsInlineBox() const {
+ return layout_result && layout_result->PhysicalFragment().IsInlineBox();
+ }
+ bool HasInFlowFragment() const {
+ return fragment || inline_item ||
+ (layout_result && !layout_result->PhysicalFragment().IsFloating());
+ }
+ bool HasInFlowOrFloatingFragment() const {
+ return fragment || inline_item || layout_result;
+ }
+ bool HasOutOfFlowFragment() const { return out_of_flow_positioned_box; }
+ bool HasFragment() const {
+ return HasInFlowOrFloatingFragment() || HasOutOfFlowFragment();
+ }
+ bool IsControl() const {
+ return inline_item && inline_item->Type() == NGInlineItem::kControl;
+ }
+ bool CanCreateFragmentItem() const { return HasInFlowOrFloatingFragment(); }
+ bool HasBidiLevel() const { return bidi_level != 0xff; }
+ bool IsPlaceholder() const { return !HasFragment() && !HasBidiLevel(); }
+ bool IsOpaqueToBidiReordering() const {
+ if (IsPlaceholder())
+ return true;
+ // Skip all inline boxes. Fragments for inline boxes maybe created earlier
+ // if they have no children.
+ if (layout_result) {
+ const LayoutObject* layout_object =
+ layout_result->PhysicalFragment().GetLayoutObject();
+ DCHECK(layout_object);
+ if (layout_object->IsLayoutInline())
+ return true;
+ }
+ return false;
+ }
+
+ const LogicalOffset& Offset() const { return rect.offset; }
+ LayoutUnit InlineOffset() const { return rect.offset.inline_offset; }
+ LayoutUnit BlockOffset() const { return rect.offset.block_offset; }
+ LayoutUnit BlockEndOffset() const { return rect.BlockEndOffset(); }
+ const LogicalSize& Size() const { return rect.size; }
+ LogicalSize MarginSize() const { return {inline_size, Size().block_size}; }
+
+ const NGPhysicalFragment* PhysicalFragment() const {
+ if (layout_result)
+ return &layout_result->PhysicalFragment();
+ return fragment.get();
+ }
+ const LayoutObject* GetLayoutObject() const;
+ LayoutObject* GetMutableLayoutObject() const;
+ const Node* GetNode() const;
+ const ComputedStyle* Style() const;
+
+ unsigned StartOffset() const { return text_offset.start; }
+ unsigned EndOffset() const { return text_offset.end; }
+
+ TextDirection ResolvedDirection() const {
+ // Inline boxes are not leaves that they don't have directions.
+ DCHECK(HasBidiLevel() || IsInlineBox());
+ return HasBidiLevel() ? DirectionFromLevel(bidi_level)
+ : TextDirection::kLtr;
+ }
+
+ scoped_refptr<const NGLayoutResult> layout_result;
+ scoped_refptr<const NGPhysicalTextFragment> fragment;
+
+ // Data to create a text fragment from.
+ const NGInlineItem* inline_item = nullptr;
+ scoped_refptr<const ShapeResultView> shape_result;
+ NGTextOffset text_offset;
+
+ // Data to create a generated text fragment.
+ String text_content;
+
+ 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.
+ LogicalRect rect;
+ // The offset of a positioned float wrt. the root BFC. This should only be
+ // set for positioned floats.
+ NGBfcOffset bfc_offset;
+ // The inline size of the margin box.
+ LayoutUnit inline_size;
+ LayoutUnit margin_line_left;
+ // The index of |box_data_list_|, used in |PrepareForReorder()| and
+ // |UpdateAfterReorder()| to track children of boxes across BiDi reorder.
+ unsigned box_data_index = 0;
+ // For an inline box, shows the number of descendant |Child|ren, including
+ // empty ones. Includes itself, so 1 means no descendants. 0 if not an
+ // inline box. Available only after |CreateBoxFragments()|.
+ unsigned children_count = 0;
+ UBiDiLevel bidi_level = 0xff;
+ // The current text direction for OOF positioned items.
+ TextDirection container_direction = TextDirection::kLtr;
+
+ bool is_hidden_for_paint = false;
+};
+
+// A vector of Child.
+// Unlike the fragment builder, chlidren are mutable.
+// Callers can add to the fragment builder in a batch once finalized.
+class NGLogicalLineItems {
+ STACK_ALLOCATED();
+
+ public:
+ NGLogicalLineItems() = default;
+ void operator=(NGLogicalLineItems&& other) {
+ children_ = std::move(other.children_);
+ }
+
+ NGLogicalLineItem& operator[](wtf_size_t i) { return children_[i]; }
+ const NGLogicalLineItem& operator[](wtf_size_t i) const {
+ return children_[i];
+ }
+
+ wtf_size_t size() const { return children_.size(); }
+ bool IsEmpty() const { return children_.IsEmpty(); }
+ void ReserveInitialCapacity(unsigned capacity) {
+ children_.ReserveInitialCapacity(capacity);
+ }
+ void Shrink(wtf_size_t size) { children_.Shrink(size); }
+
+ using iterator = Vector<NGLogicalLineItem, 16>::iterator;
+ iterator begin() { return children_.begin(); }
+ iterator end() { return children_.end(); }
+ using const_iterator = Vector<NGLogicalLineItem, 16>::const_iterator;
+ const_iterator begin() const { return children_.begin(); }
+ const_iterator end() const { return children_.end(); }
+ using reverse_iterator = Vector<NGLogicalLineItem, 16>::reverse_iterator;
+ reverse_iterator rbegin() { return children_.rbegin(); }
+ reverse_iterator rend() { return children_.rend(); }
+ using const_reverse_iterator =
+ Vector<NGLogicalLineItem, 16>::const_reverse_iterator;
+ const_reverse_iterator rbegin() const { return children_.rbegin(); }
+ const_reverse_iterator rend() const { return children_.rend(); }
+
+ NGLogicalLineItem* FirstInFlowChild();
+ NGLogicalLineItem* LastInFlowChild();
+
+ // Add a child. Accepts all constructor arguments for |NGLogicalLineItem|.
+ template <class... Args>
+ void AddChild(Args&&... args) {
+ children_.emplace_back(std::forward<Args>(args)...);
+ }
+ void InsertChild(unsigned index, NGLogicalLineItem&& item) {
+ WillInsertChild(index);
+ children_.insert(index, item);
+ }
+ void InsertChild(unsigned index,
+ scoped_refptr<const NGLayoutResult> layout_result,
+ const LogicalRect& rect,
+ unsigned children_count) {
+ WillInsertChild(index);
+ children_.insert(
+ index, NGLogicalLineItem(std::move(layout_result), rect, children_count,
+ /* bidi_level */ 0));
+ }
+
+ void MoveInInlineDirection(LayoutUnit);
+ void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end);
+ void MoveInBlockDirection(LayoutUnit);
+ void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end);
+
+ // Create |NGPhysicalTextFragment| for all text children.
+ void CreateTextFragments(WritingMode writing_mode,
+ const String& text_content);
+
+ private:
+ void WillInsertChild(unsigned index);
+
+ Vector<NGLogicalLineItem, 16> children_;
+};
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGLogicalLineItem)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LOGICAL_LINE_ITEM_H_
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 5fb2fef7142..a199d2c241b 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
@@ -513,6 +513,35 @@ Position NGOffsetMapping::GetFirstPosition(unsigned offset) const {
return CreatePositionForOffsetMapping(node, dom_offset);
}
+const NGOffsetMappingUnit* NGOffsetMapping::GetFirstMappingUnit(
+ unsigned offset) const {
+ // Find the first unit where |unit.TextContentEnd() <= offset|
+ if (units_.IsEmpty() || units_.front().TextContentStart() > offset)
+ return nullptr;
+ const NGOffsetMappingUnit* result =
+ std::lower_bound(units_.begin(), units_.end(), offset,
+ [](const NGOffsetMappingUnit& unit, unsigned offset) {
+ return unit.TextContentEnd() < offset;
+ });
+ if (result == units_.end())
+ return nullptr;
+ const NGOffsetMappingUnit* next_unit = std::next(result);
+ if (next_unit != units_.end() && next_unit->TextContentStart() == offset) {
+ // For offset=2, returns [1] instead of [0].
+ // For offset=3, returns [3] instead of [2],
+ // in below example:
+ // text_content = "ab\ncd"
+ // offset mapping unit:
+ // [0] I DOM:0-2 TC:0-2 "ab"
+ // [1] C DOM:2-3 TC:2-2
+ // [2] I DOM:3-4 TC:2-3 "\n"
+ // [3] C DOM:4-5 TC:3-3
+ // [4] I DOM:5-7 TC:3-5 "cd"
+ return next_unit;
+ }
+ return result;
+}
+
const NGOffsetMappingUnit* NGOffsetMapping::GetLastMappingUnit(
unsigned offset) const {
// Find the last unit where |unit.TextContentStart() <= offset|
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 d5869ad6ea9..c4ee39eb926 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
@@ -214,6 +214,10 @@ class CORE_EXPORT NGOffsetMapping {
base::span<const NGOffsetMappingUnit>
GetMappingUnitsForTextContentOffsetRange(unsigned start, unsigned end) const;
+ // Returns the first |NGOffsetMappingUnit| where |TextContentStart() >=
+ // offset| including unit for generated content.
+ const NGOffsetMappingUnit* GetFirstMappingUnit(unsigned offset) const;
+
// Returns the last |NGOffsetMappingUnit| where |TextContentStart() >= offset|
// including unit for generated content.
const NGOffsetMappingUnit* GetLastMappingUnit(unsigned offset) const;
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 da14a44e464..d31c0787812 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
@@ -46,12 +46,12 @@ bool operator!=(const NGOffsetMappingUnit& unit,
return !operator==(unit, other);
}
-void PrintTo(const NGOffsetMappingUnit& unit, std::ostream& ostream) {
+void PrintTo(const NGOffsetMappingUnit& unit, std::ostream* ostream) {
static const char* kTypeNames[] = {"Identity", "Collapsed", "Expanded"};
- ostream << "{" << kTypeNames[static_cast<unsigned>(unit.GetType())] << " "
- << unit.GetLayoutObject() << " dom=" << unit.DOMStart() << "-"
- << unit.DOMEnd() << " tc=" << unit.TextContentStart() << "-"
- << unit.TextContentEnd() << "}";
+ *ostream << "{" << kTypeNames[static_cast<unsigned>(unit.GetType())] << " "
+ << unit.GetLayoutObject() << " dom=" << unit.DOMStart() << "-"
+ << unit.DOMEnd() << " tc=" << unit.TextContentStart() << "-"
+ << unit.TextContentEnd() << "}";
}
bool operator==(const Vector<NGOffsetMappingUnit>& units1,
@@ -72,19 +72,19 @@ bool operator==(const Vector<NGOffsetMappingUnit>& units,
return units == ToVector(range);
}
-void PrintTo(const Vector<NGOffsetMappingUnit>& units, std::ostream& ostream) {
- ostream << "[";
+void PrintTo(const Vector<NGOffsetMappingUnit>& units, std::ostream* ostream) {
+ *ostream << "[";
const char* comma = "";
for (const auto& unit : units) {
- ostream << comma;
+ *ostream << comma;
PrintTo(unit, ostream);
comma = ", ";
}
- ostream << "]";
+ *ostream << "]";
}
void PrintTo(const base::span<const NGOffsetMappingUnit>& range,
- std::ostream& ostream) {
+ std::ostream* ostream) {
PrintTo(ToVector(range), ostream);
}
@@ -145,6 +145,17 @@ class NGOffsetMappingTest : public NGLayoutTest {
return result.ToString();
}
+ Vector<NGOffsetMappingUnit> GetFirstLast(const std::string& caret_text) {
+ const auto offset = caret_text.find('|');
+ return {*GetOffsetMapping().GetFirstMappingUnit(offset),
+ *GetOffsetMapping().GetLastMappingUnit(offset)};
+ }
+
+ Vector<NGOffsetMappingUnit> GetUnits(wtf_size_t index1, wtf_size_t index2) {
+ const auto& units = GetOffsetMapping().GetUnits();
+ return {units[index1], units[index2]};
+ }
+
String TestCollapsingWithCSSWhiteSpace(String text, String whitespace) {
StringBuilder html;
html.Append("<div id=t style=\"white-space:");
@@ -1081,6 +1092,10 @@ TEST_F(NGOffsetMappingTest, Table) {
ASSERT_EQ(1u, result.GetRanges().size());
TEST_RANGE(result.GetRanges(), foo_node, 0u, 3u);
+
+ EXPECT_EQ(GetUnits(1, 1), GetFirstLast("|foo"));
+ EXPECT_EQ(GetUnits(1, 1), GetFirstLast("f|oo"));
+ EXPECT_EQ(GetUnits(2, 2), GetFirstLast("foo|"));
}
TEST_F(NGOffsetMappingTest, GetMappingForInlineBlock) {
@@ -1134,6 +1149,35 @@ TEST_F(NGOffsetMappingTest, NoWrapSpaceAndCollapsibleSpace) {
1u, 5u, 5u);
TEST_UNIT(mapping.GetUnits()[2], NGOffsetMappingUnitType::kIdentity, bar, 1u,
4u, 5u, 8u);
+
+ EXPECT_EQ(GetUnits(0, 0), GetFirstLast("|foo Xbar"));
+ EXPECT_EQ(GetUnits(0, 0), GetFirstLast("foo| Xbar"));
+ EXPECT_EQ(GetUnits(0, 0), GetFirstLast("foo |Xbar"));
+ EXPECT_EQ(GetUnits(2, 2), GetFirstLast("foo X|bar"));
+}
+
+TEST_F(NGOffsetMappingTest, PreLine) {
+ InsertStyleElement("#t { white-space: pre-line; }");
+ SetupHtml("t", "<div id=t>ab \n cd</div>");
+ const LayoutObject& text_ab_n_cd = *layout_object_;
+ const NGOffsetMapping& result = GetOffsetMapping();
+
+ EXPECT_EQ("ab\ncd", result.GetText());
+
+ EXPECT_EQ((Vector<NGOffsetMappingUnit>{
+ NGOffsetMappingUnit(kIdentity, text_ab_n_cd, 0u, 2u, 0u, 2u),
+ NGOffsetMappingUnit(kCollapsed, text_ab_n_cd, 2u, 3u, 2u, 2u),
+ NGOffsetMappingUnit(kIdentity, text_ab_n_cd, 3u, 4u, 2u, 3u),
+ NGOffsetMappingUnit(kCollapsed, text_ab_n_cd, 4u, 5u, 3u, 3u),
+ NGOffsetMappingUnit(kIdentity, text_ab_n_cd, 5u, 7u, 3u, 5u)}),
+ result.GetUnits());
+
+ EXPECT_EQ(GetUnits(0, 0), GetFirstLast("|ab\ncd"));
+ EXPECT_EQ(GetUnits(0, 0), GetFirstLast("a|b\ncd"));
+ EXPECT_EQ(GetUnits(1, 2), GetFirstLast("ab|\ncd"));
+ EXPECT_EQ(GetUnits(3, 4), GetFirstLast("ab\n|cd"));
+ EXPECT_EQ(GetUnits(4, 4), GetFirstLast("ab\nc|d"));
+ EXPECT_EQ(GetUnits(4, 4), GetFirstLast("ab\ncd|"));
}
TEST_F(NGOffsetMappingTest, BiDiAroundForcedBreakInPreLine) {
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 ac726c4dea4..c61c09c99f9 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
@@ -71,15 +71,21 @@ NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics() const {
namespace {
// Include the inline-size of the line-box in the overflow.
+// Do not update block offset and block size of |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))
+ if (IsHorizontalWritingMode(container_writing_mode)) {
inline_rect.size.width = rect.size.width;
- else
+ inline_rect.offset.top = overflow->offset.top;
+ inline_rect.size.height = overflow->size.height;
+ } else {
inline_rect.size.height = rect.size.height;
+ inline_rect.offset.left = overflow->offset.left;
+ inline_rect.size.width = overflow->size.width;
+ }
overflow->UniteEvenIfEmpty(inline_rect);
}
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 f7993f84dd7..4d256625f13 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
@@ -52,7 +52,7 @@ NGPhysicalTextFragment::NGPhysicalTextFragment(
DCHECK_LE(text_offset_.end, source.EndOffset());
DCHECK(shape_result_ || IsFlowControl()) << *this;
base_or_resolved_direction_ = source.base_or_resolved_direction_;
- ink_overflow_computed_ = false;
+ ink_overflow_computed_or_mathml_paint_info_ = false;
}
NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder)
@@ -60,12 +60,12 @@ NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder)
kFragmentText,
static_cast<unsigned>(builder->text_type_)),
text_(builder->text_),
- text_offset_({builder->start_offset_, builder->end_offset_}),
+ text_offset_(builder->text_offset_),
shape_result_(std::move(builder->shape_result_)) {
DCHECK(shape_result_ || IsFlowControl()) << *this;
base_or_resolved_direction_ =
static_cast<unsigned>(builder->ResolvedDirection());
- ink_overflow_computed_ = false;
+ ink_overflow_computed_or_mathml_paint_info_ = false;
}
bool NGPhysicalTextFragment::IsGeneratedText() const {
@@ -78,10 +78,8 @@ LayoutUnit NGPhysicalTextFragment::InlinePositionForOffset(
unsigned offset,
LayoutUnit (*round_function)(float),
AdjustMidCluster adjust_mid_cluster) const {
- scoped_refptr<NGFragmentItem> item =
- base::MakeRefCounted<NGFragmentItem>(*this);
- return item->InlinePositionForOffset(Text(), offset, round_function,
- adjust_mid_cluster);
+ return NGFragmentItem(*this).InlinePositionForOffset(
+ Text(), offset, round_function, adjust_mid_cluster);
}
// TODO(yosin): We should move |NGFragmentItem::InlinePositionForOffset" to
@@ -125,17 +123,14 @@ LayoutUnit NGFragmentItem::InlinePositionForOffset(StringView text,
LayoutUnit NGPhysicalTextFragment::InlinePositionForOffset(
unsigned offset) const {
- scoped_refptr<NGFragmentItem> item =
- base::MakeRefCounted<NGFragmentItem>(*this);
- return item->InlinePositionForOffset(Text(), offset);
+ return NGFragmentItem(*this).InlinePositionForOffset(Text(), offset);
}
std::pair<LayoutUnit, LayoutUnit>
NGPhysicalTextFragment::LineLeftAndRightForOffsets(unsigned start_offset,
unsigned end_offset) const {
- scoped_refptr<NGFragmentItem> item =
- base::MakeRefCounted<NGFragmentItem>(*this);
- return item->LineLeftAndRightForOffsets(Text(), start_offset, end_offset);
+ return NGFragmentItem(*this).LineLeftAndRightForOffsets(Text(), start_offset,
+ end_offset);
}
// TODO(yosin): We should move |NGFragmentItem::InlinePositionForOffset" to
@@ -162,9 +157,7 @@ std::pair<LayoutUnit, LayoutUnit> NGFragmentItem::LineLeftAndRightForOffsets(
PhysicalRect NGPhysicalTextFragment::LocalRect(unsigned start_offset,
unsigned end_offset) const {
- scoped_refptr<NGFragmentItem> item =
- base::MakeRefCounted<NGFragmentItem>(*this);
- return item->LocalRect(Text(), start_offset, end_offset);
+ return NGFragmentItem(*this).LocalRect(Text(), start_offset, end_offset);
}
// TODO(yosin): We should move |NGFragmentItem::InlinePositionForOffset" to
@@ -194,7 +187,7 @@ PhysicalRect NGFragmentItem::LocalRect(StringView text,
}
PhysicalRect NGPhysicalTextFragment::SelfInkOverflow() const {
- if (!ink_overflow_computed_)
+ if (!ink_overflow_computed_or_mathml_paint_info_)
ComputeSelfInkOverflow();
if (ink_overflow_)
return ink_overflow_->self_ink_overflow;
@@ -202,7 +195,7 @@ PhysicalRect NGPhysicalTextFragment::SelfInkOverflow() const {
}
void NGPhysicalTextFragment::ComputeSelfInkOverflow() const {
- ink_overflow_computed_ = true;
+ ink_overflow_computed_or_mathml_paint_info_ = true;
if (UNLIKELY(!shape_result_)) {
ink_overflow_ = nullptr;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc
new file mode 100644
index 00000000000..dcb51cdf645
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc
@@ -0,0 +1,305 @@
+// 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/inline/ng_ruby_utils.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_result.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+// TODO(layout-dev): Using ScrollableOverflow() is same as legacy
+// LayoutRubyRun. However its result is not good with some fonts/platforms.
+// See crbug.com/1082087.
+LayoutUnit LastLineTextLogicalBottom(const NGPhysicalBoxFragment& container,
+ LayoutUnit default_value) {
+ const ComputedStyle& container_style = container.Style();
+ if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ if (!container.Items())
+ return default_value;
+ NGInlineCursor cursor(*container.Items());
+ cursor.MoveToLastLine();
+ const auto* line_item = cursor.CurrentItem();
+ if (!line_item)
+ return default_value;
+ DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine);
+ DCHECK(line_item->LineBoxFragment());
+ PhysicalRect line_rect =
+ line_item->LineBoxFragment()->ScrollableOverflowForLine(
+ container, container_style, *line_item, cursor);
+ return container.ConvertChildToLogical(line_rect).BlockEndOffset();
+ }
+
+ const NGPhysicalLineBoxFragment* last_line = nullptr;
+ PhysicalOffset last_line_offset;
+ for (const auto& child_link : container.PostLayoutChildren()) {
+ if (const auto* maybe_line =
+ DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) {
+ last_line = maybe_line;
+ last_line_offset = child_link.offset;
+ }
+ }
+ if (!last_line)
+ return default_value;
+ PhysicalRect line_rect =
+ last_line->ScrollableOverflow(container, container_style);
+ line_rect.Move(last_line_offset);
+ return container.ConvertChildToLogical(line_rect).BlockEndOffset();
+}
+
+// TODO(layout-dev): Using ScrollableOverflow() is same as legacy
+// LayoutRubyRun. However its result is not good with some fonts/platforms.
+// See crbug.com/1082087.
+LayoutUnit FirstLineTextLogicalTop(const NGPhysicalBoxFragment& container,
+ LayoutUnit default_value) {
+ const ComputedStyle& container_style = container.Style();
+ if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
+ if (!container.Items())
+ return default_value;
+ NGInlineCursor cursor(*container.Items());
+ cursor.MoveToFirstLine();
+ const auto* line_item = cursor.CurrentItem();
+ if (!line_item)
+ return default_value;
+ DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine);
+ DCHECK(line_item->LineBoxFragment());
+ PhysicalRect line_rect =
+ line_item->LineBoxFragment()->ScrollableOverflowForLine(
+ container, container_style, *line_item, cursor);
+ return container.ConvertChildToLogical(line_rect).offset.block_offset;
+ }
+
+ for (const auto& child_link : container.PostLayoutChildren()) {
+ if (const auto* line = DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) {
+ PhysicalRect line_rect =
+ line->ScrollableOverflow(container, container_style);
+ line_rect.Move(child_link.offset);
+ return container.ConvertChildToLogical(line_rect).offset.block_offset;
+ }
+ }
+ return default_value;
+}
+
+// See LayoutRubyRun::GetOverhang().
+NGAnnotationOverhang GetOverhang(const NGInlineItemResult& item) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
+ NGAnnotationOverhang overhang;
+ if (!item.layout_result)
+ return overhang;
+
+ const auto& run_fragment =
+ To<NGPhysicalContainerFragment>(item.layout_result->PhysicalFragment());
+ LayoutUnit start_overhang = LayoutUnit::Max();
+ LayoutUnit end_overhang = LayoutUnit::Max();
+ bool found_line = false;
+ const ComputedStyle* ruby_text_style = nullptr;
+ for (const auto& child_link : run_fragment.PostLayoutChildren()) {
+ const NGPhysicalFragment& child_fragment = *child_link.get();
+ const LayoutObject* layout_object = child_fragment.GetLayoutObject();
+ if (!layout_object)
+ continue;
+ if (layout_object->IsRubyText()) {
+ ruby_text_style = layout_object->Style();
+ continue;
+ }
+ if (layout_object->IsRubyBase()) {
+ const ComputedStyle& base_style = child_fragment.Style();
+ const WritingMode writing_mode = base_style.GetWritingMode();
+ const LayoutUnit base_inline_size =
+ NGFragment(writing_mode, child_fragment).InlineSize();
+ // RubyBase's inline_size is always same as RubyRun's inline_size.
+ // Overhang values are offsets from RubyBase's inline edges to
+ // the outmost text.
+ for (const auto& base_child_link :
+ To<NGPhysicalContainerFragment>(child_fragment)
+ .PostLayoutChildren()) {
+ const LayoutUnit line_inline_size =
+ NGFragment(writing_mode, *base_child_link).InlineSize();
+ if (line_inline_size == LayoutUnit())
+ continue;
+ found_line = true;
+ const LayoutUnit start =
+ base_child_link.offset
+ .ConvertToLogical(writing_mode, base_style.Direction(),
+ child_fragment.Size(),
+ base_child_link.get()->Size())
+ .inline_offset;
+ const LayoutUnit end = base_inline_size - start - line_inline_size;
+ start_overhang = std::min(start_overhang, start);
+ end_overhang = std::min(end_overhang, end);
+ }
+ }
+ }
+
+ if (!found_line || !ruby_text_style)
+ return overhang;
+ DCHECK_NE(start_overhang, LayoutUnit::Max());
+ DCHECK_NE(end_overhang, LayoutUnit::Max());
+ // We allow overhang up to the half of ruby text font size.
+ const LayoutUnit half_width_of_ruby_font =
+ LayoutUnit(ruby_text_style->FontSize()) / 2;
+ overhang.start = std::min(start_overhang, half_width_of_ruby_font);
+ overhang.end = std::min(end_overhang, half_width_of_ruby_font);
+ return overhang;
+}
+
+// See LayoutRubyRun::GetOverhang().
+bool CanApplyStartOverhang(const NGLineInfo& line_info,
+ LayoutUnit& start_overhang) {
+ if (start_overhang <= LayoutUnit())
+ return false;
+ DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
+ const NGInlineItemResults& items = line_info.Results();
+ // Requires at least the current item and the previous item.
+ if (items.size() < 2)
+ return false;
+ // Find a previous item other than kOpenTag/kCloseTag.
+ // Searching items in the logical order doesn't work well with bidi
+ // reordering. However, it's difficult to compute overhang after bidi
+ // reordering because it affects line breaking.
+ wtf_size_t previous_index = items.size() - 2;
+ while ((items[previous_index].item->Type() == NGInlineItem::kOpenTag ||
+ items[previous_index].item->Type() == NGInlineItem::kCloseTag) &&
+ previous_index > 0)
+ --previous_index;
+ const NGInlineItemResult& previous_item = items[previous_index];
+ if (previous_item.item->Type() != NGInlineItem::kText)
+ return false;
+ const NGInlineItem& current_item = *items.back().item;
+ if (previous_item.item->Style()->FontSize() >
+ current_item.Style()->FontSize())
+ return false;
+ start_overhang = std::min(start_overhang, previous_item.inline_size);
+ return true;
+}
+
+// See LayoutRubyRun::GetOverhang().
+LayoutUnit CommitPendingEndOverhang(NGLineInfo* line_info) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
+ DCHECK(line_info);
+ NGInlineItemResults* items = line_info->MutableResults();
+ if (items->size() < 2U)
+ return LayoutUnit();
+ const NGInlineItemResult& text_item = items->back();
+ DCHECK_EQ(text_item.item->Type(), NGInlineItem::kText);
+ wtf_size_t i = items->size() - 2;
+ while ((*items)[i].item->Type() != NGInlineItem::kAtomicInline) {
+ const auto type = (*items)[i].item->Type();
+ if (type != NGInlineItem::kOpenTag && type != NGInlineItem::kCloseTag)
+ return LayoutUnit();
+ if (i-- == 0)
+ return LayoutUnit();
+ }
+ NGInlineItemResult& atomic_inline_item = (*items)[i];
+ if (!atomic_inline_item.layout_result->PhysicalFragment().IsRubyRun())
+ return LayoutUnit();
+ if (atomic_inline_item.pending_end_overhang <= LayoutUnit())
+ return LayoutUnit();
+ if (atomic_inline_item.item->Style()->FontSize() <
+ text_item.item->Style()->FontSize())
+ return LayoutUnit();
+ // Ideally we should refer to inline_size of |text_item| instead of the width
+ // of the NGInlineItem's ShapeResult. However it's impossible to compute
+ // inline_size of |text_item| before calling BreakText(), and BreakText()
+ // requires precise |position_| which takes |end_overhang| into account.
+ LayoutUnit end_overhang =
+ std::min(atomic_inline_item.pending_end_overhang,
+ LayoutUnit(text_item.item->TextShapeResult()->Width()));
+ DCHECK_EQ(atomic_inline_item.margins.inline_end, LayoutUnit());
+ atomic_inline_item.margins.inline_end = -end_overhang;
+ atomic_inline_item.inline_size -= end_overhang;
+ atomic_inline_item.pending_end_overhang = LayoutUnit();
+ return end_overhang;
+}
+
+NGAnnotationMetrics ComputeAnnotationOverflow(
+ const NGLogicalLineItems& logical_line,
+ const NGLineHeightMetrics& line_box_metrics,
+ LayoutUnit line_over,
+ const ComputedStyle& line_style) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
+ // Min/max position of content without line-height.
+ LayoutUnit content_over = line_over + line_box_metrics.ascent;
+ LayoutUnit content_under = content_over;
+
+ // Min/max position of annotations.
+ LayoutUnit annotation_over = content_over;
+ LayoutUnit annotation_under = content_over;
+
+ const LayoutUnit line_under = line_over + line_box_metrics.LineHeight();
+ bool has_over_emphasis = false;
+ bool has_under_emphasis = false;
+ for (const NGLogicalLineItem& item : logical_line) {
+ if (item.HasInFlowFragment()) {
+ if (!item.IsControl()) {
+ content_over = std::min(content_over, item.BlockOffset());
+ content_under = std::max(content_under, item.BlockEndOffset());
+ }
+ if (const auto* style = item.Style()) {
+ if (style->GetTextEmphasisMark() != TextEmphasisMark::kNone) {
+ if (style->GetTextEmphasisLineLogicalSide() == LineLogicalSide::kOver)
+ has_over_emphasis = true;
+ else
+ has_under_emphasis = true;
+ }
+ }
+ }
+
+ // Accumulate |AnnotationOverflow| from ruby runs. All ruby run items have
+ // |layout_result|.
+ const NGLayoutResult* layout_result = item.layout_result.get();
+ if (!layout_result)
+ continue;
+ LayoutUnit overflow = layout_result->AnnotationOverflow();
+ if (IsFlippedLinesWritingMode(line_style.GetWritingMode()))
+ overflow = -overflow;
+ if (overflow < LayoutUnit()) {
+ annotation_over =
+ std::min(annotation_over, item.rect.offset.block_offset + overflow);
+ } else if (overflow > LayoutUnit()) {
+ const LayoutUnit logical_bottom =
+ item.rect.offset.block_offset +
+ layout_result->PhysicalFragment()
+ .Size()
+ .ConvertToLogical(line_style.GetWritingMode())
+ .block_size;
+ annotation_under = std::max(annotation_under, logical_bottom + overflow);
+ }
+ }
+
+ // Probably this is an empty line. We should secure font-size space.
+ const LayoutUnit font_size(line_style.ComputedFontSize());
+ if (content_under - content_over < font_size) {
+ LayoutUnit half_leading = (line_box_metrics.LineHeight() - font_size) / 2;
+ half_leading = half_leading.ClampNegativeToZero();
+ content_over = line_over + half_leading;
+ content_under = line_under - half_leading;
+ }
+
+ // Don't provide annotation space if text-emphasis exists.
+ // TODO(layout-dev): If the text-emphasis is in [line_over, line_under],
+ // this line can provide annotation space.
+ if (has_over_emphasis)
+ content_over = line_over;
+ if (has_under_emphasis)
+ content_under = line_under;
+
+ const LayoutUnit overflow_over =
+ (line_over - annotation_over).ClampNegativeToZero();
+ const LayoutUnit overflow_under =
+ (annotation_under - line_under).ClampNegativeToZero();
+ return {overflow_over, overflow_under,
+ // With some fonts, text fragment sizes can exceed line-height.
+ // We need ClampNegativeToZero().
+ overflow_over ? LayoutUnit()
+ : (content_over - line_over).ClampNegativeToZero(),
+ overflow_under ? LayoutUnit()
+ : (line_under - content_under).ClampNegativeToZero()};
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h
new file mode 100644
index 00000000000..9d3afe91ff3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.h
@@ -0,0 +1,85 @@
+// 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_INLINE_NG_RUBY_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_RUBY_UTILS_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+
+namespace blink {
+
+class ComputedStyle;
+class NGLineInfo;
+class NGLogicalLineItems;
+class NGPhysicalBoxFragment;
+struct NGInlineItemResult;
+struct NGLineHeightMetrics;
+
+// Returns the logical bottom offset of the last line text, relative to
+// |container| origin. This is used to decide ruby annotation box position.
+//
+// See NGBlockLayoutAlgorithm::LayoutRubyText().
+LayoutUnit LastLineTextLogicalBottom(const NGPhysicalBoxFragment& container,
+ LayoutUnit default_value);
+
+// Returns the logical top offset of the first line text, relative to
+// |container| origin. This is used to decide ruby annotation box position.
+//
+// See NGBlockLayoutAlgorithm::LayoutRubyText().
+LayoutUnit FirstLineTextLogicalTop(const NGPhysicalBoxFragment& container,
+ LayoutUnit default_value);
+
+struct NGAnnotationOverhang {
+ LayoutUnit start;
+ LayoutUnit end;
+};
+
+// Returns overhang values of the specified NGInlineItemResult representing
+// LayoutNGRubyRun.
+//
+// This is used by NGLineBreaker.
+NGAnnotationOverhang GetOverhang(const NGInlineItemResult& item);
+
+// Returns true if |start_overhang| is applied to a previous item, and
+// clamp |start_overhang| to the width of the previous item.
+//
+// This is used by NGLineBreaker.
+bool CanApplyStartOverhang(const NGLineInfo& line_info,
+ LayoutUnit& start_overhang);
+
+// This should be called after NGInlineItemResult for a text is added in
+// NGLineBreaker::HandleText().
+//
+// This function may update a NGInlineItemResult representing RubyRun
+// in |line_info|
+LayoutUnit CommitPendingEndOverhang(NGLineInfo* line_info);
+
+// Stores ComputeAnnotationOverflow() results.
+//
+// |overflow_over| and |space_over| are exclusive. Only one of them can be
+// non-zero. |overflow_under| and |space_under| are exclusive too.
+// All fields never be negative.
+struct NGAnnotationMetrics {
+ // The amount of annotation overflow at the line-over side.
+ LayoutUnit overflow_over;
+ // The amount of annotation overflow at the line-under side.
+ LayoutUnit overflow_under;
+ // The amount of annotation space which the next line at the line-over
+ // side can consume.
+ LayoutUnit space_over;
+ // The amount of annotation space which the next line at the line-under
+ // side can consume.
+ LayoutUnit space_under;
+};
+
+// Compute over/under annotation overflow/space for the specified line.
+NGAnnotationMetrics ComputeAnnotationOverflow(
+ const NGLogicalLineItems& logical_line,
+ const NGLineHeightMetrics& line_box_metrics,
+ LayoutUnit line_over,
+ const ComputedStyle& line_style);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_RUBY_UTILS_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 bd872d67343..e6b086e8968 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
@@ -17,51 +17,47 @@ NGTextFragmentBuilder::NGTextFragmentBuilder(
const NGPhysicalTextFragment& fragment)
: NGFragmentBuilder(fragment),
text_(fragment.text_),
- start_offset_(fragment.StartOffset()),
- end_offset_(fragment.EndOffset()),
+ text_offset_(fragment.TextOffset()),
shape_result_(fragment.TextShapeResult()),
text_type_(fragment.TextType()) {}
-void NGTextFragmentBuilder::SetItem(const String& text_content,
- NGInlineItemResult* item_result,
- LayoutUnit line_height) {
- DCHECK(item_result);
- const NGInlineItem* item = item_result->item;
- DCHECK(item);
- DCHECK_NE(item->TextType(), NGTextType::kLayoutGenerated)
+void NGTextFragmentBuilder::SetItem(
+ const String& text_content,
+ const NGInlineItem& item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset,
+ const LogicalSize& size) {
+ DCHECK_NE(item.TextType(), NGTextType::kLayoutGenerated)
<< "Please use SetText() instead.";
- DCHECK(item->Style());
+ DCHECK(item.Style());
- text_type_ = item->TextType();
+ text_type_ = item.TextType();
text_ = text_content;
- start_offset_ = item_result->start_offset;
- end_offset_ = item_result->end_offset;
- resolved_direction_ = item->Direction();
- SetStyle(item->Style(), item->StyleVariant());
- size_ = {item_result->inline_size, line_height};
- shape_result_ = std::move(item_result->shape_result);
- layout_object_ = item->GetLayoutObject();
+ text_offset_ = text_offset;
+ resolved_direction_ = item.Direction();
+ SetStyle(item.Style(), item.StyleVariant());
+ size_ = size;
+ shape_result_ = std::move(shape_result);
+ layout_object_ = item.GetLayoutObject();
}
void NGTextFragmentBuilder::SetText(
LayoutObject* layout_object,
const String& text,
scoped_refptr<const ComputedStyle> style,
- bool is_ellipsis_style,
- scoped_refptr<const ShapeResultView> shape_result) {
+ NGStyleVariant style_variant,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const LogicalSize& size) {
DCHECK(layout_object);
DCHECK(style);
DCHECK(shape_result);
text_type_ = NGTextType::kLayoutGenerated;
text_ = text;
- start_offset_ = shape_result->StartIndex();
- end_offset_ = shape_result->EndIndex();
+ text_offset_ = {shape_result->StartIndex(), shape_result->EndIndex()};
resolved_direction_ = shape_result->Direction();
- SetStyle(style, is_ellipsis_style ? NGStyleVariant::kEllipsis
- : NGStyleVariant::kStandard);
- size_ = {shape_result->SnappedWidth(),
- NGLineHeightMetrics(*style).LineHeight()};
+ SetStyle(style, style_variant);
+ size_ = size;
shape_result_ = std::move(shape_result);
layout_object_ = layout_object;
}
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 1d028144f27..7e90f264e2f 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
@@ -15,14 +15,13 @@ namespace blink {
class LayoutObject;
class ShapeResultView;
-struct NGInlineItemResult;
class CORE_EXPORT NGTextFragmentBuilder final : public NGFragmentBuilder {
STACK_ALLOCATED();
public:
- NGTextFragmentBuilder(WritingMode writing_mode)
- : NGFragmentBuilder(writing_mode, TextDirection::kLtr) {}
+ explicit NGTextFragmentBuilder(WritingMode writing_mode)
+ : NGFragmentBuilder({writing_mode, TextDirection::kLtr}) {}
NGTextFragmentBuilder(const NGPhysicalTextFragment& fragment);
@@ -30,23 +29,25 @@ class CORE_EXPORT NGTextFragmentBuilder final : public NGFragmentBuilder {
// NOTE: Takes ownership of the shape result within the item result.
void SetItem(const String& text_content,
- NGInlineItemResult*,
- LayoutUnit line_height);
+ const NGInlineItem& inline_item,
+ scoped_refptr<const ShapeResultView> shape_result,
+ const NGTextOffset& text_offset,
+ const LogicalSize& size);
// Set text for generated text, e.g. hyphen and ellipsis.
void SetText(LayoutObject*,
const String& text,
scoped_refptr<const ComputedStyle>,
- bool is_ellipsis_style,
- scoped_refptr<const ShapeResultView>);
+ NGStyleVariant style_variant,
+ scoped_refptr<const ShapeResultView>,
+ const LogicalSize& size);
// Creates the fragment. Can only be called once.
scoped_refptr<const NGPhysicalTextFragment> ToTextFragment();
private:
String text_;
- unsigned start_offset_;
- unsigned end_offset_;
+ NGTextOffset text_offset_;
scoped_refptr<const ShapeResultView> shape_result_;
NGTextType text_type_ = NGTextType::kNormal;
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 0d6c7251095..8e196f9850f 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,7 +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 "base/check_op.h"
#include "third_party/blink/renderer/core/core_export.h"
namespace blink {
@@ -23,6 +23,7 @@ struct CORE_EXPORT NGTextOffset {
}
void AssertValid() const { DCHECK_GE(end, start); }
+ void AssertNotEmpty() const { DCHECK_GT(end, start); }
unsigned start;
unsigned end;
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 a7904987197..2d0f8c53b64 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
@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
+#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
@@ -252,6 +253,33 @@ bool LayoutNGBlockFlowMixin<Base>::NodeAtPoint(
accumulated_offset, action);
}
+// Move specified position to start/end of non-editable region.
+// If it can be found, we prefer a visually equivalent position that is
+// editable.
+// See also LayoutObject::CreatePositionWithAffinity()
+// Example:
+// <editable><non-editable>|abc</non-editable></editable>
+// =>
+// <editable>|<non-editable>abc</non-editable></editable>
+static PositionWithAffinity AdjustForEditingBoundary(
+ const PositionWithAffinity& position_with_affinity) {
+ if (position_with_affinity.IsNull())
+ return position_with_affinity;
+ const Position& position = position_with_affinity.GetPosition();
+ const Node& node = *position.ComputeContainerNode();
+ if (HasEditableStyle(node))
+ return position_with_affinity;
+ const Position& forward =
+ MostForwardCaretPosition(position, kCanCrossEditingBoundary);
+ if (HasEditableStyle(*forward.ComputeContainerNode()))
+ return PositionWithAffinity(forward);
+ const Position& backward =
+ MostBackwardCaretPosition(position, kCanCrossEditingBoundary);
+ if (HasEditableStyle(*backward.ComputeContainerNode()))
+ return PositionWithAffinity(backward);
+ return position_with_affinity;
+}
+
template <typename Base>
PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
const PhysicalOffset& point) const {
@@ -272,7 +300,7 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
Base::OffsetForContents(point_in_contents);
if (const PositionWithAffinity position =
paint_fragment->PositionForPoint(point_in_contents))
- return position;
+ return AdjustForEditingBoundary(position);
} else if (const NGPhysicalBoxFragment* fragment = CurrentFragment()) {
if (const NGFragmentItems* items = fragment->Items()) {
// The given offset is relative to this |LayoutBlockFlow|. Convert to the
@@ -283,7 +311,7 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
if (const PositionWithAffinity position =
cursor.PositionForPointInInlineFormattingContext(
point_in_contents, *fragment))
- return position;
+ return AdjustForEditingBoundary(position);
}
}
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 1c82b8a6480..32822417c99 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
@@ -92,7 +92,8 @@ MinMaxSizes LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths() const {
NGConstraintSpace space = ConstraintSpaceForMinMaxSizes();
MinMaxSizes sizes =
node.ComputeMinMaxSizes(node.Style().GetWritingMode(),
- MinMaxSizesInput(available_logical_height),
+ MinMaxSizesInput(available_logical_height,
+ MinMaxSizesType::kContent),
&space)
.sizes;
@@ -154,8 +155,7 @@ void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() {
NGBlockNode container_node(container);
NGBoxFragmentBuilder container_builder(
container_node, scoped_refptr<const ComputedStyle>(container_style),
- /* space */ nullptr, container_style->GetWritingMode(),
- container_style->Direction());
+ /* space */ nullptr, container_style->GetWritingDirection());
container_builder.SetIsNewFormattingContext(
container_node.CreatesNewFormattingContext());
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 5702a72422c..8d1e30248e4 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,7 +7,7 @@
#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"
+#include "third_party/blink/renderer/core/layout/list_marker.h"
namespace blink {
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 c3a6d3f0b9f..c45686e298d 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,7 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
-#include "third_party/blink/renderer/core/layout/ng/list/list_marker.h"
+#include "third_party/blink/renderer/core/layout/list_marker.h"
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
deleted file mode 100644
index 0ff22412114..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.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_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 {
-
-LayoutNGListMarkerImage::LayoutNGListMarkerImage(Element* element)
- : LayoutImage(element) {}
-
-LayoutNGListMarkerImage* LayoutNGListMarkerImage::CreateAnonymous(
- Document* document) {
- LayoutNGListMarkerImage* object = new LayoutNGListMarkerImage(nullptr);
- object->SetDocumentForAnonymous(document);
- return object;
-}
-
-bool LayoutNGListMarkerImage::IsOfType(LayoutObjectType type) const {
- return type == kLayoutObjectNGListMarkerImage || LayoutImage::IsOfType(type);
-}
-
-// 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).
-void LayoutNGListMarkerImage::ComputeIntrinsicSizingInfoByDefaultSize(
- IntrinsicSizingInfo& intrinsic_sizing_info) const {
- const SimpleFontData* font_data = Style()->GetFont().PrimaryFont();
- DCHECK(font_data);
- if (!font_data)
- return;
-
- LayoutUnit bullet_width =
- font_data->GetFontMetrics().Ascent() / LayoutUnit(2);
- LayoutSize default_object_size(bullet_width, bullet_width);
- FloatSize concrete_size = ImageResource()->ImageSizeWithDefaultSize(
- Style()->EffectiveZoom(), default_object_size);
- concrete_size.Scale(ImageDevicePixelRatio());
- LayoutSize image_size(RoundedLayoutSize(concrete_size));
-
- intrinsic_sizing_info.size.SetWidth(image_size.Width());
- intrinsic_sizing_info.size.SetHeight(image_size.Height());
- intrinsic_sizing_info.has_width = true;
- intrinsic_sizing_info.has_height = true;
-}
-
-void LayoutNGListMarkerImage::ComputeIntrinsicSizingInfo(
- IntrinsicSizingInfo& intrinsic_sizing_info) const {
- LayoutImage::ComputeIntrinsicSizingInfo(intrinsic_sizing_info);
-
- // If this is an image without intrinsic width and height, compute the
- // concrete object size by using the specified default object size.
- if (intrinsic_sizing_info.size.IsEmpty() && ImageResource())
- ComputeIntrinsicSizingInfoByDefaultSize(intrinsic_sizing_info);
-}
-
-} // namespace blink
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
deleted file mode 100644
index 86bdb5ec592..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h
+++ /dev/null
@@ -1,31 +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_LIST_LAYOUT_NG_LIST_MARKER_IMAGE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_LIST_MARKER_IMAGE_H_
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/layout_image.h"
-
-namespace blink {
-
-class Document;
-
-class CORE_EXPORT LayoutNGListMarkerImage final : public LayoutImage {
- public:
- explicit LayoutNGListMarkerImage(Element*);
- static LayoutNGListMarkerImage* CreateAnonymous(Document*);
-
- bool IsLayoutNGObject() const override { return true; }
-
- private:
- bool IsOfType(LayoutObjectType) const override;
-
- void ComputeIntrinsicSizingInfoByDefaultSize(IntrinsicSizingInfo&) const;
- void ComputeIntrinsicSizingInfo(IntrinsicSizingInfo&) const final;
-};
-
-} // 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.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h
index 3b30f46349b..fa7cb43175a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_outside_list_marker.h
@@ -7,8 +7,8 @@
#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/list_marker.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 {
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
deleted file mode 100644
index b2533d42d7a..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// 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
deleted file mode 100644
index 0ecf1844689..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/list_marker.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 e0e08726a1f..491b22f50ba 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
@@ -4,7 +4,6 @@
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
-#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_outside_list_marker.h"
@@ -36,7 +35,7 @@ bool NGUnpositionedListMarker::IsImage() const {
LayoutUnit NGUnpositionedListMarker::InlineOffset(
const LayoutUnit marker_inline_size) const {
DCHECK(marker_layout_object_);
- auto margins = LayoutListMarker::InlineMarginsForOutside(
+ auto margins = ListMarker::InlineMarginsForOutside(
marker_layout_object_->StyleRef(), IsImage(), marker_inline_size);
return margins.first;
}
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
index 831821243aa..01c72e4fa4c 100644
--- 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
@@ -124,10 +124,7 @@ FractionStackParameters GetFractionStackParameters(const ComputedStyle& style) {
NGMathFractionLayoutAlgorithm::NGMathFractionLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- border_scrollbar_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding +
- params.fragment_geometry.scrollbar) {
+ : NGLayoutAlgorithm(params) {
DCHECK(params.space.IsNewFormattingContext());
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
@@ -142,8 +139,7 @@ void NGMathFractionLayoutAlgorithm::GatherChildren(NGBlockNode* numerator,
NGBlockNode block_child = To<NGBlockNode>(child);
if (child.IsOutOfFlowPositioned()) {
container_builder_.AddOutOfFlowChildCandidate(
- block_child, {border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start});
+ block_child, BorderScrollbarPadding().StartOffset());
continue;
}
if (!*numerator) {
@@ -169,17 +165,14 @@ scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() {
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);
+ Node(), ChildAvailableSize(), 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);
+ Node(), ChildAvailableSize(), ConstraintSpace(), denominator);
scoped_refptr<const NGLayoutResult> denominator_layout_result =
denominator.Layout(denominator_space);
auto denominator_margins = ComputeMarginsFor(
@@ -238,8 +231,8 @@ scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() {
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;
+ fraction_ascent += BorderScrollbarPadding().block_start;
+ fraction_descent += BorderScrollbarPadding().block_end;
LayoutUnit total_block_size = fraction_ascent + fraction_descent;
container_builder_.SetBaseline(fraction_ascent);
@@ -247,14 +240,13 @@ scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() {
LogicalOffset numerator_offset;
LogicalOffset denominator_offset;
numerator_offset.inline_offset =
- border_scrollbar_padding_.inline_start + numerator_margins.inline_start +
- (child_available_size.inline_size -
+ BorderScrollbarPadding().inline_start + numerator_margins.inline_start +
+ (ChildAvailableSize().inline_size -
(numerator_fragment.InlineSize() + numerator_margins.InlineSum())) /
2;
denominator_offset.inline_offset =
- border_scrollbar_padding_.inline_start +
- denominator_margins.inline_start +
- (child_available_size.inline_size -
+ BorderScrollbarPadding().inline_start + denominator_margins.inline_start +
+ (ChildAvailableSize().inline_size -
(denominator_fragment.InlineSize() + denominator_margins.InlineSum())) /
2;
@@ -274,11 +266,11 @@ scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() {
denominator.StoreMargins(ConstraintSpace(), denominator_margins);
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_scrollbar_padding_, total_block_size,
- border_box_size.inline_size);
+ ConstraintSpace(), Style(), BorderPadding(), total_block_size,
+ container_builder_.InitialBorderBoxSize().inline_size);
container_builder_.SetIntrinsicBlockSize(total_block_size);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), container_builder_.Borders(),
&container_builder_)
@@ -290,7 +282,7 @@ scoped_refptr<const NGLayoutResult> NGMathFractionLayoutAlgorithm::Layout() {
MinMaxSizesResult NGMathFractionLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& child_input) const {
if (auto result = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_))
+ Node(), BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
@@ -310,7 +302,7 @@ MinMaxSizesResult NGMathFractionLayoutAlgorithm::ComputeMinMaxSizes(
child_result.depends_on_percentage_block_size;
}
- sizes += border_scrollbar_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
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
index 83640d5826a..05b8761c6f6 100644
--- 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
@@ -22,7 +22,6 @@ class CORE_EXPORT NGMathFractionLayoutAlgorithm
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const final;
void GatherChildren(NGBlockNode* numerator, NGBlockNode* denominator);
- const NGBoxStrut border_scrollbar_padding_;
};
} // namespace blink
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
index a4f4ef38039..851c08fc2fc 100644
--- 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
@@ -29,11 +29,7 @@ inline LayoutUnit InlineOffsetForDisplayMathCentering(
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) {
+ : NGLayoutAlgorithm(params) {
DCHECK(params.space.IsNewFormattingContext());
DCHECK(!ConstraintSpace().HasBlockFragmentation());
container_builder_.SetIsNewFormattingContext(
@@ -42,7 +38,7 @@ NGMathRowLayoutAlgorithm::NGMathRowLayoutAlgorithm(
}
void NGMathRowLayoutAlgorithm::LayoutRowItems(
- NGContainerFragmentBuilder::ChildrenVector* children,
+ ChildrenVector* children,
LayoutUnit* max_row_block_baseline,
LogicalSize* row_total_size) {
LayoutUnit inline_offset, max_row_ascent, max_row_descent;
@@ -53,13 +49,12 @@ void NGMathRowLayoutAlgorithm::LayoutRowItems(
// 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});
+ To<NGBlockNode>(child), BorderScrollbarPadding().StartOffset());
continue;
}
const ComputedStyle& child_style = child.Style();
NGConstraintSpace child_space = CreateConstraintSpaceForMathChild(
- Node(), child_available_size_, ConstraintSpace(), child);
+ Node(), ChildAvailableSize(), ConstraintSpace(), child);
scoped_refptr<const NGLayoutResult> result =
To<NGBlockNode>(child).Layout(child_space, nullptr /* break token */);
const NGPhysicalContainerFragment& physical_fragment =
@@ -79,8 +74,9 @@ void NGMathRowLayoutAlgorithm::LayoutRowItems(
// TODO(rbuis): Operators can add lspace and rspace.
children->emplace_back(
+ To<NGBlockNode>(child), margins,
LogicalOffset{inline_offset, margins.block_start - ascent},
- &physical_fragment);
+ std::move(&physical_fragment));
inline_offset += fragment.InlineSize() + margins.inline_end;
@@ -103,10 +99,8 @@ scoped_refptr<const NGLayoutResult> NGMathRowLayoutAlgorithm::Layout() {
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;
+ ChildrenVector children;
LayoutRowItems(&children, &max_row_block_baseline, &max_row_size);
// Add children taking into account centering, baseline and
@@ -114,23 +108,24 @@ scoped_refptr<const NGLayoutResult> NGMathRowLayoutAlgorithm::Layout() {
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;
+
+ LogicalOffset adjust_offset = BorderScrollbarPadding().StartOffset();
+ adjust_offset += LogicalOffset{center_offset, max_row_block_baseline};
+ for (auto& child_data : children) {
+ child_data.offset += adjust_offset;
container_builder_.AddChild(
- To<NGPhysicalContainerFragment>(*child.fragment), child.offset);
+ To<NGPhysicalContainerFragment>(*child_data.fragment),
+ child_data.offset);
+ child_data.child.StoreMargins(ConstraintSpace(), child_data.margins);
}
- container_builder_.SetBaseline(border_scrollbar_padding_.block_start +
- max_row_block_baseline);
+ container_builder_.SetBaseline(adjust_offset.block_offset);
auto block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_,
- max_row_size.block_size + border_scrollbar_padding_.BlockSum(),
+ ConstraintSpace(), Style(), BorderPadding(),
+ max_row_size.block_size + BorderScrollbarPadding().BlockSum(),
border_box_size.inline_size);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(
Node(), ConstraintSpace(),
@@ -144,7 +139,7 @@ scoped_refptr<const NGLayoutResult> NGMathRowLayoutAlgorithm::Layout() {
MinMaxSizesResult NGMathRowLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& child_input) const {
if (auto result = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_))
+ Node(), BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
@@ -171,7 +166,7 @@ MinMaxSizesResult NGMathRowLayoutAlgorithm::ComputeMinMaxSizes(
sizes.Encompass(LayoutUnit());
DCHECK_LE(sizes.min_size, sizes.max_size);
- sizes += border_scrollbar_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
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
index 1463e6256f6..478fc8167c0 100644
--- 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
@@ -19,21 +19,34 @@ class CORE_EXPORT NGMathRowLayoutAlgorithm
NGBoxFragmentBuilder,
NGBlockBreakToken> {
public:
- NGMathRowLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
-
- protected:
- void LayoutRowItems(NGContainerFragmentBuilder::ChildrenVector*,
- LayoutUnit* max_row_block_baseline,
- LogicalSize* row_total_size);
+ explicit NGMathRowLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
+
+ struct ChildWithOffsetAndMargins {
+ DISALLOW_NEW();
+ ChildWithOffsetAndMargins(const NGBlockNode& child,
+ const NGBoxStrut& margins,
+ LogicalOffset offset,
+ scoped_refptr<const NGPhysicalFragment> fragment)
+ : child(child),
+ margins(margins),
+ offset(offset),
+ fragment(std::move(fragment)) {}
+
+ NGBlockNode child;
+ NGBoxStrut margins;
+ LogicalOffset offset;
+ scoped_refptr<const NGPhysicalFragment> fragment;
+ };
+ typedef Vector<ChildWithOffsetAndMargins, 4> ChildrenVector;
private:
scoped_refptr<const NGLayoutResult> Layout() final;
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const final;
- LogicalSize child_available_size_;
- const NGBoxStrut border_padding_;
- const NGBoxStrut border_scrollbar_padding_;
+ void LayoutRowItems(ChildrenVector*,
+ LayoutUnit* max_row_block_baseline,
+ LogicalSize* row_total_size);
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
index afdfca8c5b8..31b2a7a4f77 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.cc
@@ -79,16 +79,11 @@ ScriptsVerticalParameters GetScriptsVerticalParameters(
NGMathScriptsLayoutAlgorithm::NGMathScriptsLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- border_scrollbar_padding_(params.fragment_geometry.border +
- params.fragment_geometry.scrollbar +
- params.fragment_geometry.padding) {
+ : NGLayoutAlgorithm(params) {
DCHECK(params.space.IsNewFormattingContext());
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
- child_available_size_ = ShrinkAvailableSize(
- container_builder_.InitialBorderBoxSize(), border_scrollbar_padding_);
}
void NGMathScriptsLayoutAlgorithm::GatherChildren(
@@ -103,8 +98,7 @@ void NGMathScriptsLayoutAlgorithm::GatherChildren(
if (child.IsOutOfFlowPositioned()) {
if (container_builder) {
container_builder->AddOutOfFlowChildCandidate(
- block_child, {border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start});
+ block_child, BorderScrollbarPadding().StartOffset());
}
continue;
}
@@ -232,7 +226,7 @@ NGMathScriptsLayoutAlgorithm::ChildAndMetrics
NGMathScriptsLayoutAlgorithm::LayoutAndGetMetrics(NGBlockNode child) const {
ChildAndMetrics child_and_metrics;
auto constraint_space = CreateConstraintSpaceForMathChild(
- Node(), child_available_size_, ConstraintSpace(), child);
+ Node(), ChildAvailableSize(), ConstraintSpace(), child);
child_and_metrics.result =
child.Layout(constraint_space, nullptr /*break_token*/);
NGBoxFragment fragment(
@@ -265,14 +259,17 @@ scoped_refptr<const NGLayoutResult> NGMathScriptsLayoutAlgorithm::Layout() {
VerticalMetrics metrics =
GetVerticalMetrics(base_metrics, sub_metrics, sup_metrics);
+ const LogicalOffset content_start_offset =
+ BorderScrollbarPadding().StartOffset();
+
LayoutUnit ascent =
std::max(base_metrics.ascent, metrics.ascent + metrics.sup_shift) +
- border_scrollbar_padding_.block_start;
+ content_start_offset.block_offset;
LayoutUnit descent =
std::max(base_metrics.descent, metrics.descent + metrics.sub_shift);
// TODO(rbuis): take into account italic correction.
- LayoutUnit inline_offset = border_scrollbar_padding_.inline_start +
- base_metrics.margins.inline_start;
+ LayoutUnit inline_offset =
+ content_start_offset.inline_offset + base_metrics.margins.inline_start;
LogicalOffset base_offset(
inline_offset,
@@ -302,15 +299,14 @@ scoped_refptr<const NGLayoutResult> NGMathScriptsLayoutAlgorithm::Layout() {
container_builder_.SetBaseline(ascent);
LayoutUnit intrinsic_block_size =
- ascent + descent + border_scrollbar_padding_.block_end;
+ ascent + descent + BorderScrollbarPadding().block_end;
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_scrollbar_padding_,
- intrinsic_block_size,
+ ConstraintSpace(), Style(), BorderPadding(), intrinsic_block_size,
container_builder_.InitialBorderBoxSize().inline_size);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), container_builder_.Borders(),
&container_builder_)
@@ -322,7 +318,7 @@ scoped_refptr<const NGLayoutResult> NGMathScriptsLayoutAlgorithm::Layout() {
MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& child_input) const {
if (auto result = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_))
+ Node(), BorderScrollbarPadding()))
return *result;
NGBlockNode base = nullptr;
@@ -384,7 +380,7 @@ MinMaxSizesResult NGMathScriptsLayoutAlgorithm::ComputeMinMaxSizes(
NOTREACHED();
break;
}
- sizes += border_scrollbar_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
index 8d8f34ea40c..ce65ecd1df9 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h
@@ -57,9 +57,6 @@ class CORE_EXPORT NGMathScriptsLayoutAlgorithm
const ChildAndMetrics& sup_metrics) const;
scoped_refptr<const NGLayoutResult> Layout() final;
-
- LogicalSize child_available_size_;
- const NGBoxStrut border_scrollbar_padding_;
};
} // namespace blink
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
index 6dad93301a8..51be5e7fb8b 100644
--- 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
@@ -11,9 +11,7 @@ namespace blink {
NGMathSpaceLayoutAlgorithm::NGMathSpaceLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding) {
+ : NGLayoutAlgorithm(params) {
DCHECK(params.fragment_geometry.scrollbar.IsEmpty());
container_builder_.SetIsNewFormattingContext(true);
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
@@ -22,27 +20,28 @@ NGMathSpaceLayoutAlgorithm::NGMathSpaceLayoutAlgorithm(
scoped_refptr<const NGLayoutResult> NGMathSpaceLayoutAlgorithm::Layout() {
DCHECK(!BreakToken());
+ LayoutUnit intrinsic_block_size = BorderScrollbarPadding().BlockSum();
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_, border_padding_.BlockSum(),
+ ConstraintSpace(), Style(), BorderPadding(), intrinsic_block_size,
container_builder_.InitialBorderBoxSize().inline_size);
- container_builder_.SetIntrinsicBlockSize(border_padding_.BlockSum());
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
container_builder_.SetBaseline(
- border_padding_.block_start +
+ BorderScrollbarPadding().block_start +
ValueForLength(Style().GetMathBaseline(), LayoutUnit()));
return container_builder_.ToBoxFragment();
}
MinMaxSizesResult NGMathSpaceLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput&) const {
- if (auto result =
- CalculateMinMaxSizesIgnoringChildren(Node(), border_padding_))
+ if (auto result = CalculateMinMaxSizesIgnoringChildren(
+ Node(), BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
- sizes += border_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, /* depends_on_percentage_block_size */ false};
}
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
index 838d058f895..5306bf446cf 100644
--- 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
@@ -21,8 +21,6 @@ class CORE_EXPORT NGMathSpaceLayoutAlgorithm
scoped_refptr<const NGLayoutResult> Layout() final;
MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const final;
-
- const NGBoxStrut border_padding_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
index 1b419ddb83e..872196de21a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.cc
@@ -75,10 +75,7 @@ UnderOverVerticalParameters GetUnderOverVerticalParameters(
NGMathUnderOverLayoutAlgorithm::NGMathUnderOverLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- border_scrollbar_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding +
- params.fragment_geometry.scrollbar) {
+ : NGLayoutAlgorithm(params) {
DCHECK(params.space.IsNewFormattingContext());
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
@@ -94,8 +91,7 @@ void NGMathUnderOverLayoutAlgorithm::GatherChildren(NGBlockNode* base,
NGBlockNode block_child = To<NGBlockNode>(child);
if (child.IsOutOfFlowPositioned()) {
container_builder_.AddOutOfFlowChildCandidate(
- block_child, {border_scrollbar_padding_.inline_start,
- border_scrollbar_padding_.block_start});
+ block_child, BorderScrollbarPadding().StartOffset());
continue;
}
if (!*base) {
@@ -135,10 +131,11 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
GatherChildren(&base, &over, &under);
const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
- auto child_available_size =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
- LayoutUnit block_offset = border_scrollbar_padding_.block_start;
+ const LogicalOffset content_start_offset =
+ BorderScrollbarPadding().StartOffset();
+
+ LayoutUnit block_offset = content_start_offset.block_offset;
UnderOverVerticalParameters parameters =
GetUnderOverVerticalParameters(Style());
// TODO(rbuis): handle stretchy operators.
@@ -148,7 +145,7 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
// therefore centered relative to themselves).
if (over) {
auto over_space = CreateConstraintSpaceForMathChild(
- Node(), child_available_size, ConstraintSpace(), over);
+ Node(), ChildAvailableSize(), ConstraintSpace(), over);
scoped_refptr<const NGLayoutResult> over_layout_result =
over.Layout(over_space);
NGBoxStrut over_margins =
@@ -158,8 +155,8 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
To<NGPhysicalBoxFragment>(over_layout_result->PhysicalFragment()));
block_offset += parameters.over_extra_ascender + over_margins.block_start;
LogicalOffset over_offset = {
- border_scrollbar_padding_.inline_start + over_margins.inline_start +
- (child_available_size.inline_size -
+ content_start_offset.inline_offset + over_margins.inline_start +
+ (ChildAvailableSize().inline_size -
(over_fragment.InlineSize() + over_margins.InlineSum())) /
2,
block_offset};
@@ -180,7 +177,7 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
}
auto base_space = CreateConstraintSpaceForMathChild(
- Node(), child_available_size, ConstraintSpace(), base);
+ Node(), ChildAvailableSize(), ConstraintSpace(), base);
auto base_layout_result = base.Layout(base_space);
auto base_margins =
ComputeMarginsFor(base_space, base.Style(), ConstraintSpace());
@@ -191,8 +188,8 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
block_offset += base_margins.block_start;
LogicalOffset base_offset = {
- border_scrollbar_padding_.inline_start + base_margins.inline_start +
- (child_available_size.inline_size -
+ content_start_offset.inline_offset + base_margins.inline_start +
+ (ChildAvailableSize().inline_size -
(base_fragment.InlineSize() + base_margins.InlineSum())) /
2,
block_offset};
@@ -203,7 +200,7 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
if (under) {
auto under_space = CreateConstraintSpaceForMathChild(
- Node(), child_available_size, ConstraintSpace(), under);
+ Node(), ChildAvailableSize(), ConstraintSpace(), under);
scoped_refptr<const NGLayoutResult> under_layout_result =
under.Layout(under_space);
NGBoxStrut under_margins =
@@ -221,8 +218,8 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
parameters.under_shift_min - under_ascent);
}
LogicalOffset under_offset = {
- border_scrollbar_padding_.inline_start + under_margins.inline_start +
- (child_available_size.inline_size -
+ content_start_offset.inline_offset + under_margins.inline_start +
+ (ChildAvailableSize().inline_size -
(under_fragment.InlineSize() + under_margins.InlineSum())) /
2,
block_offset};
@@ -238,14 +235,14 @@ scoped_refptr<const NGLayoutResult> NGMathUnderOverLayoutAlgorithm::Layout() {
base_fragment.Baseline().value_or(base_fragment.BlockSize());
container_builder_.SetBaseline(base_offset.block_offset + base_ascent);
- block_offset += border_scrollbar_padding_.block_end;
+ block_offset += BorderScrollbarPadding().block_end;
- LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_scrollbar_padding_, block_offset,
- border_box_size.inline_size);
+ LayoutUnit block_size =
+ ComputeBlockSizeForFragment(ConstraintSpace(), Style(), BorderPadding(),
+ block_offset, border_box_size.inline_size);
container_builder_.SetIntrinsicBlockSize(block_offset);
- container_builder_.SetBlockSize(block_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), container_builder_.Borders(),
&container_builder_)
@@ -259,7 +256,7 @@ MinMaxSizesResult NGMathUnderOverLayoutAlgorithm::ComputeMinMaxSizes(
DCHECK(IsValidMathMLScript(Node()));
if (auto result = CalculateMinMaxSizesIgnoringChildren(
- Node(), border_scrollbar_padding_))
+ Node(), BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
@@ -279,7 +276,7 @@ MinMaxSizesResult NGMathUnderOverLayoutAlgorithm::ComputeMinMaxSizes(
child_result.depends_on_percentage_block_size;
}
- sizes += border_scrollbar_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h
index a324e1b6032..c5d7d931943 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h
@@ -26,8 +26,6 @@ class CORE_EXPORT NGMathUnderOverLayoutAlgorithm
void GatherChildren(NGBlockNode* base,
NGBlockNode* second,
NGBlockNode* third);
-
- const NGBoxStrut border_scrollbar_padding_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h
new file mode 100644
index 00000000000..157a3f09651
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h
@@ -0,0 +1,28 @@
+// 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_MATHML_PAINT_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATHML_PAINT_INFO_H_
+
+#include <unicode/uchar.h>
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+
+namespace blink {
+
+class ShapeResultView;
+
+struct CORE_EXPORT NGMathMLPaintInfo {
+ USING_FAST_MALLOC(NGMathMLPaintInfo);
+
+ public:
+ scoped_refptr<const ShapeResultView> operator_shape_result_view;
+ LayoutUnit operator_inline_size;
+ LayoutUnit operator_ascent;
+ LayoutUnit operator_descent;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATHML_PAINT_INFO_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 fd2180c4c50..f8acd6fe3ed 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
@@ -369,15 +369,21 @@ base::Optional<LayoutUnit> ComputeAbsoluteDialogYPosition(
return base::nullopt;
}
- auto* scrollable_area = dialog.GetDocument().View()->LayoutViewport();
+ auto& document = dialog.GetDocument();
+ auto* scrollable_area = document.View()->LayoutViewport();
LayoutUnit top =
LayoutUnit((dialog.Style()->GetPosition() == EPosition::kFixed)
? 0
: scrollable_area->ScrollOffsetInt().Height());
- int visible_height = dialog.GetDocument().View()->Height();
+ if (top)
+ UseCounter::Count(document, WebFeature::kDialogWithNonZeroScrollOffset);
+
+ int visible_height = document.View()->Height();
if (height < visible_height)
top += (visible_height - height) / 2;
+ else if (height > visible_height)
+ UseCounter::Count(document, WebFeature::kDialogHeightLargerThanViewport);
dialog_node->SetCentered(top);
return top;
}
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 eea8efbb3da..dc210bf78e6 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
@@ -27,13 +27,15 @@ NGBlockBreakToken::NGBlockBreakToken(
unsigned sequence_number,
const NGBreakTokenVector& child_break_tokens,
NGBreakAppeal break_appeal,
- bool has_seen_all_children)
+ bool has_seen_all_children,
+ bool is_at_block_end)
: 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;
+ is_at_block_end_ = is_at_block_end;
for (wtf_size_t i = 0; i < child_break_tokens.size(); ++i) {
child_break_tokens_[i] = child_break_tokens[i].get();
child_break_tokens_[i]->AddRef();
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 ee2bcc92fba..917d753b120 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
@@ -30,7 +30,8 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
unsigned sequence_number,
const NGBreakTokenVector& child_break_tokens,
NGBreakAppeal break_appeal,
- bool has_seen_all_children) {
+ bool has_seen_all_children,
+ bool is_at_block_end) {
// We store the children list inline in the break token as a flexible
// array. Therefore, we need to make sure to allocate enough space for
// that array here, which requires a manual allocation + placement new.
@@ -38,9 +39,10 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
sizeof(NGBlockBreakToken) +
child_break_tokens.size() * sizeof(NGBreakToken*),
::WTF::GetStringWithTypeName<NGBlockBreakToken>());
- new (data) NGBlockBreakToken(PassKey(), node, consumed_block_size,
- sequence_number, child_break_tokens,
- break_appeal, has_seen_all_children);
+ new (data)
+ NGBlockBreakToken(PassKey(), node, consumed_block_size, sequence_number,
+ child_break_tokens, break_appeal,
+ has_seen_all_children, is_at_block_end);
return base::AdoptRef(static_cast<NGBlockBreakToken*>(data));
}
@@ -95,6 +97,30 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
// have one (since all children are either finished, or have a break token).
bool HasSeenAllChildren() const { return has_seen_all_children_; }
+ // Return true if layout was past the block-end border edge of the node when
+ // it fragmented. This typically means that something is overflowing the node,
+ // and that establishes a parallel flow [1]. Subsequent content may be put
+ // into the same fragmentainer as a fragment whose break token is in this
+ // state, as long as it fits.
+ //
+ // [1] https://www.w3.org/TR/css-break-3/#parallel-flows
+ //
+ // <div style="columns:2; column-fill:auto; height:100px;">
+ // <div id="a" style="height:100px;">
+ // <div id="inner" style="height:200px;"></div>
+ // </div>
+ // <div id="b" style="margin-top:-30px; height:30px;"></div>
+ // </div>
+ //
+ // #a and #b will be in the first column, while #inner will be in both the
+ // first and second one. The important detail here is that we're at the end of
+ // #a exactly at the bottom of the first column - even if #a broke inside
+ // because of #child. This means that we have no space left as such, but we're
+ // not ready to proceed to the next column. Anything that can fit at the
+ // bottom of a column (either because it actually has 0 height, or e.g. a
+ // negative top margin) will be put into that column, not the next.
+ bool IsAtBlockEnd() const { return is_at_block_end_; }
+
// The break tokens for children of the layout node.
//
// Each child we have visited previously in the block-flow layout algorithm
@@ -125,7 +151,8 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
unsigned sequence_number,
const NGBreakTokenVector& child_break_tokens,
NGBreakAppeal break_appeal,
- bool has_seen_all_children);
+ bool has_seen_all_children,
+ bool is_at_block_end);
explicit NGBlockBreakToken(PassKey, NGLayoutInputNode node);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
index 3b7636ad530..d75eb17c8d5 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
@@ -43,9 +43,6 @@ class CORE_EXPORT NGBlockChildIterator {
Entry NextChild(
const NGInlineBreakToken* previous_inline_break_token = nullptr);
- // Return true if there are no more children to process.
- bool IsAtEnd() const { return !child_; }
-
private:
NGLayoutInputNode child_;
const NGBlockBreakToken* break_token_;
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 01b46282762..91a0e741387 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
@@ -59,19 +59,19 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) {
NGBreakTokenVector empty_tokens_list;
scoped_refptr<NGBreakToken> child_token1 = NGBlockBreakToken::Create(
node1, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
scoped_refptr<NGBreakToken> child_token2 = NGBlockBreakToken::Create(
node2, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
scoped_refptr<NGBreakToken> child_token3 = NGBlockBreakToken::Create(
node3, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
NGBreakTokenVector child_break_tokens;
child_break_tokens.push_back(child_token1);
scoped_refptr<NGBlockBreakToken> parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
NGBlockChildIterator iterator(node1, parent_token.get());
ASSERT_EQ(NGBlockChildIterator::Entry(node1, child_token1.get()),
@@ -87,7 +87,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) {
child_break_tokens.push_back(child_token2);
parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
iterator = NGBlockChildIterator(node1, parent_token.get());
ASSERT_EQ(NGBlockChildIterator::Entry(node1, child_token1.get()),
@@ -104,7 +104,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) {
child_break_tokens.push_back(child_token3);
parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
iterator = NGBlockChildIterator(node1, parent_token.get());
ASSERT_EQ(NGBlockChildIterator::Entry(node2, child_token2.get()),
@@ -120,7 +120,7 @@ TEST_F(NGBlockChildIteratorTest, BreakTokens) {
child_break_tokens.push_back(child_token3);
parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
iterator = NGBlockChildIterator(node1, parent_token.get());
ASSERT_EQ(NGBlockChildIterator::Entry(node1, child_token1.get()),
@@ -146,13 +146,13 @@ TEST_F(NGBlockChildIteratorTest, SeenAllChildren) {
NGBreakTokenVector empty_tokens_list;
scoped_refptr<NGBreakToken> child_token1 = NGBlockBreakToken::Create(
node1, LayoutUnit(), 0, empty_tokens_list, kBreakAppealPerfect,
- /* has_seen_all_children */ false);
+ /* has_seen_all_children */ false, /* is_at_block_end */ false);
NGBreakTokenVector child_break_tokens;
child_break_tokens.push_back(child_token1);
scoped_refptr<NGBlockBreakToken> parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ true);
+ /* has_seen_all_children */ true, /* is_at_block_end */ false);
// We have a break token for #child1, but have seen all children. This happens
// e.g. when #child1 has overflow into a new fragmentainer, while #child2 was
@@ -167,7 +167,7 @@ TEST_F(NGBlockChildIteratorTest, SeenAllChildren) {
child_break_tokens.clear();
parent_token = NGBlockBreakToken::Create(
container, LayoutUnit(), 0, child_break_tokens, kBreakAppealPerfect,
- /* has_seen_all_children */ true);
+ /* has_seen_all_children */ true, /* is_at_block_end */ false);
// We have no break tokens, but have seen all children. This happens e.g. when
// we have a large container with fixed block-size, with empty space at the
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 ab6b62882b8..d2a1a60c73d 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
@@ -14,6 +14,8 @@
#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_ruby_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.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_child_iterator.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h"
@@ -38,96 +40,24 @@
namespace blink {
namespace {
-// Returns the logical bottom offset of the last line text, relative to
-// |container| origin. This is used to decide ruby annotation box position.
-//
-// TODO(layout-dev): Using ScrollableOverflow() is same as legacy
-// LayoutRubyRun. However its result is not good with some fonts/platforms.
-LayoutUnit LastLineTextLogicalBottom(const NGPhysicalBoxFragment& container,
- LayoutUnit default_value) {
- const ComputedStyle& container_style = container.Style();
- if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
- if (!container.Items())
- return default_value;
- NGInlineCursor cursor(*container.Items());
- cursor.MoveToLastLine();
- const auto* line_item = cursor.CurrentItem();
- if (!line_item)
- return default_value;
- DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine);
- DCHECK(line_item->LineBoxFragment());
- PhysicalRect line_rect =
- line_item->LineBoxFragment()->ScrollableOverflowForLine(
- container, container_style, *line_item, cursor);
- return line_rect
- .ConvertToLogical(container_style.GetWritingMode(),
- container_style.Direction(), container.Size(),
- cursor.Current().Size())
- .BlockEndOffset();
- }
-
- const NGPhysicalLineBoxFragment* last_line = nullptr;
- PhysicalOffset last_line_offset;
- for (const auto& child_link : container.PostLayoutChildren()) {
- if (const auto* maybe_line =
- DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) {
- last_line = maybe_line;
- last_line_offset = child_link.offset;
- }
- }
- if (!last_line)
- return default_value;
- PhysicalRect line_rect =
- last_line->ScrollableOverflow(container, container_style);
- line_rect.Move(last_line_offset);
- return line_rect
- .ConvertToLogical(container_style.GetWritingMode(),
- container_style.Direction(), container.Size(),
- last_line->Size())
- .BlockEndOffset();
-}
-
-// Returns the logical top offset of the first line text, relative to
-// |container| origin. This is used to decide ruby annotation box position.
-//
-// TODO(layout-dev): Using ScrollableOverflow() is same as legacy
-// LayoutRubyRun. However its result is not good with some fonts/platforms.
-LayoutUnit FirstLineTextLogicalTop(const NGPhysicalBoxFragment& container,
- LayoutUnit default_value) {
- const ComputedStyle& container_style = container.Style();
- if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
- if (!container.Items())
- return default_value;
- NGInlineCursor cursor(*container.Items());
- cursor.MoveToFirstLine();
- const auto* line_item = cursor.CurrentItem();
- if (!line_item)
- return default_value;
- DCHECK_EQ(line_item->Type(), NGFragmentItem::kLine);
- DCHECK(line_item->LineBoxFragment());
- PhysicalRect line_rect =
- line_item->LineBoxFragment()->ScrollableOverflowForLine(
- container, container_style, *line_item, cursor);
- return line_rect
- .ConvertToLogical(container_style.GetWritingMode(),
- container_style.Direction(), container.Size(),
- cursor.Current().Size())
- .offset.block_offset;
- }
-
- for (const auto& child_link : container.PostLayoutChildren()) {
- if (const auto* line = DynamicTo<NGPhysicalLineBoxFragment>(*child_link)) {
- PhysicalRect line_rect =
- line->ScrollableOverflow(container, container_style);
- line_rect.Move(child_link.offset);
- return line_rect
- .ConvertToLogical(container_style.GetWritingMode(),
- container_style.Direction(), container.Size(),
- line->Size())
- .offset.block_offset;
- }
- }
- return default_value;
+bool HasLineEvenIfEmpty(LayoutBox* box) {
+ LayoutBlockFlow* const block_flow = DynamicTo<LayoutBlockFlow>(box);
+ if (!block_flow)
+ return false;
+ // Note: |block_flow->NeedsCollectInline()| is true after removing all
+ // children from block[1].
+ // [1] editing/inserting/insert_after_delete.html
+ LayoutObject* const child = GetLayoutObjectForFirstChildNode(block_flow);
+ if (!child) {
+ // Note: |block_flow->ChildrenInline()| can be both true or false:
+ // - true: just after construction, <div></div>
+ // - true: one of child is inline them remove all, <div>abc</div>
+ // - false: all children are block then remove all, <div><p></p></div>
+ return block_flow->HasLineIfEmpty();
+ }
+ if (!AreNGBlockFlowChildrenInline(block_flow))
+ return false;
+ return NGInlineNode(block_flow).HasLineEvenIfEmpty();
}
inline scoped_refptr<const NGLayoutResult> LayoutBlockChild(
@@ -268,16 +198,11 @@ inline bool IsEarlyBreakpoint(const NGEarlyBreak& breakpoint,
NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
: NGLayoutAlgorithm(params),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
- border_scrollbar_padding_(border_padding_ +
- params.fragment_geometry.scrollbar),
previous_result_(params.previous_result),
is_resuming_(IsResumingLayout(params.break_token)),
exclusion_space_(params.space.ExclusionSpace()),
lines_until_clamp_(params.space.LinesUntilClamp()),
early_break_(params.early_break) {
- AdjustForFragmentation(BreakToken(), &border_scrollbar_padding_);
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
@@ -293,8 +218,8 @@ void NGBlockLayoutAlgorithm::SetBoxType(NGPhysicalFragment::NGBoxType type) {
MinMaxSizesResult NGBlockLayoutAlgorithm::ComputeMinMaxSizes(
const MinMaxSizesInput& input) const {
- if (auto result = CalculateMinMaxSizesIgnoringChildren(
- node_, border_scrollbar_padding_))
+ if (auto result =
+ CalculateMinMaxSizesIgnoringChildren(node_, BorderScrollbarPadding()))
return *result;
MinMaxSizes sizes;
@@ -336,7 +261,8 @@ MinMaxSizesResult NGBlockLayoutAlgorithm::ComputeMinMaxSizes(
float_right_inline_size = LayoutUnit();
}
- MinMaxSizesInput child_input(input.percentage_resolution_block_size);
+ MinMaxSizesInput child_input(input.percentage_resolution_block_size,
+ input.type);
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;
@@ -434,7 +360,7 @@ MinMaxSizesResult NGBlockLayoutAlgorithm::ComputeMinMaxSizes(
DCHECK_GE(sizes.min_size, LayoutUnit());
DCHECK_LE(sizes.min_size, sizes.max_size) << Node().ToString();
- sizes += border_scrollbar_padding_.InlineSum();
+ sizes += BorderScrollbarPadding().InlineSum();
return {sizes, depends_on_percentage_block_size};
}
@@ -499,7 +425,8 @@ NOINLINE scoped_refptr<const NGLayoutResult>
NGBlockLayoutAlgorithm::LayoutWithItemsBuilder(
const NGInlineNode& first_child,
NGInlineChildLayoutContext* context) {
- NGFragmentItemsBuilder items_builder(first_child);
+ NGFragmentItemsBuilder items_builder(
+ first_child, container_builder_.GetWritingDirection());
container_builder_.SetItemsBuilder(&items_builder);
context->SetItemsBuilder(&items_builder);
scoped_refptr<const NGLayoutResult> result = Layout(context);
@@ -542,22 +469,14 @@ NGBlockLayoutAlgorithm::RelayoutIgnoringLineClamp() {
inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
NGInlineChildLayoutContext* inline_child_layout_context) {
- const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
- child_available_size_ =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
-
child_percentage_size_ = CalculateChildPercentageSize(
- ConstraintSpace(), Node(), child_available_size_);
+ ConstraintSpace(), Node(), ChildAvailableSize());
replaced_child_percentage_size_ = CalculateReplacedChildPercentageSize(
- ConstraintSpace(), Node(), child_available_size_,
- border_scrollbar_padding_, border_padding_);
+ ConstraintSpace(), Node(), ChildAvailableSize(), BorderScrollbarPadding(),
+ BorderPadding());
- // All of the above calculations with border_scrollbar_padding_ shouldn't
- // include the table cell's intrinsic padding. We can now add this.
- if (ConstraintSpace().IsTableCell()) {
- border_scrollbar_padding_ += ComputeIntrinsicPadding(
- ConstraintSpace(), Style(), container_builder_.Scrollbar());
- }
+ container_builder_.AdjustBorderScrollbarPaddingForFragmentation(BreakToken());
+ container_builder_.AdjustBorderScrollbarPaddingForTableCell();
DCHECK_EQ(!!inline_child_layout_context,
Node().IsInlineFormattingContextRoot());
@@ -581,14 +500,19 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
}
if (RuntimeEnabledFeatures::BlockFlowHandlesWebkitLineClampEnabled() &&
- Style().IsDeprecatedWebkitBoxWithVerticalLineClamp() &&
- !ignore_line_clamp_)
- lines_until_clamp_ = Style().LineClamp();
+ Style().IsDeprecatedWebkitBoxWithVerticalLineClamp()) {
+ if (!ignore_line_clamp_)
+ lines_until_clamp_ = Style().LineClamp();
+ } else if (Style().HasLineClamp()) {
+ UseCounter::Count(Node().GetDocument(),
+ WebFeature::kWebkitLineClampWithoutWebkitBox);
+ }
- LayoutUnit content_edge = border_scrollbar_padding_.block_start;
+ LayoutUnit content_edge = BorderScrollbarPadding().block_start;
NGPreviousInflowPosition previous_inflow_position = {
LayoutUnit(), ConstraintSpace().MarginStrut(),
+ is_resuming_ ? LayoutUnit() : container_builder_.Padding().block_start,
/* self_collapsing_child_had_clearance */ false};
// Do not collapse margins between parent and its child if:
@@ -601,11 +525,10 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// D: We're forced to stop margin collapsing by a CSS property
//
// In all those cases we can and must resolve the BFC block offset now.
- if (border_scrollbar_padding_.block_start || is_resuming_ ||
+ if (content_edge || is_resuming_ ||
ConstraintSpace().IsNewFormattingContext()) {
bool discard_subsequent_margins =
- previous_inflow_position.margin_strut.discard_margins &&
- !border_scrollbar_padding_.block_start;
+ previous_inflow_position.margin_strut.discard_margins && !content_edge;
if (!ResolveBfcBlockOffset(&previous_inflow_position)) {
// There should be no preceding content that depends on the BFC block
// offset of a new formatting context block, and likewise when resuming
@@ -724,7 +647,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
}
container_builder_.AddBreakBeforeChild(child, kBreakAppealPerfect,
/* is_forced_break */ false);
- SetFragmentainerOutOfSpace(&previous_inflow_position);
+ ConsumeRemainingFragmentainerSpace(&previous_inflow_position);
break;
}
@@ -751,10 +674,13 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
return container_builder_.Abort(status);
}
if (ConstraintSpace().HasBlockFragmentation()) {
- if (container_builder_.DidBreak() &&
- IsFragmentainerOutOfSpace(
- previous_inflow_position.logical_block_offset))
+ // A child break in a parallel flow doesn't affect whether we should
+ // break here or not.
+ if (container_builder_.HasInflowChildBreakInside()) {
+ // But if the break happened in the same flow, we'll now just finish
+ // layout of the fragment. No more siblings should be processed.
break;
+ }
// We need to propagate the initial break-before value up our container
// chain, until we reach a container that's not a first child. If we get
@@ -783,7 +709,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
NGLayoutResult::kNeedsRelayoutWithNoForcedTruncateAtLineClamp);
}
- if (child_iterator.IsAtEnd()) {
+ if (!child_iterator.NextChild(previous_inline_break_token.get()).node) {
// 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
// overflow) still needing more space than what the current fragmentainer
@@ -800,6 +726,37 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// offset, as that could give us a negative content box size.
intrinsic_block_size_ = content_edge;
+ // Add line height for empty content editable or button with empty label, e.g.
+ // <div contenteditable></div>, <input type="button" value="">
+ if (container_builder_.HasSeenAllChildren() &&
+ HasLineEvenIfEmpty(Node().GetLayoutBox())) {
+ intrinsic_block_size_ +=
+ std::max(intrinsic_block_size_,
+ Node().GetLayoutBox()->LogicalHeightForEmptyLine());
+ // Test [1][2] require baseline offset for empty editable.
+ // [1] css3/flexbox/baseline-for-empty-line.html
+ // [2] inline-block/contenteditable-baseline.html
+ const LayoutBlock* const layout_block =
+ To<LayoutBlock>(Node().GetLayoutBox());
+ if (auto baseline_offset = layout_block->BaselineForEmptyLine(
+ layout_block->IsHorizontalWritingMode() ? kHorizontalLine
+ : kVerticalLine))
+ container_builder_.SetBaseline(*baseline_offset);
+ }
+
+ // Collapse annotation overflow and padding.
+ // logical_block_offset already contains block-end annotation overflow.
+ // However, if the container has non-zero block-end padding, the annotation
+ // can extend on the padding. So we decrease logical_block_offset by
+ // shareable part of the annotation overflow and the padding.
+ if (previous_inflow_position.block_end_annotation_space < LayoutUnit()) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGRubyEnabled());
+ const LayoutUnit annotation_overflow =
+ -previous_inflow_position.block_end_annotation_space;
+ previous_inflow_position.logical_block_offset -=
+ std::min(container_builder_.Padding().block_end, annotation_overflow);
+ }
+
// To save space of the stack when we recurse into children, the rest of this
// function is continued within |FinishLayout|. However it should be read as
// one function.
@@ -822,9 +779,10 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
// 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_;
+ intrinsic_block_size_ = *intrinsic_block_size_when_clamped_ +
+ BorderScrollbarPadding().block_end;
end_margin_strut = NGMarginStrut();
- } else if (border_scrollbar_padding_.block_end ||
+ } else if (BorderScrollbarPadding().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
@@ -833,16 +791,7 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
// - 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
- // *last* quirky margin.
- // TODO: revisit previous implementation to avoid changing behavior and
- // https://html.spec.whatwg.org/C/#margin-collapsing-quirks
- LayoutUnit margin_strut_sum = node_.IsQuirkyContainer()
- ? end_margin_strut.QuirkyContainerSum()
- : end_margin_strut.Sum();
+
if (!container_builder_.BfcBlockOffset()) {
// If we have collapsed through the block start and all children (if any),
// now is the time to determine the BFC block offset, because finally we
@@ -857,6 +806,22 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
}
DCHECK(container_builder_.BfcBlockOffset());
} else {
+ // 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 *last* quirky
+ // margin.
+ LayoutUnit margin_strut_sum = node_.IsQuirkyContainer()
+ ? end_margin_strut.QuirkyContainerSum()
+ : end_margin_strut.Sum();
+
+ if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+ LayoutUnit bfc_block_offset =
+ *container_builder_.BfcBlockOffset() +
+ previous_inflow_position->logical_block_offset;
+ margin_strut_sum = AdjustedMarginAfterFinalChildFragment(
+ ConstraintSpace(), bfc_block_offset, margin_strut_sum);
+ }
+
// The trailing margin strut will be part of our intrinsic block size, but
// only if there is something that separates the end margin strut from the
// input margin strut (typically child content, block start
@@ -870,7 +835,7 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
previous_inflow_position->logical_block_offset + margin_strut_sum);
}
- intrinsic_block_size_ += border_scrollbar_padding_.block_end;
+ intrinsic_block_size_ += BorderScrollbarPadding().block_end;
end_margin_strut = NGMarginStrut();
} else {
// Update our intrinsic block size to be just past the block-end border edge
@@ -884,15 +849,20 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
container_builder_.SetOverflowBlockSize(intrinsic_block_size_);
intrinsic_block_size_ = ClampIntrinsicBlockSize(
- ConstraintSpace(), Node(), border_scrollbar_padding_,
+ ConstraintSpace(), Node(), BorderScrollbarPadding(),
intrinsic_block_size_,
CalculateQuirkyBodyMarginBlockSum(end_margin_strut));
+ LayoutUnit previously_consumed_block_size;
+ if (UNLIKELY(BreakToken()))
+ previously_consumed_block_size = BreakToken()->ConsumedBlockSize();
+
// Recompute the block-axis size now that we know our content size.
border_box_size.block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_, intrinsic_block_size_,
+ ConstraintSpace(), Style(), BorderPadding(),
+ previously_consumed_block_size + intrinsic_block_size_,
border_box_size.inline_size);
- container_builder_.SetBlockSize(border_box_size.block_size);
+ container_builder_.SetFragmentsTotalBlockSize(border_box_size.block_size);
// If our BFC block-offset is still unknown, we check:
// - If we have a non-zero block-size (margins don't collapse through us).
@@ -1005,14 +975,22 @@ bool NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache(
To<NGPhysicalBoxFragment>(previous_result_->PhysicalFragment());
const NGFragmentItems* previous_items = previous_fragment.Items();
DCHECK(previous_items);
+
+ // Find reusable lines. Fail if no items are reusable.
previous_items->DirtyLinesFromNeedsLayout(inline_node.GetLayoutBlockFlow());
+ const NGFragmentItem* end_item = previous_items->EndOfReusableItems();
+ DCHECK(end_item);
+ if (!end_item || end_item == &previous_items->front())
+ return false;
const auto& children = container_builder_.Children();
const wtf_size_t children_before = children.size();
+ NGFragmentItemsBuilder* items_builder = container_builder_.ItemsBuilder();
const NGConstraintSpace& space = ConstraintSpace();
- const auto result = container_builder_.ItemsBuilder()->AddPreviousItems(
- *previous_items, space.GetWritingMode(), space.Direction(),
- previous_fragment.Size(), &container_builder_, /* stop_at_dirty */ true);
+ DCHECK_EQ(items_builder->GetWritingMode(), space.GetWritingMode());
+ DCHECK_EQ(items_builder->Direction(), space.Direction());
+ const auto result = items_builder->AddPreviousItems(
+ *previous_items, previous_fragment.Size(), &container_builder_, end_item);
if (UNLIKELY(!result.succeeded)) {
DCHECK_EQ(children.size(), children_before);
@@ -1038,7 +1016,7 @@ void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned(
const NGPreviousInflowPosition& previous_inflow_position,
NGBlockNode child) {
DCHECK(child.IsOutOfFlowPositioned());
- LogicalOffset static_offset = {border_scrollbar_padding_.inline_start,
+ LogicalOffset static_offset = {BorderScrollbarPadding().inline_start,
previous_inflow_position.logical_block_offset};
// We only include the margin strut in the OOF static-position if we know we
@@ -1064,12 +1042,12 @@ void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned(
NGBfcOffset origin_bfc_offset = {
ConstraintSpace().BfcOffset().line_offset +
- border_scrollbar_padding_.LineLeft(Style().Direction()),
+ BorderScrollbarPadding().LineLeft(Style().Direction()),
origin_bfc_block_offset};
static_offset.inline_offset += CalculateOutOfFlowStaticInlineLevelOffset(
Style(), origin_bfc_offset, exclusion_space_,
- child_available_size_.inline_size);
+ ChildAvailableSize().inline_size);
}
container_builder_.AddOutOfFlowChildCandidate(child, static_offset);
@@ -1083,27 +1061,17 @@ void NGBlockLayoutAlgorithm::HandleFloat(
DCHECK(!IsResumingLayout(child_break_token) ||
container_builder_.BfcBlockOffset());
- if (broke_before_float_) {
- // We have already broken before a float. This means that we cannot place
- // any more floats now, as a float isn't allowed to start before any
- // preceding float.
- DCHECK(!child_break_token);
- container_builder_.AddBreakBeforeChild(child, base::nullopt,
- /* is_forced_break */ false);
- return;
- }
-
// If we don't have a BFC block-offset yet, the "expected" BFC block-offset
// is used to optimistically place floats.
NGBfcOffset origin_bfc_offset = {
ConstraintSpace().BfcOffset().line_offset +
- border_scrollbar_padding_.LineLeft(ConstraintSpace().Direction()),
+ BorderScrollbarPadding().LineLeft(ConstraintSpace().Direction()),
container_builder_.BfcBlockOffset()
? NextBorderEdge(previous_inflow_position)
: ConstraintSpace().ExpectedBfcBlockOffset()};
NGUnpositionedFloat unpositioned_float(
- child, child_break_token, child_available_size_, child_percentage_size_,
+ child, child_break_token, ChildAvailableSize(), child_percentage_size_,
replaced_child_percentage_size_, origin_bfc_offset, ConstraintSpace(),
Style());
@@ -1127,39 +1095,27 @@ void NGBlockLayoutAlgorithm::HandleFloat(
// 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()) {
+ if (positioned_float.need_break_before) {
DCHECK(ConstraintSpace().HasBlockFragmentation());
- if (!child_break_token && token->BreakAppeal() != kBreakAppealPerfect) {
- LayoutUnit fragmentainer_block_offset =
- ConstraintSpace().FragmentainerOffsetAtBfc() +
- positioned_float.bfc_offset.block_offset;
- if (fragmentainer_block_offset > LayoutUnit()) {
- // The float broke inside, and not at an ideal breakpoint. Break before
- // the float instead. Note that we don't check if we're at a valid class
- // A or C breakpoint (we only check that we're not at the start of the
- // fragmentainer (in which case breaking typically wouldn't eliminate
- // the unappealing break inside the float)). While no other browsers do
- // this either, we should consider doing this in the future. For now,
- // don't let the float affect the appeal of breaking inside this
- // container.
- BreakBeforeChild(ConstraintSpace(), child, layout_result,
- fragmentainer_block_offset,
- /* appeal */ base::nullopt,
- /* is_forced_break */ false, &container_builder_);
-
- // Then carry on with layout of this container. The float constitutes a
- // parallel flow, and there may be siblings that could still fit in the
- // current fragmentainer.
- broke_before_float_ = true;
- return;
- }
- }
+ LayoutUnit fragmentainer_block_offset =
+ ConstraintSpace().FragmentainerOffsetAtBfc() +
+ positioned_float.bfc_offset.block_offset;
+ BreakBeforeChild(ConstraintSpace(), child, *positioned_float.layout_result,
+ fragmentainer_block_offset,
+ /* appeal */ base::nullopt,
+ /* is_forced_break */ false, &container_builder_);
+
+ // After breaking before the float, carry on with layout of this
+ // container. The float constitutes a parallel flow, and there may be
+ // siblings that could still fit in the current fragmentainer.
+ return;
}
// TODO(mstensho): There should be a class A breakpoint between a float and
// another float, and also between a float and an in-flow block.
+ const NGPhysicalFragment& physical_fragment =
+ positioned_float.layout_result->PhysicalFragment();
LayoutUnit float_inline_size =
NGFragment(ConstraintSpace().GetWritingMode(), physical_fragment)
.InlineSize();
@@ -1193,7 +1149,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleNewFormattingContext(
LayoutUnit child_origin_line_offset =
ConstraintSpace().BfcOffset().line_offset +
- border_scrollbar_padding_.LineLeft(direction);
+ BorderScrollbarPadding().LineLeft(direction);
// If the child has a block-start margin, and the BFC block offset is still
// unresolved, and we have preceding adjoining floats, things get complicated
@@ -1379,7 +1335,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleNewFormattingContext(
// The margins we store will be used by e.g. getComputedStyle().
// When calculating these values, ignore any floats that might have
// affected the child. This is what Edge does.
- ResolveInlineMargins(child_style, Style(), child_available_size_.inline_size,
+ ResolveInlineMargins(child_style, Style(), ChildAvailableSize().inline_size,
fragment.InlineSize(), &child_data.margins);
To<NGBlockNode>(child).StoreMargins(ConstraintSpace(), child_data.margins);
@@ -1411,8 +1367,8 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
DCHECK(container_builder_.BfcBlockOffset());
LayoutOpportunityVector opportunities =
- exclusion_space_.AllLayoutOpportunities(
- origin_offset, child_available_size_.inline_size);
+ exclusion_space_.AllLayoutOpportunities(origin_offset,
+ ChildAvailableSize().inline_size);
// We should always have at least one opportunity.
DCHECK_GT(opportunities.size(), 0u);
@@ -1444,14 +1400,14 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
bool can_expand_outside_opportunity =
opportunity.rect.start_offset.line_offset ==
origin_offset.line_offset &&
- opportunity.rect.InlineSize() == child_available_size_.inline_size;
+ opportunity.rect.InlineSize() == ChildAvailableSize().inline_size;
if (can_expand_outside_opportunity) {
// No floats have affected the available inline-size, adjust the
// available inline-size by the margins.
DCHECK_EQ(line_left_offset, origin_offset.line_offset);
DCHECK_EQ(line_right_offset,
- origin_offset.line_offset + child_available_size_.inline_size);
+ origin_offset.line_offset + ChildAvailableSize().inline_size);
line_left_offset += line_left_margin;
line_right_offset -= line_right_margin;
} else {
@@ -1463,7 +1419,7 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
origin_offset.line_offset + line_left_margin.ClampNegativeToZero());
line_right_offset = std::min(line_right_offset,
origin_offset.line_offset +
- child_available_size_.inline_size -
+ ChildAvailableSize().inline_size -
line_right_margin.ClampNegativeToZero());
}
LayoutUnit opportunity_size =
@@ -1480,7 +1436,7 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
NGConstraintSpace child_space = CreateConstraintSpaceForChild(
child, child_data,
- {child_available_inline_size, child_available_size_.block_size},
+ {child_available_inline_size, ChildAvailableSize().block_size},
/* is_new_fc */ true, opportunity.rect.start_offset.block_offset);
// All formatting context roots (like this child) should start with an empty
@@ -1616,8 +1572,9 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::HandleInflow(
ComputeChildData(*previous_inflow_position, child, child_break_token,
/* is_new_fc */ false);
NGConstraintSpace child_space = CreateConstraintSpaceForChild(
- child, child_data, child_available_size_, /* is_new_fc */ false,
- forced_bfc_block_offset, has_clearance_past_adjoining_floats);
+ child, child_data, ChildAvailableSize(), /* is_new_fc */ false,
+ forced_bfc_block_offset, has_clearance_past_adjoining_floats,
+ previous_inflow_position->block_end_annotation_space);
scoped_refptr<const NGLayoutResult> layout_result =
LayoutInflow(child_space, child_break_token, early_break_, &child,
inline_child_layout_context);
@@ -1795,7 +1752,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow(
self_collapsing_child_needs_relayout) &&
child_bfc_block_offset) {
NGConstraintSpace new_child_space = CreateConstraintSpaceForChild(
- child, *child_data, child_available_size_, /* is_new_fc */ false,
+ child, *child_data, ChildAvailableSize(), /* is_new_fc */ false,
child_bfc_block_offset);
layout_result =
LayoutInflow(new_child_space, child_break_token, early_break_, &child,
@@ -1810,7 +1767,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow(
child_bfc_block_offset = layout_result->BfcBlockOffset();
DCHECK(child_bfc_block_offset);
new_child_space = CreateConstraintSpaceForChild(
- child, *child_data, child_available_size_, /* is_new_fc */ false,
+ child, *child_data, ChildAvailableSize(), /* is_new_fc */ false,
child_bfc_block_offset);
layout_result =
LayoutInflow(new_child_space, child_break_token, early_break_, &child,
@@ -1892,7 +1849,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow(
// required by e.g. getComputedStyle()).
if (!child_data->margins_fully_resolved) {
ResolveInlineMargins(child.Style(), Style(),
- child_available_size_.inline_size,
+ ChildAvailableSize().inline_size,
fragment.InlineSize(), &child_data->margins);
child_data->margins_fully_resolved = true;
}
@@ -1914,7 +1871,10 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow(
if (UNLIKELY(ConstraintSpace().IsInColumnBfc())) {
if (NGBlockNode spanner_node = layout_result->ColumnSpanner()) {
container_builder_.SetColumnSpanner(spanner_node);
- if (!container_builder_.DidBreak()) {
+ // TODO(mstensho): DidBreakSelf() is always false here, so this check is
+ // wrong. Still, no failing tests! Please investigate.
+ // HasInflowChildBreakInside() ought to be a better choice.
+ if (!container_builder_.DidBreakSelf()) {
// If we still haven't found a descendant at which to resume column
// layout after the spanner, look for one now.
if (NGLayoutInputNode next = child.NextSibling()) {
@@ -1939,8 +1899,7 @@ NGLayoutResult::EStatus NGBlockLayoutAlgorithm::FinishInflow(
// 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;
+ previous_inflow_position->logical_block_offset;
}
}
return NGLayoutResult::kSuccess;
@@ -1986,7 +1945,7 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData(
NGBfcOffset child_bfc_offset = {
ConstraintSpace().BfcOffset().line_offset +
- border_scrollbar_padding_.LineLeft(ConstraintSpace().Direction()) +
+ BorderScrollbarPadding().LineLeft(ConstraintSpace().Direction()) +
margins.LineLeft(ConstraintSpace().Direction()),
BfcBlockOffset() + logical_block_offset};
@@ -2065,7 +2024,15 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
if (!container_builder_.BfcBlockOffset())
DCHECK_EQ(logical_block_offset, LayoutUnit());
} else {
- logical_block_offset = logical_offset.block_offset + fragment.BlockSize();
+ // We add AnnotationOverflow unconditionally here. Then, we cancel it if
+ // - The next line box has block-start annotation space, or
+ // - There are no following child boxes and this container has block-end
+ // padding.
+ //
+ // See NGInlineLayoutAlgorithm::CreateLine() and
+ // BlockLayoutAlgorithm::Layout().
+ logical_block_offset = logical_offset.block_offset + fragment.BlockSize() +
+ layout_result.AnnotationOverflow();
}
NGMarginStrut margin_strut = layout_result.EndMarginStrut();
@@ -2095,7 +2062,13 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
(previous_inflow_position.self_collapsing_child_had_clearance &&
is_self_collapsing);
- return {logical_block_offset, margin_strut,
+ LayoutUnit annotation_space = layout_result.BlockEndAnnotationSpace();
+ if (layout_result.AnnotationOverflow() > LayoutUnit()) {
+ DCHECK(!annotation_space);
+ annotation_space = -layout_result.AnnotationOverflow();
+ }
+
+ return {logical_block_offset, margin_strut, annotation_space,
self_or_sibling_self_collapsing_child_had_clearance};
}
@@ -2123,21 +2096,8 @@ LayoutUnit NGBlockLayoutAlgorithm::FragmentainerSpaceAvailable() const {
*container_builder_.BfcBlockOffset();
}
-bool NGBlockLayoutAlgorithm::IsFragmentainerOutOfSpace(
- LayoutUnit block_offset) const {
- if (did_break_before_child_)
- return true;
- if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
- return false;
- if (!container_builder_.BfcBlockOffset().has_value())
- return false;
- return block_offset >= FragmentainerSpaceAvailable();
-}
-
-void NGBlockLayoutAlgorithm::SetFragmentainerOutOfSpace(
+void NGBlockLayoutAlgorithm::ConsumeRemainingFragmentainerSpace(
NGPreviousInflowPosition* previous_inflow_position) {
- did_break_before_child_ = true;
-
if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
// The remaining part of the fragmentainer (the unusable space for child
// content, due to the break) should still be occupied by this container.
@@ -2148,7 +2108,8 @@ void NGBlockLayoutAlgorithm::SetFragmentainerOutOfSpace(
bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
if (Node().IsInlineFormattingContextRoot() && !early_break_) {
- if (container_builder_.DidBreak() || first_overflowing_line_) {
+ if (container_builder_.HasInflowChildBreakInside() ||
+ first_overflowing_line_) {
if (first_overflowing_line_ &&
first_overflowing_line_ < container_builder_.LineCount()) {
int line_number;
@@ -2176,31 +2137,22 @@ bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
}
}
- if (!ConstraintSpace().HasKnownFragmentainerBlockSize())
+ if (container_builder_.IsFragmentainerBoxType()) {
+ // We're building fragmentainers. Just copy the block-size from the
+ // constraint space. Calculating the size the regular way would cause some
+ // problems with overflow. For one, we don't want to produce a break token
+ // if there's no child content that requires it.
+ LayoutUnit consumed_block_size =
+ BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
+ LayoutUnit block_size = ConstraintSpace().FragmentainerBlockSize();
+ container_builder_.SetFragmentBlockSize(block_size);
+ container_builder_.SetConsumedBlockSize(consumed_block_size + block_size);
return true;
+ }
- LayoutUnit consumed_block_size =
- BreakToken() ? BreakToken()->ConsumedBlockSize() : LayoutUnit();
- LayoutUnit space_left = FragmentainerSpaceAvailable();
- LayoutUnit block_size;
- if (container_builder_.BoxType() == NGPhysicalFragment::kColumnBox &&
- ConstraintSpace().HasKnownFragmentainerBlockSize()) {
- // We're building column fragments, and we know the column size. Just use
- // that. Calculating the size the regular way would cause some problems with
- // overflow. For one, we don't want to produce a break token if there's no
- // child content that requires it.
- block_size = ConstraintSpace().FragmentainerBlockSize();
- } else {
- block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_,
- consumed_block_size + intrinsic_block_size_,
- container_builder_.InitialBorderBoxSize().inline_size);
-
- block_size -= consumed_block_size;
- DCHECK_GE(block_size, LayoutUnit())
- << "Adding and subtracting the consumed_block_size shouldn't leave the "
- "block_size for this fragment smaller than zero.";
-
+ LayoutUnit space_left = kIndefiniteSize;
+ if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+ space_left = FragmentainerSpaceAvailable();
if (space_left <= LayoutUnit()) {
// The amount of space available may be zero, or even negative, if the
// border-start edge of this block starts exactly at, or even after the
@@ -2216,8 +2168,8 @@ bool NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
}
}
- FinishFragmentation(ConstraintSpace(), BreakToken(), block_size,
- intrinsic_block_size_, space_left, &container_builder_);
+ FinishFragmentation(Node(), ConstraintSpace(), BreakToken(), BorderPadding(),
+ space_left, &container_builder_);
return true;
}
@@ -2250,7 +2202,7 @@ NGBreakStatus NGBlockLayoutAlgorithm::BreakBeforeChildIfNeeded(
BreakBeforeChild(ConstraintSpace(), child, layout_result,
fragmentainer_block_offset, kBreakAppealPerfect,
/* is_forced_break */ true, &container_builder_);
- SetFragmentainerOutOfSpace(previous_inflow_position);
+ ConsumeRemainingFragmentainerSpace(previous_inflow_position);
return NGBreakStatus::kBrokeBefore;
}
}
@@ -2340,7 +2292,7 @@ NGBreakStatus NGBlockLayoutAlgorithm::BreakBeforeChildIfNeeded(
&container_builder_))
return NGBreakStatus::kNeedsEarlierBreak;
- SetFragmentainerOutOfSpace(previous_inflow_position);
+ ConsumeRemainingFragmentainerSpace(previous_inflow_position);
return NGBreakStatus::kBrokeBefore;
}
@@ -2348,8 +2300,9 @@ void NGBlockLayoutAlgorithm::UpdateEarlyBreakBetweenLines() {
// We shouldn't be here if we already know where to break.
DCHECK(!early_break_);
- // If the child already broke, it's a little too late to look for breakpoints.
- DCHECK(!container_builder_.DidBreak());
+ // If something in this flow already broke, it's a little too late to look for
+ // breakpoints.
+ DCHECK(!container_builder_.HasInflowChildBreakInside());
int line_count = container_builder_.LineCount();
if (line_count < 2)
@@ -2412,7 +2365,7 @@ NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins(
NGConstraintSpaceBuilder builder(ConstraintSpace(),
child_style.GetWritingMode(),
/* is_new_fc */ false);
- builder.SetAvailableSize(child_available_size_);
+ builder.SetAvailableSize(ChildAvailableSize());
builder.SetPercentageResolutionSize(child_percentage_size_);
NGConstraintSpace space = builder.ToConstraintSpace();
@@ -2435,7 +2388,8 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
const LogicalSize child_available_size,
bool is_new_fc,
const base::Optional<LayoutUnit> child_bfc_block_offset,
- bool has_clearance_past_adjoining_floats) {
+ bool has_clearance_past_adjoining_floats,
+ LayoutUnit block_start_annotation_space) {
const ComputedStyle& style = Style();
const ComputedStyle& child_style = child.Style();
WritingMode child_writing_mode =
@@ -2550,6 +2504,7 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
// child establishes a new formatting context or not.
builder.SetDiscardingMarginStrut();
}
+ builder.SetBlockStartAnnotationSpace(block_start_annotation_space);
if (ConstraintSpace().HasBlockFragmentation()) {
LayoutUnit fragmentainer_offset_delta;
@@ -2746,7 +2701,7 @@ bool NGBlockLayoutAlgorithm::PositionOrPropagateListMarker(
}
list_marker.AddToBox(space, baseline_type, content,
- border_scrollbar_padding_, *marker_layout_result,
+ BorderScrollbarPadding(), *marker_layout_result,
*content_baseline, content_offset,
&container_builder_);
return true;
@@ -2797,7 +2752,7 @@ bool NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes(
if (container_builder_.BfcBlockOffset()) {
intrinsic_block_size_ = std::max(marker_block_size, intrinsic_block_size_);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
- container_builder_.SetBlockSize(
+ container_builder_.SetFragmentsTotalBlockSize(
std::max(marker_block_size, container_builder_.Size().block_size));
}
return true;
@@ -2824,17 +2779,19 @@ void NGBlockLayoutAlgorithm::LayoutRubyText(
NGConstraintSpaceBuilder builder(
ConstraintSpace(), ruby_text_child->Style().GetWritingMode(), true);
- builder.SetAvailableSize(child_available_size_);
+ builder.SetAvailableSize(ChildAvailableSize());
scoped_refptr<const NGLayoutResult> result =
To<NGBlockNode>(*ruby_text_child)
.Layout(builder.ToConstraintSpace(), break_token.get());
- LayoutUnit ruby_text_top;
+ LayoutUnit ruby_text_box_top;
const NGPhysicalBoxFragment& ruby_text_fragment =
To<NGPhysicalBoxFragment>(result->PhysicalFragment());
- if (Style().IsFlippedLinesWritingMode() ==
- (Style().GetRubyPosition() == RubyPosition::kAfter)) {
+ RubyPosition block_start_position = Style().IsFlippedLinesWritingMode()
+ ? RubyPosition::kAfter
+ : RubyPosition::kBefore;
+ if (Style().GetRubyPosition() == block_start_position) {
LayoutUnit last_line_ruby_text_bottom = LastLineTextLogicalBottom(
ruby_text_fragment, result->IntrinsicBlockSize());
@@ -2850,30 +2807,53 @@ void NGBlockLayoutAlgorithm::LayoutRubyText(
}
}
}
- ruby_text_top = first_line_top - last_line_ruby_text_bottom;
+ ruby_text_box_top = first_line_top - last_line_ruby_text_bottom;
+ const LayoutUnit ruby_text_top =
+ ruby_text_box_top +
+ FirstLineTextLogicalTop(ruby_text_fragment, LayoutUnit());
+ if (ruby_text_top < LayoutUnit())
+ container_builder_.SetAnnotationOverflow(ruby_text_top);
} else {
LayoutUnit first_line_ruby_text_top =
FirstLineTextLogicalTop(ruby_text_fragment, LayoutUnit());
// Find a fragment for RubyBase, and get the bottom of text in it.
LayoutUnit last_line_bottom;
+ LayoutUnit base_logical_bottom;
for (const auto& child : container_builder_.Children()) {
if (const auto* layout_object = child.fragment->GetLayoutObject()) {
if (layout_object->IsRubyBase()) {
- last_line_bottom = LastLineTextLogicalBottom(
- To<NGPhysicalBoxFragment>(*child.fragment),
+ LayoutUnit base_block_size =
child.fragment->Size()
.ConvertToLogical(Style().GetWritingMode())
- .block_size);
+ .block_size;
+ last_line_bottom = LastLineTextLogicalBottom(
+ To<NGPhysicalBoxFragment>(*child.fragment), base_block_size);
last_line_bottom += child.offset.block_offset;
+ base_logical_bottom = child.offset.block_offset + base_block_size;
break;
}
}
}
- ruby_text_top = last_line_bottom - first_line_ruby_text_top;
+ ruby_text_box_top = last_line_bottom - first_line_ruby_text_top;
+ LayoutUnit ruby_text_height =
+ ruby_text_fragment.Size()
+ .ConvertToLogical(Style().GetWritingMode())
+ .block_size;
+ ruby_text_height =
+ LastLineTextLogicalBottom(ruby_text_fragment, ruby_text_height);
+ LayoutUnit logical_bottom_overflow =
+ ruby_text_box_top + ruby_text_height - base_logical_bottom;
+ if (logical_bottom_overflow > LayoutUnit())
+ container_builder_.SetAnnotationOverflow(logical_bottom_overflow);
}
container_builder_.AddResult(*result,
- LogicalOffset(LayoutUnit(), ruby_text_top));
+ LogicalOffset(LayoutUnit(), ruby_text_box_top));
+ // RubyText provides baseline if RubyBase didn't.
+ // This behavior doesn't make much sense, but it's compatible with the legacy
+ // layout.
+ if (!container_builder_.Baseline())
+ PropagateBaselineFromChild(ruby_text_fragment, ruby_text_box_top);
}
} // namespace blink
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 b7599118a1a..55a5c8352cc 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
@@ -32,6 +32,9 @@ class NGFragment;
struct NGPreviousInflowPosition {
LayoutUnit logical_block_offset;
NGMarginStrut margin_strut;
+ // > 0: Block-end annotation space of the previous line
+ // < 0: Block-end annotation overflow of the previous line
+ LayoutUnit block_end_annotation_space;
bool self_collapsing_child_had_clearance;
};
@@ -113,7 +116,8 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
const LogicalSize child_available_size,
bool is_new_fc,
const base::Optional<LayoutUnit> bfc_block_offset = base::nullopt,
- bool has_clearance_past_adjoining_floats = false);
+ bool has_clearance_past_adjoining_floats = false,
+ LayoutUnit block_start_annotation_space = LayoutUnit());
// @return Estimated BFC block offset for the "to be layout" child.
NGInflowChildData ComputeChildData(const NGPreviousInflowPosition&,
@@ -215,14 +219,11 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// for the node being laid out by this algorithm.
LayoutUnit FragmentainerSpaceAvailable() const;
- // Return true if the node being laid out by this fragmentainer has used all
- // the available space in the current fragmentainer.
- // |block_offset| is the border-edge relative block offset we want to check
- // whether fits within the fragmentainer or not.
- bool IsFragmentainerOutOfSpace(LayoutUnit block_offset) const;
-
- // Signal that we've reached the end of the fragmentainer.
- void SetFragmentainerOutOfSpace(NGPreviousInflowPosition*);
+ // Consume all remaining fragmentainer space. This happens when we decide to
+ // break before a child.
+ //
+ // https://www.w3.org/TR/css-break-3/#box-splitting
+ void ConsumeRemainingFragmentainerSpace(NGPreviousInflowPosition*);
// Final adjustments before fragment creation. We need to prevent the fragment
// from crossing fragmentainer boundaries, and rather create a break token if
@@ -342,15 +343,6 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// |ruby_text_child|. This is called only if IsRubyText() returns true.
void LayoutRubyText(NGLayoutInputNode* ruby_text_child);
- // Border + padding sum, resolved from the node's computed style.
- const NGBoxStrut border_padding_;
-
- // Border + scrollbar + padding sum for the fragment to be generated (most
- // importantly, for non-first fragments, leading block border + scrollbar +
- // padding is zero).
- NGBoxStrut border_scrollbar_padding_;
-
- LogicalSize child_available_size_;
LogicalSize child_percentage_size_;
LogicalSize replaced_child_percentage_size_;
@@ -386,14 +378,6 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// A or B breakpoint (between block-level siblings or line box siblings).
bool has_processed_first_child_ = false;
- // Set once we've inserted a break before a float. We need to know this, so
- // that we don't attempt to lay out any more floats in the current
- // fragmentainer. Floats aren't allowed have an earlier block-start offset
- // than earlier floats.
- bool broke_before_float_ = false;
-
- bool did_break_before_child_ = false;
-
NGExclusionSpace exclusion_space_;
// If set, this is the number of lines until a clamp. A value of 1 indicates
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 6b75c41df3b..1af502775f3 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
@@ -45,7 +45,8 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
NGBlockLayoutAlgorithm algorithm({node, fragment_geometry, space});
MinMaxSizesInput input(
- /* percentage_resolution_block_size */ (LayoutUnit()));
+ /* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent);
return algorithm.ComputeMinMaxSizes(input).sizes;
}
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 80d066499e2..2d1a57926ad 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
@@ -196,6 +196,10 @@ void UpdateLegacyMultiColumnFlowThread(
LayoutMultiColumnSet* column_set =
ToLayoutMultiColumnSetOrNull(flow_thread->FirstMultiColumnBox());
for (const auto& child : fragment.Children()) {
+ // TODO(almaher): Remove check for out of flow.
+ if (child->IsOutOfFlowPositioned())
+ continue;
+
if (child->GetLayoutObject() &&
child->GetLayoutObject()->IsColumnSpanAll()) {
// Column spanners are not part of the fragmentation context. We'll use
@@ -258,11 +262,16 @@ void UpdateLegacyMultiColumnFlowThread(
flow_thread->ClearNeedsLayout();
}
-NGConstraintSpace CreateConstraintSpaceForMinMax(const NGBlockNode& node) {
+NGConstraintSpace CreateConstraintSpaceForMinMax(
+ const NGBlockNode& node,
+ const MinMaxSizesInput& input) {
NGConstraintSpaceBuilder builder(node.Style().GetWritingMode(),
node.Style().GetWritingMode(),
node.CreatesNewFormattingContext());
builder.SetTextDirection(node.Style().Direction());
+ builder.SetAvailableSize(LogicalSize());
+ builder.SetPercentageResolutionSize(
+ {LayoutUnit(), input.percentage_resolution_block_size});
return builder.ToConstraintSpace();
}
@@ -351,7 +360,7 @@ bool CanUseCachedIntrinsicInlineSizes(const MinMaxSizesInput& input,
scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
const NGConstraintSpace& constraint_space,
const NGBlockBreakToken* break_token,
- const NGEarlyBreak* early_break) {
+ const NGEarlyBreak* early_break) const {
// Use the old layout code and synthesize a fragment.
if (!CanUseNewLayout())
return RunLegacyLayout(constraint_space);
@@ -508,7 +517,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
}
scoped_refptr<const NGLayoutResult> NGBlockNode::SimplifiedLayout(
- const NGPhysicalFragment& previous_fragment) {
+ const NGPhysicalFragment& previous_fragment) const {
scoped_refptr<const NGLayoutResult> previous_result =
box_->GetCachedLayoutResult();
DCHECK(previous_result);
@@ -587,7 +596,7 @@ NGBlockNode::CachedLayoutResultForOutOfFlowPositioned(
return cached_layout_result;
}
-void NGBlockNode::PrepareForLayout() {
+void NGBlockNode::PrepareForLayout() const {
auto* block = DynamicTo<LayoutBlock>(box_);
if (block && block->HasOverflowClip()) {
DCHECK(block->GetScrollableArea());
@@ -605,7 +614,7 @@ void NGBlockNode::FinishLayout(
LayoutBlockFlow* block_flow,
const NGConstraintSpace& constraint_space,
const NGBlockBreakToken* break_token,
- scoped_refptr<const NGLayoutResult> layout_result) {
+ scoped_refptr<const NGLayoutResult> layout_result) const {
// 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.
@@ -676,7 +685,7 @@ void NGBlockNode::FinishLayout(
MinMaxSizesResult NGBlockNode::ComputeMinMaxSizes(
WritingMode container_writing_mode,
const MinMaxSizesInput& input,
- const NGConstraintSpace* constraint_space) {
+ const NGConstraintSpace* constraint_space) const {
// TODO(layoutng) Can UpdateMarkerTextIfNeeded call be moved
// somewhere else? List items need up-to-date markers before layout.
if (IsListItem())
@@ -687,6 +696,10 @@ MinMaxSizesResult NGBlockNode::ComputeMinMaxSizes(
// If we're orthogonal, run layout to compute the sizes.
if (is_orthogonal_flow_root) {
+ // If we have an aspect ratio, we may be able to avoid laying out the
+ // child as an optimization, if performance testing shows this to be
+ // important.
+
MinMaxSizes sizes;
// Some other areas of the code can query the intrinsic-sizes while outside
// of the layout phase.
@@ -706,6 +719,24 @@ MinMaxSizesResult NGBlockNode::ComputeMinMaxSizes(
return {sizes, /* depends_on_percentage_block_size */ false};
}
+ // Synthesize a zero space if not provided.
+ auto zero_constraint_space = CreateConstraintSpaceForMinMax(*this, input);
+ if (!constraint_space)
+ constraint_space = &zero_constraint_space;
+
+ if (Style().AspectRatio() && input.type == MinMaxSizesType::kContent) {
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(*constraint_space, *this);
+ NGBoxStrut border_padding =
+ fragment_geometry.border + fragment_geometry.padding;
+ LayoutUnit size_from_ar = ComputeInlineSizeFromAspectRatio(
+ *constraint_space, Style(), border_padding);
+ if (size_from_ar != kIndefiniteSize) {
+ return {{size_from_ar, size_from_ar},
+ Style().LogicalHeight().IsPercentOrCalc()};
+ }
+ }
+
bool can_use_cached_intrinsic_inline_sizes =
CanUseCachedIntrinsicInlineSizes(input, *this);
@@ -723,11 +754,6 @@ MinMaxSizesResult NGBlockNode::ComputeMinMaxSizes(
return {sizes, depends_on_percentage_block_size};
}
- // Synthesize a zero space if not provided.
- auto zero_constraint_space = CreateConstraintSpaceForMinMax(*this);
- if (!constraint_space)
- constraint_space = &zero_constraint_space;
-
NGFragmentGeometry fragment_geometry =
CalculateInitialMinMaxFragmentGeometry(*constraint_space, *this);
@@ -918,23 +944,42 @@ String NGBlockNode::ToString() const {
void NGBlockNode::CopyFragmentDataToLayoutBox(
const NGConstraintSpace& constraint_space,
const NGLayoutResult& layout_result,
- const NGBlockBreakToken* previous_break_token) {
+ const NGBlockBreakToken* previous_break_token) const {
const auto& physical_fragment =
To<NGPhysicalBoxFragment>(layout_result.PhysicalFragment());
NGBoxFragment fragment(constraint_space.GetWritingMode(),
constraint_space.Direction(), physical_fragment);
LogicalSize fragment_logical_size = fragment.Size();
- // For each fragment we process, we'll accumulate the logical height and
- // logical intrinsic content box height. We reset it at the first fragment,
- // and accumulate at each method call for fragments belonging to the same
- // layout object. Logical width will only be set at the first fragment and is
- // expected to remain the same throughout all subsequent fragments, since
- // legacy layout doesn't support non-uniform fragmentainer widths.
- LayoutUnit intrinsic_content_logical_height;
+ NGBoxStrut borders = fragment.Borders();
+ NGBoxStrut scrollbars = ComputeScrollbars(constraint_space, *this);
+ NGBoxStrut padding = fragment.Padding();
+ NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
+ bool is_last_fragment = !physical_fragment.BreakToken();
+
+ // For each fragment we process, we'll accumulate the logical height. We reset
+ // it at the first fragment, and accumulate at each method call for fragments
+ // belonging to the same layout object. Logical width will only be set at the
+ // first fragment and is expected to remain the same throughout all subsequent
+ // fragments, since legacy layout doesn't support non-uniform fragmentainer
+ // widths.
if (LIKELY(physical_fragment.IsFirstForNode())) {
box_->SetSize(LayoutSize(physical_fragment.Size().width,
physical_fragment.Size().height));
+ // If this is a fragment from a node that didn't break into multiple
+ // fragments, write back the intrinsic size. We skip this if the node has
+ // fragmented, since intrinsic block-size is rather meaningless in that
+ // case, because the block-size may have been affected by something on the
+ // outside (i.e. the fragmentainer).
+ //
+ // If we had a fixed block size, our children will have sized themselves
+ // relative to the fixed size, which would make our intrinsic size incorrect
+ // (too big). So skip the write-back in that case, too.
+ if (LIKELY(is_last_fragment && !constraint_space.IsFixedBlockSize())) {
+ box_->SetIntrinsicContentLogicalHeight(
+ layout_result.IntrinsicBlockSize() -
+ border_scrollbar_padding.BlockSum());
+ }
} else {
DCHECK_EQ(box_->LogicalWidth(), fragment_logical_size.inline_size)
<< "Variable fragment inline size not supported";
@@ -942,24 +987,6 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
if (previous_break_token)
logical_height += previous_break_token->ConsumedBlockSize();
box_->SetLogicalHeight(logical_height);
- intrinsic_content_logical_height = box_->IntrinsicContentLogicalHeight();
- }
-
- intrinsic_content_logical_height += layout_result.IntrinsicBlockSize();
-
- NGBoxStrut borders = fragment.Borders();
- NGBoxStrut scrollbars = ComputeScrollbars(constraint_space, *this);
- NGBoxStrut padding = fragment.Padding();
- NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
- bool is_last_fragment = !physical_fragment.BreakToken();
-
- if (LIKELY(is_last_fragment))
- intrinsic_content_logical_height -= border_scrollbar_padding.BlockSum();
- if (!constraint_space.IsFixedBlockSize()) {
- // If we had a fixed block size, our children will have sized themselves
- // relative to the fixed size, which would make our intrinsic size
- // incorrect (too big).
- box_->SetIntrinsicContentLogicalHeight(intrinsic_content_logical_height);
}
// TODO(mstensho): This should always be done by the parent algorithm, since
@@ -1041,7 +1068,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
void NGBlockNode::PlaceChildrenInLayoutBox(
const NGPhysicalBoxFragment& physical_fragment,
- const NGBlockBreakToken* previous_break_token) {
+ const NGBlockBreakToken* previous_break_token) const {
LayoutBox* rendered_legend = nullptr;
for (const auto& child_fragment : physical_fragment.Children()) {
// Skip any line-boxes we have as children, this is handled within
@@ -1078,12 +1105,14 @@ void NGBlockNode::PlaceChildrenInLayoutBox(
}
void NGBlockNode::PlaceChildrenInFlowThread(
- const NGPhysicalBoxFragment& physical_fragment) {
+ const NGPhysicalBoxFragment& physical_fragment) const {
const NGBlockBreakToken* previous_break_token = nullptr;
for (const auto& child : physical_fragment.Children()) {
const LayoutObject* child_object = child->GetLayoutObject();
if (child_object && child_object != box_) {
- DCHECK(child_object->IsColumnSpanAll());
+ // TODO(almaher): Remove check for out of flow.
+ DCHECK(child_object->IsColumnSpanAll() ||
+ child_object->IsOutOfFlowPositioned());
CopyChildFragmentPosition(To<NGPhysicalBoxFragment>(*child), child.offset,
physical_fragment);
continue;
@@ -1102,7 +1131,7 @@ void NGBlockNode::CopyChildFragmentPosition(
const NGPhysicalBoxFragment& child_fragment,
PhysicalOffset offset,
const NGPhysicalBoxFragment& container_fragment,
- const NGBlockBreakToken* previous_container_break_token) {
+ const NGBlockBreakToken* previous_container_break_token) const {
LayoutBox* layout_box = ToLayoutBox(child_fragment.GetMutableLayoutObject());
if (!layout_box)
return;
@@ -1140,7 +1169,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren(
const NGPhysicalContainerFragment& container,
LayoutUnit initial_container_width,
bool initial_container_is_flipped,
- PhysicalOffset offset) {
+ PhysicalOffset offset) const {
DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
for (const auto& child : container.Children()) {
if (child->IsContainer()) {
@@ -1185,7 +1214,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren(
void NGBlockNode::CopyFragmentItemsToLayoutBox(
const NGPhysicalBoxFragment& container,
- const NGFragmentItems& items) {
+ const NGFragmentItems& items) const {
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
bool initial_container_is_flipped = Style().IsFlippedBlocksWritingMode();
@@ -1321,7 +1350,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline(
}
scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout(
- const NGConstraintSpace& constraint_space) {
+ const NGConstraintSpace& constraint_space) const {
// This is an exit-point from LayoutNG to the legacy engine. This means that
// we need to be at a formatting context boundary, since NG and legacy don't
// cooperate on e.g. margin collapsing.
@@ -1371,7 +1400,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout(
// TODO(kojii): Implement use_first_line_style.
NGBoxFragmentBuilder builder(*this, box_->Style(), &constraint_space,
- writing_mode, box_->StyleRef().Direction());
+ {writing_mode, box_->StyleRef().Direction()});
builder.SetIsNewFormattingContext(
constraint_space.IsNewFormattingContext());
builder.SetInitialFragmentGeometry(fragment_geometry);
@@ -1452,7 +1481,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunSimplifiedLayout(
void NGBlockNode::CopyBaselinesFromLegacyLayout(
const NGConstraintSpace& constraint_space,
- NGBoxFragmentBuilder* builder) {
+ NGBoxFragmentBuilder* builder) const {
// 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
@@ -1479,7 +1508,7 @@ void NGBlockNode::CopyBaselinesFromLegacyLayout(
}
LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout(
- const NGConstraintSpace& constraint_space) {
+ const NGConstraintSpace& constraint_space) const {
LineDirectionMode line_direction = box_->IsHorizontalWritingMode()
? LineDirectionMode::kHorizontalLine
: LineDirectionMode::kVerticalLine;
@@ -1512,7 +1541,7 @@ LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout(
// in the parents writing mode.
void NGBlockNode::UpdateShapeOutsideInfoIfNeeded(
const NGLayoutResult& layout_result,
- LayoutUnit percentage_resolution_inline_size) {
+ LayoutUnit percentage_resolution_inline_size) const {
if (!box_->IsFloating() || !box_->GetShapeOutsideInfo())
return;
@@ -1550,7 +1579,7 @@ void NGBlockNode::StoreMargins(const NGPhysicalBoxStrut& physical_margins) {
void NGBlockNode::AddColumnResult(
scoped_refptr<const NGLayoutResult> result,
- const NGBlockBreakToken* incoming_break_token) {
+ const NGBlockBreakToken* incoming_break_token) const {
wtf_size_t index = FragmentIndex(incoming_break_token);
GetFlowThread(To<LayoutBlockFlow>(box_))->AddLayoutResult(result, index);
}
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 9befdc8f5e0..87891e8467f 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
@@ -37,7 +37,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
scoped_refptr<const NGLayoutResult> Layout(
const NGConstraintSpace& constraint_space,
const NGBlockBreakToken* break_token = nullptr,
- const NGEarlyBreak* = nullptr);
+ const NGEarlyBreak* = nullptr) const;
// This method is just for use within the |NGSimplifiedLayoutAlgorithm|.
//
@@ -45,7 +45,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
// space used to generate the |NGLayoutResult|.
// Otherwise it will simply return the previous layout result generated.
scoped_refptr<const NGLayoutResult> SimplifiedLayout(
- const NGPhysicalFragment& previous_fragment);
+ const NGPhysicalFragment& previous_fragment) const;
// This method is just for use within the |NGOutOfFlowLayoutPart|.
//
@@ -82,9 +82,10 @@ 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.
- MinMaxSizesResult ComputeMinMaxSizes(WritingMode container_writing_mode,
- const MinMaxSizesInput&,
- const NGConstraintSpace* = nullptr);
+ MinMaxSizesResult ComputeMinMaxSizes(
+ WritingMode container_writing_mode,
+ const MinMaxSizesInput&,
+ const NGConstraintSpace* = nullptr) const;
MinMaxSizes ComputeMinMaxSizesFromLegacy(const MinMaxSizesInput&) const;
@@ -152,7 +153,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
// LayoutObject-less, but we still need to keep the fragments generated
// somewhere.
void AddColumnResult(scoped_refptr<const NGLayoutResult>,
- const NGBlockBreakToken* incoming_break_token);
+ const NGBlockBreakToken* incoming_break_token) const;
static bool CanUseNewLayout(const LayoutBox&);
bool CanUseNewLayout() const;
@@ -160,11 +161,12 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
String ToString() const;
private:
- void PrepareForLayout();
+ void PrepareForLayout() const;
// Runs layout on the underlying LayoutObject and creates a fragment for the
// resulting geometry.
- scoped_refptr<const NGLayoutResult> RunLegacyLayout(const NGConstraintSpace&);
+ scoped_refptr<const NGLayoutResult> RunLegacyLayout(
+ const NGConstraintSpace&) const;
scoped_refptr<const NGLayoutResult> RunSimplifiedLayout(
const NGLayoutAlgorithmParams&,
@@ -175,37 +177,39 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
void FinishLayout(LayoutBlockFlow*,
const NGConstraintSpace&,
const NGBlockBreakToken*,
- scoped_refptr<const NGLayoutResult>);
+ scoped_refptr<const NGLayoutResult>) const;
// After we run the layout algorithm, this function copies back the geometry
// data to the layout box.
void CopyFragmentDataToLayoutBox(
const NGConstraintSpace&,
const NGLayoutResult&,
- const NGBlockBreakToken* previous_break_token);
+ const NGBlockBreakToken* previous_break_token) const;
void CopyFragmentItemsToLayoutBox(const NGPhysicalBoxFragment& container,
- const NGFragmentItems& items);
+ const NGFragmentItems& items) const;
void CopyFragmentDataToLayoutBoxForInlineChildren(
const NGPhysicalContainerFragment& container,
LayoutUnit initial_container_width,
bool initial_container_is_flipped,
- PhysicalOffset offset = {});
- void PlaceChildrenInLayoutBox(const NGPhysicalBoxFragment&,
- const NGBlockBreakToken* previous_break_token);
- void PlaceChildrenInFlowThread(const NGPhysicalBoxFragment&);
+ PhysicalOffset offset = {}) const;
+ void PlaceChildrenInLayoutBox(
+ const NGPhysicalBoxFragment&,
+ const NGBlockBreakToken* previous_break_token) const;
+ void PlaceChildrenInFlowThread(const NGPhysicalBoxFragment&) const;
void CopyChildFragmentPosition(
const NGPhysicalBoxFragment& child_fragment,
PhysicalOffset,
const NGPhysicalBoxFragment& container_fragment,
- const NGBlockBreakToken* previous_container_break_token = nullptr);
+ const NGBlockBreakToken* previous_container_break_token = nullptr) const;
void CopyBaselinesFromLegacyLayout(const NGConstraintSpace&,
- NGBoxFragmentBuilder*);
- LayoutUnit AtomicInlineBaselineFromLegacyLayout(const NGConstraintSpace&);
+ NGBoxFragmentBuilder*) const;
+ LayoutUnit AtomicInlineBaselineFromLegacyLayout(
+ const NGConstraintSpace&) const;
void UpdateShapeOutsideInfoIfNeeded(
const NGLayoutResult&,
- LayoutUnit percentage_resolution_inline_size);
+ LayoutUnit percentage_resolution_inline_size) const;
};
template <>
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 3ad5edcf391..7cf812f28b1 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
@@ -178,7 +178,8 @@ TEST_F(NGBlockNodeForTest, MinAndMaxContent) {
box.ComputeMinMaxSizes(
WritingMode::kHorizontalTb,
MinMaxSizesInput(
- /* percentage_resolution_block_size */ LayoutUnit()))
+ /* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent))
.sizes;
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_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index 8a0c0a6ebf2..532fef71209 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
@@ -159,7 +159,10 @@ void NGBoxFragmentBuilder::AddBreakBeforeChild(
}
DCHECK(has_block_fragmentation_);
- SetDidBreak();
+
+ if (!has_inflow_child_break_inside_)
+ has_inflow_child_break_inside_ = !child.IsFloatingOrOutOfFlowPositioned();
+
if (auto* child_inline_node = DynamicTo<NGInlineNode>(child)) {
if (inline_break_tokens_.IsEmpty()) {
// In some cases we may want to break before the first line, as a last
@@ -197,6 +200,7 @@ void NGBoxFragmentBuilder::AddBreakToken(
scoped_refptr<const NGBreakToken> token) {
DCHECK(token.get());
child_break_tokens_.push_back(std::move(token));
+ has_inflow_child_break_inside_ = true;
}
void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
@@ -245,9 +249,20 @@ void NGBoxFragmentBuilder::PropagateBreak(
const NGLayoutResult& child_layout_result) {
if (LIKELY(!has_block_fragmentation_))
return;
- if (!did_break_) {
- const auto* token = child_layout_result.PhysicalFragment().BreakToken();
- did_break_ = token && !token->IsFinished();
+ if (!has_inflow_child_break_inside_) {
+ // Figure out if this child break is in the same flow as this parent. If
+ // it's an out-of-flow positioned box, it's not. If it's in a parallel flow,
+ // it's also not.
+ const auto& child_fragment =
+ To<NGPhysicalBoxFragment>(child_layout_result.PhysicalFragment());
+ if (!child_fragment.IsFloatingOrOutOfFlowPositioned()) {
+ if (const auto* token = child_fragment.BreakToken()) {
+ if (!token->IsFinished() &&
+ (!token->IsBlockType() ||
+ !To<NGBlockBreakToken>(token)->IsAtBlockEnd()))
+ has_inflow_child_break_inside_ = true;
+ }
+ }
}
if (child_layout_result.HasForcedBreak()) {
SetHasForcedBreak();
@@ -280,10 +295,10 @@ scoped_refptr<const NGLayoutResult> NGBoxFragmentBuilder::ToBoxFragment(
child_break_tokens_.push_back(std::move(token));
}
}
- if (did_break_) {
+ if (DidBreakSelf() || HasChildBreakInside()) {
break_token_ = NGBlockBreakToken::Create(
node_, consumed_block_size_, sequence_number_, child_break_tokens_,
- break_appeal_, has_seen_all_children_);
+ break_appeal_, has_seen_all_children_, is_at_block_end_);
}
}
@@ -351,7 +366,7 @@ void NGBoxFragmentBuilder::ComputeInlineContainerGeometryFromFragmentTree(
// This function has detailed knowledge of inline fragment tree structure,
// and will break if this changes.
DCHECK_GE(InlineSize(), LayoutUnit());
- DCHECK_GE(BlockSize(), LayoutUnit());
+ DCHECK_GE(FragmentBlockSize(), LayoutUnit());
#if DCHECK_IS_ON()
// Make sure all entries are continuation root.
for (const auto& entry : *inline_containing_block_map)
@@ -407,7 +422,7 @@ void NGBoxFragmentBuilder::ComputeInlineContainerGeometry(
// This function requires that we have the final size of the fragment set
// upon the builder.
DCHECK_GE(InlineSize(), LayoutUnit());
- DCHECK_GE(BlockSize(), LayoutUnit());
+ DCHECK_GE(FragmentBlockSize(), LayoutUnit());
#if DCHECK_IS_ON()
// Make sure all entries are a continuation root.
@@ -420,9 +435,10 @@ void NGBoxFragmentBuilder::ComputeInlineContainerGeometry(
if (items_builder_) {
// To access the items correctly we need to convert them to the physical
// coordinate space.
+ DCHECK_EQ(items_builder_->GetWritingMode(), GetWritingMode());
+ DCHECK_EQ(items_builder_->Direction(), Direction());
GatherInlineContainerFragmentsFromItems(
- items_builder_->Items(GetWritingMode(), Direction(),
- ToPhysicalSize(Size(), GetWritingMode())),
+ items_builder_->Items(ToPhysicalSize(Size(), GetWritingMode())),
PhysicalOffset(), inline_containing_block_map, &containing_linebox_map);
return;
}
@@ -463,13 +479,15 @@ void NGBoxFragmentBuilder::SetLastBaselineToBlockEndMarginEdgeIfNeeded() {
// 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());
- SetLastBaseline(BlockSize() + margins.block_end);
+ SetLastBaseline(FragmentBlockSize() + margins.block_end);
}
#if DCHECK_IS_ON()
void NGBoxFragmentBuilder::CheckNoBlockFragmentation() const {
- DCHECK(!did_break_);
+ DCHECK(!HasChildBreakInside());
+ DCHECK(!HasInflowChildBreakInside());
+ DCHECK(!DidBreakSelf());
DCHECK(!has_forced_break_);
DCHECK_EQ(consumed_block_size_, LayoutUnit());
DCHECK_EQ(minimal_space_shortage_, LayoutUnit::Max());
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 67cb8f120c3..26b0856df32 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
@@ -10,9 +10,12 @@
#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_fragment_items_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.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"
#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/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
@@ -29,31 +32,25 @@ class CORE_EXPORT NGBoxFragmentBuilder final
NGBoxFragmentBuilder(NGLayoutInputNode node,
scoped_refptr<const ComputedStyle> style,
const NGConstraintSpace* space,
- WritingMode writing_mode,
- TextDirection direction)
+ WritingDirectionMode writing_direction)
: NGContainerFragmentBuilder(node,
std::move(style),
space,
- writing_mode,
- direction),
+ writing_direction),
box_type_(NGPhysicalFragment::NGBoxType::kNormalBox),
- is_inline_formatting_context_(node.IsInline()),
- did_break_(false) {}
+ is_inline_formatting_context_(node.IsInline()) {}
// Build a fragment for LayoutObject without NGLayoutInputNode. LayoutInline
// has NGInlineItem but does not have corresponding NGLayoutInputNode.
NGBoxFragmentBuilder(LayoutObject* layout_object,
scoped_refptr<const ComputedStyle> style,
- WritingMode writing_mode,
- TextDirection direction)
+ WritingDirectionMode writing_direction)
: NGContainerFragmentBuilder(/* node */ nullptr,
std::move(style),
/* space */ nullptr,
- writing_mode,
- direction),
+ writing_direction),
box_type_(NGPhysicalFragment::NGBoxType::kNormalBox),
- is_inline_formatting_context_(true),
- did_break_(false) {
+ is_inline_formatting_context_(true) {
layout_object_ = layout_object;
}
@@ -62,6 +59,32 @@ class CORE_EXPORT NGBoxFragmentBuilder final
initial_fragment_geometry_ = &initial_fragment_geometry;
size_ = initial_fragment_geometry_->border_box_size;
is_initial_block_size_indefinite_ = size_.block_size == kIndefiniteSize;
+
+ border_padding_ =
+ initial_fragment_geometry.border + initial_fragment_geometry.padding;
+ border_scrollbar_padding_ =
+ border_padding_ + initial_fragment_geometry.scrollbar;
+ if (space_) {
+ child_available_size_ = CalculateChildAvailableSize(
+ *space_, To<NGBlockNode>(node_), size_, border_scrollbar_padding_);
+ }
+ }
+
+ void AdjustBorderScrollbarPaddingForFragmentation(
+ const NGBlockBreakToken* break_token) {
+ if (LIKELY(!break_token))
+ return;
+ if (break_token->IsBreakBefore())
+ return;
+ border_scrollbar_padding_.block_start = LayoutUnit();
+ }
+
+ void AdjustBorderScrollbarPaddingForTableCell() {
+ if (!space_->IsTableCell())
+ return;
+
+ border_scrollbar_padding_ +=
+ ComputeIntrinsicPadding(*space_, *style_, Scrollbar());
}
const NGFragmentGeometry& InitialFragmentGeometry() const {
@@ -69,6 +92,52 @@ class CORE_EXPORT NGBoxFragmentBuilder final
return *initial_fragment_geometry_;
}
+ // Use the block-size setters/getters further down instead of the inherited
+ // ones.
+ LayoutUnit BlockSize() const = delete;
+ void SetBlockSize(LayoutUnit block_size) = delete;
+
+ // Set the total border-box block-size of all the fragments to be generated
+ // from this node (as if we stitched them together). Layout algorithms are
+ // expected to pass this value, and at the end of layout (if block
+ // fragmentation is needed), the fragmentation machinery will be invoked to
+ // adjust the block-size to the correct size, ensuring that we break at the
+ // best location.
+ void SetFragmentsTotalBlockSize(LayoutUnit block_size) {
+#if DCHECK_IS_ON()
+ // Note that we just store the block-size in a shared field. We have a flag
+ // for debugging, to assert that we know what we're doing when attempting to
+ // access the data.
+ block_size_is_for_all_fragments_ = true;
+#endif
+ size_.block_size = block_size;
+ }
+ LayoutUnit FragmentsTotalBlockSize() const {
+#if DCHECK_IS_ON()
+ if (has_block_fragmentation_)
+ DCHECK(block_size_is_for_all_fragments_);
+#endif
+ return size_.block_size;
+ }
+
+ // Set the final block-size of this fragment.
+ void SetFragmentBlockSize(LayoutUnit block_size) {
+#if DCHECK_IS_ON()
+ // Note that we just store the block-size in a shared field. We have a flag
+ // for debugging, to assert that we know what we're doing when attempting to
+ // access the data.
+ block_size_is_for_all_fragments_ = false;
+#endif
+ size_.block_size = block_size;
+ }
+ LayoutUnit FragmentBlockSize() const {
+#if DCHECK_IS_ON()
+ if (has_block_fragmentation_)
+ DCHECK(!block_size_is_for_all_fragments_);
+#endif
+ return size_.block_size;
+ }
+
void SetOverflowBlockSize(LayoutUnit overflow_block_size) {
overflow_block_size_ = overflow_block_size;
}
@@ -92,6 +161,22 @@ class CORE_EXPORT NGBoxFragmentBuilder final
DCHECK(initial_fragment_geometry_);
return initial_fragment_geometry_->border_box_size;
}
+ const NGBoxStrut& BorderPadding() const {
+ DCHECK(initial_fragment_geometry_);
+ return border_padding_;
+ }
+ const NGBoxStrut& BorderScrollbarPadding() const {
+ DCHECK(initial_fragment_geometry_);
+ return border_scrollbar_padding_;
+ }
+ // The child available-size is subtly different from the content-box size of
+ // an element. For an anonymous-block the child available-size is equal to
+ // its non-anonymous parent (similar to percentages).
+ const LogicalSize& ChildAvailableSize() const {
+ DCHECK(initial_fragment_geometry_);
+ DCHECK(space_);
+ return child_available_size_;
+ }
// Add a break token for a child that doesn't yet have any fragments, because
// its first fragment is to be produced in the next fragmentainer. This will
@@ -108,6 +193,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final
// descendants, propagating fragmentainer breaks, and more.
void AddResult(const NGLayoutResult&, const LogicalOffset);
+ // Manually add a break token to the builder. Note that we're assuming that
+ // this break token is for content in the same flow as this parent.
void AddBreakToken(scoped_refptr<const NGBreakToken>);
void AddOutOfFlowLegacyCandidate(NGBlockNode,
@@ -126,10 +213,31 @@ class CORE_EXPORT NGBoxFragmentBuilder final
sequence_number_ = sequence_number;
}
- // Specify that we broke.
- //
- // This will result in a fragment which has an unfinished break token.
- void SetDidBreak() { did_break_ = true; }
+ // Return true if we broke inside this node on our own initiative (typically
+ // not because of a child break, but rather due to the size of this node).
+ bool DidBreakSelf() const { return did_break_self_; }
+ void SetDidBreakSelf() { did_break_self_ = true; }
+
+ // Return true if we need to break before or inside any child, doesn't matter
+ // if it's in-flow or not. As long as there are only breaks in parallel flows,
+ // we may continue layout, but when we're done, we'll need to create a break
+ // token for this fragment nevertheless, so that we re-enter, descend and
+ // resume at the broken children in the next fragmentainer.
+ bool HasChildBreakInside() const {
+ if (!child_break_tokens_.IsEmpty())
+ return true;
+ // Inline nodes produce a "finished" trailing break token even if we don't
+ // need to block-fragment.
+ return !inline_break_tokens_.IsEmpty() &&
+ !inline_break_tokens_.back()->IsFinished();
+ }
+
+ // Return true if we need to break before or inside any in-flow child that
+ // doesn't establish a parallel flow. When this happens, we want to finish our
+ // fragment, create a break token, and resume in the next fragmentainer.
+ bool HasInflowChildBreakInside() const {
+ return has_inflow_child_break_inside_;
+ }
// Report space shortage, i.e. how much more space would have been sufficient
// to prevent some piece of content from breaking. This information may be
@@ -198,6 +306,9 @@ class CORE_EXPORT NGBoxFragmentBuilder final
// children have been fully laid out, or have break tokens. No more children
// left to discover.
void SetHasSeenAllChildren() { has_seen_all_children_ = true; }
+ bool HasSeenAllChildren() { return has_seen_all_children_; }
+
+ void SetIsAtBlockEnd() { is_at_block_end_ = true; }
void SetColumnSpanner(NGBlockNode spanner) { column_spanner_ = spanner; }
bool FoundColumnSpanner() const { return !!column_spanner_; }
@@ -246,6 +357,9 @@ class CORE_EXPORT NGBoxFragmentBuilder final
void SetBoxType(NGPhysicalFragment::NGBoxType box_type) {
box_type_ = box_type;
}
+ bool IsFragmentainerBoxType() const {
+ return BoxType() == NGPhysicalFragment::kColumnBox;
+ }
void SetIsFieldsetContainer() { is_fieldset_container_ = true; }
void SetIsLegacyLayoutRoot() { is_legacy_layout_root_ = true; }
@@ -254,8 +368,22 @@ class CORE_EXPORT NGBoxFragmentBuilder final
}
void SetIsMathMLFraction() { is_math_fraction_ = true; }
-
- bool DidBreak() const { return did_break_; }
+ void SetMathMLPaintInfo(
+ UChar operator_character,
+ scoped_refptr<const ShapeResultView> operator_shape_result_view,
+ LayoutUnit operator_inline_size,
+ LayoutUnit operator_ascent,
+ LayoutUnit operator_descent) {
+ if (!mathml_paint_info_)
+ mathml_paint_info_ = std::make_unique<NGMathMLPaintInfo>();
+
+ mathml_paint_info_->operator_shape_result_view =
+ std::move(operator_shape_result_view);
+
+ mathml_paint_info_->operator_inline_size = operator_inline_size;
+ mathml_paint_info_->operator_ascent = operator_ascent;
+ mathml_paint_info_->operator_descent = operator_descent;
+ }
void SetBorderEdges(NGBorderEdges border_edges) {
border_edges_ = border_edges;
@@ -329,12 +457,15 @@ class CORE_EXPORT NGBoxFragmentBuilder final
void SetHasForcedBreak() {
has_forced_break_ = true;
- minimal_space_shortage_ = LayoutUnit();
+ minimal_space_shortage_ = LayoutUnit::Max();
}
scoped_refptr<const NGLayoutResult> ToBoxFragment(WritingMode);
const NGFragmentGeometry* initial_fragment_geometry_ = nullptr;
+ NGBoxStrut border_padding_;
+ NGBoxStrut border_scrollbar_padding_;
+ LogicalSize child_available_size_;
LayoutUnit overflow_block_size_ = kIndefiniteSize;
LayoutUnit intrinsic_block_size_;
@@ -347,12 +478,14 @@ class CORE_EXPORT NGBoxFragmentBuilder final
bool is_initial_block_size_indefinite_ = false;
bool is_inline_formatting_context_;
bool is_first_for_node_ = true;
- bool did_break_;
+ bool did_break_self_ = false;
+ bool has_inflow_child_break_inside_ = false;
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;
+ bool is_at_block_end_ = false;
LayoutUnit consumed_block_size_;
unsigned sequence_number_ = 0;
@@ -373,6 +506,14 @@ class CORE_EXPORT NGBoxFragmentBuilder final
scoped_refptr<SerializedScriptValue> custom_layout_data_;
base::Optional<int> lines_until_clamp_;
+ std::unique_ptr<NGMathMLPaintInfo> mathml_paint_info_;
+
+#if DCHECK_IS_ON()
+ // Describes what size_.block_size represents; either the size of a single
+ // fragment (false), or the size of all fragments for a node (true).
+ bool block_size_is_for_all_fragments_ = false;
+#endif
+
friend class NGPhysicalBoxFragment;
friend class NGLayoutResult;
};
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h
index 29c7bb6009b..5b927db4bfd 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h
@@ -78,6 +78,7 @@ class CORE_EXPORT NGBreakToken : public RefCounted<NGBreakToken> {
flags_(0),
is_break_before_(false),
is_forced_break_(false),
+ is_at_block_end_(false),
break_appeal_(kBreakAppealPerfect),
has_seen_all_children_(false) {
DCHECK_EQ(type, static_cast<NGBreakTokenType>(node.Type()));
@@ -104,6 +105,11 @@ class CORE_EXPORT NGBreakToken : public RefCounted<NGBreakToken> {
unsigned is_forced_break_ : 1;
+ // Set when layout is past the block-end border edge. If we break when we're
+ // in this state, it means that something is overflowing, and thus establishes
+ // a parallel flow.
+ unsigned is_at_block_end_ : 1;
+
// If the break is unforced, this is the appeal of the break. Higher is
// better. Violating breaking rules decreases appeal. Forced breaks always
// have perfect appeal.
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 2db4791c47a..5950ccb01a1 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
@@ -88,32 +88,30 @@ void PushSpannerBreakTokens(
NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- early_break_(params.early_break),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
- border_scrollbar_padding_(border_padding_ +
- params.fragment_geometry.scrollbar) {
- AdjustForFragmentation(BreakToken(), &border_scrollbar_padding_);
+ : NGLayoutAlgorithm(params), early_break_(params.early_break) {
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
+ container_builder_.AdjustBorderScrollbarPaddingForFragmentation(BreakToken());
}
scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
- LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
- content_box_size_ =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
-
- DCHECK_GE(content_box_size_.inline_size, LayoutUnit());
+ const LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
+ // TODO(mstensho): This isn't the content-box size, as
+ // |BorderScrollbarPadding()| has been adjusted for fragmentation. Verify
+ // that this is the correct size.
+ column_block_size_ =
+ ShrinkLogicalSize(border_box_size, BorderScrollbarPadding()).block_size;
+
+ DCHECK_GE(ChildAvailableSize().inline_size, LayoutUnit());
column_inline_size_ =
- ResolveUsedColumnInlineSize(content_box_size_.inline_size, Style());
+ ResolveUsedColumnInlineSize(ChildAvailableSize().inline_size, Style());
column_inline_progression_ =
column_inline_size_ +
- ResolveUsedColumnGap(content_box_size_.inline_size, Style());
+ ResolveUsedColumnGap(ChildAvailableSize().inline_size, Style());
used_column_count_ =
- ResolveUsedColumnCount(content_box_size_.inline_size, Style());
+ ResolveUsedColumnCount(ChildAvailableSize().inline_size, Style());
// If we know the block-size of the fragmentainers in an outer fragmentation
// context (if any), our columns may be constrained by that, meaning that we
@@ -129,7 +127,7 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
container_builder_.SetIsBlockFragmentationContextRoot();
- intrinsic_block_size_ = border_scrollbar_padding_.block_start;
+ intrinsic_block_size_ = BorderScrollbarPadding().block_start;
NGBreakStatus break_status = LayoutChildren();
if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
@@ -138,11 +136,13 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
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());
+ DCHECK(!IsResumingLayout(BreakToken()));
return container_builder_.Abort(NGLayoutResult::kOutOfFragmentainerSpace);
}
+ intrinsic_block_size_ += BorderScrollbarPadding().block_end;
+
// Figure out how much space we've already been able to process in previous
// fragments, if this multicol container participates in an outer
// fragmentation context.
@@ -155,22 +155,23 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
LayoutUnit block_size;
if (border_box_size.block_size == kIndefiniteSize) {
// Get the block size from the contents if it's auto.
- block_size = intrinsic_block_size_ + border_scrollbar_padding_.block_end;
+ block_size = intrinsic_block_size_;
} else {
// TODO(mstensho): end border and padding may overflow the parent
// fragmentainer, and we should avoid that.
block_size = border_box_size.block_size - previously_consumed_block_size;
}
- if (is_constrained_by_outer_fragmentation_context_) {
+ container_builder_.SetFragmentsTotalBlockSize(previously_consumed_block_size +
+ block_size);
+ container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
+
+ if (ConstraintSpace().HasBlockFragmentation()) {
// In addition to establishing one, we're nested inside another
// fragmentation context.
FinishFragmentation(
- ConstraintSpace(), BreakToken(), block_size, intrinsic_block_size_,
+ Node(), ConstraintSpace(), BreakToken(), BorderPadding(),
FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
- } else {
- container_builder_.SetBlockSize(block_size);
- container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
}
NGOutOfFlowLayoutPart(
@@ -211,7 +212,7 @@ MinMaxSizesResult NGColumnLayoutAlgorithm::ComputeMinMaxSizes(
// TODO(mstensho): Need to include spanners.
- result.sizes += border_scrollbar_padding_.InlineSum();
+ result.sizes += BorderScrollbarPadding().InlineSum();
return result;
}
@@ -295,7 +296,12 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutChildren() {
if (!result) {
// Not enough outer fragmentainer space to produce any columns at all.
- container_builder_.SetDidBreak();
+
+ // TODO(mstensho): Explicitly marking that we broke shouldn't be necessary
+ // here, ideally. But the fragmentation machinery needs this hint in some
+ // cases. There's probably a break token missing.
+ container_builder_.SetDidBreakSelf();
+
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
@@ -370,6 +376,9 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutChildren() {
// resuming.
container_builder_.SetHasSeenAllChildren();
+ // TODO(mstensho): Truncate the child margin if it overflows the
+ // fragmentainer, by using AdjustedMarginAfterFinalChildFragment().
+
intrinsic_block_size_ += margin_strut.Sum();
}
@@ -379,7 +388,7 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutChildren() {
scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
const NGBlockBreakToken* next_column_token,
NGMarginStrut* margin_strut) {
- LogicalSize column_size(column_inline_size_, content_box_size_.block_size);
+ LogicalSize column_size(column_inline_size_, column_block_size_);
// If block-size is non-auto, subtract the space for content we've consumed in
// previous fragments. This is necessary when we're nested inside another
@@ -462,7 +471,7 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
// preceding columns in this row and there are also no preceding rows.
bool is_first_fragmentainer = !column_break_token && !BreakToken();
- LayoutUnit column_inline_offset(border_scrollbar_padding_.inline_start);
+ LayoutUnit column_inline_offset(BorderScrollbarPadding().inline_start);
int actual_column_count = 0;
int forced_break_count = 0;
@@ -526,7 +535,6 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
if (zero_outer_space_left)
return nullptr;
- container_builder_.SetDidBreak();
container_builder_.SetBreakAppeal(kBreakAppealPerfect);
break;
}
@@ -535,52 +543,68 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::LayoutRow(
} while (column_break_token);
// TODO(mstensho): Nested column balancing.
- if (container_builder_.DidBreak())
+ if (container_builder_.DidBreakSelf())
break;
- if (!balance_columns && result->ColumnSpanner()) {
- // We always have to balance columns preceding a spanner, so if we didn't
- // do that initially, switch over to column balancing mode now, and lay
- // out again.
- balance_columns = true;
- new_columns.clear();
- column_size.block_size =
- CalculateBalancedColumnBlockSize(column_size, next_column_token);
- continue;
+ if (!balance_columns) {
+ if (result->ColumnSpanner()) {
+ // We always have to balance columns preceding a spanner, so if we
+ // didn't do that initially, switch over to column balancing mode now,
+ // and lay out again.
+ balance_columns = true;
+ new_columns.clear();
+ column_size.block_size =
+ CalculateBalancedColumnBlockSize(column_size, next_column_token);
+ continue;
+ }
+
+ // Balancing not enabled. We're done.
+ break;
}
- // If we overflowed (actual column count larger than what we have room for),
- // and we're supposed to calculate the column lengths automatically (column
- // balancing), see if we're able to stretch them.
+ // We're balancing columns. Check if the column block-size that we laid out
+ // with was satisfactory. If not, stretch and retry, if possible.
//
- // We can only stretch the columns if we have at least one column that could
- // take more content, and we also need to know the stretch amount (minimal
- // space shortage). We need at least one soft break opportunity to do
- // this. If forced breaks cause too many breaks, there's no stretch amount
- // that could prevent the actual column count from overflowing.
+ // If we overflowed (actual column count larger than what we have room for),
+ // see if we're able to stretch them. We can only stretch the columns if we
+ // have at least one column that could take more content.
//
+ // If we didn't exceed used column-count, we're done.
+ if (actual_column_count <= used_column_count_)
+ break;
+
+ // We're in a situation where we'd like to stretch the columns, but then we
+ // need to know the stretch amount (minimal space shortage).
+ if (minimal_space_shortage == LayoutUnit::Max())
+ break;
+
+ // We also need at least one soft break opportunity. If forced breaks cause
+ // too many breaks, there's no stretch amount that could prevent the columns
+ // from overflowing.
+ if (actual_column_count <= forced_break_count + 1)
+ break;
+
// TODO(mstensho): Handle this situation also when we're inside another
// balanced multicol container, rather than bailing (which we do now, to
// avoid infinite loops). If we exhaust the inner column-count in such
// cases, that piece of information may have to be propagated to the outer
// multicol, and instead stretch there (not here). We have no such mechanism
// in place yet.
- if (balance_columns && actual_column_count > used_column_count_ &&
- actual_column_count > forced_break_count + 1 &&
- minimal_space_shortage != LayoutUnit::Max() &&
- !ConstraintSpace().IsInsideBalancedColumns()) {
- LayoutUnit new_column_block_size = StretchColumnBlockSize(
- minimal_space_shortage, column_size.block_size);
-
- DCHECK_GE(new_column_block_size, column_size.block_size);
- if (new_column_block_size > column_size.block_size) {
- // Remove column fragments and re-attempt layout with taller columns.
- new_columns.clear();
- column_size.block_size = new_column_block_size;
- continue;
- }
- }
- break;
+ if (ConstraintSpace().IsInsideBalancedColumns())
+ break;
+
+ LayoutUnit new_column_block_size =
+ StretchColumnBlockSize(minimal_space_shortage, column_size.block_size);
+
+ // Give up if we cannot get taller columns. The multicol container may have
+ // a specified block-size preventing taller columns, for instance.
+ DCHECK_GE(new_column_block_size, column_size.block_size);
+ if (new_column_block_size <= column_size.block_size)
+ break;
+
+ // Remove column fragments and re-attempt layout with taller columns.
+ new_columns.clear();
+ column_size.block_size = new_column_block_size;
} while (true);
bool is_empty = false;
@@ -631,7 +655,7 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutSpanner(
*spanner_break_token = nullptr;
const ComputedStyle& spanner_style = spanner_node.Style();
NGBoxStrut margins = ComputeMarginsFor(
- spanner_style, content_box_size_.inline_size,
+ spanner_style, ChildAvailableSize().inline_size,
ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
if (break_token) {
@@ -693,11 +717,11 @@ NGBreakStatus NGColumnLayoutAlgorithm::LayoutSpanner(
NGFragment fragment(ConstraintSpace().GetWritingMode(),
result->PhysicalFragment());
- ResolveInlineMargins(spanner_style, Style(), content_box_size_.inline_size,
+ ResolveInlineMargins(spanner_style, Style(), ChildAvailableSize().inline_size,
fragment.InlineSize(), &margins);
LogicalOffset offset(
- border_scrollbar_padding_.inline_start + margins.inline_start,
+ BorderScrollbarPadding().inline_start + margins.inline_start,
block_offset);
container_builder_.AddResult(*result, offset);
@@ -815,7 +839,7 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
// Then distribute as many implicit breaks into the content runs as we need.
int used_column_count =
- ResolveUsedColumnCount(content_box_size_.inline_size, Style());
+ ResolveUsedColumnCount(ChildAvailableSize().inline_size, Style());
for (int columns_found = content_runs.size();
columns_found < used_column_count; columns_found++) {
// The tallest content run (with all assumed implicit breaks added so far
@@ -869,15 +893,15 @@ LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
// First of all we need to convert the size to a value that can be compared
// against the resolved properties on the multicol container. That means that
// we have to convert the value from content-box to border-box.
- LayoutUnit extra = border_scrollbar_padding_.BlockSum();
+ LayoutUnit extra = BorderScrollbarPadding().BlockSum();
size += extra;
const ComputedStyle& style = Style();
LayoutUnit max = ResolveMaxBlockLength(
- ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(),
+ ConstraintSpace(), style, BorderPadding(), style.LogicalMaxHeight(),
LengthResolvePhase::kLayout);
LayoutUnit extent = ResolveMainBlockLength(
- ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size,
+ ConstraintSpace(), style, BorderPadding(), style.LogicalHeight(), size,
LengthResolvePhase::kLayout);
if (extent != kIndefiniteSize) {
// A specified height/width will just constrain the maximum length.
@@ -987,8 +1011,8 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForSpanner(
LayoutUnit block_offset) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
- space_builder.SetAvailableSize(content_box_size_);
- space_builder.SetPercentageResolutionSize(content_box_size_);
+ space_builder.SetAvailableSize(ChildAvailableSize());
+ space_builder.SetPercentageResolutionSize(ChildAvailableSize());
if (ConstraintSpace().HasBlockFragmentation()) {
SetupSpaceBuilderForFragmentation(ConstraintSpace(), spanner, block_offset,
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 70735070448..44f402fdb9c 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
@@ -66,7 +66,7 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
LayoutUnit ConstrainColumnBlockSize(LayoutUnit size) const;
LayoutUnit CurrentContentBlockOffset() const {
- return intrinsic_block_size_ - border_scrollbar_padding_.block_start;
+ return intrinsic_block_size_ - BorderScrollbarPadding().block_start;
}
// Finalize layout after breaking before column contents.
@@ -97,18 +97,10 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
// When set, this will specify where to break before or inside.
const NGEarlyBreak* early_break_ = nullptr;
- // Border + padding sum, resolved from the node's computed style.
- const NGBoxStrut border_padding_;
-
- // Border + scrollbar + padding sum for the fragment to be generated (most
- // importantly, for non-first fragments, leading block border + scrollbar +
- // padding is zero).
- NGBoxStrut border_scrollbar_padding_;
-
- LogicalSize content_box_size_;
int used_column_count_;
LayoutUnit column_inline_size_;
LayoutUnit column_inline_progression_;
+ LayoutUnit column_block_size_;
LayoutUnit intrinsic_block_size_;
bool is_constrained_by_outer_fragmentation_context_ = false;
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 89e5c4de416..cbb3aa027fb 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
@@ -2053,7 +2053,9 @@ TEST_F(NGColumnLayoutAlgorithmTest, UnsatisfiableOrphansAndWidows) {
EXPECT_EQ(expectation, dump);
}
-TEST_F(NGColumnLayoutAlgorithmTest, WidowsAndAbspos) {
+// TODO(1079031): Re-enable once layout for fragmented positioned elements is
+// complete.
+TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_WidowsAndAbspos) {
SetBodyInnerHTML(R"HTML(
<style>
#parent {
@@ -2719,7 +2721,8 @@ TEST_F(NGColumnLayoutAlgorithmTest, MinMax) {
NGColumnLayoutAlgorithm algorithm({node, fragment_geometry, space});
base::Optional<MinMaxSizes> sizes;
MinMaxSizesInput zero_input(
- /* percentage_resolution_block_size */ (LayoutUnit()));
+ /* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent);
// Both column-count and column-width set.
style->SetColumnCount(3);
@@ -4327,7 +4330,9 @@ TEST_F(NGColumnLayoutAlgorithmTest, NestedWithTallSpanner) {
EXPECT_EQ(expectation, dump);
}
-TEST_F(NGColumnLayoutAlgorithmTest, AbsposFitsInOneColumn) {
+// TODO(1079031): Re-enable once layout for fragmented positioned elements is
+// complete.
+TEST_F(NGColumnLayoutAlgorithmTest, DISABLED_AbsposFitsInOneColumn) {
SetBodyInnerHTML(R"HTML(
<div id="container">
<div style="columns:3; width:320px; height:100px; column-gap:10px; column-fill:auto;">
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 6b49934f15d..069997ac3e0 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
@@ -460,6 +460,16 @@ class CORE_EXPORT NGConstraintSpace final {
return HasRareData() && rare_data_->is_restricted_block_size_table_cell;
}
+ // The amount of available space for block-start side annotation.
+ // For the first box, this is the padding-block-start value of the container.
+ // Otherwise, this comes from NGLayoutResult::BlockEndAnnotationSpace().
+ // If the value is negative, it's block-end annotation overflow of the
+ // previous box.
+ LayoutUnit BlockStartAnnotationSpace() const {
+ return HasRareData() ? rare_data_->BlockStartAnnotationSpace()
+ : LayoutUnit();
+ }
+
NGMarginStrut MarginStrut() const {
return HasRareData() ? rare_data_->MarginStrut() : NGMarginStrut();
}
@@ -676,6 +686,7 @@ class CORE_EXPORT NGConstraintSpace final {
: percentage_resolution_size(other.percentage_resolution_size),
replaced_percentage_resolution_block_size(
other.replaced_percentage_resolution_block_size),
+ block_start_annotation_space(other.block_start_annotation_space),
bfc_offset(other.bfc_offset),
fragmentainer_block_size(other.fragmentainer_block_size),
fragmentainer_offset_at_bfc(other.fragmentainer_offset_at_bfc),
@@ -784,6 +795,14 @@ class CORE_EXPORT NGConstraintSpace final {
return stretch_data_.IsInitialForMaySkipLayout();
}
+ LayoutUnit BlockStartAnnotationSpace() const {
+ return block_start_annotation_space;
+ }
+
+ void SetBlockStartAnnotationSpace(LayoutUnit space) {
+ block_start_annotation_space = space;
+ }
+
NGMarginStrut MarginStrut() const {
return data_union_type == kBlockData ? block_data_.margin_strut
: NGMarginStrut();
@@ -903,6 +922,7 @@ class CORE_EXPORT NGConstraintSpace final {
LogicalSize percentage_resolution_size;
LayoutUnit replaced_percentage_resolution_block_size;
+ LayoutUnit block_start_annotation_space;
NGBfcOffset bfc_offset;
LayoutUnit fragmentainer_block_size = kIndefiniteSize;
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 30655178c30..5da0ad3a734 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
@@ -215,6 +215,11 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
space_.bitfields_.cache_slot = static_cast<unsigned>(slot);
}
+ void SetBlockStartAnnotationSpace(LayoutUnit space) {
+ if (space)
+ space_.EnsureRareData()->SetBlockStartAnnotationSpace(space);
+ }
+
void SetMarginStrut(const NGMarginStrut& margin_strut) {
#if DCHECK_IS_ON()
DCHECK(!is_margin_strut_set_);
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 2be126bca70..02912fb434a 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
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.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_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -75,11 +76,40 @@ void NGContainerFragmentBuilder::PropagateChildData(
IsInlineContainerForNode(descendant.node, inline_container))
new_inline_container = inline_container;
+ // |oof_positioned_candidates_| should not have duplicated entries.
+ DCHECK(std::none_of(
+ oof_positioned_candidates_.begin(), oof_positioned_candidates_.end(),
+ [&descendant](const NGLogicalOutOfFlowPositionedNode& node) {
+ return node.node == descendant.node;
+ }));
oof_positioned_candidates_.emplace_back(descendant.node, static_position,
new_inline_container);
}
}
+ if (const NGPhysicalBoxFragment* fragment =
+ DynamicTo<NGPhysicalBoxFragment>(&child)) {
+ if (fragment->HasOutOfFlowPositionedFragmentainerDescendants()) {
+ const auto& out_of_flow_fragmentainer_descendants =
+ fragment->OutOfFlowPositionedFragmentainerDescendants();
+
+ for (const auto& descendant : out_of_flow_fragmentainer_descendants) {
+ const NGPhysicalContainerFragment* containing_block_fragment =
+ descendant.containing_block_fragment.get();
+ if (!containing_block_fragment)
+ containing_block_fragment = fragment;
+
+ NGLogicalStaticPosition static_position =
+ descendant.static_position.ConvertToLogical(
+ GetWritingMode(), Direction(), PhysicalSize());
+ oof_positioned_fragmentainer_descendants_.emplace_back(
+ descendant.node, static_position, descendant.inline_container,
+ /* needs_block_offset_adjustment */ false,
+ containing_block_fragment);
+ }
+ }
+ }
+
// For the |has_orthogonal_flow_roots_| flag, we don't care about the type of
// child (OOF-positioned, etc), it is for *any* descendant.
if (child.HasOrthogonalFlowRoots() ||
@@ -128,10 +158,9 @@ void NGContainerFragmentBuilder::PropagateChildData(
// Compute |has_floating_descendants_for_paint_| to optimize tree traversal
// in paint.
if (!has_floating_descendants_for_paint_) {
- // TODO(layout-dev): The |NGPhysicalFragment::IsAtomicInline| check should
- // be checking for any children which paint all phases atomically.
if (child.IsFloating() || child.IsLegacyLayoutRoot() ||
- (child.HasFloatingDescendantsForPaint() && !child.IsAtomicInline()))
+ (child.HasFloatingDescendantsForPaint() &&
+ !child.IsPaintedAtomically()))
has_floating_descendants_for_paint_ = true;
}
@@ -219,6 +248,11 @@ void NGContainerFragmentBuilder::AddOutOfFlowInlineChildCandidate(
NGLogicalStaticPosition::kBlockStart);
}
+void NGContainerFragmentBuilder::AddOutOfFlowFragmentainerDescendant(
+ const NGLogicalOutOfFlowPositionedNode& descendant) {
+ oof_positioned_fragmentainer_descendants_.push_back(descendant);
+}
+
void NGContainerFragmentBuilder::AddOutOfFlowDescendant(
const NGLogicalOutOfFlowPositionedNode& descendant) {
oof_positioned_descendants_.push_back(descendant);
@@ -251,6 +285,13 @@ void NGContainerFragmentBuilder::SwapOutOfFlowPositionedCandidates(
has_oof_candidate_that_needs_block_offset_adjustment_ = false;
}
+void NGContainerFragmentBuilder::SwapOutOfFlowFragmentainerDescendants(
+ Vector<NGLogicalOutOfFlowPositionedNode>* descendants) {
+ DCHECK(descendants->IsEmpty());
+ DCHECK(!has_oof_candidate_that_needs_block_offset_adjustment_);
+ std::swap(oof_positioned_fragmentainer_descendants_, *descendants);
+}
+
void NGContainerFragmentBuilder::
MoveOutOfFlowDescendantCandidatesToDescendants() {
DCHECK(oof_positioned_descendants_.IsEmpty());
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 4edb57dcf10..1b007aafac9 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
@@ -126,16 +126,26 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
const LogicalOffset& child_offset,
TextDirection inline_container_direction);
+ void AddOutOfFlowFragmentainerDescendant(
+ const NGLogicalOutOfFlowPositionedNode& descendant);
+
void AddOutOfFlowDescendant(
const NGLogicalOutOfFlowPositionedNode& descendant);
void SwapOutOfFlowPositionedCandidates(
Vector<NGLogicalOutOfFlowPositionedNode>* candidates);
+ void SwapOutOfFlowFragmentainerDescendants(
+ Vector<NGLogicalOutOfFlowPositionedNode>* descendants);
+
bool HasOutOfFlowPositionedCandidates() const {
return !oof_positioned_candidates_.IsEmpty();
}
+ bool HasOutOfFlowFragmentainerDescendants() const {
+ return !oof_positioned_fragmentainer_descendants_.IsEmpty();
+ }
+
// This method should only be used within the inline layout algorithm. It is
// used to convert all OOF-positioned candidates to descendants.
//
@@ -173,6 +183,20 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
is_fragmentation_context_root_ = true;
}
+ bool IsBlockFragmentationContextRoot() const {
+ return is_fragmentation_context_root_;
+ }
+
+ // See NGLayoutResult::AnnotationOverflow().
+ void SetAnnotationOverflow(LayoutUnit overflow) {
+ annotation_overflow_ = overflow;
+ }
+
+ // See NGLayoutRsult::BlockEndAnnotatioSpace().
+ void SetBlockEndAnnotationSpace(LayoutUnit space) {
+ block_end_annotation_space_ = space;
+ }
+
const NGConstraintSpace* ConstraintSpace() const { return space_; }
#if DCHECK_IS_ON()
@@ -187,9 +211,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
NGContainerFragmentBuilder(NGLayoutInputNode node,
scoped_refptr<const ComputedStyle> style,
const NGConstraintSpace* space,
- WritingMode writing_mode,
- TextDirection direction)
- : NGFragmentBuilder(std::move(style), writing_mode, direction),
+ WritingDirectionMode writing_direction)
+ : NGFragmentBuilder(std::move(style), writing_direction),
node_(node),
space_(space) {
layout_object_ = node.GetLayoutBox();
@@ -211,6 +234,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
NGExclusionSpace exclusion_space_;
Vector<NGLogicalOutOfFlowPositionedNode> oof_positioned_candidates_;
+ Vector<NGLogicalOutOfFlowPositionedNode>
+ oof_positioned_fragmentainer_descendants_;
Vector<NGLogicalOutOfFlowPositionedNode> oof_positioned_descendants_;
NGUnpositionedListMarker unpositioned_list_marker_;
@@ -225,6 +250,11 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
scoped_refptr<const NGEarlyBreak> early_break_;
NGBreakAppeal break_appeal_ = kBreakAppealLastResort;
+ // See NGLayoutResult::AnnotationOverflow().
+ LayoutUnit annotation_overflow_;
+ // See NGLayoutResult::BlockEndAnotationSpace().
+ LayoutUnit block_end_annotation_space_;
+
NGAdjoiningObjectTypes adjoining_object_types_ = kAdjoiningNone;
bool has_adjoining_object_descendants_ = false;
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 2e117e138f2..17ddb935ff0 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
@@ -23,10 +23,9 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
: NGLayoutAlgorithm(params),
writing_mode_(ConstraintSpace().GetWritingMode()),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
consumed_block_size_(BreakToken() ? BreakToken()->ConsumedBlockSize()
: LayoutUnit()) {
+ DCHECK(params.fragment_geometry.scrollbar.IsEmpty());
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
@@ -38,21 +37,22 @@ NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm(
// 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_;
+ adjusted_border_padding_ = BorderPadding();
AdjustForFragmentation(BreakToken(), &adjusted_border_padding_);
}
scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// 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
- // the fieldset contents anonymous box (if any). Fieldset scrollbars and
- // padding will not be applied to the fieldset container itself, but rather to
- // the fieldset contents anonymous child box. The reason for this is that the
- // rendered legend shouldn't be part of the scrollport; the legend is
- // essentially a part of the block-start border, and should not scroll along
- // 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.
+ // the fieldset contents anonymous box (if any).
+ // Fieldset scrollbars and padding will not be applied to the fieldset
+ // container itself, but rather to the fieldset contents anonymous child box.
+ // The reason for this is that the rendered legend shouldn't be part of the
+ // scrollport; the legend is essentially a part of the block-start border,
+ // and should not scroll along 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.
// Calculate the amount of the border block-start that was consumed in
// previous fragments.
@@ -72,7 +72,7 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// Recompute the block-axis size now that we know our content size.
border_box_size_.block_size =
- ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_,
+ ComputeBlockSizeForFragment(ConstraintSpace(), Style(), BorderPadding(),
intrinsic_block_size_ + consumed_block_size_,
border_box_size_.inline_size);
@@ -93,16 +93,16 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// 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_;
+ LayoutUnit all_fragments_block_size = border_box_size_.block_size;
+ container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
+ container_builder_.SetFragmentsTotalBlockSize(all_fragments_block_size);
container_builder_.SetIsFieldsetContainer();
- if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+
+ if (ConstraintSpace().HasBlockFragmentation()) {
FinishFragmentation(
- ConstraintSpace(), BreakToken(), block_size, intrinsic_block_size_,
+ Node(), ConstraintSpace(), BreakToken(), BorderPadding(),
FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
- } else {
- container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
- container_builder_.SetBlockSize(block_size);
}
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_,
@@ -158,7 +158,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutChildren() {
NGBoxStrut borders_with_legend = borders_;
borders_with_legend.block_start = intrinsic_block_size_;
LogicalSize adjusted_padding_box_size =
- ShrinkAvailableSize(border_box_size_, borders_with_legend);
+ ShrinkLogicalSize(border_box_size_, borders_with_legend);
if (adjusted_padding_box_size.block_size != kIndefiniteSize) {
// If intrinsic_block_size_ does not include the border block-start that was
@@ -226,10 +226,8 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
// 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);
+ LogicalSize percentage_size = CalculateChildPercentageSize(
+ ConstraintSpace(), Node(), ChildAvailableSize());
NGBoxStrut legend_margins = ComputeMarginsFor(
legend.Style(), percentage_size.inline_size,
ConstraintSpace().GetWritingMode(), ConstraintSpace().Direction());
@@ -243,7 +241,7 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
LayoutUnit block_offset = legend_margins.block_start;
do {
auto legend_space = CreateConstraintSpaceForLegend(
- legend, content_box_size, percentage_size, block_offset);
+ legend, ChildAvailableSize(), percentage_size, block_offset);
result = legend.Layout(legend_space, legend_break_token.get());
// TODO(layout-dev): Handle abortions caused by block fragmentation.
@@ -277,8 +275,16 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
}
LayoutUnit legend_margin_box_block_size =
- NGFragment(writing_mode_, physical_fragment).BlockSize() +
- legend_margins.BlockSum();
+ legend_margins.block_start +
+ NGFragment(writing_mode_, physical_fragment).BlockSize();
+
+ LayoutUnit block_end_margin = legend_margins.block_end;
+ if (ConstraintSpace().HasKnownFragmentainerBlockSize()) {
+ block_end_margin = AdjustedMarginAfterFinalChildFragment(
+ ConstraintSpace(), legend_margin_box_block_size, block_end_margin);
+ }
+ legend_margin_box_block_size += block_end_margin;
+
LayoutUnit space_left = borders_.block_start - legend_margin_box_block_size;
if (space_left > LayoutUnit()) {
@@ -303,6 +309,8 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutLegend(
// the size of the legend instead of the border.
intrinsic_block_size_ = legend_margin_box_block_size;
+ is_legend_past_border_ = true;
+
// Don't adjust the block-start offset of the fragment border if it broke.
if (BreakToken() || (ConstraintSpace().HasKnownFragmentainerBlockSize() &&
legend_margin_box_block_size >
@@ -345,11 +353,12 @@ NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent(
NGBreakStatus break_status = NGBreakStatus::kContinue;
if (ConstraintSpace().HasBlockFragmentation()) {
+ bool has_container_separation = is_legend_past_border_;
// TODO(almaher): The legend should be treated as out-of-flow.
break_status = BreakBeforeChildIfNeeded(
ConstraintSpace(), fieldset_content, *result.get(),
ConstraintSpace().FragmentainerOffsetAtBfc() + intrinsic_block_size_,
- /*has_container_separation*/ has_legend, &container_builder_);
+ has_container_separation, &container_builder_);
EBreakBetween break_after = JoinFragmentainerBreakValues(
result->FinalBreakAfter(), fieldset_content.Style().BreakAfter());
container_builder_.SetPreviousBreakAfter(break_after);
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 a6a6d9996a0..57fc9dfefa7 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
@@ -52,7 +52,6 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
const WritingMode writing_mode_;
- const NGBoxStrut border_padding_;
NGBoxStrut borders_;
NGBoxStrut padding_;
@@ -76,6 +75,13 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
// If true, this indicates that the legend broke during the current layout
// pass.
bool legend_broke_ = false;
+
+ // If true, the legend is taller than the block-start border, so that it
+ // sticks below it, allowing for a class C breakpoint [1] before any fieldset
+ // content.
+ //
+ // [1] https://www.w3.org/TR/css-break-3/#possible-breaks
+ bool is_legend_past_border_ = 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 8f88b0d0ee2..1ced64ad3c0 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
@@ -45,7 +45,8 @@ class NGFieldsetLayoutAlgorithmTest
NGFieldsetLayoutAlgorithm algorithm({node, fragment_geometry, space});
MinMaxSizesInput input(
- /* percentage_resolution_block_size */ (LayoutUnit()));
+ /* percentage_resolution_block_size */ LayoutUnit(),
+ MinMaxSizesType::kContent);
return algorithm.ComputeMinMaxSizes(input).sizes;
}
@@ -1905,6 +1906,60 @@ TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation) {
}
// Tests that a fieldset with a large border and a small legend fragment
+// correctly. In this case, since the legend doesn't stick below the block-start
+// border, there's no class C breakpoint before the fieldset contents.
+// Therefore, prefer breaking before the fieldset to breaking before the child
+// DIV.
+TEST_F(NGFieldsetLayoutAlgorithmTest, SmallerLegendLargeBorderFragmentation2) {
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ #fieldset { margin:0; border:30px solid; padding:0px; width:100px; }
+ #legend { padding:0; width:10px; height:5px; }
+ </style>
+ <div id="container" style="width:300px;">
+ <div style="width:33px; height:70px;"></div>
+ <fieldset id="fieldset">
+ <legend id="legend"></legend>
+ <div style="width:44px; height:30px; break-inside:avoid;"></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:300x100
+ offset:0,0 size:33x70
+)DUMP";
+ EXPECT_EQ(expectation, dump);
+
+ fragment = NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(
+ node, space, fragment->BreakToken());
+ EXPECT_FALSE(fragment->BreakToken());
+
+ dump = DumpFragmentTree(fragment.get());
+ expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+ offset:unplaced size:300x90
+ offset:0,0 size:160x90
+ offset:30,12.5 size:10x5
+ offset:30,30 size:100x30
+ offset:0,0 size:44x30
+)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) {
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 f772d4d8bbe..46cced99c7d 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
@@ -196,9 +196,9 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
NGExclusionSpace* exclusion_space) {
DCHECK(unpositioned_float);
const NGConstraintSpace& parent_space = unpositioned_float->parent_space;
+ NGBlockNode node = unpositioned_float->node;
bool is_same_writing_mode =
- unpositioned_float->node.Style().GetWritingMode() ==
- parent_space.GetWritingMode();
+ node.Style().GetWritingMode() == parent_space.GetWritingMode();
bool is_fragmentable =
is_same_writing_mode && parent_space.HasBlockFragmentation();
@@ -206,6 +206,7 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
scoped_refptr<const NGLayoutResult> layout_result;
NGBoxStrut fragment_margins;
NGLayoutOpportunity opportunity;
+ bool need_break_before = false;
if (!is_fragmentable) {
// We may be able to re-use the fragment from when we calculated the
@@ -223,8 +224,7 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
float_fragment.InlineSize());
} else {
fragment_margins = ComputeMarginsFor(
- unpositioned_float->node.Style(),
- unpositioned_float->percentage_size.inline_size,
+ node.Style(), unpositioned_float->percentage_size.inline_size,
parent_space.GetWritingMode(), parent_space.Direction());
AdjustForFragmentation(unpositioned_float->token.get(), &fragment_margins);
@@ -250,8 +250,7 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
NGConstraintSpace space = CreateConstraintSpaceForFloat(
*unpositioned_float, fragmentainer_delta);
- layout_result = unpositioned_float->node.Layout(
- space, unpositioned_float->token.get());
+ layout_result = node.Layout(space, unpositioned_float->token.get());
// If we knew the right block-offset up front, we're done.
if (!optimistically_placed)
@@ -282,9 +281,42 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
break;
} while (true);
- if (const NGBreakToken* break_token =
- layout_result->PhysicalFragment().BreakToken())
- fragment_margins.block_end = LayoutUnit();
+ LayoutUnit fragmentainer_margin_edge_block_offset =
+ parent_space.FragmentainerOffsetAtBfc() +
+ opportunity.rect.start_offset.block_offset;
+
+ // Note that we don't check if we're at a valid class A, B or C breakpoint
+ // (we only check that we're not at the start of the fragmentainer (in which
+ // case breaking typically wouldn't eliminate the unappealing break inside
+ // the float)). While no other browsers do this either, we should consider
+ // doing this in the future. But for now, don't let the float affect the
+ // appeal of breaking inside this container.
+ //
+ // If we're past the fragmentainer start, we can consider breaking before
+ // this float. Otherwise we cannot, or there'd be no content
+ // progression. The common fragmentation machinery assumes that margins can
+ // collapse with fragmentainer boundaries, but this isn't the case for
+ // floats. We don't allow float margins to collapse with anything, nor be
+ // split into multiple fragmentainers. Hence this additional check. Note
+ // that we might want to reconsider this behavior, since browsers disagree
+ // (what we do now is relatively similar to legacy Blink, though). Should we
+ // split a margin in cases where it helps prevent fragmentainer overflow?
+ // Should we always split them if they occur at fragmentainer boundaries? Or
+ // even allow them to collapse with the fragmentainer boundary? Exact
+ // behavior is currently unspecified.
+ if (fragmentainer_margin_edge_block_offset > LayoutUnit()) {
+ LayoutUnit fragmentainer_block_offset =
+ fragmentainer_margin_edge_block_offset + fragment_margins.block_start;
+ if (!MovePastBreakpoint(parent_space, node, *layout_result,
+ fragmentainer_block_offset, kBreakAppealPerfect,
+ /* builder */ nullptr)) {
+ need_break_before = true;
+ } else if (layout_result->PhysicalFragment().BreakToken()) {
+ // We need to resume in the next fragmentainer, which means that
+ // there'll be no block-end margin here.
+ fragment_margins.block_end = LayoutUnit();
+ }
+ }
}
NGFragment float_fragment(parent_space.GetWritingMode(),
@@ -300,13 +332,25 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
}
// Add the float as an exclusion.
- scoped_refptr<const NGExclusion> exclusion =
- CreateExclusion(float_fragment, float_margin_bfc_offset, fragment_margins,
- *unpositioned_float,
- unpositioned_float->IsLineRight(parent_space.Direction())
- ? EFloat::kRight
- : EFloat::kLeft);
- exclusion_space->Add(std::move(exclusion));
+ if (need_break_before) {
+ // Create a special exclusion past everything. This will prevent us from
+ // adding any more floats in this formatting context to the current
+ // fragmentainer, and also make clearance behave correctly (e.g. an in-flow
+ // block with clear:left after a float:left that got pushed to the next
+ // fragmentainer means that the in-flow block also needs to be pushed, while
+ // if the in-flow block has clear:right, it may still be allowed in the
+ // current fragmentainer).
+ NGBfcOffset past_everything(LayoutUnit(), LayoutUnit::Max());
+ scoped_refptr<const NGExclusion> exclusion =
+ NGExclusion::Create(NGBfcRect(past_everything, past_everything),
+ node.Style().Floating(parent_space.Direction()));
+ exclusion_space->Add(std::move(exclusion));
+ } else {
+ scoped_refptr<const NGExclusion> exclusion = CreateExclusion(
+ float_fragment, float_margin_bfc_offset, fragment_margins,
+ *unpositioned_float, node.Style().Floating(parent_space.Direction()));
+ exclusion_space->Add(std::move(exclusion));
+ }
// Adjust the float's bfc_offset to its border-box (instead of margin-box).
NGBfcOffset float_bfc_offset(
@@ -314,7 +358,8 @@ NGPositionedFloat PositionFloat(NGUnpositionedFloat* unpositioned_float,
fragment_margins.LineLeft(parent_space.Direction()),
float_margin_bfc_offset.block_offset + fragment_margins.block_start);
- return NGPositionedFloat(std::move(layout_result), float_bfc_offset);
+ return NGPositionedFloat(std::move(layout_result), float_bfc_offset,
+ need_break_before);
}
} // namespace blink
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..221149e728d 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
@@ -11,8 +11,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/text/text_direction.h"
-#include "third_party/blink/renderer/platform/text/writing_mode.h"
+#include "third_party/blink/renderer/platform/text/writing_direction_mode.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
@@ -37,8 +36,13 @@ class CORE_EXPORT NGFragmentBuilder {
style_variant_ = style_variant;
}
- WritingMode GetWritingMode() const { return writing_mode_; }
- TextDirection Direction() const { return direction_; }
+ WritingDirectionMode GetWritingDirection() const {
+ return writing_direction_;
+ }
+ WritingMode GetWritingMode() const {
+ return writing_direction_.GetWritingMode();
+ }
+ TextDirection Direction() const { return writing_direction_.Direction(); }
LayoutUnit InlineSize() const { return size_.inline_size; }
LayoutUnit BlockSize() const { return size_.block_size; }
@@ -51,30 +55,26 @@ class CORE_EXPORT NGFragmentBuilder {
protected:
NGFragmentBuilder(scoped_refptr<const ComputedStyle> style,
- WritingMode writing_mode,
- TextDirection direction)
+ WritingDirectionMode writing_direction)
: style_(std::move(style)),
- writing_mode_(writing_mode),
- direction_(direction),
+ writing_direction_(writing_direction),
style_variant_(NGStyleVariant::kStandard) {
DCHECK(style_);
}
- NGFragmentBuilder(WritingMode writing_mode, TextDirection direction)
- : writing_mode_(writing_mode), direction_(direction) {}
+ explicit NGFragmentBuilder(WritingDirectionMode writing_direction)
+ : writing_direction_(writing_direction) {}
NGFragmentBuilder(const NGPhysicalFragment& fragment)
: style_(&fragment.Style()),
- writing_mode_(style_->GetWritingMode()),
- direction_(style_->Direction()),
+ writing_direction_(style_->GetWritingDirection()),
style_variant_(fragment.StyleVariant()),
- size_(fragment.Size().ConvertToLogical(writing_mode_)),
+ size_(fragment.Size().ConvertToLogical(GetWritingMode())),
layout_object_(fragment.GetMutableLayoutObject()),
is_hidden_for_paint_(fragment.IsHiddenForPaint()) {}
protected:
scoped_refptr<const ComputedStyle> style_;
- WritingMode writing_mode_;
- TextDirection direction_;
+ WritingDirectionMode writing_direction_;
NGStyleVariant style_variant_;
LogicalSize size_;
LayoutObject* layout_object_ = nullptr;
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
index 6275cba58ee..c3d6fe3245f 100644
--- 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
@@ -97,7 +97,7 @@ void NGFragmentChildIterator::UpdateSelfFromFragment(
current_.link_.fragment->GetLayoutObject());
current_.break_token_for_fragmentainer_only_ = false;
} else if (is_fragmentation_context_root_ && previous_fragment) {
- if (previous_fragment->IsColumnBox()) {
+ if (previous_fragment->IsFragmentainerBox()) {
// 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
@@ -112,8 +112,10 @@ void NGFragmentChildIterator::UpdateSelfFromFragment(
// rendered legend. 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.
+ // TODO(almaher): Remove check for out of flow.
DCHECK(
previous_fragment->IsRenderedLegend() ||
+ previous_fragment->IsOutOfFlowPositioned() ||
NGBlockNode(ToLayoutBox(previous_fragment->GetMutableLayoutObject()))
.IsColumnSpanAll());
@@ -129,28 +131,7 @@ void NGFragmentChildIterator::UpdateSelfFromFragment(
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();
- }
+ current_.cursor_->MoveToNextSkippingChildren();
UpdateSelfFromCursor();
if (current_.cursor_->CurrentItem())
return true;
@@ -175,25 +156,6 @@ void NGFragmentChildIterator::UpdateSelfFromCursor() {
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() {
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
index c4927f0acad..5f295d91867 100644
--- 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
@@ -89,7 +89,7 @@ class CORE_EXPORT NGFragmentChildIterator {
// 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())
+ !link_.fragment->IsFragmentainerBox())
return nullptr;
}
return block_break_token_;
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
index a0ddc40690f..db9a91cc8b3 100644
--- 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
@@ -140,7 +140,7 @@ 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="inner_multicol" style="columns:2; column-fill:auto;">
<div id="child1" style="width:11px; height:350px;"></div>
<div id="child2" style="width:22px; height:350px;"></div>
</div>
@@ -193,5 +193,54 @@ TEST_F(NGFragmentationTest, MultipleFragmentsNestedMulticol) {
EXPECT_EQ(child2->GetPhysicalFragment(3)->Size(), PhysicalSize(22, 100));
}
+TEST_F(NGFragmentationTest, HasSeenAllChildrenIfc) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="container">
+ <div style="columns:3; column-fill:auto; height:50px; line-height:20px; orphans:1; widows:1;">
+ <div id="ifc" style="height:300px;">
+ <br><br>
+ <br><br>
+ <br><br>
+ <br>
+ </div>
+ </div>
+ </div>
+ )HTML");
+
+ RunBlockLayoutAlgorithm(GetElementById("container"));
+
+ const LayoutBox* ifc = ToLayoutBox(GetLayoutObjectByElementId("ifc"));
+ ASSERT_EQ(ifc->PhysicalFragmentCount(), 6u);
+ const NGPhysicalBoxFragment* fragment = ifc->GetPhysicalFragment(0);
+ const NGBlockBreakToken* break_token =
+ DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ ASSERT_TRUE(break_token);
+ EXPECT_FALSE(break_token->HasSeenAllChildren());
+
+ fragment = ifc->GetPhysicalFragment(1);
+ break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ ASSERT_TRUE(break_token);
+ EXPECT_FALSE(break_token->HasSeenAllChildren());
+
+ fragment = ifc->GetPhysicalFragment(2);
+ break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ ASSERT_TRUE(break_token);
+ EXPECT_FALSE(break_token->HasSeenAllChildren());
+
+ fragment = ifc->GetPhysicalFragment(3);
+ break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ ASSERT_TRUE(break_token);
+ EXPECT_TRUE(break_token->HasSeenAllChildren());
+
+ fragment = ifc->GetPhysicalFragment(4);
+ break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ ASSERT_TRUE(break_token);
+ EXPECT_TRUE(break_token->HasSeenAllChildren());
+
+ fragment = ifc->GetPhysicalFragment(5);
+ break_token = DynamicTo<NGBlockBreakToken>(fragment->BreakToken());
+ EXPECT_FALSE(break_token);
+}
+
} // 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 abc1aa6fc79..92343c933f1 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
@@ -10,6 +10,7 @@
#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_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -209,30 +210,101 @@ void SetupFragmentBuilderForFragmentation(
builder->SetSequenceNumber(sequence_number);
}
-void FinishFragmentation(const NGConstraintSpace& space,
+bool IsNodeFullyGrown(NGBlockNode node,
+ const NGConstraintSpace& space,
+ LayoutUnit current_total_block_size,
+ const NGBoxStrut& border_padding,
+ LayoutUnit inline_size) {
+ // Pass an "infinite" intrinsic size to see how the block-size is
+ // constrained. If it doesn't affect the block size, it means that the node
+ // cannot grow any further.
+ LayoutUnit max_block_size = ComputeBlockSizeForFragment(
+ space, node.Style(), border_padding, LayoutUnit::Max(), inline_size);
+ DCHECK_GE(max_block_size, current_total_block_size);
+ return max_block_size == current_total_block_size;
+}
+
+void FinishFragmentation(NGBlockNode node,
+ const NGConstraintSpace& space,
const NGBlockBreakToken* previous_break_token,
- LayoutUnit block_size,
- LayoutUnit intrinsic_block_size,
+ const NGBoxStrut& border_padding,
LayoutUnit space_left,
NGBoxFragmentBuilder* builder) {
LayoutUnit previously_consumed_block_size;
if (previous_break_token && !previous_break_token->IsBreakBefore())
previously_consumed_block_size = previous_break_token->ConsumedBlockSize();
- if (builder->DidBreak()) {
- // One of our children broke. Even if we fit within the remaining space, we
- // need to prepare a break token.
- builder->SetConsumedBlockSize(std::min(space_left, block_size) +
- previously_consumed_block_size);
- builder->SetBlockSize(std::min(space_left, block_size));
- builder->SetIntrinsicBlockSize(space_left);
+ LayoutUnit fragments_total_block_size = builder->FragmentsTotalBlockSize();
+ LayoutUnit wanted_block_size =
+ fragments_total_block_size - previously_consumed_block_size;
+ DCHECK_GE(wanted_block_size, LayoutUnit());
+
+ LayoutUnit final_block_size = wanted_block_size;
+ if (space_left != kIndefiniteSize)
+ final_block_size = std::min(final_block_size, space_left);
+ builder->SetConsumedBlockSize(previously_consumed_block_size +
+ final_block_size);
+ builder->SetFragmentBlockSize(final_block_size);
+
+ if (space_left == kIndefiniteSize) {
+ // We don't know how space is available (initial column balancing pass), so
+ // we won't break.
+ builder->SetIsAtBlockEnd();
return;
}
- if (block_size > space_left) {
- // Need a break inside this block.
- builder->SetConsumedBlockSize(space_left + previously_consumed_block_size);
- builder->SetDidBreak();
+ if (builder->HasChildBreakInside()) {
+ // We broke before or inside one of our children. Even if we fit within the
+ // remaining space, and even if the child involved in the break were to be
+ // in a parallel flow, we still need to prepare a break token for this node,
+ // so that we can resume layout of its broken or unstarted children in the
+ // next fragmentainer.
+ //
+ // If we're at the end of the node, we need to mark the outgoing break token
+ // as such. This is a way for the parent algorithm to determine whether we
+ // need to insert a break there, or whether we may continue with any sibling
+ // content. If we are allowed to continue, while there's still child content
+ // left to be laid out, said content ends up in a parallel flow.
+ // https://www.w3.org/TR/css-break-3/#parallel-flows
+ //
+ // TODO(mstensho): The spec actually says that we enter a parallel flow once
+ // we're past the block-end *content edge*, but here we're checking against
+ // the *border edge* instead. Does it matter?
+ if (previous_break_token && previous_break_token->IsAtBlockEnd()) {
+ builder->SetIsAtBlockEnd();
+ // We entered layout already at the end of the block (but with overflowing
+ // children). So we should take up no more space on our own.
+ DCHECK_EQ(wanted_block_size, LayoutUnit());
+ } else if (wanted_block_size <= space_left) {
+ // We have room for the calculated block-size in the current
+ // fragmentainer, but we need to figure out whether this node is going to
+ // produce more non-zero block-size fragments or not.
+ //
+ // If the block-size is constrained / fixed (in which case
+ // IsNodeFullyGrown() will return true now), we know that we're at the
+ // end. If block-size is unconstrained (or at least allowed to grow a bit
+ // more), we're only at the end if no in-flow content inside broke.
+ if (!builder->HasInflowChildBreakInside() ||
+ IsNodeFullyGrown(node, space, fragments_total_block_size,
+ border_padding,
+ builder->InitialBorderBoxSize().inline_size))
+ builder->SetIsAtBlockEnd();
+
+ // If we're going to break just because of floats or out-of-flow child
+ // breaks, no break appeal will have been recorded so far, since we only
+ // update the appeal at same-flow breakpoints, and since we start off by
+ // assuming the lowest appeal, upgrade it now. There's nothing here that
+ // makes breaking inside less appealing than perfect.
+ if (!builder->HasInflowChildBreakInside())
+ builder->SetBreakAppeal(kBreakAppealPerfect);
+ }
+ return;
+ }
+
+ if (wanted_block_size > space_left) {
+ // No child inside broke, but we need a break inside this block anyway, due
+ // to its size.
+ builder->SetDidBreakSelf();
NGBreakAppeal break_appeal = kBreakAppealPerfect;
if (!previously_consumed_block_size) {
// This is the first fragment generated for the node. Avoid breaking
@@ -246,18 +318,14 @@ void FinishFragmentation(const NGConstraintSpace& space,
break_appeal = kBreakAppealLastResort;
}
builder->SetBreakAppeal(break_appeal);
- builder->SetBlockSize(space_left);
- builder->SetIntrinsicBlockSize(space_left);
if (space.BlockFragmentationType() == kFragmentColumn &&
!space.IsInitialColumnBalancingPass())
- builder->PropagateSpaceShortage(block_size - space_left);
+ builder->PropagateSpaceShortage(wanted_block_size - space_left);
return;
}
// The end of the block fits in the current fragmentainer.
- builder->SetConsumedBlockSize(previously_consumed_block_size + block_size);
- builder->SetBlockSize(block_size);
- builder->SetIntrinsicBlockSize(intrinsic_block_size);
+ builder->SetIsAtBlockEnd();
}
NGBreakStatus BreakBeforeChildIfNeeded(const NGConstraintSpace& space,
@@ -386,7 +454,7 @@ bool MovePastBreakpoint(const NGConstraintSpace& space,
NGFragment fragment(space.GetWritingMode(), physical_fragment);
if (!space.HasKnownFragmentainerBlockSize()) {
- if (space.IsInitialColumnBalancingPass()) {
+ if (space.IsInitialColumnBalancingPass() && builder) {
if (child.IsMonolithic() ||
(child.IsBlock() &&
IsAvoidBreakValue(space, child.Style().BreakInside()))) {
@@ -408,13 +476,13 @@ bool MovePastBreakpoint(const NGConstraintSpace& space,
// If we haven't used any space at all in the fragmentainer yet, we cannot
// break before this child, or there'd be no progress. We'd risk creating an
// infinite number of fragmentainers without putting any content into them.
- bool refuse_break = space_left >= space.FragmentainerBlockSize();
+ bool refuse_break_before = space_left >= space.FragmentainerBlockSize();
// If the child starts past the end of the fragmentainer (probably due to a
// block-start margin), we must break before it.
bool must_break_before = space_left < LayoutUnit();
if (must_break_before) {
- DCHECK(!refuse_break);
+ DCHECK(!refuse_break_before);
return false;
}
@@ -426,15 +494,20 @@ bool MovePastBreakpoint(const NGConstraintSpace& space,
// Allow breaking inside if it has the same appeal or higher than breaking
// before or breaking earlier. Also, if breaking before is impossible, break
// inside regardless of appeal.
- if (refuse_break || (appeal_inside >= appeal_before &&
- (!builder->HasEarlyBreak() ||
- appeal_inside >= builder->BreakAppeal()))) {
- builder->SetBreakAppeal(appeal_inside);
+ bool want_break_inside = refuse_break_before;
+ if (!want_break_inside && appeal_inside >= appeal_before) {
+ if (!builder || !builder->HasEarlyBreak() ||
+ appeal_inside >= builder->BreakAppeal())
+ want_break_inside = true;
+ }
+ if (want_break_inside) {
+ if (builder)
+ builder->SetBreakAppeal(appeal_inside);
return true;
}
} else {
bool need_break;
- if (refuse_break) {
+ if (refuse_break_before) {
need_break = false;
} else if (child.IsMonolithic()) {
// If the monolithic piece of content (e.g. a line, or block-level
@@ -451,7 +524,7 @@ bool MovePastBreakpoint(const NGConstraintSpace& space,
}
if (!need_break) {
- if (child.IsBlock()) {
+ if (child.IsBlock() && builder) {
// If this doesn't happen, though, we're tentatively not going to break
// before or inside this child, but we'll check the appeal of breaking
// there anyway. It may be the best breakpoint we'll ever find. (Note
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 b12b66333ff..23c52f2d447 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
@@ -70,8 +70,11 @@ NGBreakAppeal CalculateBreakAppealInside(const NGConstraintSpace& space,
// start of the current block formatting context. Note that if the start of the
// current block formatting context is in a previous fragmentainer, the size of
// the current fragmentainer is returned instead.
+// In the case of initial column balancing, the size is unknown, in which case
+// kIndefiniteSize is returned.
inline LayoutUnit FragmentainerSpaceAtBfcStart(const NGConstraintSpace& space) {
- DCHECK(space.HasKnownFragmentainerBlockSize());
+ if (!space.HasKnownFragmentainerBlockSize())
+ return kIndefiniteSize;
return space.FragmentainerBlockSize() - space.FragmentainerOffsetAtBfc();
}
@@ -111,11 +114,27 @@ inline void SetupFragmentBuilderForFragmentation(const NGConstraintSpace&,
const NGInlineBreakToken*,
NGLineBoxFragmentBuilder*) {}
-// Write fragmentation information to the fragment builder after layout.
-void FinishFragmentation(const NGConstraintSpace&,
+// Return true if the node is fully grown at its current size.
+// |current_total_block_size| is the total block-size of the node, as if all
+// fragments were stitched together.
+bool IsNodeFullyGrown(NGBlockNode,
+ const NGConstraintSpace&,
+ LayoutUnit current_total_block_size,
+ const NGBoxStrut& border_padding,
+ LayoutUnit inline_size);
+
+// Update and write fragmentation information to the fragment builder after
+// layout. This will update the block-size stored in the builder. When
+// calculating the block-size, a layout algorithm will include the accumulated
+// block-size of all fragments generated for this node - as if they were all
+// stitched together as one tall fragment. This is the most convenient thing to
+// do, since any block-size specified in CSS applies to the entire box,
+// regardless of fragmentation. This function will update the block-size to the
+// actual fragment size, by examining possible breakpoints, if necessary.
+void FinishFragmentation(NGBlockNode node,
+ const NGConstraintSpace&,
const NGBlockBreakToken* previous_break_token,
- LayoutUnit block_size,
- LayoutUnit intrinsic_block_size,
+ const NGBoxStrut& border_padding,
LayoutUnit space_left,
NGBoxFragmentBuilder*);
@@ -225,6 +244,19 @@ bool AttemptSoftBreak(const NGConstraintSpace&,
NGBreakAppeal appeal_before,
NGBoxFragmentBuilder*);
+// Return the adjusted child margin to be applied at the end of a fragment.
+// Margins should collapse with the fragmentainer boundary. |bfc_block_offset|
+// is the BFC offset where the margin should be applied (i.e. after the
+// block-end border edge of the last child fragment).
+inline LayoutUnit AdjustedMarginAfterFinalChildFragment(
+ const NGConstraintSpace& space,
+ LayoutUnit bfc_block_offset,
+ LayoutUnit block_end_margin) {
+ LayoutUnit space_left =
+ FragmentainerSpaceAtBfcStart(space) - bfc_block_offset;
+ return std::min(block_end_margin, space_left.ClampNegativeToZero());
+}
+
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENTATION_UTILS_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.cc
deleted file mode 100644
index ee14539bc1c..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_grid_layout_algorithm.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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_grid_layout_algorithm.h"
-
-namespace blink {
-
-NGGridLayoutAlgorithm::NGGridLayoutAlgorithm(
- const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params) {
- DCHECK(params.space.IsNewFormattingContext());
- DCHECK(!params.break_token);
- container_builder_.SetIsNewFormattingContext(true);
- container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
-}
-
-scoped_refptr<const NGLayoutResult> NGGridLayoutAlgorithm::Layout() {
- return container_builder_.ToBoxFragment();
-}
-
-MinMaxSizesResult NGGridLayoutAlgorithm::ComputeMinMaxSizes(
- const MinMaxSizesInput& input) const {
- return {MinMaxSizes(), /* depends_on_percentage_block_size */ true};
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
index e5ed5b152e8..a3b4b610629 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_ink_overflow.h"
#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -58,7 +59,8 @@ void NGInkOverflow::ComputeTextInkOverflow(
}
PhysicalRect local_ink_overflow =
- LogicalRect(ink_overflow).ConvertToPhysical(writing_mode, size);
+ WritingModeConverter({writing_mode, TextDirection::kLtr}, size)
+ .ToPhysical(LogicalRect(ink_overflow));
// Uniting the frame rect ensures that non-ink spaces such side bearings, or
// even space characters, are included in the visual rect for decorations.
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 90c7bd7b3f0..2eaf870c75f 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
@@ -77,8 +77,7 @@ class CORE_EXPORT NGLayoutAlgorithm : public NGLayoutAlgorithmOperations {
container_builder_(node,
style,
&space,
- space.GetWritingMode(),
- direction) {
+ {space.GetWritingMode(), direction}) {
if (UNLIKELY(space.HasBlockFragmentation())) {
DCHECK(space.IsAnonymous() || !node.IsMonolithic());
SetupFragmentBuilderForFragmentation(space, BreakToken(),
@@ -113,6 +112,16 @@ class CORE_EXPORT NGLayoutAlgorithm : public NGLayoutAlgorithmOperations {
const NGBreakTokenType* BreakToken() const { return break_token_.get(); }
+ const NGBoxStrut& BorderPadding() const {
+ return container_builder_.BorderPadding();
+ }
+ const NGBoxStrut& BorderScrollbarPadding() const {
+ return container_builder_.BorderScrollbarPadding();
+ }
+ const LogicalSize& ChildAvailableSize() const {
+ return container_builder_.ChildAvailableSize();
+ }
+
NGInputNodeType node_;
// The break token from which we are currently resuming layout.
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 fe3212c1b9c..380dc1b5c3a 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
@@ -63,7 +63,7 @@ void AppendNodeToString(NGLayoutInputNode node,
MinMaxSizesResult NGLayoutInputNode::ComputeMinMaxSizes(
WritingMode writing_mode,
const MinMaxSizesInput& input,
- const NGConstraintSpace* space) {
+ const NGConstraintSpace* space) const {
if (auto* inline_node = DynamicTo<NGInlineNode>(this))
return inline_node->ComputeMinMaxSizes(writing_mode, input, space);
return To<NGBlockNode>(*this).ComputeMinMaxSizes(writing_mode, input, space);
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 59c09f148d1..26ce67d1fc4 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
@@ -27,6 +27,11 @@ class NGPaintFragment;
struct MinMaxSizes;
struct PhysicalSize;
+// min/max-content take the CSS aspect-ratio property into account.
+// In some cases that's undesirable; this enum lets you choose not
+// to do that using |kIntrinsic|.
+enum class MinMaxSizesType { kContent, kIntrinsic };
+
// The 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.
@@ -41,11 +46,15 @@ struct MinMaxSizesInput {
//
// 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 MinMaxSizesInput(LayoutUnit percentage_resolution_block_size)
- : percentage_resolution_block_size(percentage_resolution_block_size) {}
+ MinMaxSizesInput(LayoutUnit percentage_resolution_block_size,
+ MinMaxSizesType type)
+ : percentage_resolution_block_size(percentage_resolution_block_size),
+ type(type) {}
LayoutUnit float_left_inline_size;
LayoutUnit float_right_inline_size;
LayoutUnit percentage_resolution_block_size;
+
+ MinMaxSizesType type;
};
// The output of the min/max inline size calculation algorithm. Contains the
@@ -92,6 +101,9 @@ class CORE_EXPORT NGLayoutInputNode {
bool IsOutOfFlowPositioned() const {
return IsBlock() && box_->IsOutOfFlowPositioned();
}
+ bool IsFloatingOrOutOfFlowPositioned() const {
+ return IsFloating() || IsOutOfFlowPositioned();
+ }
bool IsReplaced() const { return box_->IsLayoutReplaced(); }
bool IsAbsoluteContainer() const {
return box_->CanContainAbsolutePositionObjects();
@@ -120,7 +132,7 @@ class CORE_EXPORT NGLayoutInputNode {
return IsBlock() && box_->IsLayoutNGFieldset();
}
bool IsRubyRun() const { return IsBlock() && box_->IsRubyRun(); }
- bool IsRubyText() const { return IsBlock() && box_->IsRubyText(); }
+ bool IsRubyText() const { return box_->IsRubyText(); }
// Return true if this is the legend child of a fieldset that gets special
// treatment (i.e. placed over the block-start border).
@@ -129,6 +141,8 @@ class CORE_EXPORT NGLayoutInputNode {
}
bool IsTable() const { return IsBlock() && box_->IsTable(); }
+ bool IsTableCaption() const { return IsBlock() && box_->IsTableCaption(); }
+
bool IsMathRoot() const { return box_->IsMathMLRoot(); }
bool IsAnonymousBlock() const { return box_->IsAnonymousBlock(); }
@@ -166,9 +180,10 @@ class CORE_EXPORT NGLayoutInputNode {
}
// Returns the border-box min/max content sizes for the node.
- MinMaxSizesResult ComputeMinMaxSizes(WritingMode,
- const MinMaxSizesInput&,
- const NGConstraintSpace* = nullptr);
+ MinMaxSizesResult ComputeMinMaxSizes(
+ WritingMode,
+ const MinMaxSizesInput&,
+ const NGConstraintSpace* = nullptr) const;
// Returns intrinsic sizing information for replaced elements.
// ComputeReplacedSize can use it to compute actual replaced size.
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 6f3e2393746..337bfb89d45 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
@@ -49,24 +49,6 @@ 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);
-#endif
- EnsureRareData()->minimal_space_shortage = builder->minimal_space_shortage_;
- }
- if (builder->tallest_unbreakable_block_size_ >= LayoutUnit()) {
- auto* rare_data = EnsureRareData();
- rare_data->tallest_unbreakable_block_size =
- builder->tallest_unbreakable_block_size_;
-#if DCHECK_IS_ON()
- rare_data->has_tallest_unbreakable_block_size = true;
-#endif
- }
if (builder->overflow_block_size_ != kIndefiniteSize &&
builder->overflow_block_size_ != intrinsic_block_size_) {
EnsureRareData()->overflow_block_size = builder->overflow_block_size_;
@@ -75,15 +57,45 @@ NGLayoutResult::NGLayoutResult(
EnsureRareData()->custom_layout_data =
std::move(builder->custom_layout_data_);
}
- 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 =
- static_cast<unsigned>(builder->previous_break_after_);
- bitfields_.has_forced_break = builder->has_forced_break_;
+ if (builder->annotation_overflow_)
+ EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
+ if (builder->block_end_annotation_space_) {
+ EnsureRareData()->block_end_annotation_space =
+ builder->block_end_annotation_space_;
+ }
+
+ if (builder->has_block_fragmentation_) {
+ RareData* rare_data = EnsureRareData();
+
+ // We don't support fragment caching when block-fragmenting, so mark the
+ // result as non-reusable.
+ rare_data->is_single_use = true;
+
+ if (builder->tallest_unbreakable_block_size_ >= LayoutUnit()) {
+ rare_data->tallest_unbreakable_block_size =
+ builder->tallest_unbreakable_block_size_;
+#if DCHECK_IS_ON()
+ rare_data->has_tallest_unbreakable_block_size = true;
+#endif
+ }
+ if (builder->minimal_space_shortage_ != LayoutUnit::Max()) {
+#if DCHECK_IS_ON()
+ DCHECK(!rare_data->has_tallest_unbreakable_block_size);
+#endif
+ rare_data->minimal_space_shortage = builder->minimal_space_shortage_;
+ }
+
+ if (builder->column_spanner_)
+ rare_data->column_spanner = builder->column_spanner_;
+
+ bitfields_.initial_break_before =
+ static_cast<unsigned>(builder->initial_break_before_);
+ bitfields_.final_break_after =
+ static_cast<unsigned>(builder->previous_break_after_);
+ bitfields_.has_forced_break = builder->has_forced_break_;
+ }
}
NGLayoutResult::NGLayoutResult(
@@ -174,6 +186,12 @@ NGLayoutResult::NGLayoutResult(
if (builder->end_margin_strut_ != NGMarginStrut())
EnsureRareData()->end_margin_strut = builder->end_margin_strut_;
+ if (builder->annotation_overflow_ > LayoutUnit())
+ EnsureRareData()->annotation_overflow = builder->annotation_overflow_;
+ if (builder->block_end_annotation_space_) {
+ EnsureRareData()->block_end_annotation_space =
+ builder->block_end_annotation_space_;
+ }
if (builder->unpositioned_list_marker_) {
EnsureRareData()->unpositioned_list_marker =
builder->unpositioned_list_marker_;
@@ -294,4 +312,12 @@ void NGLayoutResult::CheckSameForSimplifiedLayout(
}
#endif
+#if DCHECK_IS_ON()
+void NGLayoutResult::AssertSoleBoxFragment() const {
+ DCHECK(physical_fragment_->IsBox());
+ DCHECK(To<NGPhysicalBoxFragment>(PhysicalFragment()).IsFirstForNode());
+ DCHECK(!physical_fragment_->BreakToken());
+}
+#endif
+
} // namespace blink
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 5d15b09f808..4674ca56060 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
@@ -68,6 +68,24 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
return HasRareData() ? rare_data_->lines_until_clamp : 0;
}
+ // How much an annotation box overflow from this box.
+ // This is for LayoutNGRubyRun and line boxes.
+ // 0 : No overflow
+ // -N : Overflowing by N px at block-start side
+ // This happens only for LayoutRubyRun.
+ // N : Overflowing by N px at block-end side
+ LayoutUnit AnnotationOverflow() const {
+ return HasRareData() ? rare_data_->annotation_overflow : LayoutUnit();
+ }
+
+ // The amount of available space for block-start side annotations of the
+ // next box.
+ // This never be negative.
+ LayoutUnit BlockEndAnnotationSpace() const {
+ return HasRareData() ? rare_data_->block_end_annotation_space
+ : LayoutUnit();
+ }
+
LogicalOffset OutOfFlowPositionedOffset() const {
DCHECK(bitfields_.has_oof_positioned_offset);
return HasRareData() ? rare_data_->oof_positioned_offset
@@ -145,8 +163,18 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
return HasRareData() ? rare_data_->end_margin_strut : NGMarginStrut();
}
+ // Get the intrinsic block-size of the fragment (i.e. the block-size the
+ // fragment would get if no block-size constraints were applied). This is not
+ // supported (and should not be needed [1]) if the node got split into
+ // multiple fragments.
+ //
+ // [1] If a node gets block-fragmented, it means that it has possibly been
+ // constrained and/or stretched by something extrinsic (i.e. the
+ // fragmentainer), so the value returned here wouldn't be useful.
const LayoutUnit IntrinsicBlockSize() const {
- DCHECK(physical_fragment_->IsBox());
+#if DCHECK_IS_ON()
+ AssertSoleBoxFragment();
+#endif
return intrinsic_block_size_;
}
@@ -373,6 +401,8 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
NGExclusionSpace exclusion_space;
scoped_refptr<SerializedScriptValue> custom_layout_data;
LayoutUnit overflow_block_size = kIndefiniteSize;
+ LayoutUnit annotation_overflow;
+ LayoutUnit block_end_annotation_space;
#if DCHECK_IS_ON()
bool has_tallest_unbreakable_block_size = false;
#endif
@@ -383,6 +413,10 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
bool HasRareData() const { return bitfields_.has_rare_data; }
RareData* EnsureRareData();
+#if DCHECK_IS_ON()
+ void AssertSoleBoxFragment() const;
+#endif
+
struct Bitfields {
DISALLOW_NEW();
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 db86d7af448..1a629ac1261 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
@@ -212,14 +212,17 @@ NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatusWithGeometry(
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.
+ // The intrinsic size of flex-boxes can depend on the %-block-size. This
+ // occurs when:
+ // - A column flex-box has "max-height: 100%" (or similar) on itself.
+ // - A row flex-box has "height: 100%" (or similar) and children which
+ // stretch to this size.
//
// 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()) {
+ // TODO(dgrogan): We can hit the cache here for row flexboxes when they
+ // don't have stretchy children.
+ if (layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) {
if (new_space.PercentageResolutionBlockSize() !=
old_space.PercentageResolutionBlockSize())
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 25629c7019c..261ed63c908 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
@@ -72,7 +72,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.IsNone())
+ length.IsMinIntrinsic() || length.IsFitContent() || length.IsNone())
return true;
if (length.IsPercentOrCalc()) {
if (phase == LengthResolvePhase::kIntrinsic)
@@ -127,11 +127,12 @@ LayoutUnit ResolveInlineLengthInternal(
}
case Length::kMinContent:
case Length::kMaxContent:
+ case Length::kMinIntrinsic:
case Length::kFitContent: {
DCHECK(min_max_sizes.has_value());
LayoutUnit available_size = constraint_space.AvailableSize().inline_size;
LayoutUnit value;
- if (length.IsMinContent()) {
+ if (length.IsMinContent() || length.IsMinIntrinsic()) {
value = min_max_sizes->min_size;
} else if (length.IsMaxContent() || available_size == LayoutUnit::Max()) {
// If the available space is infinite, fit-content resolves to
@@ -200,6 +201,7 @@ LayoutUnit ResolveBlockLengthInternal(
case Length::kAuto:
case Length::kMinContent:
case Length::kMaxContent:
+ case Length::kMinIntrinsic:
case Length::kFitContent:
#if DCHECK_IS_ON()
// Due to how content_size is calculated, it should always include border
@@ -270,7 +272,7 @@ MinMaxSizesResult ComputeMinAndMaxContentContributionInternal(
: style.Height();
if (inline_size.IsAuto() || inline_size.IsPercentOrCalc() ||
inline_size.IsFillAvailable() || inline_size.IsFitContent()) {
- result = min_max_sizes_func();
+ result = min_max_sizes_func(MinMaxSizesType::kContent);
} else {
if (IsParallelWritingMode(parent_writing_mode, child_writing_mode)) {
MinMaxSizes sizes;
@@ -279,7 +281,10 @@ MinMaxSizesResult ComputeMinAndMaxContentContributionInternal(
result = {sizes, /* depends_on_percentage_block_size */ false};
} else {
auto IntrinsicBlockSizeFunc = [&]() -> LayoutUnit {
- return min_max_sizes_func().sizes.max_size;
+ return min_max_sizes_func(inline_size.IsMinIntrinsic()
+ ? MinMaxSizesType::kIntrinsic
+ : MinMaxSizesType::kContent)
+ .sizes.max_size;
};
MinMaxSizes sizes;
sizes = ResolveMainBlockLength(space, style, border_padding, inline_size,
@@ -326,7 +331,7 @@ MinMaxSizes ComputeMinAndMaxContentContributionForTest(
WritingMode parent_writing_mode,
const ComputedStyle& style,
const MinMaxSizes& min_max_sizes) {
- auto MinMaxSizesFunc = [&]() -> MinMaxSizesResult {
+ auto MinMaxSizesFunc = [&](MinMaxSizesType) -> MinMaxSizesResult {
return {min_max_sizes, false};
};
return ComputeMinAndMaxContentContributionInternal(parent_writing_mode, style,
@@ -374,17 +379,19 @@ MinMaxSizesResult ComputeMinAndMaxContentContribution(
}
}
- auto MinMaxSizesFunc = [&]() -> MinMaxSizesResult {
+ auto MinMaxSizesFunc = [&](MinMaxSizesType type) -> MinMaxSizesResult {
+ MinMaxSizesInput input_copy(input);
+ input_copy.type = type;
// We need to set up a constraint space with correct fallback available
// inline-size in case of orthogonal children.
NGConstraintSpace indefinite_constraint_space;
const NGConstraintSpace* child_constraint_space = nullptr;
if (!IsParallelWritingMode(parent_writing_mode, child_writing_mode)) {
- indefinite_constraint_space =
- CreateIndefiniteConstraintSpaceForChild(parent_style, child);
+ indefinite_constraint_space = CreateIndefiniteConstraintSpaceForChild(
+ parent_style, input_copy, child);
child_constraint_space = &indefinite_constraint_space;
}
- return child.ComputeMinMaxSizes(parent_writing_mode, input,
+ return child.ComputeMinMaxSizes(parent_writing_mode, input_copy,
child_constraint_space);
};
@@ -418,13 +425,12 @@ LayoutUnit ComputeInlineSizeForFragment(
const ComputedStyle& style = node.Style();
Length logical_width = style.LogicalWidth();
- auto MinMaxSizesFunc = [&]() -> MinMaxSizesResult {
+ auto MinMaxSizesFunc = [&](MinMaxSizesType type) -> MinMaxSizesResult {
if (override_min_max_sizes_for_test)
return {*override_min_max_sizes_for_test, false};
- return node.ComputeMinMaxSizes(
- space.GetWritingMode(),
- MinMaxSizesInput(space.PercentageResolutionBlockSize()), &space);
+ MinMaxSizesInput input(space.PercentageResolutionBlockSize(), type);
+ return node.ComputeMinMaxSizes(space.GetWritingMode(), input, &space);
};
Length min_length = style.LogicalMinWidth();
@@ -439,8 +445,9 @@ LayoutUnit ComputeInlineSizeForFragment(
// if we need to apply the implied minimum size:
// https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
if (style.OverflowInlineDirection() == EOverflow::kVisible &&
- min_length.IsAuto())
- min_length = Length::MinContent();
+ min_length.IsAuto()) {
+ min_length = Length::MinIntrinsic();
+ }
} else {
if (logical_width.IsAuto() && space.IsShrinkToFit())
logical_width = Length::FitContent();
@@ -498,8 +505,7 @@ LayoutUnit ComputeBlockSizeForFragmentInternal(
LengthResolvePhase::kLayout,
opt_percentage_resolution_block_size_for_min_max);
if (UNLIKELY((extent == kIndefiniteSize || logical_height.IsAuto()) &&
- style.LogicalAspectRatio() && inline_size &&
- !style.LogicalWidth().IsAuto())) {
+ style.LogicalAspectRatio() && inline_size)) {
extent =
BlockSizeFromAspectRatio(border_padding, *style.LogicalAspectRatio(),
style.BoxSizing(), *inline_size);
@@ -782,9 +788,9 @@ LayoutUnit ResolveUsedColumnInlineSize(LayoutUnit available_size,
LayoutUnit ResolveUsedColumnGap(LayoutUnit available_size,
const ComputedStyle& style) {
- if (style.ColumnGap().IsNormal())
- return LayoutUnit(style.GetFontDescription().ComputedPixelSize());
- return ValueForLength(style.ColumnGap().GetLength(), available_size);
+ if (const base::Optional<Length>& column_gap = style.ColumnGap())
+ return ValueForLength(*column_gap, available_size);
+ return LayoutUnit(style.GetFontDescription().ComputedPixelSize());
}
NGPhysicalBoxStrut ComputePhysicalMargins(
@@ -916,10 +922,9 @@ NGBoxStrut ComputePadding(const NGConstraintSpace& constraint_space,
return padding;
}
-NGBoxStrut ComputeScrollbars(const NGConstraintSpace& constraint_space,
- const NGLayoutInputNode node) {
+NGBoxStrut ComputeScrollbarsForNonAnonymous(const NGBlockNode& node) {
const ComputedStyle& style = node.Style();
- if (constraint_space.IsAnonymous() || style.IsOverflowVisible())
+ if (style.IsOverflowVisible())
return NGBoxStrut();
NGPhysicalBoxStrut sizes;
const LayoutBox* layout_box = node.GetLayoutBox();
@@ -1103,19 +1108,33 @@ NGFragmentGeometry CalculateInitialMinMaxFragmentGeometry(
return {/* border_box_size */ LogicalSize(), border, scrollbar, padding};
}
-LogicalSize ShrinkAvailableSize(LogicalSize size, const NGBoxStrut& inset) {
- DCHECK_NE(size.inline_size, kIndefiniteSize);
- size.inline_size -= inset.InlineSum();
- size.inline_size = std::max(size.inline_size, LayoutUnit());
-
+LogicalSize ShrinkLogicalSize(LogicalSize size, const NGBoxStrut& insets) {
+ if (size.inline_size != kIndefiniteSize) {
+ size.inline_size =
+ (size.inline_size - insets.InlineSum()).ClampNegativeToZero();
+ }
if (size.block_size != kIndefiniteSize) {
- size.block_size -= inset.BlockSum();
- size.block_size = std::max(size.block_size, LayoutUnit());
+ size.block_size =
+ (size.block_size - insets.BlockSum()).ClampNegativeToZero();
}
return size;
}
+LogicalSize CalculateChildAvailableSize(
+ const NGConstraintSpace& space,
+ const NGBlockNode& node,
+ const LogicalSize border_box_size,
+ const NGBoxStrut& border_scrollbar_padding) {
+ LogicalSize child_available_size =
+ ShrinkLogicalSize(border_box_size, border_scrollbar_padding);
+
+ if (space.IsAnonymous() || node.IsAnonymousBlock())
+ child_available_size.block_size = space.AvailableSize().block_size;
+
+ return child_available_size;
+}
+
namespace {
// Implements the common part of the child percentage size calculation. Deals
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 ef8e775331d..d2a15b80bfd 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
@@ -91,8 +91,12 @@ inline LayoutUnit ResolveMinInlineLength(
return border_padding.InlineSum();
base::Optional<MinMaxSizes> min_max_sizes;
- if (length.IsIntrinsic())
- min_max_sizes = min_max_sizes_func().sizes;
+ if (length.IsIntrinsic()) {
+ min_max_sizes =
+ min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic
+ : MinMaxSizesType::kContent)
+ .sizes;
+ }
return ResolveInlineLengthInternal(constraint_space, style, border_padding,
min_max_sizes, length);
@@ -126,8 +130,12 @@ inline LayoutUnit ResolveMaxInlineLength(
return LayoutUnit::Max();
base::Optional<MinMaxSizes> min_max_sizes;
- if (length.IsIntrinsic())
- min_max_sizes = min_max_sizes_func().sizes;
+ if (length.IsIntrinsic()) {
+ min_max_sizes =
+ min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic
+ : MinMaxSizesType::kContent)
+ .sizes;
+ }
return ResolveInlineLengthInternal(constraint_space, style, border_padding,
min_max_sizes, length);
@@ -157,8 +165,12 @@ inline LayoutUnit ResolveMainInlineLength(
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().sizes;
+ if (length.IsIntrinsic()) {
+ min_max_sizes =
+ min_max_sizes_func(length.IsMinIntrinsic() ? MinMaxSizesType::kIntrinsic
+ : MinMaxSizesType::kContent)
+ .sizes;
+ }
return ResolveInlineLengthInternal(constraint_space, style, border_padding,
min_max_sizes, length);
@@ -451,8 +463,15 @@ inline NGLineBoxStrut ComputeLinePadding(
style.IsFlippedLinesWritingMode());
}
-CORE_EXPORT NGBoxStrut ComputeScrollbars(const NGConstraintSpace&,
- const NGLayoutInputNode);
+CORE_EXPORT NGBoxStrut ComputeScrollbarsForNonAnonymous(const NGBlockNode&);
+
+inline NGBoxStrut ComputeScrollbars(const NGConstraintSpace& space,
+ const NGBlockNode& node) {
+ if (space.IsAnonymous())
+ return NGBoxStrut();
+
+ return ComputeScrollbarsForNonAnonymous(node);
+}
// Return true if we need to know the inline size of the fragment in order to
// calculate its line-left offset. This is the case when we have auto margins,
@@ -500,10 +519,15 @@ CORE_EXPORT NGFragmentGeometry
CalculateInitialMinMaxFragmentGeometry(const NGConstraintSpace&,
const NGBlockNode&);
-// Shrink and return the available size by an inset. This may e.g. be used to
-// convert from border-box to content-box size. Indefinite block size is
-// allowed, in which case the inset will be ignored for block size.
-LogicalSize ShrinkAvailableSize(LogicalSize size, const NGBoxStrut& inset);
+// Shrinks the logical |size| by |insets|.
+LogicalSize ShrinkLogicalSize(LogicalSize size, const NGBoxStrut& insets);
+
+// Calculates the available size that children of the node should use.
+LogicalSize CalculateChildAvailableSize(
+ const NGConstraintSpace&,
+ const NGBlockNode& node,
+ const LogicalSize border_box_size,
+ const NGBoxStrut& border_scrollbar_padding);
// Calculates the percentage resolution size that children of the node should
// use.
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 4020a8fd3b7..74a8fcb12ec 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
@@ -19,6 +19,7 @@
#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_positioned_node.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
@@ -113,7 +114,7 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
default_containing_block_.direction = container_style.Direction();
default_containing_block_.content_size_for_absolute =
- ShrinkAvailableSize(container_builder_->Size(), border_scrollbar);
+ ShrinkLogicalSize(container_builder_->Size(), border_scrollbar);
default_containing_block_.content_size_for_fixed =
initial_containing_block_fixed_size
? *initial_containing_block_fixed_size
@@ -124,6 +125,15 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
}
void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
+ if (container_builder_->IsBlockFragmentationContextRoot()) {
+ Vector<NGLogicalOutOfFlowPositionedNode> fragmentainer_descendants;
+ container_builder_->SwapOutOfFlowFragmentainerDescendants(
+ &fragmentainer_descendants);
+
+ if (!fragmentainer_descendants.IsEmpty())
+ LayoutFragmentainerDescendants(&fragmentainer_descendants);
+ }
+
Vector<NGLogicalOutOfFlowPositionedNode> candidates;
const LayoutObject* current_container = container_builder_->GetLayoutObject();
// If the container is display-locked, then we skip the layout of descendants,
@@ -267,14 +277,53 @@ bool NGOutOfFlowLayoutPart::SweepLegacyCandidates(
return true;
}
+// Retrieve the stored ContainingBlockInfo needed for placing positioned nodes.
+// When fragmenting, the ContainingBlockInfo is not stored ahead of time and
+// must be generated on demand. The reason being that during fragmentation, we
+// wait to place positioned nodes until they've reached the fragmentation
+// context root. In such cases, we cannot use |default_containing_block_| since
+// the fragmentation root is not the containing block of the positioned nodes.
+// Rather, we must generate their ContainingBlockInfo based on the provided
+// |containing_block_fragment|.
const NGOutOfFlowLayoutPart::ContainingBlockInfo&
NGOutOfFlowLayoutPart::GetContainingBlockInfo(
- const NGLogicalOutOfFlowPositionedNode& candidate) const {
+ const NGLogicalOutOfFlowPositionedNode& candidate,
+ const NGPhysicalContainerFragment* containing_block_fragment) {
if (candidate.inline_container) {
const auto it = containing_blocks_map_.find(candidate.inline_container);
DCHECK(it != containing_blocks_map_.end());
return it->value;
}
+ if (containing_block_fragment) {
+ DCHECK(container_builder_->IsBlockFragmentationContextRoot());
+
+ const LayoutObject* containing_block =
+ containing_block_fragment->GetLayoutObject();
+ DCHECK(containing_block);
+ auto it = containing_blocks_map_.find(containing_block);
+ if (it != containing_blocks_map_.end())
+ return it->value;
+
+ const ComputedStyle& style = containing_block->StyleRef();
+ LogicalSize size = containing_block_fragment->Size().ConvertToLogical(
+ style.GetWritingMode());
+ const NGPhysicalBoxFragment* fragment =
+ To<NGPhysicalBoxFragment>(containing_block_fragment);
+
+ // TODO(1079031): This should eventually include scrollbar and border.
+ NGBoxStrut border = fragment->Borders().ConvertToLogical(
+ style.GetWritingMode(), style.Direction());
+ LogicalSize content_size = ShrinkLogicalSize(size, border);
+ LogicalOffset container_offset =
+ LogicalOffset(border.inline_start, border.block_start);
+
+ ContainingBlockInfo containing_block_info{style.Direction(), content_size,
+ content_size, container_offset};
+
+ return containing_blocks_map_
+ .insert(containing_block, containing_block_info)
+ .stored_value->value;
+ }
return default_containing_block_;
}
@@ -433,6 +482,10 @@ void NGOutOfFlowLayoutPart::LayoutCandidates(
candidate.static_position);
if (IsContainingBlockForCandidate(candidate) &&
(!only_layout || layout_box == only_layout)) {
+ if (container_space_.HasBlockFragmentation()) {
+ container_builder_->AddOutOfFlowFragmentainerDescendant(candidate);
+ continue;
+ }
scoped_refptr<const NGLayoutResult> result =
LayoutCandidate(candidate, only_layout);
container_builder_->AddChild(result->PhysicalFragment(),
@@ -521,7 +574,8 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
do {
scoped_refptr<const NGLayoutResult> layout_result =
Layout(node, candidate_constraint_space, candidate_static_position,
- container_content_size, container_info, only_layout);
+ container_content_size, container_info,
+ default_containing_block_.direction, only_layout);
if (!freeze_scrollbars.has_value()) {
// Since out-of-flow positioning sets up a constraint space with fixed
@@ -545,14 +599,85 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutCandidate(
} while (true);
}
+void NGOutOfFlowLayoutPart::LayoutFragmentainerDescendants(
+ Vector<NGLogicalOutOfFlowPositionedNode>* descendants) {
+ while (descendants->size() > 0) {
+ for (auto& descendant : *descendants) {
+ scoped_refptr<const NGLayoutResult> result =
+ LayoutFragmentainerDescendant(descendant);
+
+ // TODO(almaher): Add children to the correct fragmentainer.
+ container_builder_->AddChild(result->PhysicalFragment(),
+ result->OutOfFlowPositionedOffset(),
+ descendant.inline_container);
+ }
+ // Sweep any descendants that might have been added.
+ // This happens when an absolute container has a fixed child.
+ descendants->Shrink(0);
+ container_builder_->SwapOutOfFlowFragmentainerDescendants(descendants);
+ }
+}
+
+scoped_refptr<const NGLayoutResult>
+NGOutOfFlowLayoutPart::LayoutFragmentainerDescendant(
+ const NGLogicalOutOfFlowPositionedNode& descendant) {
+ // TODO(almaher): Properly implement the layout algorithm for fragmented
+ // positioned elements.
+ NGBlockNode node = descendant.node;
+ const NGPhysicalContainerFragment* containing_block_fragment =
+ descendant.containing_block_fragment.get();
+
+ DCHECK(containing_block_fragment &&
+ containing_block_fragment->GetLayoutObject() ==
+ node.GetLayoutBox()->ContainingBlock());
+
+ const ContainingBlockInfo& container_info =
+ GetContainingBlockInfo(descendant, containing_block_fragment);
+ const TextDirection default_direction =
+ containing_block_fragment->Style().Direction();
+ const ComputedStyle& descendant_style = node.Style();
+ const WritingMode descendant_writing_mode = descendant_style.GetWritingMode();
+ const TextDirection descendant_direction = descendant_style.Direction();
+
+ LogicalSize container_content_size =
+ container_info.ContentSize(descendant_style.GetPosition());
+ PhysicalSize container_physical_content_size =
+ ToPhysicalSize(container_content_size, writing_mode_);
+
+ // Adjust the |static_position| (which is currently relative to the default
+ // container's border-box). ng_absolute_utils expects the static position to
+ // be relative to the container's padding-box.
+ NGLogicalStaticPosition static_position = descendant.static_position;
+ static_position.offset -= container_info.container_offset;
+
+ NGLogicalStaticPosition descendant_static_position =
+ static_position
+ .ConvertToPhysical(writing_mode_, default_direction,
+ container_physical_content_size)
+ .ConvertToLogical(descendant_writing_mode, descendant_direction,
+ container_physical_content_size);
+
+ // Need a constraint space to resolve offsets.
+ NGConstraintSpaceBuilder builder(writing_mode_, descendant_writing_mode,
+ /* is_new_fc */ true);
+ builder.SetTextDirection(descendant_direction);
+ builder.SetAvailableSize(container_content_size);
+ builder.SetPercentageResolutionSize(container_content_size);
+ NGConstraintSpace descendant_constraint_space = builder.ToConstraintSpace();
+
+ return Layout(node, descendant_constraint_space, descendant_static_position,
+ container_content_size, container_info, default_direction,
+ nullptr);
+}
+
scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
NGBlockNode node,
const NGConstraintSpace& candidate_constraint_space,
const NGLogicalStaticPosition& candidate_static_position,
LogicalSize container_content_size,
const ContainingBlockInfo& container_info,
+ const TextDirection default_direction,
const LayoutBox* only_layout) {
- const TextDirection default_direction = default_containing_block_.direction;
const ComputedStyle& candidate_style = node.Style();
const WritingMode candidate_writing_mode = candidate_style.GetWritingMode();
const TextDirection candidate_direction = candidate_style.Direction();
@@ -588,7 +713,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
if (AbsoluteNeedsChildInlineSize(candidate_style) ||
NeedMinMaxSize(candidate_style) || should_be_considered_as_replaced) {
- MinMaxSizesInput input(kIndefiniteSize);
+ MinMaxSizesInput input(kIndefiniteSize, MinMaxSizesType::kContent);
if (is_replaced) {
input.percentage_resolution_block_size =
container_content_size_in_candidate_writing_mode.block_size;
@@ -610,19 +735,19 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
base::Optional<LogicalSize> replaced_size;
base::Optional<LogicalSize> replaced_aspect_ratio;
- bool is_replaced_with_only_aspect_ratio = false;
+ bool has_aspect_ratio_without_intrinsic_size = false;
if (is_replaced) {
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();
+ has_aspect_ratio_without_intrinsic_size = !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_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)
+ if (has_aspect_ratio_without_intrinsic_size)
min_max_sizes = MinMaxSizes{LayoutUnit(), LayoutUnit::NearlyMax()};
} else if (should_be_considered_as_replaced) {
replaced_size =
@@ -641,10 +766,10 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
if (!is_replaced && should_be_considered_as_replaced)
replaced_size.reset();
- // Replaced elements with only aspect ratio compute their block size from
+ // Elements with only aspect ratio compute their block size from
// inline size and aspect ratio.
// https://www.w3.org/TR/css-sizing-3/#intrinsic-sizes
- if (is_replaced_with_only_aspect_ratio) {
+ if (has_aspect_ratio_without_intrinsic_size) {
replaced_size = LogicalSize(
node_dimensions.size.inline_size,
(replaced_aspect_ratio->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 777309f62ef..92bbec5bb1f 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
@@ -23,6 +23,7 @@ class NGBlockNode;
class NGBoxFragmentBuilder;
class NGConstraintSpace;
class NGLayoutResult;
+class NGPhysicalContainerFragment;
struct NGLogicalOutOfFlowPositionedNode;
// Helper class for positioning of out-of-flow blocks.
@@ -96,7 +97,8 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
bool SweepLegacyCandidates(HashSet<const LayoutObject*>* placed_objects);
const ContainingBlockInfo& GetContainingBlockInfo(
- const NGLogicalOutOfFlowPositionedNode&) const;
+ const NGLogicalOutOfFlowPositionedNode&,
+ const NGPhysicalContainerFragment* = nullptr);
void ComputeInlineContainingBlocks(
const Vector<NGLogicalOutOfFlowPositionedNode>&);
@@ -109,11 +111,18 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
const NGLogicalOutOfFlowPositionedNode&,
const LayoutBox* only_layout);
+ void LayoutFragmentainerDescendants(
+ Vector<NGLogicalOutOfFlowPositionedNode>* descendants);
+
+ scoped_refptr<const NGLayoutResult> LayoutFragmentainerDescendant(
+ const NGLogicalOutOfFlowPositionedNode&);
+
scoped_refptr<const NGLayoutResult> Layout(NGBlockNode,
const NGConstraintSpace&,
const NGLogicalStaticPosition&,
LogicalSize container_content_size,
const ContainingBlockInfo&,
+ const TextDirection,
const LayoutBox* only_layout);
bool IsContainingBlockForCandidate(const NGLogicalOutOfFlowPositionedNode&);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
index c950e7f86e4..f1ec239d16c 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc
@@ -5,14 +5,44 @@
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
namespace {
-using NGOutOfFlowLayoutPartTest = NGLayoutTest;
+class NGOutOfFlowLayoutPartTest
+ : public NGBaseLayoutAlgorithmTest,
+ private ScopedLayoutNGBlockFragmentationForTest {
+ protected:
+ NGOutOfFlowLayoutPartTest() : 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);
+ }
+
+ String DumpFragmentTree(Element* element) {
+ auto fragment = RunBlockLayoutAlgorithm(element);
+ return DumpFragmentTree(fragment.get());
+ }
+
+ String DumpFragmentTree(const blink::NGPhysicalBoxFragment* fragment) {
+ NGPhysicalFragment::DumpFlags flags =
+ NGPhysicalFragment::DumpHeaderText | NGPhysicalFragment::DumpSubtree |
+ NGPhysicalFragment::DumpIndentation | NGPhysicalFragment::DumpOffset |
+ NGPhysicalFragment::DumpSize;
+
+ return fragment->DumpFragmentTree(flags);
+ }
+};
// Fixed blocks inside absolute blocks trigger otherwise unused while loop
// inside NGOutOfFlowLayoutPart::Run.
@@ -73,5 +103,55 @@ TEST_F(NGOutOfFlowLayoutPartTest, FixedInsideAbs) {
EXPECT_EQ(fixed_2->OffsetTop(), LayoutUnit(9));
}
+// Tests that positioned nodes fragment correctly.
+// TODO(almaher): Reenable once the layout algorithm for fragmented positioned
+// items is in a more stable state.
+TEST_F(NGOutOfFlowLayoutPartTest, DISABLED_PositionedFragmentation) {
+ SetBodyInnerHTML(
+ R"HTML(
+ <style>
+ #multicol {
+ column-count: 2; height: 40px; column-fill:auto;
+ }
+ .rel {
+ position: relative;
+ }
+ .abs {
+ position: absolute;
+ }
+ </style>
+ <div id="container">
+ <div id="multicol">
+ <div style="width:100px; height:50px;"></div>
+ <div class="rel">
+ <div class="abs" style="width:5px; top: 10px; height:5px;">
+ </div>
+ <div class="rel">
+ <div class="abs" style="width:10px; top: 20px; height:10px;">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ )HTML");
+ String dump = DumpFragmentTree(GetElementById("container"));
+
+ // TODO(almaher): Positioned nodes are not currently placed in the correct
+ // fragment.
+ String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+ offset:unplaced size:1000x40
+ offset:0,0 size:1000x40
+ offset:0,0 size:499.5x40
+ offset:0,0 size:100x40
+ offset:500.5,0 size:499.5x40
+ offset:0,0 size:100x10
+ offset:0,10 size:499.5x0
+ offset:0,0 size:499.5x0
+ offset:0,20 size:10x10
+ offset:0,10 size:5x5
+)DUMP";
+ EXPECT_EQ(expectation, dump);
+}
+
} // namespace
} // namespace blink
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 3f9a5127140..6b332b1b4ee 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
@@ -19,8 +19,15 @@ namespace blink {
// as a positioned-node reaches its containing block, it gets placed, and
// doesn't bubble further up the tree.
//
+// However, when fragmentation comes into play, we no longer place a
+// positioned-node as soon as it reaches its containing block. Instead, we
+// continue to bubble the positioned node up until it reaches the
+// fragmentation context root. There, it will get placed and properly
+// fragmented.
+//
// This needs its static position [1] to be placed correctly in its containing
-// block.
+// block. And in the case of fragmentation, this also needs the containing block
+// fragment to be placed correctly within the fragmentation context root.
//
// This is struct is allowed to be stored/persisted.
//
@@ -30,14 +37,18 @@ struct CORE_EXPORT NGPhysicalOutOfFlowPositionedNode {
NGPhysicalStaticPosition static_position;
// Continuation root of the optional inline container.
const LayoutInline* inline_container;
+ scoped_refptr<const NGPhysicalContainerFragment> containing_block_fragment;
NGPhysicalOutOfFlowPositionedNode(
NGBlockNode node,
NGPhysicalStaticPosition static_position,
- const LayoutInline* inline_container = nullptr)
+ const LayoutInline* inline_container = nullptr,
+ scoped_refptr<const NGPhysicalContainerFragment>
+ containing_block_fragment = nullptr)
: node(node),
static_position(static_position),
- inline_container(inline_container) {
+ inline_container(inline_container),
+ containing_block_fragment(std::move(containing_block_fragment)) {
DCHECK(!inline_container ||
inline_container == inline_container->ContinuationRoot());
}
@@ -55,16 +66,20 @@ struct NGLogicalOutOfFlowPositionedNode {
// Continuation root of the optional inline container.
const LayoutInline* inline_container;
bool needs_block_offset_adjustment;
+ scoped_refptr<const NGPhysicalContainerFragment> containing_block_fragment;
NGLogicalOutOfFlowPositionedNode(
NGBlockNode node,
NGLogicalStaticPosition static_position,
const LayoutInline* inline_container = nullptr,
- bool needs_block_offset_adjustment = false)
+ bool needs_block_offset_adjustment = false,
+ scoped_refptr<const NGPhysicalContainerFragment>
+ containing_block_fragment = nullptr)
: node(node),
static_position(static_position),
inline_container(inline_container),
- needs_block_offset_adjustment(needs_block_offset_adjustment) {
+ needs_block_offset_adjustment(needs_block_offset_adjustment),
+ containing_block_fragment(std::move(containing_block_fragment)) {
DCHECK(!inline_container ||
inline_container == inline_container->ContinuationRoot());
}
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 27aa6125ce8..3752e5dc4f9 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
@@ -17,28 +17,21 @@ namespace blink {
NGPageLayoutAlgorithm::NGPageLayoutAlgorithm(
const NGLayoutAlgorithmParams& params)
- : NGLayoutAlgorithm(params),
- border_padding_(params.fragment_geometry.border +
- params.fragment_geometry.padding),
- border_scrollbar_padding_(border_padding_ +
- params.fragment_geometry.scrollbar) {
+ : NGLayoutAlgorithm(params) {
container_builder_.SetIsNewFormattingContext(
params.space.IsNewFormattingContext());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
scoped_refptr<const NGLayoutResult> NGPageLayoutAlgorithm::Layout() {
- LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
- LogicalSize content_box_size =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
- LogicalSize page_size = content_box_size;
+ LogicalSize page_size = ChildAvailableSize();
NGConstraintSpace child_space = CreateConstraintSpaceForPages(page_size);
WritingMode writing_mode = ConstraintSpace().GetWritingMode();
scoped_refptr<const NGBlockBreakToken> break_token = BreakToken();
LayoutUnit intrinsic_block_size;
- LogicalOffset page_offset(border_scrollbar_padding_.StartOffset());
+ LogicalOffset page_offset = BorderScrollbarPadding().StartOffset();
// TODO(mstensho): Handle auto block size.
LogicalOffset page_progression(LayoutUnit(), page_size.block_size);
@@ -64,11 +57,11 @@ scoped_refptr<const NGLayoutResult> NGPageLayoutAlgorithm::Layout() {
container_builder_.SetIntrinsicBlockSize(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.inline_size);
- container_builder_.SetBlockSize(border_box_size.block_size);
+ // Compute the block-axis size now that we know our content size.
+ LayoutUnit block_size = ComputeBlockSizeForFragment(
+ ConstraintSpace(), Style(), BorderPadding(), intrinsic_block_size,
+ container_builder_.InitialBorderBoxSize().inline_size);
+ container_builder_.SetFragmentsTotalBlockSize(block_size);
NGOutOfFlowLayoutPart(
Node(), ConstraintSpace(),
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 4b9ebf55713..d8613b602c2 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
@@ -30,9 +30,6 @@ class CORE_EXPORT NGPageLayoutAlgorithm
private:
NGConstraintSpace CreateConstraintSpaceForPages(
const LogicalSize& size) const;
-
- NGBoxStrut border_padding_;
- NGBoxStrut border_scrollbar_padding_;
};
} // namespace blink
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 fd78b87d9c6..bd3601db795 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
@@ -52,15 +52,20 @@ scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create(
const NGPhysicalBoxStrut padding =
builder->initial_fragment_geometry_->padding.ConvertToPhysical(
builder->GetWritingMode(), builder->Direction());
+ auto& mathml_paint_info = builder->mathml_paint_info_;
size_t byte_size = sizeof(NGPhysicalBoxFragment) +
sizeof(NGLink) * builder->children_.size() +
(borders.IsZero() ? 0 : sizeof(borders)) +
- (padding.IsZero() ? 0 : sizeof(padding));
+ (padding.IsZero() ? 0 : sizeof(padding)) +
+ (mathml_paint_info ? sizeof(NGMathMLPaintInfo*) : 0);
if (const NGFragmentItemsBuilder* items_builder = builder->ItemsBuilder()) {
// Omit |NGFragmentItems| if there were no items; e.g., display-lock.
if (items_builder->Size())
byte_size += NGFragmentItems::ByteSizeFor(items_builder->Size());
}
+ if (builder->HasOutOfFlowFragmentainerDescendants())
+ byte_size += sizeof(NGPhysicalOutOfFlowPositionedNode);
+
// We store the children list inline in the fragment as a flexible
// array. Therefore, we need to make sure to allocate enough space for
// that array here, which requires a manual allocation + placement new.
@@ -68,8 +73,9 @@ 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(PassKey(), builder, borders, padding,
- block_or_line_writing_mode);
+ new (data)
+ NGPhysicalBoxFragment(PassKey(), builder, borders, padding,
+ mathml_paint_info, block_or_line_writing_mode);
return base::AdoptRef(static_cast<NGPhysicalBoxFragment*>(data));
}
@@ -78,6 +84,7 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment(
NGBoxFragmentBuilder* builder,
const NGPhysicalBoxStrut& borders,
const NGPhysicalBoxStrut& padding,
+ std::unique_ptr<NGMathMLPaintInfo>& mathml_paint_info,
WritingMode block_or_line_writing_mode)
: NGPhysicalContainerFragment(builder,
block_or_line_writing_mode,
@@ -94,8 +101,9 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment(
has_fragment_items_ = true;
NGFragmentItems* items =
const_cast<NGFragmentItems*>(ComputeItemsAddress());
- items_builder->ToFragmentItems(block_or_line_writing_mode,
- builder->Direction(), Size(), items);
+ DCHECK_EQ(items_builder->GetWritingMode(), block_or_line_writing_mode);
+ DCHECK_EQ(items_builder->Direction(), builder->Direction());
+ items_builder->ToFragmentItems(Size(), items);
}
}
@@ -105,6 +113,13 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment(
has_padding_ = !padding.IsZero();
if (has_padding_)
*const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress()) = padding;
+ ink_overflow_computed_or_mathml_paint_info_ = !!mathml_paint_info;
+ if (ink_overflow_computed_or_mathml_paint_info_) {
+ memset(ComputeMathMLPaintInfoAddress(), 0, sizeof(NGMathMLPaintInfo));
+ new (static_cast<void*>(ComputeMathMLPaintInfoAddress()))
+ NGMathMLPaintInfo(*mathml_paint_info);
+ }
+
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_;
@@ -130,6 +145,28 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment(
last_baseline_ = LayoutUnit::Min();
}
+ PhysicalSize size = Size();
+ has_oof_positioned_fragmentainer_descendants_ = false;
+ if (!builder->oof_positioned_fragmentainer_descendants_.IsEmpty()) {
+ has_oof_positioned_fragmentainer_descendants_ = true;
+ Vector<NGPhysicalOutOfFlowPositionedNode>*
+ oof_positioned_fragmentainer_descendants =
+ const_cast<Vector<NGPhysicalOutOfFlowPositionedNode>*>(
+ ComputeOutOfFlowPositionedFragmentainerDescendantsAddress());
+ new (oof_positioned_fragmentainer_descendants)
+ Vector<NGPhysicalOutOfFlowPositionedNode>();
+ oof_positioned_fragmentainer_descendants->ReserveCapacity(
+ builder->oof_positioned_fragmentainer_descendants_.size());
+ for (const auto& descendant :
+ builder->oof_positioned_fragmentainer_descendants_) {
+ oof_positioned_fragmentainer_descendants->emplace_back(
+ descendant.node,
+ descendant.static_position.ConvertToPhysical(
+ builder->Style().GetWritingMode(), builder->Direction(), size),
+ descendant.inline_container, descendant.containing_block_fragment);
+ }
+ }
+
#if DCHECK_IS_ON()
CheckIntegrity();
#endif
@@ -139,7 +176,7 @@ scoped_refptr<const NGLayoutResult>
NGPhysicalBoxFragment::CloneAsHiddenForPaint() const {
const ComputedStyle& style = Style();
NGBoxFragmentBuilder builder(GetMutableLayoutObject(), &style,
- style.GetWritingMode(), style.Direction());
+ style.GetWritingDirection());
builder.SetBoxType(BoxType());
NGFragmentGeometry initial_fragment_geometry{
Size().ConvertToLogical(style.GetWritingMode())};
@@ -327,7 +364,7 @@ PhysicalRect NGPhysicalBoxFragment::ScrollableOverflowFromChildren() const {
}
// Traverse child fragments.
- const bool children_inline = IsInlineFormattingContext();
+ const bool add_inline_children = !items && IsInlineFormattingContext();
// Only add overflow for fragments NG has not reflected into Legacy.
// These fragments are:
// - inline fragments,
@@ -337,7 +374,7 @@ PhysicalRect NGPhysicalBoxFragment::ScrollableOverflowFromChildren() const {
for (const auto& child : Children()) {
if (child->IsFloatingOrOutOfFlowPositioned()) {
context.AddFloatingOrOutOfFlowPositionedChild(*child, child.Offset());
- } else if (children_inline && child->IsLineBox()) {
+ } else if (add_inline_children && child->IsLineBox()) {
context.AddLineBoxChild(To<NGPhysicalLineBoxFragment>(*child),
child.Offset());
}
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 8b034f8a7db..d3ecedd282b 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
@@ -8,6 +8,7 @@
#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_fragment_items.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/platform/graphics/scroll_types.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -29,6 +30,7 @@ class CORE_EXPORT NGPhysicalBoxFragment final
NGBoxFragmentBuilder* builder,
const NGPhysicalBoxStrut& borders,
const NGPhysicalBoxStrut& padding,
+ std::unique_ptr<NGMathMLPaintInfo>& mathml_paint_info,
WritingMode block_or_line_writing_mode);
scoped_refptr<const NGLayoutResult> CloneAsHiddenForPaint() const;
@@ -36,6 +38,8 @@ class CORE_EXPORT NGPhysicalBoxFragment final
~NGPhysicalBoxFragment() {
if (has_fragment_items_)
ComputeItemsAddress()->~NGFragmentItems();
+ if (ink_overflow_computed_or_mathml_paint_info_)
+ ComputeMathMLPaintInfoAddress()->~NGMathMLPaintInfo();
for (const NGLink& child : Children())
child.fragment->Release();
}
@@ -70,6 +74,20 @@ class CORE_EXPORT NGPhysicalBoxFragment final
return *ComputePaddingAddress();
}
+ bool HasOutOfFlowPositionedFragmentainerDescendants() const {
+ return has_oof_positioned_fragmentainer_descendants_;
+ }
+
+ base::span<NGPhysicalOutOfFlowPositionedNode>
+ OutOfFlowPositionedFragmentainerDescendants() const {
+ if (!HasOutOfFlowPositionedFragmentainerDescendants())
+ return base::span<NGPhysicalOutOfFlowPositionedNode>();
+ Vector<NGPhysicalOutOfFlowPositionedNode>* descendants =
+ const_cast<Vector<NGPhysicalOutOfFlowPositionedNode>*>(
+ ComputeOutOfFlowPositionedFragmentainerDescendantsAddress());
+ return {descendants->data(), descendants->size()};
+ }
+
NGPixelSnappedPhysicalBoxStrut PixelSnappedPadding() const {
if (!has_padding_)
return NGPixelSnappedPhysicalBoxStrut();
@@ -152,15 +170,23 @@ class CORE_EXPORT NGPhysicalBoxFragment final
bool check_same_block_size) const;
#endif
+ bool HasExtraMathMLPainting() const {
+ return IsMathMLFraction() || ink_overflow_computed_or_mathml_paint_info_;
+ }
+
private:
const NGFragmentItems* ComputeItemsAddress() const {
- DCHECK(has_fragment_items_ || has_borders_ || has_padding_);
+ DCHECK(has_fragment_items_ || has_borders_ || has_padding_ ||
+ ink_overflow_computed_or_mathml_paint_info_ ||
+ has_oof_positioned_fragmentainer_descendants_);
const NGLink* children_end = children_ + Children().size();
return reinterpret_cast<const NGFragmentItems*>(children_end);
}
const NGPhysicalBoxStrut* ComputeBordersAddress() const {
- DCHECK(has_borders_ || has_padding_);
+ DCHECK(has_borders_ || has_padding_ ||
+ ink_overflow_computed_or_mathml_paint_info_ ||
+ has_oof_positioned_fragmentainer_descendants_);
const NGFragmentItems* items = ComputeItemsAddress();
if (!has_fragment_items_)
return reinterpret_cast<const NGPhysicalBoxStrut*>(items);
@@ -169,11 +195,31 @@ class CORE_EXPORT NGPhysicalBoxFragment final
}
const NGPhysicalBoxStrut* ComputePaddingAddress() const {
- DCHECK(has_padding_);
+ DCHECK(has_padding_ || ink_overflow_computed_or_mathml_paint_info_ ||
+ has_oof_positioned_fragmentainer_descendants_);
const NGPhysicalBoxStrut* address = ComputeBordersAddress();
return has_borders_ ? address + 1 : address;
}
+ NGMathMLPaintInfo* ComputeMathMLPaintInfoAddress() const {
+ DCHECK(ink_overflow_computed_or_mathml_paint_info_ ||
+ has_oof_positioned_fragmentainer_descendants_);
+ NGPhysicalBoxStrut* address =
+ const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress());
+ return has_padding_ ? reinterpret_cast<NGMathMLPaintInfo*>(address + 1)
+ : reinterpret_cast<NGMathMLPaintInfo*>(address);
+ }
+
+ const Vector<NGPhysicalOutOfFlowPositionedNode>*
+ ComputeOutOfFlowPositionedFragmentainerDescendantsAddress() const {
+ DCHECK(has_oof_positioned_fragmentainer_descendants_);
+ NGMathMLPaintInfo* address = ComputeMathMLPaintInfoAddress();
+ address =
+ ink_overflow_computed_or_mathml_paint_info_ ? address + 1 : address;
+ return reinterpret_cast<const Vector<NGPhysicalOutOfFlowPositionedNode>*>(
+ address);
+ }
+
#if DCHECK_IS_ON()
void CheckIntegrity() const;
#endif
@@ -181,7 +227,8 @@ class CORE_EXPORT NGPhysicalBoxFragment final
LayoutUnit baseline_;
LayoutUnit last_baseline_;
NGLink children_[];
- // borders and padding come from after |children_| if they are not zero.
+ // borders, padding, and oof_positioned_fragmentainer_descendants come after
+ // |children_| if they are not zero.
};
template <>
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 929f8b346b1..5ddbaceda94 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
#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"
@@ -19,11 +20,11 @@ namespace blink {
namespace {
struct SameSizeAsNGPhysicalContainerFragment : NGPhysicalFragment {
+ wtf_size_t size;
void* break_token;
std::unique_ptr<Vector<NGPhysicalOutOfFlowPositionedNode>>
oof_positioned_descendants_;
void* pointer;
- wtf_size_t size;
};
static_assert(sizeof(NGPhysicalContainerFragment) ==
@@ -39,13 +40,13 @@ NGPhysicalContainerFragment::NGPhysicalContainerFragment(
NGFragmentType type,
unsigned sub_type)
: NGPhysicalFragment(builder, type, sub_type),
+ num_children_(builder->children_.size()),
break_token_(std::move(builder->break_token_)),
oof_positioned_descendants_(
builder->oof_positioned_descendants_.IsEmpty()
? nullptr
: new Vector<NGPhysicalOutOfFlowPositionedNode>()),
- buffer_(buffer),
- num_children_(builder->children_.size()) {
+ buffer_(buffer) {
has_floating_descendants_for_paint_ =
builder->has_floating_descendants_for_paint_;
has_adjoining_object_descendants_ =
@@ -71,11 +72,12 @@ NGPhysicalContainerFragment::NGPhysicalContainerFragment(
// Because flexible arrays need to be the last member in a class, we need to
// have the buffer passed as a constructor argument and have the actual
// storage be part of the subclass.
+ const WritingModeConverter converter(
+ {block_or_line_writing_mode, builder->Direction()}, size);
wtf_size_t i = 0;
for (auto& child : builder->children_) {
- buffer[i].offset = child.offset.ConvertToPhysical(
- block_or_line_writing_mode, builder->Direction(), size,
- child.fragment->Size());
+ buffer[i].offset =
+ converter.ToPhysical(child.offset, child.fragment->Size());
// Call the move constructor to move without |AddRef|. Fragments in
// |builder| are not used after |this| was constructed.
static_assert(
@@ -110,6 +112,8 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
}
if (item.Type() == NGFragmentItem::kBox) {
if (const NGPhysicalBoxFragment* child_box = item.BoxFragment()) {
+ if (const NGPhysicalFragment* post_layout = child_box->PostLayout())
+ child_box = To<NGPhysicalBoxFragment>(post_layout);
DCHECK(!child_box->IsOutOfFlowPositioned());
AddOutlineRectsForDescendant(
{child_box, item.OffsetInContainerBlock()}, outline_rects,
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 f025de6caf1..a155b84e3f5 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
@@ -187,6 +187,7 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);
+ wtf_size_t num_children_;
scoped_refptr<const NGBreakToken> break_token_;
const std::unique_ptr<Vector<NGPhysicalOutOfFlowPositionedNode>>
oof_positioned_descendants_;
@@ -194,7 +195,6 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
// Because flexible arrays need to be the last member in a class, the actual
// storage is in the subclass and we just keep a pointer to it here.
const NGLink* buffer_;
- wtf_size_t num_children_;
};
template <>
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 4d3daa543ce..c5370218ab6 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
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
#include "third_party/blink/renderer/core/layout/layout_block.h"
#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"
@@ -23,6 +24,9 @@ namespace {
struct SameSizeAsNGPhysicalFragment
: RefCounted<const NGPhysicalFragment, NGPhysicalFragmentTraits> {
+ // |flags_for_free_maybe| is used to support an additional increase in size
+ // needed for DCHECK and 32-bit builds.
+ unsigned flags_for_free_maybe;
void* layout_object;
PhysicalSize size;
unsigned flags;
@@ -213,18 +217,18 @@ void NGPhysicalFragmentTraits::Destruct(const NGPhysicalFragment* fragment) {
NGPhysicalFragment::NGPhysicalFragment(NGFragmentBuilder* builder,
NGFragmentType type,
unsigned sub_type)
- : layout_object_(builder->layout_object_),
+ : has_floating_descendants_for_paint_(false),
+ layout_object_(builder->layout_object_),
size_(ToPhysicalSize(builder->size_, builder->GetWritingMode())),
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)builder->style_variant_),
is_hidden_for_paint_(builder->is_hidden_for_paint_),
- has_floating_descendants_for_paint_(false),
is_fieldset_container_(false),
is_legacy_layout_root_(false),
is_painted_atomically_(false),
has_baseline_(false) {
- DCHECK(builder->layout_object_);
+ CHECK(builder->layout_object_);
}
NGPhysicalFragment::NGPhysicalFragment(LayoutObject* layout_object,
@@ -232,18 +236,18 @@ NGPhysicalFragment::NGPhysicalFragment(LayoutObject* layout_object,
PhysicalSize size,
NGFragmentType type,
unsigned sub_type)
- : layout_object_(layout_object),
+ : has_floating_descendants_for_paint_(false),
+ layout_object_(layout_object),
size_(size),
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)style_variant),
is_hidden_for_paint_(false),
- has_floating_descendants_for_paint_(false),
is_fieldset_container_(false),
is_legacy_layout_root_(false),
is_painted_atomically_(false),
has_baseline_(false) {
- DCHECK(layout_object);
+ CHECK(layout_object);
}
// Keep the implementation of the destructor here, to avoid dependencies on
@@ -295,7 +299,7 @@ bool NGPhysicalFragment::IsPlacedByLayoutNG() const {
// to set.
if (IsLineBox())
return false;
- if (IsColumnBox())
+ if (IsFragmentainerBox())
return true;
const LayoutBlock* container = layout_object_->ContainingBlock();
if (!container)
@@ -315,14 +319,15 @@ const FragmentData* NGPhysicalFragment::GetFragmentData() const {
}
const NGPhysicalFragment* NGPhysicalFragment::PostLayout() const {
- if (IsBox() && !IsInlineBox()) {
- if (const auto* block = DynamicTo<LayoutBlockFlow>(GetLayoutObject())) {
- if (block->IsRelayoutBoundary()) {
- const NGPhysicalFragment* new_fragment = block->CurrentFragment();
- if (new_fragment && new_fragment != this)
- return new_fragment;
- }
- }
+ const auto* layout_box = ToLayoutBoxOrNull(GetLayoutObject());
+ if (UNLIKELY(!layout_box))
+ return nullptr;
+
+ if (layout_box->PhysicalFragmentCount() == 1) {
+ const NGPhysicalFragment* post_layout = layout_box->GetPhysicalFragment(0);
+ DCHECK(post_layout);
+ if (UNLIKELY(post_layout && post_layout != this))
+ return post_layout;
}
return nullptr;
}
@@ -506,6 +511,18 @@ bool NGPhysicalFragment::ShouldPaintDragCaret() const {
return false;
}
+LogicalRect NGPhysicalFragment::ConvertChildToLogical(
+ const PhysicalRect& physical_rect) const {
+ return WritingModeConverter(Style().GetWritingDirection(), Size())
+ .ToLogical(physical_rect);
+}
+
+PhysicalRect NGPhysicalFragment::ConvertChildToPhysical(
+ const LogicalRect& logical_rect) const {
+ return WritingModeConverter(Style().GetWritingDirection(), Size())
+ .ToPhysical(logical_rect);
+}
+
String NGPhysicalFragment::ToString() const {
StringBuilder output;
output.AppendFormat("Type: '%d' Size: '%s'", Type(),
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 4cebbebf948..d14c4b9eb1e 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
@@ -25,9 +25,9 @@ class FragmentData;
class Node;
class NGFragmentBuilder;
class NGInlineItem;
-class PaintLayer;
-
class NGPhysicalFragment;
+class PaintLayer;
+struct LogicalRect;
struct CORE_EXPORT NGPhysicalFragmentTraits {
static void Destruct(const NGPhysicalFragment*);
@@ -98,6 +98,7 @@ class CORE_EXPORT NGPhysicalFragment
bool IsColumnBox() const {
return IsBox() && BoxType() == NGBoxType::kColumnBox;
}
+ bool IsFragmentainerBox() const { return IsColumnBox(); }
// An atomic inline is represented as a kFragmentBox, such as inline block and
// replaced elements.
bool IsAtomicInline() const {
@@ -133,7 +134,7 @@ class CORE_EXPORT NGPhysicalFragment
//
// [1] https://www.w3.org/TR/css-display-3/#box-tree
// [2] https://www.w3.org/TR/css-break-3/#fragmentation-container
- bool IsCSSBox() const { return !IsLineBox() && !IsColumnBox(); }
+ bool IsCSSBox() const { return !IsLineBox() && !IsFragmentainerBox(); }
bool IsBlockFlow() const;
bool IsAnonymousBlock() const {
@@ -142,6 +143,7 @@ class CORE_EXPORT NGPhysicalFragment
bool IsListMarker() const {
return IsCSSBox() && layout_object_->IsLayoutNGOutsideListMarker();
}
+ bool IsRubyRun() const { return layout_object_->IsRubyRun(); }
// Return true if this fragment is a container established by a fieldset
// element. Such a fragment contains an optional rendered legend fragment and
@@ -336,6 +338,11 @@ class CORE_EXPORT NGPhysicalFragment
// be confused with the CSS 'direction' property.
TextDirection ResolvedDirection() const;
+ // Helper functions to convert between |PhysicalRect| and |LogicalRect| of a
+ // child.
+ LogicalRect ConvertChildToLogical(const PhysicalRect& physical_rect) const;
+ PhysicalRect ConvertChildToPhysical(const LogicalRect& logical_rect) const;
+
// Utility functions for caret painting. Note that carets are painted as part
// of the containing block's foreground.
bool ShouldPaintCursorCaret() const;
@@ -386,14 +393,6 @@ class CORE_EXPORT NGPhysicalFragment
const Vector<NGInlineItem>& InlineItemsOfContainingBlock() const;
- LayoutObject* layout_object_;
- const PhysicalSize size_;
-
- const unsigned type_ : 2; // NGFragmentType
- const unsigned sub_type_ : 3; // NGBoxType, NGTextType, or NGLineBoxType
- const unsigned style_variant_ : 2; // NGStyleVariant
- const unsigned is_hidden_for_paint_ : 1;
-
// The following bitfields are only to be used by NGPhysicalContainerFragment
// (it's defined here to save memory, since that class has no bitfields).
unsigned has_floating_descendants_for_paint_ : 1;
@@ -405,8 +404,6 @@ 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;
- // 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
@@ -416,8 +413,19 @@ class CORE_EXPORT NGPhysicalFragment
unsigned border_edge_ : 4; // NGBorderEdges::Physical
unsigned has_borders_ : 1;
unsigned has_padding_ : 1;
- unsigned is_math_fraction_ : 1;
unsigned is_first_for_node_ : 1;
+ unsigned has_oof_positioned_fragmentainer_descendants_ : 1;
+
+ LayoutObject* layout_object_;
+ const PhysicalSize size_;
+
+ const unsigned type_ : 2; // NGFragmentType
+ const unsigned sub_type_ : 3; // NGBoxType, NGTextType, or NGLineBoxType
+ const unsigned style_variant_ : 2; // NGStyleVariant
+ const unsigned is_hidden_for_paint_ : 1;
+ unsigned is_math_fraction_ : 1;
+ // base (line box) or resolve (text) direction
+ unsigned base_or_resolved_direction_ : 1; // TextDirection
// The following are only used by NGPhysicalBoxFragment but are initialized
// for all types to allow methods using them to be inlined.
@@ -429,10 +437,11 @@ class CORE_EXPORT NGPhysicalFragment
// The following bitfields are only to be used by NGPhysicalTextFragment
// (it's defined here to save memory, since that class has no bitfields).
- mutable unsigned ink_overflow_computed_ : 1;
+ mutable unsigned ink_overflow_computed_or_mathml_paint_info_ : 1;
// Note: We've used 32-bit bit field. If you need more bits, please think to
- // share bit fields.
+ // share bit fields, or put them before layout_object_ to fill the gap after
+ // RefCounted on 64-bit systems.
private:
friend struct NGPhysicalFragmentTraits;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h
index e24bf5b2c18..84473ecc6d8 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_positioned_float.h
@@ -17,8 +17,11 @@ class NGLayoutResult;
// Contains the information necessary for copying back data to a FloatingObject.
struct CORE_EXPORT NGPositionedFloat {
NGPositionedFloat(scoped_refptr<const NGLayoutResult> layout_result,
- const NGBfcOffset& bfc_offset)
- : layout_result(layout_result), bfc_offset(bfc_offset) {}
+ const NGBfcOffset& bfc_offset,
+ bool need_break_before = false)
+ : layout_result(layout_result),
+ bfc_offset(bfc_offset),
+ need_break_before(need_break_before) {}
NGPositionedFloat(NGPositionedFloat&&) noexcept = default;
NGPositionedFloat(const NGPositionedFloat&) = default;
NGPositionedFloat& operator=(NGPositionedFloat&&) = default;
@@ -26,6 +29,7 @@ struct CORE_EXPORT NGPositionedFloat {
scoped_refptr<const NGLayoutResult> layout_result;
NGBfcOffset bfc_offset;
+ bool need_break_before = false;
};
} // namespace blink
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 58f9ccbd1c8..400a7226def 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.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_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
@@ -24,8 +25,7 @@ NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm(
const NGLayoutResult& result)
: NGLayoutAlgorithm(params),
previous_result_(result),
- writing_mode_(Style().GetWritingMode()),
- direction_(Style().Direction()) {
+ writing_direction_(Style().GetWritingDirection()) {
// Currently this only supports block-flow layout due to the static-position
// calculations. If support for other layout types is added this logic will
// need to be changed.
@@ -112,20 +112,19 @@ NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm(
container_builder_.SetOverflowBlockSize(result.OverflowBlockSize());
LayoutUnit new_block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(),
- container_builder_.Borders() + container_builder_.Padding(),
- result.IntrinsicBlockSize(),
+ ConstraintSpace(), Style(), BorderPadding(), result.IntrinsicBlockSize(),
container_builder_.InitialBorderBoxSize().inline_size);
// Only block-flow is allowed to change its block-size during "simplified"
// layout, all other layout types must remain the same size.
if (is_block_flow) {
- container_builder_.SetBlockSize(new_block_size);
+ container_builder_.SetFragmentBlockSize(new_block_size);
} else {
LayoutUnit old_block_size =
- NGFragment(writing_mode_, physical_fragment).BlockSize();
+ NGFragment(writing_direction_.GetWritingMode(), physical_fragment)
+ .BlockSize();
DCHECK_EQ(old_block_size, new_block_size);
- container_builder_.SetBlockSize(old_block_size);
+ container_builder_.SetFragmentBlockSize(old_block_size);
}
// We need the previous physical container size to calculate the position of
@@ -193,8 +192,8 @@ scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() {
if (const NGFragmentItems* previous_items = previous_fragment.Items()) {
auto* items_builder = container_builder_.ItemsBuilder();
DCHECK(items_builder);
- items_builder->AddPreviousItems(*previous_items, writing_mode_,
- direction_,
+ DCHECK_EQ(items_builder->GetWritingDirection(), writing_direction_);
+ items_builder->AddPreviousItems(*previous_items,
previous_physical_container_size_);
}
}
@@ -217,7 +216,7 @@ scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() {
NOINLINE scoped_refptr<const NGLayoutResult>
NGSimplifiedLayoutAlgorithm::LayoutWithItemsBuilder() {
- NGFragmentItemsBuilder items_builder;
+ NGFragmentItemsBuilder items_builder(writing_direction_);
container_builder_.SetItemsBuilder(&items_builder);
scoped_refptr<const NGLayoutResult> result = Layout();
// Ensure stack-allocated |NGFragmentItemsBuilder| is not used anymore.
@@ -233,9 +232,10 @@ void NGSimplifiedLayoutAlgorithm::AddChildFragment(
DCHECK_EQ(old_fragment->Size(), new_fragment.Size());
// Determine the previous position in the logical coordinate system.
- LogicalOffset child_offset = old_fragment.Offset().ConvertToLogical(
- writing_mode_, direction_, previous_physical_container_size_,
- new_fragment.Size());
+ LogicalOffset child_offset =
+ WritingModeConverter(writing_direction_,
+ previous_physical_container_size_)
+ .ToLogical(old_fragment.Offset(), new_fragment.Size());
// Add the new fragment to the builder.
container_builder_.AddChild(new_fragment, child_offset);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h
index bebbcf7425c..54963f70e9e 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h
@@ -57,8 +57,7 @@ class CORE_EXPORT NGSimplifiedLayoutAlgorithm
const NGLayoutResult& previous_result_;
NGBoxStrut border_scrollbar_padding_;
- const WritingMode writing_mode_;
- const TextDirection direction_;
+ const WritingDirectionMode writing_direction_;
PhysicalSize previous_physical_container_size_;
};
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 bcda1289de2..1b4213395d2 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
@@ -25,6 +25,7 @@ bool AdjustToClearance(LayoutUnit clearance_offset, NGBfcOffset* offset) {
NGConstraintSpace CreateIndefiniteConstraintSpaceForChild(
const ComputedStyle& container_style,
+ const MinMaxSizesInput& input,
NGLayoutInputNode child) {
WritingMode parent_writing_mode = container_style.GetWritingMode();
WritingMode child_writing_mode = child.Style().GetWritingMode();
@@ -37,7 +38,8 @@ NGConstraintSpace CreateIndefiniteConstraintSpaceForChild(
builder.SetCacheSlot(NGCacheSlot::kMeasure);
builder.SetAvailableSize(indefinite_size);
- builder.SetPercentageResolutionSize(indefinite_size);
+ builder.SetPercentageResolutionSize(
+ {kIndefiniteSize, input.percentage_resolution_block_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_space_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h
index af96da0cb51..916a2898678 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.h
@@ -21,12 +21,14 @@ CORE_EXPORT bool AdjustToClearance(LayoutUnit clearance_offset,
NGBfcOffset* offset);
// Create a child constraint space with no sizing data, except for fallback
-// inline sizing for orthongonal flow roots. This will not and can not be used
-// for final layout, but is needed in an intermediate measure pass that
-// calculates the min/max size contribution from a child that establishes an
-// orthogonal flow root.
+// inline sizing for orthogonal flow roots and a percentage resolution block
+// size based on |input| (for calculating aspect-ratio based sizes). This will
+// not and can not be used for final layout, but is needed in an intermediate
+// measure pass that calculates the min/max size contribution from a child that
+// establishes an orthogonal flow root.
NGConstraintSpace CreateIndefiniteConstraintSpaceForChild(
const ComputedStyle& container_style,
+ const MinMaxSizesInput& input,
NGLayoutInputNode child);
// Calculate and set the available inline fallback size for orthogonal flow
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 b67b5756cc9..d45bd12d0f5 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
@@ -37,10 +37,12 @@ int NGTextDecorationOffset::ComputeUnderlineOffsetForUnder(
int offset_int = offset.Floor();
// Gaps are not needed for TextTop because it generally has internal
- // leadings.
+ // leadings. Overline needs to grow upwards, hence subtract thickness.
if (position_type == FontVerticalPositionType::TextTop)
- return offset_int;
- return !IsLineOverSide(position_type) ? offset_int + 1 : offset_int - 1;
+ return offset_int - floorf(text_decoration_thickness);
+ return !IsLineOverSide(position_type)
+ ? offset_int + 1
+ : offset_int - 1 - floorf(text_decoration_thickness);
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
index 5fed73b3c50..90f2147df54 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
@@ -14,6 +14,19 @@
namespace blink {
+namespace {
+
+inline bool NeedsTableSection(const LayoutObject& object) {
+ // Return true if 'object' can't exist in an anonymous table without being
+ // wrapped in a table section box.
+ EDisplay display = object.StyleRef().Display();
+ return display != EDisplay::kTableCaption &&
+ display != EDisplay::kTableColumnGroup &&
+ display != EDisplay::kTableColumn;
+}
+
+} // namespace
+
LayoutNGTable::LayoutNGTable(Element* element)
: LayoutNGMixin<LayoutBlock>(element) {}
@@ -33,6 +46,61 @@ void LayoutNGTable::UpdateBlockLayout(bool relayout_children) {
UpdateInFlowBlockLayout();
}
+void LayoutNGTable::AddChild(LayoutObject* child, LayoutObject* before_child) {
+ bool wrap_in_anonymous_section = !child->IsTableCaption() &&
+ !child->IsLayoutTableCol() &&
+ !child->IsTableSection();
+
+ if (!wrap_in_anonymous_section) {
+ if (before_child && before_child->Parent() != this)
+ before_child = SplitAnonymousBoxesAroundChild(before_child);
+ LayoutBox::AddChild(child, before_child);
+ return;
+ }
+
+ if (!before_child && LastChild() && LastChild()->IsTableSection() &&
+ LastChild()->IsAnonymous() && !LastChild()->IsBeforeContent()) {
+ LastChild()->AddChild(child);
+ return;
+ }
+
+ if (before_child && !before_child->IsAnonymous() &&
+ before_child->Parent() == this) {
+ LayoutNGTableSection* section =
+ DynamicTo<LayoutNGTableSection>(before_child->PreviousSibling());
+ if (section && section->IsAnonymous()) {
+ section->AddChild(child);
+ return;
+ }
+ }
+
+ LayoutObject* last_box = before_child;
+ while (last_box && last_box->Parent()->IsAnonymous() &&
+ !last_box->IsTableSection() && NeedsTableSection(*last_box))
+ last_box = last_box->Parent();
+ if (last_box && last_box->IsAnonymous() && last_box->IsTablePart() &&
+ !IsAfterContent(last_box)) {
+ if (before_child == last_box)
+ before_child = last_box->SlowFirstChild();
+ last_box->AddChild(child, before_child);
+ return;
+ }
+
+ if (before_child && !before_child->IsTableSection() &&
+ NeedsTableSection(*before_child))
+ before_child = nullptr;
+
+ LayoutBox* section =
+ LayoutObjectFactory::CreateAnonymousTableSectionWithParent(*this);
+ AddChild(section, before_child);
+ section->AddChild(child);
+}
+
+LayoutBox* LayoutNGTable::CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const {
+ return LayoutObjectFactory::CreateAnonymousTableWithParent(*parent);
+}
+
bool LayoutNGTable::IsFirstCell(const LayoutNGTableCellInterface& cell) const {
const LayoutNGTableRowInterface* row = cell.RowInterface();
if (row->FirstCellInterface() != &cell)
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
index 06186b60675..e82945eff9a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
@@ -33,6 +33,12 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>,
void UpdateBlockLayout(bool relayout_children) override;
+ void AddChild(LayoutObject* child,
+ LayoutObject* before_child = nullptr) override;
+
+ LayoutBox* CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const override;
+
// LayoutBlock methods end.
// LayoutNGTableInterface methods start.
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
index 8f501b93c79..cca5eb8198d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
@@ -37,6 +37,11 @@ void LayoutNGTableCell::ColSpanOrRowSpanChanged() {
UpdateColAndRowSpanFlags();
}
+LayoutBox* LayoutNGTableCell::CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const {
+ return LayoutObjectFactory::CreateAnonymousTableCellWithParent(*parent);
+}
+
Length LayoutNGTableCell::StyleOrColLogicalWidth() const {
// TODO(atotic) TablesNG cannot easily get col width before layout.
return StyleRef().LogicalWidth();
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
index 4c70aef0a31..c1295a4b73f 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
@@ -37,6 +37,9 @@ class CORE_EXPORT LayoutNGTableCell
// compat.
const char* GetName() const final { return "LayoutNGTableCellNew"; }
+ LayoutBox* CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const override;
+
// LayoutBlockFlow methods end.
// LayoutNGTableCellInterface methods start.
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc
index 48db1645c21..d790450ca6a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
+#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row_interface.h"
@@ -20,6 +21,57 @@ bool LayoutNGTableRow::IsEmpty() const {
return !FirstChild();
}
+void LayoutNGTableRow::AddChild(LayoutObject* child,
+ LayoutObject* before_child) {
+ if (!child->IsTableCell()) {
+ LayoutObject* last = before_child;
+ if (!last)
+ last = LastCell();
+ if (last && last->IsAnonymous() && last->IsTableCell() &&
+ !last->IsBeforeOrAfterContent()) {
+ LayoutBlockFlow* last_cell = To<LayoutBlockFlow>(last);
+ if (before_child == last_cell)
+ before_child = last_cell->FirstChild();
+ last_cell->AddChild(child, before_child);
+ return;
+ }
+
+ if (before_child && !before_child->IsAnonymous() &&
+ before_child->Parent() == this) {
+ LayoutObject* cell = before_child->PreviousSibling();
+ if (cell && cell->IsTableCell() && cell->IsAnonymous()) {
+ cell->AddChild(child);
+ return;
+ }
+ }
+
+ // If before_child is inside an anonymous cell, insert into the cell.
+ if (last && !last->IsTableCell() && last->Parent() &&
+ last->Parent()->IsAnonymous() &&
+ !last->Parent()->IsBeforeOrAfterContent()) {
+ last->Parent()->AddChild(child, before_child);
+ return;
+ }
+
+ LayoutBlockFlow* cell =
+ LayoutObjectFactory::CreateAnonymousTableCellWithParent(*this);
+ AddChild(cell, before_child);
+ cell->AddChild(child);
+ return;
+ }
+
+ if (before_child && before_child->Parent() != this)
+ before_child = SplitAnonymousBoxesAroundChild(before_child);
+
+ DCHECK(!before_child || before_child->IsTableCell());
+ LayoutNGMixin<LayoutBlock>::AddChild(child, before_child);
+}
+
+LayoutBox* LayoutNGTableRow::CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const {
+ return LayoutObjectFactory::CreateAnonymousTableRowWithParent(*parent);
+}
+
unsigned LayoutNGTableRow::RowIndex() const {
unsigned index = 0;
for (LayoutObject* child = Parent()->SlowFirstChild(); child;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h
index 8b08dd07582..14048ea90b6 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h
@@ -28,6 +28,12 @@ class CORE_EXPORT LayoutNGTableRow : public LayoutNGMixin<LayoutBlock>,
const char* GetName() const override { return "LayoutNGTableRow"; }
+ void AddChild(LayoutObject* child,
+ LayoutObject* before_child = nullptr) override;
+
+ LayoutBox* CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const override;
+
// Whether a row has opaque background depends on many factors, e.g. border
// spacing, border collapsing, missing cells, etc.
// For simplicity, just conservatively assume all table rows are not opaque.
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
index 06225acabcb..65b2c1fbbb3 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
+#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h"
#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
@@ -25,6 +26,58 @@ bool LayoutNGTableSection::IsEmpty() const {
return true;
}
+void LayoutNGTableSection::AddChild(LayoutObject* child,
+ LayoutObject* before_child) {
+ if (!child->IsTableRow()) {
+ LayoutObject* last = before_child;
+ if (!last)
+ last = LastChild();
+ if (last && last->IsAnonymous() && last->IsTablePart() &&
+ !last->IsBeforeOrAfterContent()) {
+ if (before_child == last)
+ before_child = last->SlowFirstChild();
+ last->AddChild(child, before_child);
+ return;
+ }
+
+ if (before_child && !before_child->IsAnonymous() &&
+ before_child->Parent() == this) {
+ LayoutObject* row = before_child->PreviousSibling();
+ if (row && row->IsTableRow() && row->IsAnonymous()) {
+ row->AddChild(child);
+ return;
+ }
+ }
+
+ // If before_child is inside an anonymous cell/row, insert into the cell or
+ // into the anonymous row containing it, if there is one.
+ LayoutObject* last_box = last;
+ while (last_box && last_box->Parent()->IsAnonymous() &&
+ !last_box->IsTableRow())
+ last_box = last_box->Parent();
+ if (last_box && last_box->IsAnonymous() &&
+ !last_box->IsBeforeOrAfterContent()) {
+ last_box->AddChild(child, before_child);
+ return;
+ }
+
+ LayoutObject* row =
+ LayoutObjectFactory::CreateAnonymousTableRowWithParent(*this);
+ AddChild(row, before_child);
+ row->AddChild(child);
+ return;
+ }
+ if (before_child && before_child->Parent() != this)
+ before_child = SplitAnonymousBoxesAroundChild(before_child);
+
+ LayoutNGMixin<LayoutBlock>::AddChild(child, before_child);
+}
+
+LayoutBox* LayoutNGTableSection::CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const {
+ return LayoutObjectFactory::CreateAnonymousTableSectionWithParent(*parent);
+}
+
LayoutNGTableInterface* LayoutNGTableSection::TableInterface() const {
return ToInterface<LayoutNGTableInterface>(Parent());
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h
index 64136c51551..8e9420392a3 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h
@@ -27,6 +27,12 @@ class CORE_EXPORT LayoutNGTableSection : public LayoutNGMixin<LayoutBlock>,
const char* GetName() const override { return "LayoutNGTableSection"; }
+ void AddChild(LayoutObject* child,
+ LayoutObject* before_child = nullptr) override;
+
+ LayoutBox* CreateAnonymousBoxWithSameTypeAs(
+ const LayoutObject* parent) const override;
+
bool AllowsOverflowClip() const override { return false; }
bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const override {
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
new file mode 100644
index 00000000000..1d54f04fbe6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -0,0 +1,631 @@
+// 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/table/ng_table_layout_algorithm_helpers.h"
+
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+
+namespace blink {
+
+namespace {
+
+// Implements spec distribution algorithm:
+// https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm
+void DistributeInlineSizeToComputedInlineSizeAuto(
+ LayoutUnit target_inline_size,
+ LayoutUnit inline_border_spacing,
+ NGTableTypes::Column* start_column,
+ NGTableTypes::Column* end_column,
+ NGTableTypes::Columns* column_constraints) {
+ if (column_constraints->size() == 0)
+ return;
+
+ unsigned all_columns_count = 0;
+ unsigned percent_columns_count = 0;
+ unsigned fixed_columns_count = 0;
+ unsigned auto_columns_count = 0;
+
+ // What guesses mean is described in table specification.
+ // https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm
+ enum { kMinGuess, kPercentageGuess, kSpecifiedGuess, kMaxGuess, kAboveMax };
+ // sizes are collected for all guesses except kAboveMax
+ LayoutUnit guess_sizes[kAboveMax];
+ LayoutUnit guess_size_total_increases[kAboveMax];
+ float total_percent = 0.0f;
+ LayoutUnit total_auto_max_inline_size;
+ LayoutUnit total_fixed_max_inline_size;
+
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ all_columns_count++;
+ if (!column->min_inline_size)
+ column->min_inline_size = LayoutUnit();
+ if (!column->max_inline_size)
+ column->max_inline_size = LayoutUnit();
+ if (column->percent) {
+ percent_columns_count++;
+ total_percent += *column->percent;
+ LayoutUnit percent_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ guess_sizes[kMinGuess] += *column->min_inline_size;
+ guess_sizes[kPercentageGuess] += percent_inline_size;
+ guess_sizes[kSpecifiedGuess] += percent_inline_size;
+ guess_sizes[kMaxGuess] += percent_inline_size;
+ guess_size_total_increases[kPercentageGuess] +=
+ percent_inline_size - *column->min_inline_size;
+ } else if (column->is_constrained) { // Fixed column
+ fixed_columns_count++;
+ total_fixed_max_inline_size += *column->max_inline_size;
+ guess_sizes[kMinGuess] += *column->min_inline_size;
+ guess_sizes[kPercentageGuess] += *column->min_inline_size;
+ guess_sizes[kSpecifiedGuess] += *column->max_inline_size;
+ guess_sizes[kMaxGuess] += *column->max_inline_size;
+ guess_size_total_increases[kSpecifiedGuess] +=
+ *column->max_inline_size - *column->min_inline_size;
+ } else { // Auto column
+ auto_columns_count++;
+ total_auto_max_inline_size += *column->max_inline_size;
+ guess_sizes[kMinGuess] += *column->min_inline_size;
+ guess_sizes[kPercentageGuess] += *column->min_inline_size;
+ guess_sizes[kSpecifiedGuess] += *column->min_inline_size;
+ guess_sizes[kMaxGuess] += *column->max_inline_size;
+ guess_size_total_increases[kMaxGuess] +=
+ *column->max_inline_size - *column->min_inline_size;
+ }
+ }
+ // Distributing inline sizes can never cause cells to be < min_inline_size.
+ // Target inline size must be wider than sum of min inline sizes.
+ // This is always true for assignable_table_inline_size, but not for
+ // colspan_cells.
+ target_inline_size = std::max(target_inline_size, guess_sizes[kMinGuess]);
+
+ unsigned starting_guess = kAboveMax;
+ for (unsigned i = kMinGuess; i != kAboveMax; ++i) {
+ if (guess_sizes[i] >= target_inline_size) {
+ starting_guess = i;
+ break;
+ }
+ }
+ switch (starting_guess) {
+ case kMinGuess: {
+ // All columns are min inline size.
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ column->computed_inline_size =
+ column->min_inline_size.value_or(LayoutUnit());
+ }
+ } break;
+ case kPercentageGuess: {
+ // Percent columns grow, auto/fixed get min inline size.
+ LayoutUnit percent_inline_size_increases =
+ guess_size_total_increases[kPercentageGuess];
+ LayoutUnit distributable_inline_size =
+ target_inline_size - guess_sizes[kMinGuess];
+ LayoutUnit rounding_error_inline_size = distributable_inline_size;
+ NGTableTypes::Column* last_column = nullptr;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ last_column = column;
+ LayoutUnit percent_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ LayoutUnit column_inline_size_increase =
+ percent_inline_size - *column->min_inline_size;
+ LayoutUnit delta;
+ if (percent_inline_size_increases != LayoutUnit()) {
+ delta = LayoutUnit(distributable_inline_size *
+ column_inline_size_increase.ToFloat() /
+ percent_inline_size_increases);
+ } else {
+ delta = LayoutUnit(distributable_inline_size.ToFloat() /
+ percent_columns_count);
+ }
+ rounding_error_inline_size -= delta;
+ column->computed_inline_size = *column->min_inline_size + delta;
+ } else {
+ // Auto/Fixed columns get min inline size.
+ column->computed_inline_size = *column->min_inline_size;
+ }
+ }
+ if (rounding_error_inline_size != LayoutUnit()) {
+ DCHECK(last_column);
+ last_column->computed_inline_size += rounding_error_inline_size;
+ }
+ } break;
+ case kSpecifiedGuess: {
+ // Fixed columns grow, auto gets min, percent gets %max
+ LayoutUnit fixed_inline_size_increase =
+ guess_size_total_increases[kSpecifiedGuess];
+ LayoutUnit distributable_inline_size =
+ target_inline_size - guess_sizes[kPercentageGuess];
+ LayoutUnit rounding_error_inline_size = distributable_inline_size;
+ NGTableTypes::Column* last_column = nullptr;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ column->computed_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ } else if (column->is_constrained) {
+ last_column = column;
+ LayoutUnit column_inline_size_increase =
+ *column->max_inline_size - *column->min_inline_size;
+ LayoutUnit delta;
+ if (fixed_inline_size_increase != LayoutUnit()) {
+ delta = LayoutUnit(distributable_inline_size *
+ column_inline_size_increase.ToFloat() /
+ fixed_inline_size_increase);
+ } else {
+ delta = LayoutUnit(distributable_inline_size.ToFloat() /
+ fixed_columns_count);
+ }
+ rounding_error_inline_size -= delta;
+ column->computed_inline_size = *column->min_inline_size + delta;
+ } else {
+ column->computed_inline_size = *column->min_inline_size;
+ }
+ }
+ if (rounding_error_inline_size != LayoutUnit()) {
+ DCHECK(last_column);
+ last_column->computed_inline_size += rounding_error_inline_size;
+ }
+ } break;
+ case kMaxGuess: {
+ // Auto columns grow, fixed gets max, percent gets %max
+ LayoutUnit auto_inline_size_increase =
+ guess_size_total_increases[kMaxGuess];
+ LayoutUnit distributable_inline_size =
+ target_inline_size - guess_sizes[kSpecifiedGuess];
+ LayoutUnit rounding_error_inline_size = distributable_inline_size;
+ NGTableTypes::Column* last_column = nullptr;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ column->computed_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ } else if (column->is_constrained) {
+ column->computed_inline_size = *column->max_inline_size;
+ } else {
+ last_column = column;
+ LayoutUnit column_inline_size_increase =
+ *column->max_inline_size - *column->min_inline_size;
+ LayoutUnit delta;
+ if (auto_inline_size_increase != LayoutUnit()) {
+ delta = LayoutUnit(distributable_inline_size *
+ column_inline_size_increase.ToFloat() /
+ auto_inline_size_increase);
+ } else {
+ delta = LayoutUnit(distributable_inline_size.ToFloat() /
+ auto_columns_count);
+ }
+ rounding_error_inline_size -= delta;
+ column->computed_inline_size = *column->min_inline_size + delta;
+ }
+ }
+ if (rounding_error_inline_size != LayoutUnit()) {
+ DCHECK(last_column);
+ last_column->computed_inline_size += rounding_error_inline_size;
+ }
+ } break;
+ case kAboveMax: {
+ LayoutUnit distributable_inline_size =
+ target_inline_size - guess_sizes[kMaxGuess];
+ if (auto_columns_count > 0) {
+ // Grow auto columns if available
+ LayoutUnit rounding_error_inline_size = distributable_inline_size;
+ NGTableTypes::Column* last_column = nullptr;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ column->computed_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ } else if (column->is_constrained) {
+ column->computed_inline_size = *column->max_inline_size;
+ } else {
+ last_column = column;
+ LayoutUnit delta;
+ if (total_auto_max_inline_size > LayoutUnit()) {
+ delta = LayoutUnit(distributable_inline_size *
+ (*column->max_inline_size).ToFloat() /
+ total_auto_max_inline_size);
+ } else {
+ delta = distributable_inline_size / auto_columns_count;
+ }
+ rounding_error_inline_size -= delta;
+ column->computed_inline_size = *column->max_inline_size + delta;
+ }
+ }
+ if (rounding_error_inline_size != LayoutUnit()) {
+ DCHECK(last_column);
+ last_column->computed_inline_size += rounding_error_inline_size;
+ }
+ } else if (fixed_columns_count > 0) {
+ // Grow fixed columns if available.
+ LayoutUnit rounding_error_inline_size = distributable_inline_size;
+ NGTableTypes::Column* last_column = nullptr;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ column->computed_inline_size =
+ column->ResolvePercentInlineSize(target_inline_size);
+ } else if (column->is_constrained) {
+ last_column = column;
+ LayoutUnit delta;
+ if (total_fixed_max_inline_size > LayoutUnit()) {
+ delta = LayoutUnit(distributable_inline_size *
+ (*column->max_inline_size).ToFloat() /
+ total_fixed_max_inline_size);
+ } else {
+ delta = distributable_inline_size / fixed_columns_count;
+ }
+ rounding_error_inline_size -= delta;
+ column->computed_inline_size = *column->max_inline_size + delta;
+ } else {
+ DCHECK(false);
+ }
+ }
+ if (rounding_error_inline_size != LayoutUnit()) {
+ DCHECK(last_column);
+ last_column->computed_inline_size += rounding_error_inline_size;
+ }
+ } else if (percent_columns_count > 0) {
+ // Grow percent columns.
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent) {
+ if (total_percent > 0.0f) {
+ column->computed_inline_size = LayoutUnit(
+ *column->percent / total_percent * target_inline_size);
+ } else {
+ column->computed_inline_size =
+ distributable_inline_size / percent_columns_count;
+ }
+ } else {
+ DCHECK(false);
+ }
+ }
+ }
+ }
+ }
+
+#if DCHECK_IS_ON()
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ DCHECK_NE(column->computed_inline_size, kIndefiniteSize);
+ }
+#endif
+}
+
+void SynchronizeAssignableTableInlineSizeAndColumnsFixed(
+ LayoutUnit target_inline_size,
+ LayoutUnit inline_border_spacing,
+ NGTableTypes::Column* start_column,
+ NGTableTypes::Column* end_column) {
+ DCHECK_NE(start_column, end_column);
+ unsigned all_columns_count = 0;
+ unsigned percent_columns_count = 0;
+ unsigned auto_columns_count = 0;
+ unsigned auto_empty_columns_count = 0;
+ unsigned fixed_columns_count = 0;
+
+ float total_percent = 0.0f;
+ LayoutUnit total_percent_inline_size;
+ LayoutUnit total_auto_max_inline_size;
+ LayoutUnit total_fixed_inline_size;
+ LayoutUnit assigned_inline_size;
+
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ all_columns_count++;
+ if (!column->min_inline_size)
+ column->min_inline_size = LayoutUnit();
+ if (!column->max_inline_size)
+ column->max_inline_size = LayoutUnit();
+ if (column->percent) {
+ percent_columns_count++;
+ total_percent += *column->percent;
+ total_percent_inline_size +=
+ LayoutUnit(*column->percent / 100 * target_inline_size);
+ } else if (column->is_constrained) { // Fixed column
+ fixed_columns_count++;
+ total_fixed_inline_size += *column->max_inline_size;
+ } else {
+ auto_columns_count++;
+ if (*column->max_inline_size == LayoutUnit())
+ auto_empty_columns_count++;
+ total_auto_max_inline_size += *column->max_inline_size;
+ }
+ }
+
+ NGTableTypes::Column* last_distributed_column = nullptr;
+ // Distribute to fixed columns.
+ if (fixed_columns_count > 0) {
+ float scale = 1.0f;
+ bool scale_available = true;
+ LayoutUnit target_fixed_size =
+ (target_inline_size - total_percent_inline_size).ClampNegativeToZero();
+ bool scale_up =
+ total_fixed_inline_size < target_fixed_size && auto_columns_count == 0;
+ // Fixed columns grow if there are no auto columns. They fill up space not
+ // taken up by percentage columns.
+ bool scale_down = total_fixed_inline_size > target_inline_size;
+ if (scale_up || scale_down) {
+ if (total_fixed_inline_size != LayoutUnit()) {
+ scale = target_fixed_size.ToFloat() / total_fixed_inline_size;
+ } else {
+ scale_available = false;
+ }
+ }
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (!column->IsFixed())
+ continue;
+ last_distributed_column = column;
+ if (scale_available) {
+ column->computed_inline_size =
+ LayoutUnit(scale * *column->max_inline_size);
+ } else {
+ DCHECK_EQ(fixed_columns_count, all_columns_count);
+ column->computed_inline_size =
+ LayoutUnit(target_inline_size.ToFloat() / fixed_columns_count);
+ }
+ assigned_inline_size += column->computed_inline_size;
+ }
+ }
+ if (assigned_inline_size >= target_inline_size)
+ return;
+ // Distribute to percent columns.
+ if (percent_columns_count > 0) {
+ float scale = 1.0f;
+ bool scale_available = true;
+ // Percent columns only grow if there are no auto columns.
+ bool scale_up = total_percent_inline_size <
+ (target_inline_size - assigned_inline_size) &&
+ auto_columns_count == 0;
+ bool scale_down =
+ total_percent_inline_size > (target_inline_size - assigned_inline_size);
+ if (scale_up || scale_down) {
+ if (total_percent_inline_size != LayoutUnit()) {
+ scale = (target_inline_size - assigned_inline_size).ToFloat() /
+ total_percent_inline_size;
+ } else {
+ scale_available = false;
+ }
+ }
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (!column->percent)
+ continue;
+ last_distributed_column = column;
+ if (scale_available) {
+ column->computed_inline_size =
+ LayoutUnit(scale * *column->percent / 100 * target_inline_size);
+ } else {
+ column->computed_inline_size =
+ LayoutUnit((target_inline_size - assigned_inline_size).ToFloat() /
+ percent_columns_count);
+ }
+ assigned_inline_size += column->computed_inline_size;
+ }
+ }
+ // Distribute to auto columns.
+ LayoutUnit distributing_inline_size =
+ target_inline_size - assigned_inline_size;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent || column->is_constrained)
+ continue;
+ last_distributed_column = column;
+ column->computed_inline_size =
+ LayoutUnit(distributing_inline_size / float(auto_columns_count));
+ assigned_inline_size += column->computed_inline_size;
+ }
+ LayoutUnit delta = target_inline_size - assigned_inline_size;
+ last_distributed_column->computed_inline_size += delta;
+}
+
+void DistributeColspanCellToColumnsFixed(
+ const NGTableTypes::ColspanCell& colspan_cell,
+ LayoutUnit inline_border_spacing,
+ NGTableTypes::Columns* column_constraints) {
+ // Fixed layout does not merge columns.
+ DCHECK_LE(colspan_cell.span,
+ column_constraints->size() - colspan_cell.start_column);
+ NGTableTypes::Column* start_column =
+ &(*column_constraints)[colspan_cell.start_column];
+ NGTableTypes::Column* end_column = start_column + colspan_cell.span;
+ DCHECK_NE(start_column, end_column);
+
+ LayoutUnit colspan_cell_min_inline_size;
+ LayoutUnit colspan_cell_max_inline_size;
+ if (colspan_cell.cell_inline_constraint.is_constrained) {
+ colspan_cell_min_inline_size =
+ (colspan_cell.cell_inline_constraint.min_inline_size -
+ (colspan_cell.span - 1) * inline_border_spacing)
+ .ClampNegativeToZero();
+ colspan_cell_max_inline_size =
+ (colspan_cell.cell_inline_constraint.max_inline_size -
+ (colspan_cell.span - 1) * inline_border_spacing)
+ .ClampNegativeToZero();
+ }
+
+ // Distribute min/max/percentage evenly between all cells.
+ // Colspanned cells only distribute min inline size if constrained.
+ LayoutUnit rounding_error_min_inline_size = colspan_cell_min_inline_size;
+ LayoutUnit rounding_error_max_inline_size = colspan_cell_max_inline_size;
+ float rounding_error_percent =
+ colspan_cell.cell_inline_constraint.percent.value_or(0.0f);
+
+ LayoutUnit new_min_size = LayoutUnit(colspan_cell_min_inline_size /
+ static_cast<float>(colspan_cell.span));
+ LayoutUnit new_max_size = LayoutUnit(colspan_cell_max_inline_size /
+ static_cast<float>(colspan_cell.span));
+ base::Optional<float> new_percent;
+ if (colspan_cell.cell_inline_constraint.percent) {
+ new_percent =
+ *colspan_cell.cell_inline_constraint.percent / colspan_cell.span;
+ }
+
+ NGTableTypes::Column* last_column;
+ for (NGTableTypes::Column* column = start_column; column < end_column;
+ ++column) {
+ last_column = column;
+ rounding_error_min_inline_size -= new_min_size;
+ rounding_error_max_inline_size -= new_max_size;
+ if (new_percent)
+ rounding_error_percent -= *new_percent;
+
+ if (!column->min_inline_size) {
+ column->is_constrained |=
+ colspan_cell.cell_inline_constraint.is_constrained;
+ column->min_inline_size = new_min_size;
+ }
+ if (!column->max_inline_size) {
+ column->is_constrained |=
+ colspan_cell.cell_inline_constraint.is_constrained;
+ column->max_inline_size = new_max_size;
+ }
+ if (!column->percent && new_percent)
+ column->percent = new_percent;
+ }
+ last_column->min_inline_size =
+ *last_column->min_inline_size + rounding_error_min_inline_size;
+ last_column->max_inline_size =
+ *last_column->max_inline_size + rounding_error_max_inline_size;
+ if (new_percent)
+ last_column->percent = *last_column->percent + rounding_error_percent;
+}
+
+void DistributeColspanCellToColumnsAuto(
+ const NGTableTypes::ColspanCell& colspan_cell,
+ LayoutUnit inline_border_spacing,
+ NGTableTypes::Columns* column_constraints) {
+ unsigned effective_span =
+ std::min(colspan_cell.span,
+ column_constraints->size() - colspan_cell.start_column);
+ NGTableTypes::Column* start_column =
+ &(*column_constraints)[colspan_cell.start_column];
+ NGTableTypes::Column* end_column = start_column + effective_span;
+
+ // Inline sizes for redistribution exclude border spacing.
+ LayoutUnit colspan_cell_min_inline_size =
+ (colspan_cell.cell_inline_constraint.min_inline_size -
+ (effective_span - 1) * inline_border_spacing)
+ .ClampNegativeToZero();
+ LayoutUnit colspan_cell_max_inline_size =
+ (colspan_cell.cell_inline_constraint.max_inline_size -
+ (effective_span - 1) * inline_border_spacing)
+ .ClampNegativeToZero();
+ base::Optional<float> colspan_cell_percent =
+ colspan_cell.cell_inline_constraint.percent;
+
+ if (colspan_cell_percent.has_value()) {
+ float columns_percent = 0.0f;
+ unsigned all_columns_count = 0;
+ unsigned percent_columns_count = 0;
+ unsigned nonpercent_columns_count = 0;
+ LayoutUnit nonpercent_columns_max_inline_size;
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (!column->max_inline_size)
+ column->max_inline_size = LayoutUnit();
+ if (!column->min_inline_size)
+ column->min_inline_size = LayoutUnit();
+ all_columns_count++;
+ if (column->percent) {
+ percent_columns_count++;
+ columns_percent += *column->percent;
+ } else {
+ nonpercent_columns_count++;
+ nonpercent_columns_max_inline_size += *column->max_inline_size;
+ }
+ }
+ float surplus_percent = *colspan_cell_percent - columns_percent;
+ if (surplus_percent > 0.0f && all_columns_count > percent_columns_count) {
+ // Distribute surplus percent to non-percent columns in proportion to
+ // max_inline_size.
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ if (column->percent)
+ continue;
+ float column_percent;
+ if (nonpercent_columns_max_inline_size != LayoutUnit()) {
+ // Column percentage is proportional to its max_inline_size.
+ column_percent = surplus_percent *
+ column->max_inline_size.value_or(LayoutUnit()) /
+ nonpercent_columns_max_inline_size;
+ } else {
+ // Distribute evenly instead.
+ // Legacy difference: Legacy forces max_inline_size to be at least
+ // 1px.
+ column_percent = surplus_percent / nonpercent_columns_count;
+ }
+ column->percent = column_percent;
+ }
+ }
+ }
+
+ // TODO(atotic) See crbug.com/531752 for discussion about differences
+ // between FF/Chrome.
+ // Minimum inline size gets distributed with standard distribution algorithm.
+ DistributeInlineSizeToComputedInlineSizeAuto(
+ colspan_cell_min_inline_size, inline_border_spacing, start_column,
+ end_column, column_constraints);
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ column->min_inline_size =
+ std::max(*column->min_inline_size, column->computed_inline_size);
+ }
+ DistributeInlineSizeToComputedInlineSizeAuto(
+ colspan_cell_max_inline_size, inline_border_spacing, start_column,
+ end_column, column_constraints);
+ for (NGTableTypes::Column* column = start_column; column != end_column;
+ ++column) {
+ column->max_inline_size =
+ std::max(*column->max_inline_size, column->computed_inline_size);
+ }
+}
+
+} // namespace
+
+void NGTableAlgorithmHelpers::DistributeColspanCellToColumns(
+ const NGTableTypes::ColspanCell& colspan_cell,
+ LayoutUnit inline_border_spacing,
+ bool is_fixed_layout,
+ NGTableTypes::Columns* column_constraints) {
+ // Clipped colspanned cells can end up having a span of 1 (which is not wide).
+ DCHECK_GT(colspan_cell.span, 1u);
+
+ if (is_fixed_layout) {
+ DistributeColspanCellToColumnsFixed(colspan_cell, inline_border_spacing,
+ column_constraints);
+ } else {
+ DistributeColspanCellToColumnsAuto(colspan_cell, inline_border_spacing,
+ column_constraints);
+ }
+}
+
+// Standard: https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm
+// After synchroniziation, assignable table inline size and sum of column
+// final inline sizes will be equal.
+void NGTableAlgorithmHelpers::SynchronizeAssignableTableInlineSizeAndColumns(
+ LayoutUnit assignable_table_inline_size,
+ LayoutUnit inline_border_spacing,
+ bool is_fixed_layout,
+ NGTableTypes::Columns* column_constraints) {
+ if (column_constraints->size() == 0)
+ return;
+ NGTableTypes::Column* start_column = &(*column_constraints)[0];
+ NGTableTypes::Column* end_column = start_column + column_constraints->size();
+ if (is_fixed_layout) {
+ SynchronizeAssignableTableInlineSizeAndColumnsFixed(
+ assignable_table_inline_size, inline_border_spacing, start_column,
+ end_column);
+ } else {
+ DistributeInlineSizeToComputedInlineSizeAuto(
+ assignable_table_inline_size, inline_border_spacing, start_column,
+ end_column, column_constraints);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
new file mode 100644
index 00000000000..dd1d9f0e694
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_HELPERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_HELPERS_H_
+
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h"
+#include "third_party/blink/renderer/core/style/computed_style_constants.h"
+
+namespace blink {
+
+// Table size distribution algorithms.
+class NGTableAlgorithmHelpers {
+ public:
+ // Compute maximum number of table columns that can deduced from
+ // single cell and its colspan.
+ static wtf_size_t ComputeMaxColumn(wtf_size_t current_column,
+ wtf_size_t colspan,
+ bool is_fixed_table_layout) {
+ // In fixed mode, every column is preserved.
+ if (is_fixed_table_layout)
+ return current_column + colspan;
+ return current_column + 1;
+ }
+
+ static void DistributeColspanCellToColumns(
+ const NGTableTypes::ColspanCell& colspan_cell,
+ LayoutUnit inline_border_spacing,
+ bool is_fixed_layout,
+ NGTableTypes::Columns* column_constraints);
+
+ static void SynchronizeAssignableTableInlineSizeAndColumns(
+ LayoutUnit assignable_table_inline_size,
+ LayoutUnit inline_border_spacing,
+ bool is_fixed_layout,
+ NGTableTypes::Columns* column_constraints);
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_HELPERS_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
new file mode 100644
index 00000000000..43df69d30d8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -0,0 +1,360 @@
+// 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/table/ng_table_layout_algorithm_types.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/table/layout_ng_table_caption.h"
+#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
+#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h"
+#include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+
+namespace blink {
+
+namespace {
+
+// Gathers css sizes. CSS values might be modified to enforce universal
+// invariants: css_max_inline_size >= css_min_inline_size
+// css_percentage_inline_size <= css_percentage_max_inline_size
+inline void InlineSizesFromStyle(
+ const ComputedStyle& style,
+ LayoutUnit inline_border_padding,
+ base::Optional<LayoutUnit>* inline_size,
+ base::Optional<LayoutUnit>* min_inline_size,
+ base::Optional<LayoutUnit>* max_inline_size,
+ base::Optional<float>* percentage_inline_size) {
+ const Length& length = style.LogicalWidth();
+ const Length& min_length = style.LogicalMinWidth();
+ const Length& max_length = style.LogicalMaxWidth();
+ bool is_content_box = style.BoxSizing() == EBoxSizing::kContentBox;
+ if (length.IsFixed()) {
+ *inline_size = LayoutUnit(length.Value());
+ if (is_content_box)
+ *inline_size = **inline_size + inline_border_padding;
+ }
+ if (min_length.IsFixed()) {
+ *min_inline_size = LayoutUnit(min_length.Value());
+ if (is_content_box)
+ *min_inline_size = **min_inline_size + inline_border_padding;
+ }
+ if (max_length.IsFixed()) {
+ *max_inline_size = LayoutUnit(max_length.Value());
+ if (is_content_box)
+ *max_inline_size = **max_inline_size + inline_border_padding;
+ if (*min_inline_size)
+ *max_inline_size = std::max(**min_inline_size, **max_inline_size);
+ }
+ if (length.IsPercent())
+ *percentage_inline_size = length.Percent();
+ if (*percentage_inline_size && max_length.IsPercent()) {
+ *percentage_inline_size =
+ std::min(**percentage_inline_size, max_length.Percent());
+ }
+ if (*min_inline_size && *max_inline_size)
+ DCHECK_GE(**max_inline_size, **min_inline_size);
+}
+
+} // namespace
+
+constexpr LayoutUnit NGTableTypes::kTableMaxInlineSize;
+
+// Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures
+// "outer min-content and outer max-content widths for colgroups"
+NGTableTypes::Column NGTableTypes::CreateColumn(
+ const ComputedStyle& style,
+ bool is_fixed_layout,
+ base::Optional<LayoutUnit> default_inline_size) {
+ base::Optional<LayoutUnit> inline_size;
+ base::Optional<LayoutUnit> min_inline_size;
+ base::Optional<LayoutUnit> max_inline_size;
+ base::Optional<float> percentage_inline_size;
+ InlineSizesFromStyle(style, LayoutUnit(), &inline_size, &min_inline_size,
+ &max_inline_size, &percentage_inline_size);
+ if (!inline_size)
+ inline_size = default_inline_size;
+ if (min_inline_size && inline_size)
+ inline_size = std::max(*inline_size, *min_inline_size);
+ bool is_constrained = inline_size.has_value();
+ if (percentage_inline_size && *percentage_inline_size == 0.0f)
+ percentage_inline_size.reset();
+ return Column{min_inline_size.value_or(LayoutUnit()), inline_size,
+ percentage_inline_size, is_constrained, kIndefiniteSize};
+}
+
+// Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures
+// "outer min-content and outer max-content widths for table cells"
+// Note: this method calls NGBlockNode::ComputeMinMaxSizes.
+NGTableTypes::CellInlineConstraint NGTableTypes::CreateCellInlineConstraint(
+ const NGLayoutInputNode& node,
+ WritingMode table_writing_mode,
+ bool is_fixed_layout,
+ const NGBoxStrut& cell_border,
+ const NGBoxStrut& cell_padding,
+ bool is_collapsed) {
+ base::Optional<LayoutUnit> css_inline_size;
+ base::Optional<LayoutUnit> css_min_inline_size;
+ base::Optional<LayoutUnit> css_max_inline_size;
+ base::Optional<float> css_percentage_inline_size;
+
+ // Algorithm:
+ // - Compute cell's minmax sizes.
+ // - Constrain by css inline-size/max-inline-size.
+ InlineSizesFromStyle(node.Style(), (cell_border + cell_padding).InlineSum(),
+ &css_inline_size, &css_min_inline_size,
+ &css_max_inline_size, &css_percentage_inline_size);
+
+ MinMaxSizesInput input(kIndefiniteSize, MinMaxSizesType::kContent);
+ MinMaxSizesResult min_max_size;
+ if (is_collapsed) {
+ NGConstraintSpaceBuilder builder(table_writing_mode,
+ node.Style().GetWritingMode(),
+ /* is_new_fc */ false);
+ builder.SetTableCellBorders(cell_border);
+ builder.SetIsTableCell(true);
+ NGConstraintSpace space = builder.ToConstraintSpace();
+ // It'd be nice to avoid computing minmax if not needed, but the criteria
+ // is not clear.
+ min_max_size = To<NGBlockNode>(node).ComputeMinMaxSizes(table_writing_mode,
+ input, &space);
+ } else {
+ min_max_size = node.ComputeMinMaxSizes(table_writing_mode, input);
+ }
+
+ // Compute min inline size.
+ LayoutUnit resolved_min_inline_size;
+ if (!is_fixed_layout) {
+ resolved_min_inline_size =
+ std::max(min_max_size.sizes.min_size,
+ css_min_inline_size.value_or(LayoutUnit()));
+ // https://quirks.spec.whatwg.org/#the-table-cell-nowrap-minimum-width-calculation-quirk
+ if (css_inline_size && node.GetDocument().InQuirksMode()) {
+ bool has_nowrap_attribute =
+ !To<Element>(node.GetLayoutBox()->GetNode())
+ ->FastGetAttribute(html_names::kNowrapAttr)
+ .IsNull();
+ if (has_nowrap_attribute && node.Style().AutoWrap()) {
+ resolved_min_inline_size =
+ std::max(resolved_min_inline_size, *css_inline_size);
+ }
+ }
+ }
+
+ // Compute resolved max inline size.
+ LayoutUnit content_max;
+ if (css_inline_size) {
+ content_max = *css_inline_size;
+ } else {
+ content_max = min_max_size.sizes.max_size;
+ }
+ if (css_max_inline_size)
+ content_max = std::min(content_max, *css_max_inline_size);
+ LayoutUnit resolved_max_inline_size =
+ std::max(resolved_min_inline_size, content_max);
+
+ bool is_constrained = css_inline_size.has_value();
+
+ DCHECK_LE(resolved_min_inline_size, resolved_max_inline_size);
+ return NGTableTypes::CellInlineConstraint{
+ resolved_min_inline_size, resolved_max_inline_size,
+ css_percentage_inline_size, is_constrained};
+}
+
+NGTableTypes::Section NGTableTypes::CreateSection(
+ const NGLayoutInputNode& section,
+ wtf_size_t start_row,
+ wtf_size_t rows,
+ LayoutUnit block_size) {
+ const Length& section_css_block_size = section.Style().LogicalHeight();
+ bool is_constrained = section_css_block_size.IsSpecified();
+ base::Optional<float> percent;
+ if (section_css_block_size.IsPercent())
+ percent = section_css_block_size.Percent();
+ bool is_tbody =
+ section.GetLayoutBox()->GetNode()->HasTagName(html_names::kTbodyTag);
+ return Section{start_row,
+ rows,
+ block_size,
+ percent,
+ is_constrained,
+ is_tbody,
+ /* needs_redistribution */ false};
+}
+
+NGTableTypes::CellBlockConstraint NGTableTypes::CreateCellBlockConstraint(
+ const NGLayoutInputNode& node,
+ LayoutUnit computed_block_size,
+ LayoutUnit baseline,
+ const NGBoxStrut& border_box_borders,
+ wtf_size_t row_index,
+ wtf_size_t column_index,
+ wtf_size_t rowspan) {
+ bool is_constrained = node.Style().LogicalHeight().IsFixed();
+ return CellBlockConstraint{computed_block_size,
+ baseline,
+ border_box_borders,
+ row_index,
+ column_index,
+ rowspan,
+ node.Style().VerticalAlign(),
+ is_constrained};
+}
+
+NGTableTypes::RowspanCell NGTableTypes::CreateRowspanCell(
+ wtf_size_t row_index,
+ wtf_size_t rowspan,
+ CellBlockConstraint* cell_block_constraint,
+ base::Optional<LayoutUnit> css_cell_block_size) {
+ if (css_cell_block_size) {
+ cell_block_constraint->min_block_size =
+ std::max(cell_block_constraint->min_block_size, *css_cell_block_size);
+ }
+ return RowspanCell{row_index, rowspan, *cell_block_constraint};
+}
+
+void NGTableTypes::CellInlineConstraint::Encompass(
+ const NGTableTypes::CellInlineConstraint& other) {
+ // Standard says:
+ // "A column is constrained if any of the cells spanning only that column has
+ // a computed width that is not "auto", and is not a percentage. This means
+ // that <td width=50></td><td max-width=100> would be treated with constrained
+ // column with width of 100.
+ if (other.min_inline_size > min_inline_size)
+ min_inline_size = other.min_inline_size;
+ if (is_constrained == other.is_constrained) {
+ max_inline_size = std::max(max_inline_size, other.max_inline_size);
+ } else if (is_constrained) {
+ max_inline_size = std::max(max_inline_size, other.min_inline_size);
+ } else {
+ DCHECK(other.is_constrained);
+ max_inline_size = std::max(min_inline_size, other.max_inline_size);
+ }
+ is_constrained = is_constrained || other.is_constrained;
+ max_inline_size = std::max(max_inline_size, other.max_inline_size);
+ percent = std::max(percent, other.percent);
+}
+
+void NGTableTypes::Column::Encompass(
+ const base::Optional<NGTableTypes::CellInlineConstraint>& cell) {
+ if (!cell)
+ return;
+
+ if (min_inline_size) {
+ if (min_inline_size < cell->min_inline_size) {
+ min_inline_size = cell->min_inline_size;
+ }
+ if (is_constrained) {
+ if (cell->is_constrained)
+ max_inline_size = std::max(*max_inline_size, cell->max_inline_size);
+ else
+ max_inline_size = std::max(*max_inline_size, cell->min_inline_size);
+ } else { // !is_constrained
+ max_inline_size = std::max(max_inline_size.value_or(LayoutUnit()),
+ cell->max_inline_size);
+ }
+ } else {
+ min_inline_size = cell->min_inline_size;
+ max_inline_size = cell->max_inline_size;
+ }
+ if (min_inline_size && max_inline_size) {
+ max_inline_size = std::max(*min_inline_size, *max_inline_size);
+ }
+ if (percent) {
+ if (cell->percent)
+ percent = std::max(*cell->percent, *percent);
+ } else {
+ percent = cell->percent;
+ }
+ is_constrained |= cell->is_constrained;
+}
+
+NGTableGroupedChildren::NGTableGroupedChildren(const NGBlockNode& table) {
+ for (NGLayoutInputNode child = table.FirstChild(); child;
+ child = child.NextSibling()) {
+ NGBlockNode block_child = To<NGBlockNode>(child);
+ if (block_child.IsTableCaption()) {
+ captions.push_back(block_child);
+ } else {
+ switch (child.Style().Display()) {
+ case EDisplay::kTableColumn:
+ case EDisplay::kTableColumnGroup:
+ columns.push_back(block_child);
+ break;
+ case EDisplay::kTableHeaderGroup:
+ headers.push_back(block_child);
+ break;
+ case EDisplay::kTableRowGroup:
+ bodies.push_back(block_child);
+ break;
+ case EDisplay::kTableFooterGroup:
+ footers.push_back(block_child);
+ break;
+ default:
+ NOTREACHED() << "unexpected table child";
+ }
+ }
+ }
+}
+
+NGTableGroupedChildrenIterator NGTableGroupedChildren::begin() const {
+ return NGTableGroupedChildrenIterator(*this);
+}
+
+NGTableGroupedChildrenIterator NGTableGroupedChildren::end() const {
+ return NGTableGroupedChildrenIterator(*this, /* is_end */ true);
+}
+
+NGTableGroupedChildrenIterator::NGTableGroupedChildrenIterator(
+ const NGTableGroupedChildren& grouped_children,
+ bool is_end)
+ : grouped_children_(grouped_children), current_vector_(nullptr) {
+ if (is_end) {
+ current_vector_ = &grouped_children_.footers;
+ current_iterator_ = current_vector_->end();
+ return;
+ }
+ AdvanceToNonEmptySection();
+}
+
+NGTableGroupedChildrenIterator& NGTableGroupedChildrenIterator::operator++() {
+ ++current_iterator_;
+ if (current_iterator_ == current_vector_->end())
+ AdvanceToNonEmptySection();
+ return *this;
+}
+
+NGBlockNode NGTableGroupedChildrenIterator::operator*() const {
+ return *current_iterator_;
+}
+
+bool NGTableGroupedChildrenIterator::operator==(
+ const NGTableGroupedChildrenIterator& rhs) const {
+ return rhs.current_vector_ == current_vector_ &&
+ rhs.current_iterator_ == current_iterator_;
+}
+
+bool NGTableGroupedChildrenIterator::operator!=(
+ const NGTableGroupedChildrenIterator& rhs) const {
+ return !(*this == rhs);
+}
+
+void NGTableGroupedChildrenIterator::AdvanceToNonEmptySection() {
+ if (current_vector_ == &grouped_children_.footers)
+ return;
+ if (!current_vector_) {
+ current_vector_ = &grouped_children_.headers;
+ } else if (current_vector_ == &grouped_children_.headers) {
+ current_vector_ = &grouped_children_.bodies;
+ } else if (current_vector_ == &grouped_children_.bodies) {
+ current_vector_ = &grouped_children_.footers;
+ }
+ current_iterator_ = current_vector_->begin();
+ // If new group is empty, recursively advance.
+ if (current_iterator_ == current_vector_->end()) {
+ AdvanceToNonEmptySection();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
new file mode 100644
index 00000000000..715a7337dd3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h
@@ -0,0 +1,287 @@
+// 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_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_
+
+#include "base/optional.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/style/computed_style_constants.h"
+#include "third_party/blink/renderer/platform/geometry/length.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class ComputedStyle;
+class NGBlockNode;
+class NGLayoutInputNode;
+
+// Define constraint classes for NGTableLayoutAlgorithm.
+class NGTableTypes {
+ public:
+ static constexpr LayoutUnit kTableMaxInlineSize = LayoutUnit::Max();
+
+ // Inline constraint for a single cell.
+ // Takes into account the cell style, and min/max content-sizes.
+ struct CellInlineConstraint {
+ DISALLOW_NEW();
+ LayoutUnit min_inline_size;
+ LayoutUnit max_inline_size;
+ base::Optional<float> percent; // 100% is stored as 100.0f
+ bool is_constrained; // True if this cell has a specified inline-size.
+
+ void Encompass(const CellInlineConstraint&);
+ };
+
+ // Inline constraints for a cell that span multiple columns.
+ struct ColspanCell {
+ DISALLOW_NEW();
+ CellInlineConstraint cell_inline_constraint;
+ wtf_size_t start_column;
+ wtf_size_t span;
+ ColspanCell(const CellInlineConstraint& cell_inline_constraint,
+ unsigned start_column,
+ unsigned span)
+ : cell_inline_constraint(cell_inline_constraint),
+ start_column(start_column),
+ span(span) {}
+ // ColspanCells are distributed in column order.
+ bool operator<(const NGTableTypes::ColspanCell& rhs) const {
+ // '<' means left to right sort.
+ // Legacy sorts right-to-left, FF, Edge left-to-right.
+ if (span == rhs.span)
+ return start_column < rhs.start_column;
+ return span < rhs.span;
+ }
+ };
+
+ // Constraint for a column.
+ struct Column {
+ DISALLOW_NEW();
+ // These members are initialized from <col> and <colgroup>, then they
+ // accumulate data from |CellInlineConstraint|s.
+ base::Optional<LayoutUnit> min_inline_size;
+ base::Optional<LayoutUnit> max_inline_size;
+ base::Optional<float> percent; // 100% is stored as 100.0f
+ // True if any cell for this column is constrained.
+ bool is_constrained = false;
+
+ // The final inline-size of the column after all constraints have been
+ // applied.
+ LayoutUnit computed_inline_size;
+ void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&);
+ LayoutUnit ResolvePercentInlineSize(
+ LayoutUnit percentage_resolution_inline_size) {
+ return std::max(
+ min_inline_size.value_or(LayoutUnit()),
+ LayoutUnit(*percent * percentage_resolution_inline_size / 100));
+ }
+ bool IsFixed() const {
+ return is_constrained && !percent && max_inline_size;
+ }
+ };
+
+ // Block constraint for a single cell.
+ struct CellBlockConstraint {
+ DISALLOW_NEW();
+ LayoutUnit min_block_size;
+ LayoutUnit baseline;
+ NGBoxStrut border_box_borders;
+ wtf_size_t row_index;
+ wtf_size_t column_index;
+ wtf_size_t rowspan;
+ EVerticalAlign vertical_align;
+ bool is_constrained; // True if this cell has a specified block-size.
+ CellBlockConstraint(LayoutUnit min_block_size,
+ LayoutUnit baseline,
+ NGBoxStrut border_box_borders,
+ wtf_size_t row_index,
+ wtf_size_t column_index,
+ wtf_size_t rowspan,
+ EVerticalAlign vertical_align,
+ bool is_constrained)
+ : min_block_size(min_block_size),
+ baseline(baseline),
+ border_box_borders(border_box_borders),
+ row_index(row_index),
+ column_index(column_index),
+ rowspan(rowspan),
+ vertical_align(vertical_align),
+ is_constrained(is_constrained) {}
+ };
+
+ // RowspanCells span multiple rows.
+ struct RowspanCell {
+ DISALLOW_NEW();
+ CellBlockConstraint cell_block_constraint;
+ wtf_size_t start_row;
+ wtf_size_t span;
+ RowspanCell(wtf_size_t start_row,
+ wtf_size_t span,
+ const CellBlockConstraint& cell_block_constraint)
+ : cell_block_constraint(cell_block_constraint),
+ start_row(start_row),
+ span(span) {}
+
+ // Original Legacy sorting criteria from
+ // CompareRowspanCellsInHeightDistributionOrder
+ bool operator<(const NGTableTypes::RowspanCell& rhs) const {
+ // Returns true if a |RowspanCell| is completely contained within another
+ // |RowspanCell|.
+ auto IsEnclosed = [](const NGTableTypes::RowspanCell& c1,
+ const NGTableTypes::RowspanCell& c2) {
+ return (c1.start_row >= c2.start_row) &&
+ (c1.start_row + c1.span) <= (c2.start_row + c2.span);
+ };
+
+ // If cells span the same rows, bigger cell is distributed first.
+ if (start_row == rhs.start_row && span == rhs.span) {
+ return cell_block_constraint.min_block_size >
+ rhs.cell_block_constraint.min_block_size;
+ }
+ // If one cell is fully enclosed by another, inner cell wins.
+ if (IsEnclosed(*this, rhs))
+ return true;
+ if (IsEnclosed(rhs, *this))
+ return false;
+ // Lower rows wins.
+ return start_row < rhs.start_row;
+ }
+ };
+
+ struct Row {
+ DISALLOW_NEW();
+ LayoutUnit block_size;
+ LayoutUnit baseline;
+ base::Optional<float> percent; // 100% is stored as 100.0f
+ wtf_size_t start_cell_index;
+ wtf_size_t cell_count;
+ // |is_constrained| is true if row has specified block-size, or contains
+ // constrained cells.
+ bool is_constrained;
+ bool has_baseline_aligned_percentage_block_size_descendants;
+ bool has_rowspan_start; // True if row originates a TD with rowspan > 1
+ bool is_collapsed;
+ };
+
+ struct ColumnLocation {
+ LayoutUnit offset; // inline offset from table edge.
+ LayoutUnit size;
+ };
+
+ struct Section {
+ wtf_size_t start_row;
+ wtf_size_t rowspan;
+ LayoutUnit block_size;
+ base::Optional<float> percent;
+ bool is_constrained;
+ bool is_tbody;
+ bool needs_redistribution;
+ };
+
+ static Column CreateColumn(const ComputedStyle&,
+ bool is_fixed_layout,
+ base::Optional<LayoutUnit> default_inline_size);
+
+ static CellInlineConstraint CreateCellInlineConstraint(
+ const NGLayoutInputNode&,
+ WritingMode table_writing_mode,
+ bool is_fixed_layout,
+ const NGBoxStrut& cell_border,
+ const NGBoxStrut& cell_padding,
+ bool is_collapsed);
+
+ static Section CreateSection(const NGLayoutInputNode&,
+ wtf_size_t start_row,
+ wtf_size_t rowspan,
+ LayoutUnit block_size);
+
+ static CellBlockConstraint CreateCellBlockConstraint(
+ const NGLayoutInputNode&,
+ LayoutUnit computed_block_size,
+ LayoutUnit baseline,
+ const NGBoxStrut& border_box_borders,
+ wtf_size_t row_index,
+ wtf_size_t column_index,
+ wtf_size_t rowspan);
+
+ static RowspanCell CreateRowspanCell(
+ wtf_size_t row_index,
+ wtf_size_t rowspan,
+ CellBlockConstraint*,
+ base::Optional<LayoutUnit> css_block_size);
+
+ using Columns = Vector<Column>;
+ // Inline constraints are optional because we need to distinguish between an
+ // empty cell, and a non-existent cell.
+ using CellInlineConstraints = Vector<base::Optional<CellInlineConstraint>>;
+ using ColspanCells = Vector<ColspanCell>;
+ using Caption = MinMaxSizes;
+ using CellBlockConstraints = Vector<CellBlockConstraint>;
+ using RowspanCells = Vector<RowspanCell>;
+ using Rows = Vector<Row>;
+ using Sections = Vector<Section>;
+ using ColumnLocations = Vector<ColumnLocation>;
+};
+
+class NGTableGroupedChildrenIterator;
+
+// Table's children grouped by type.
+// When iterating through members, make sure to handle out_of_flows correctly.
+struct NGTableGroupedChildren {
+ DISALLOW_NEW();
+
+ public:
+ explicit NGTableGroupedChildren(const NGBlockNode& table);
+
+ Vector<NGBlockNode> captions; // CAPTION
+ Vector<NGBlockNode> columns; // COLGROUP, COL
+
+ Vector<NGBlockNode> headers; // THEAD
+ Vector<NGBlockNode> bodies; // TBODY
+ Vector<NGBlockNode> footers; // TFOOT
+
+ // Default iterators iterate over tbody-like (THEAD/TBODY/TFOOT) elements.
+ NGTableGroupedChildrenIterator begin() const;
+ NGTableGroupedChildrenIterator end() const;
+};
+
+// Iterates table's sections in order:
+// thead, tbody, tfoot
+class NGTableGroupedChildrenIterator {
+ public:
+ explicit NGTableGroupedChildrenIterator(
+ const NGTableGroupedChildren& grouped_children,
+ bool is_end = false);
+
+ NGTableGroupedChildrenIterator& operator++();
+ NGBlockNode operator*() const;
+ bool operator==(const NGTableGroupedChildrenIterator& rhs) const;
+ bool operator!=(const NGTableGroupedChildrenIterator& rhs) const;
+
+ private:
+ void AdvanceToNonEmptySection();
+ const NGTableGroupedChildren& grouped_children_;
+ const Vector<NGBlockNode>* current_vector_;
+ Vector<NGBlockNode>::const_iterator current_iterator_;
+};
+
+} // namespace blink
+
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGTableTypes::CellInlineConstraint)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGTableTypes::ColspanCell)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Column)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGTableTypes::CellBlockConstraint)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGTableTypes::RowspanCell)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Row)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGTableTypes::ColumnLocation)
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGTableTypes::Section)
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_LAYOUT_ALGORITHM_TYPES_H_