summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-07-31 15:50:41 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-08-30 12:35:23 +0000
commit7b2ffa587235a47d4094787d72f38102089f402a (patch)
tree30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/third_party/blink/renderer/core/layout/ng
parentd94af01c90575348c4e81a418257f254b6f8d225 (diff)
downloadqtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647 Reviewed-by: Michael Brüning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc99
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h122
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h17
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h28
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.cc123
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h66
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset_test.cc74
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.cc69
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h45
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc60
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.cc15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h70
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.cc59
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h87
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc119
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h65
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc61
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_test.cc74
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.cc19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h66
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/README.md6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc50
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc426
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h215
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc461
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.cc80
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.cc50
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h91
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.cc69
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h15
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.cc137
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h35
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc100
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc322
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc259
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h43
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc278
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h35
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc42
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc67
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc62
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc105
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc53
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc132
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h52
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc46
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.cc16
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc67
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc76
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h41
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item_test.cc50
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc52
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h17
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc27
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc108
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h14
-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.h2
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc900
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h88
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc579
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc49
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h30
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.cc322
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_block_node.h51
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc114
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h71
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc307
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h23
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc168
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h178
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h61
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder_test.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc204
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h83
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc115
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm_test.cc20
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc152
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h18
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment.h10
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc7
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc22
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h38
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.cc36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h39
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc145
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result.h79
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc544
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.cc372
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h48
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc279
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils.h98
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc36
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_link.h68
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc384
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h23
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part_test.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.cc70
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc190
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h67
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc129
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h157
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc146
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h121
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils_test.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc213
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h68
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_space_utils.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.cc1
168 files changed, 6956 insertions, 5802 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.cc
index 66d3f693cf8..d0d4e644037 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.cc
@@ -7,7 +7,8 @@
namespace blink {
bool NGExclusion::operator==(const NGExclusion& other) const {
- return std::tie(other.rect, other.type) == std::tie(rect, type);
+ return type == other.type && rect == other.rect &&
+ shape_data == other.shape_data;
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
index 592344b5576..549698b1fbe 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.cc
@@ -16,29 +16,33 @@ namespace {
//
// We don't explicitly check the inline-size/block-size of the opportunity as
// they are always produced in the order.
-void InsertOpportunity(const NGLayoutOpportunity& opportunity,
- Vector<NGLayoutOpportunity, 4>* opportunities) {
- if (opportunities->IsEmpty()) {
- opportunities->emplace_back(opportunity);
+void InsertClosedArea(
+ const NGExclusionSpaceInternal::NGClosedArea area,
+ Vector<NGExclusionSpaceInternal::NGClosedArea, 4>* areas) {
+ if (areas->IsEmpty()) {
+ areas->emplace_back(area);
return;
}
// We go backwards through the list as there is a higher probability that a
- // new opportunity will be at the end of the list.
- for (wtf_size_t j = opportunities->size() - 1; j >= 0; --j) {
- const NGLayoutOpportunity& other = opportunities->at(j);
- if (other.rect.BlockStartOffset() <= opportunity.rect.BlockStartOffset()) {
+ // new area will be at the end of the list.
+ for (wtf_size_t j = areas->size() - 1; j >= 0; --j) {
+ const NGExclusionSpaceInternal::NGClosedArea& other = areas->at(j);
+ if (other.opportunity.rect.BlockStartOffset() <=
+ area.opportunity.rect.BlockStartOffset()) {
#if DCHECK_IS_ON()
// If we have the same block-start offset ensure that the size of the
// opportunity doesn't violate the order.
- if (other.rect.BlockStartOffset() ==
- opportunity.rect.BlockStartOffset()) {
- DCHECK_LE(other.rect.BlockSize(), opportunity.rect.BlockSize());
- DCHECK_GE(other.rect.InlineSize(), opportunity.rect.InlineSize());
+ if (other.opportunity.rect.BlockStartOffset() ==
+ area.opportunity.rect.BlockStartOffset()) {
+ DCHECK_LE(other.opportunity.rect.BlockSize(),
+ area.opportunity.rect.BlockSize());
+ DCHECK_GE(other.opportunity.rect.InlineSize(),
+ area.opportunity.rect.InlineSize());
}
#endif
- opportunities->insert(j + 1, opportunity);
+ areas->insert(j + 1, area);
return;
}
}
@@ -172,7 +176,7 @@ NGLayoutOpportunity CreateLayoutOpportunity(
} // namespace
NGExclusionSpaceInternal::NGExclusionSpaceInternal()
- : exclusions_(RefVector<scoped_refptr<const NGExclusion>>::Create()),
+ : exclusions_(base::MakeRefCounted<NGExclusionPtrArray>()),
num_exclusions_(0),
track_shape_exclusions_(false),
derived_geometry_(nullptr) {}
@@ -219,23 +223,21 @@ NGExclusionSpaceInternal::DerivedGeometry::DerivedGeometry(
}
void NGExclusionSpaceInternal::Add(scoped_refptr<const NGExclusion> exclusion) {
- DCHECK_LE(num_exclusions_, exclusions_->size());
+ DCHECK_LE(num_exclusions_, exclusions_->data.size());
bool already_exists = false;
- if (num_exclusions_ < exclusions_->size()) {
- if (*exclusion == *exclusions_->at(num_exclusions_) &&
- !exclusion->shape_data) {
+ if (num_exclusions_ < exclusions_->data.size()) {
+ if (*exclusion == *exclusions_->data.at(num_exclusions_)) {
// We might be adding an exclusion seen in a previous layout pass.
already_exists = true;
} else {
// Perform a copy-on-write if the number of exclusions has gone out of
// sync.
- scoped_refptr<RefVector<scoped_refptr<const NGExclusion>>> exclusions =
- RefVector<scoped_refptr<const NGExclusion>>::Create();
- exclusions->GetMutableVector()->AppendRange(
- exclusions_->GetVector().begin(),
- exclusions_->GetVector().begin() + num_exclusions_);
+ scoped_refptr<NGExclusionPtrArray> exclusions =
+ base::MakeRefCounted<NGExclusionPtrArray>();
+ exclusions->data.AppendRange(exclusions_->data.begin(),
+ exclusions_->data.begin() + num_exclusions_);
std::swap(exclusions_, exclusions);
}
}
@@ -261,7 +263,7 @@ void NGExclusionSpaceInternal::Add(scoped_refptr<const NGExclusion> exclusion) {
std::max(last_float_block_start_, exclusion->rect.BlockStartOffset());
if (!already_exists)
- exclusions_->emplace_back(std::move(exclusion));
+ exclusions_->data.emplace_back(std::move(exclusion));
num_exclusions_++;
}
@@ -398,7 +400,9 @@ void NGExclusionSpaceInternal::DerivedGeometry::Add(
*shelf.shape_exclusions))
: nullptr);
- InsertOpportunity(opportunity, &opportunities_);
+ InsertClosedArea(NGClosedArea(opportunity, shelf.line_left_edges,
+ shelf.line_right_edges),
+ &areas_);
}
}
@@ -532,7 +536,7 @@ NGLayoutOpportunity
NGExclusionSpaceInternal::DerivedGeometry::FindLayoutOpportunity(
const NGBfcOffset& offset,
const LayoutUnit available_inline_size,
- const NGLogicalSize& minimum_size) const {
+ const LogicalSize& minimum_size) const {
// TODO(ikilpatrick): Determine what to do for a -ve available_inline_size.
NGLayoutOpportunity return_opportunity;
@@ -580,12 +584,12 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities(
const LayoutUnit available_inline_size,
const LambdaFunc& lambda) const {
auto* shelves_it = shelves_.begin();
- auto* opps_it = opportunities_.begin();
+ auto* areas_it = areas_.begin();
auto* const shelves_end = shelves_.end();
- auto* const opps_end = opportunities_.end();
+ auto* const areas_end = areas_.end();
- while (shelves_it != shelves_end || opps_it != opps_end) {
+ while (shelves_it != shelves_end || areas_it != areas_end) {
// We should never exhaust the opportunities list before the shelves list,
// as there is always an infinitely sized shelf at the very end.
DCHECK_NE(shelves_it, shelves_end);
@@ -596,22 +600,35 @@ void NGExclusionSpaceInternal::DerivedGeometry::IterateAllLayoutOpportunities(
continue;
}
- if (opps_it != opps_end) {
- const NGLayoutOpportunity& opportunity = *opps_it;
+ if (areas_it != areas_end) {
+ const NGClosedArea& area = *areas_it;
- if (!Intersects(opportunity, offset, available_inline_size)) {
- ++opps_it;
+ if (!Intersects(area.opportunity, offset, available_inline_size)) {
+ ++areas_it;
continue;
}
- // We always prefer the closed-off opportunity, instead of the shelf
+ LayoutUnit block_start_offset = std::max(
+ area.opportunity.rect.BlockStartOffset(), offset.block_offset);
+
+ // We always prefer the closed-off area opportunity, instead of the shelf
// opportunity if they exist at the some offset.
- if (opportunity.rect.BlockStartOffset() <= shelf.block_offset) {
- if (lambda(CreateLayoutOpportunity(opportunity, offset,
- available_inline_size)))
- return;
+ if (block_start_offset <=
+ std::max(shelf.block_offset, offset.block_offset)) {
+ LayoutUnit block_end_offset = area.opportunity.rect.BlockEndOffset();
+
+ bool has_solid_edges =
+ HasSolidEdges(area.line_left_edges, block_start_offset,
+ block_end_offset) &&
+ HasSolidEdges(area.line_right_edges, block_start_offset,
+ block_end_offset);
+ if (has_solid_edges) {
+ if (lambda(CreateLayoutOpportunity(area.opportunity, offset,
+ available_inline_size)))
+ return;
+ }
- ++opps_it;
+ ++areas_it;
continue;
}
}
@@ -636,9 +653,9 @@ NGExclusionSpaceInternal::GetDerivedGeometry() const {
if (!derived_geometry_) {
derived_geometry_ =
std::make_unique<DerivedGeometry>(track_shape_exclusions_);
- DCHECK_LE(num_exclusions_, exclusions_->size());
+ DCHECK_LE(num_exclusions_, exclusions_->data.size());
for (wtf_size_t i = 0; i < num_exclusions_; ++i)
- derived_geometry_->Add(*exclusions_->GetVector()[i]);
+ derived_geometry_->Add(*exclusions_->data[i]);
}
return *derived_geometry_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
index 01a7022a8fd..0d05cbcdee2 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h
@@ -13,12 +13,13 @@
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
-#include "third_party/blink/renderer/platform/wtf/ref_vector.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
typedef Vector<NGLayoutOpportunity, 8> LayoutOpportunityVector;
+typedef base::RefCountedData<WTF::Vector<scoped_refptr<const NGExclusion>>>
+ NGExclusionPtrArray;
// This class is an implementation detail. For use of the exclusion space,
// see NGExclusionSpace below. NGExclusionSpace was designed to be cheap
@@ -39,7 +40,7 @@ class CORE_EXPORT NGExclusionSpaceInternal {
NGLayoutOpportunity FindLayoutOpportunity(
const NGBfcOffset& offset,
const LayoutUnit available_inline_size,
- const NGLogicalSize& minimum_size) const {
+ const LogicalSize& minimum_size) const {
// If the area clears all floats, we can just return the layout opportunity
// which matches the available space.
if (offset.block_offset >=
@@ -95,8 +96,8 @@ class CORE_EXPORT NGExclusionSpaceInternal {
// Pre-initializes the exclusions vector to something used in a previous
// layout pass, however keeps the number of exclusions as zero.
void PreInitialize(const NGExclusionSpaceInternal& other) {
- DCHECK_EQ(exclusions_->size(), 0u);
- DCHECK_GT(other.exclusions_->size(), 0u);
+ DCHECK(exclusions_->data.IsEmpty());
+ DCHECK_GT(other.exclusions_->data.size(), 0u);
exclusions_ = other.exclusions_;
}
@@ -106,13 +107,14 @@ class CORE_EXPORT NGExclusionSpaceInternal {
if (!other.derived_geometry_)
return;
+ track_shape_exclusions_ = other.track_shape_exclusions_;
derived_geometry_ = std::move(other.derived_geometry_);
other.derived_geometry_ = nullptr;
// Iterate through all the exclusions which were added by the layout, and
// update the DerivedGeometry.
for (wtf_size_t i = other.num_exclusions_; i < num_exclusions_; ++i) {
- const NGExclusion& exclusion = *exclusions_->at(i);
+ const NGExclusion& exclusion = *exclusions_->data.at(i);
// If we come across an exclusion with shape data, we opt-out of this
// optimization.
@@ -134,7 +136,8 @@ class CORE_EXPORT NGExclusionSpaceInternal {
// layout result.
for (wtf_size_t i = previous_input ? previous_input->num_exclusions_ : 0;
i < previous_output.num_exclusions_; ++i) {
- Add(previous_output.exclusions_->at(i)->CopyWithOffset(offset_delta));
+ Add(previous_output.exclusions_->data.at(i)->CopyWithOffset(
+ offset_delta));
}
}
@@ -143,6 +146,20 @@ class CORE_EXPORT NGExclusionSpaceInternal {
return !(*this == other);
}
+#if DCHECK_IS_ON()
+ void CheckSameForSimplifiedLayout(
+ const NGExclusionSpaceInternal& other) const {
+ DCHECK_EQ(num_exclusions_, other.num_exclusions_);
+ for (wtf_size_t i = 0; i < num_exclusions_; ++i) {
+ const auto& exclusion = *exclusions_->data.at(i);
+ const auto& other_exclusion = *other.exclusions_->data.at(i);
+ DCHECK(exclusion.rect == other_exclusion.rect);
+ DCHECK_EQ(exclusion.type, other_exclusion.type);
+ DCHECK_EQ((bool)exclusion.shape_data, (bool)other_exclusion.shape_data);
+ }
+ }
+#endif
+
// This struct represents the side of a float against the "edge" of a shelf.
struct NGShelfEdge {
NGShelfEdge(LayoutUnit block_start, LayoutUnit block_end)
@@ -224,6 +241,49 @@ class CORE_EXPORT NGExclusionSpaceInternal {
bool has_shape_exclusions;
};
+ // The closed-off area is an internal data-structure representing an area
+ // above a float. It contains a layout opportunity, and two vectors of
+ // |NGShelfEdge|. E.g.
+ //
+ // 0 1 2 3 4 5 6 7 8
+ // 0 +---+. .+---+
+ // |xxx|. .|xxx|
+ // 10 |xxx|. .|xxx|
+ // +---+. .+---+
+ // 20 ........
+ // +---+
+ // 30 |xxx|
+ // |xxx|
+ // 40 +---+
+ //
+ // In the above example the closed-off area is represented with the dotted
+ // line.
+ //
+ // It has the internal values of:
+ // {
+ // opportunity: {
+ // start_offset: {20, LayoutUnit::Min()},
+ // end_offset: {65, 25},
+ // }
+ // line_left_edges: [{0, 15}],
+ // line_right_edges: [{0, 15}],
+ // }
+ //
+ // Once a closed-off area has been created, it can never be changed due to
+ // the property that floats always align their block-start edges.
+ struct NGClosedArea {
+ NGClosedArea(NGLayoutOpportunity opportunity,
+ const Vector<NGShelfEdge, 1>& line_left_edges,
+ const Vector<NGShelfEdge, 1>& line_right_edges)
+ : opportunity(opportunity),
+ line_left_edges(line_left_edges),
+ line_right_edges(line_right_edges) {}
+
+ const NGLayoutOpportunity opportunity;
+ const Vector<NGShelfEdge, 1> line_left_edges;
+ const Vector<NGShelfEdge, 1> line_right_edges;
+ };
+
private:
// In order to reduce the amount of Vector copies, instances of a
// NGExclusionSpaceInternal can share the same exclusions_ Vector. See the
@@ -234,7 +294,7 @@ class CORE_EXPORT NGExclusionSpaceInternal {
//
// num_exclusions_ is how many exclusions *this* instance of an exclusion
// space has, which may differ to the number of exclusions in the Vector.
- scoped_refptr<RefVector<scoped_refptr<const NGExclusion>>> exclusions_;
+ scoped_refptr<NGExclusionPtrArray> exclusions_;
wtf_size_t num_exclusions_;
// These members are used for keeping track of the "lowest" offset for each
@@ -283,7 +343,7 @@ class CORE_EXPORT NGExclusionSpaceInternal {
NGLayoutOpportunity FindLayoutOpportunity(
const NGBfcOffset& offset,
const LayoutUnit available_inline_size,
- const NGLogicalSize& minimum_size) const;
+ const LogicalSize& minimum_size) const;
LayoutOpportunityVector AllLayoutOpportunities(
const NGBfcOffset& offset,
@@ -294,40 +354,22 @@ class CORE_EXPORT NGExclusionSpaceInternal {
const LayoutUnit available_inline_size,
const LambdaFunc&) const;
- // See NGShelf for a broad description of what shelves are. We always begin
- // with one, which has the internal value of:
+ // See |NGShelf| for a broad description of what shelves are. We always
+ // begin with one, which has the internal value of:
// {
// block_offset: LayoutUnit::Min(),
// line_left: LayoutUnit::Min(),
// line_right: LayoutUnit::Max(),
// }
//
- // The list of opportunities represent "closed-off" areas. E.g.
- //
- // 0 1 2 3 4 5 6 7 8
- // 0 +---+. .+---+
- // |xxx|. .|xxx|
- // 10 |xxx|. .|xxx|
- // +---+. .+---+
- // 20 ........
- // +---+
- // 30 |xxx|
- // |xxx|
- // 40 +---+
- //
- // In the above example the opportunity is represented with the dotted line.
- // It has the internal values of:
- // {
- // start_offset: {20, LayoutUnit::Min()},
- // end_offset: {65, 25},
- // }
- // Once an opportunity has been created, it can never been changed due to
- // the property that floats always align their block-start edges.
- //
- // We exploit this property by keeping this list of "closed-off" areas, and
- // removing shelves to make insertion faster.
Vector<NGShelf, 4> shelves_;
- Vector<NGLayoutOpportunity, 4> opportunities_;
+
+ // See |NGClosedArea| for a broad description of what closed-off areas are.
+ //
+ // Floats always align their block-start edges. We exploit this property by
+ // keeping a list of closed-off areas. Once a closed-off area has been
+ // created, it can never change.
+ Vector<NGClosedArea, 4> areas_;
bool track_shape_exclusions_;
};
@@ -378,7 +420,7 @@ class CORE_EXPORT NGExclusionSpace {
NGLayoutOpportunity FindLayoutOpportunity(
const NGBfcOffset& offset,
const LayoutUnit available_inline_size,
- const NGLogicalSize& minimum_size) const {
+ const LogicalSize& minimum_size) const {
if (!exclusion_space_) {
NGBfcOffset end_offset(
offset.line_offset + available_inline_size.ClampNegativeToZero(),
@@ -510,6 +552,14 @@ class CORE_EXPORT NGExclusionSpace {
return !(*this == other);
}
+#if DCHECK_IS_ON()
+ void CheckSameForSimplifiedLayout(const NGExclusionSpace& other) const {
+ DCHECK_EQ((bool)exclusion_space_, (bool)other.exclusion_space_);
+ if (exclusion_space_)
+ exclusion_space_->CheckSameForSimplifiedLayout(*other.exclusion_space_);
+ }
+#endif
+
private:
mutable std::unique_ptr<NGExclusionSpaceInternal> exclusion_space_;
};
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h
index 830ce4998ed..a54a2c4e1a0 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h
@@ -60,6 +60,13 @@ struct CORE_EXPORT NGLineLayoutOpportunity {
DCHECK_GE(float_line_right_offset, float_line_left_offset);
return float_line_right_offset - float_line_left_offset;
}
+
+ bool IsEqualToAvailableFloatInlineSize(LayoutUnit inline_size) const {
+ DCHECK_GE(float_line_right_offset, float_line_left_offset);
+ // Compare |line_right| isntead of |inline_size| to avoid returning |false|
+ // when |line_left + inline_size| exceeds |LayoutUnit::Max| and clamped.
+ return float_line_left_offset + inline_size == float_line_right_offset;
+ }
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.cc
index ac4f0548432..05270df8a34 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.cc
@@ -8,15 +8,6 @@
namespace blink {
-bool NGBfcOffset::operator==(const NGBfcOffset& other) const {
- return std::tie(other.line_offset, other.block_offset) ==
- std::tie(line_offset, block_offset);
-}
-
-bool NGBfcOffset::operator!=(const NGBfcOffset& other) const {
- return !operator==(other);
-}
-
String NGBfcOffset::ToString() const {
return String::Format("%dx%d", line_offset.ToInt(), block_offset.ToInt());
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h
index ded1830cab5..ee363f9357a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h
@@ -24,7 +24,7 @@ struct CORE_EXPORT NGBfcDelta {
// a block formatting context (BFC). BFCs are agnostic to text direction, and
// uses line_offset instead of inline_offset.
//
-// Care must be taken when converting this to a NGLogicalOffset to respect the
+// Care must be taken when converting this to a LogicalOffset to respect the
// text direction.
struct CORE_EXPORT NGBfcOffset {
NGBfcOffset() = default;
@@ -44,8 +44,12 @@ struct CORE_EXPORT NGBfcOffset {
block_offset + delta.block_offset_delta};
}
- bool operator==(const NGBfcOffset& other) const;
- bool operator!=(const NGBfcOffset& other) const;
+ bool operator==(const NGBfcOffset& other) const {
+ return std::tie(other.line_offset, other.block_offset) ==
+ std::tie(line_offset, block_offset);
+ }
+
+ bool operator!=(const NGBfcOffset& other) const { return !operator==(other); }
String ToString() const;
};
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.cc
deleted file mode 100644
index d470abab9ee..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.cc
+++ /dev/null
@@ -1,13 +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/geometry/ng_bfc_rect.h"
-
-namespace blink {
-
-bool NGBfcRect::operator==(const NGBfcRect& other) const {
- return start_offset == other.start_offset && end_offset == other.end_offset;
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h
index 03d6bb9a68e..eac77c3251f 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h
@@ -6,8 +6,8 @@
#define NGBfcRect_h
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
namespace blink {
@@ -39,7 +39,10 @@ struct CORE_EXPORT NGBfcRect {
return end_offset.line_offset - start_offset.line_offset;
}
- bool operator==(const NGBfcRect& other) const;
+ bool operator==(const NGBfcRect& other) const {
+ return start_offset == other.start_offset && end_offset == other.end_offset;
+ }
+
bool operator!=(const NGBfcRect& other) const { return !(*this == other); }
NGBfcOffset start_offset;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.cc
index 74607dc041f..4095506fb3a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.cc
@@ -41,10 +41,6 @@ NGLineBoxStrut::NGLineBoxStrut(const NGBoxStrut& flow_relative,
}
}
-LayoutRectOutsets NGPhysicalBoxStrut::ToLayoutRectOutsets() const {
- return LayoutRectOutsets(top, right, bottom, left);
-}
-
std::ostream& operator<<(std::ostream& stream, const NGLineBoxStrut& value) {
return stream << "Inline: (" << value.inline_start << " " << value.inline_end
<< ") Line: (" << value.line_over << " " << value.line_under
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h
index 78b342adff8..e4e8a9ed325 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h
@@ -8,14 +8,14 @@
#include <utility>
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/text/writing_mode.h"
namespace blink {
-class LayoutRectOutsets;
struct NGLineBoxStrut;
struct NGPhysicalBoxStrut;
@@ -43,7 +43,7 @@ struct CORE_EXPORT NGBoxStrut {
LayoutUnit InlineSum() const { return inline_start + inline_end; }
LayoutUnit BlockSum() const { return block_start + block_end; }
- NGLogicalOffset StartOffset() const { return {inline_start, block_start}; }
+ LogicalOffset StartOffset() const { return {inline_start, block_start}; }
bool IsEmpty() const { return *this == NGBoxStrut(); }
@@ -195,7 +195,16 @@ struct CORE_EXPORT NGPhysicalBoxStrut {
LayoutUnit HorizontalSum() const { return left + right; }
LayoutUnit VerticalSum() const { return top + bottom; }
- LayoutRectOutsets ToLayoutRectOutsets() const;
+ LayoutRectOutsets ToLayoutRectOutsets() const {
+ return LayoutRectOutsets(top, right, bottom, left);
+ }
+
+ bool operator==(const NGPhysicalBoxStrut& other) const {
+ return top == other.top && right == other.right && bottom == other.bottom &&
+ left == other.left;
+ }
+
+ bool IsZero() const { return !top && !right && !bottom && !left; }
LayoutUnit top;
LayoutUnit right;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h
new file mode 100644
index 00000000000..0c766c6279e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GEOMETRY_NG_FRAGMENT_GEOMETRY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GEOMETRY_NG_FRAGMENT_GEOMETRY_H_
+
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
+
+namespace blink {
+
+// This represents the initial (pre-layout) geometry of a fragment. E.g.
+// - The inline-size of the fragment.
+// - The block-size of the fragment (might be |kIndefiniteSize| if height is
+// 'auto' for example).
+// - The border, scrollbar, and padding.
+// This *doesn't* necessarily represent the final geometry of the fragment.
+struct NGFragmentGeometry {
+ LogicalSize border_box_size;
+ NGBoxStrut border;
+ NGBoxStrut scrollbar;
+ NGBoxStrut padding;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_GEOMETRY_NG_FRAGMENT_GEOMETRY_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.cc
deleted file mode 100644
index 8fead03022a..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2016 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/geometry/ng_logical_offset.h"
-
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-NGPhysicalOffset NGLogicalOffset::ConvertToPhysical(
- WritingMode mode,
- TextDirection direction,
- NGPhysicalSize outer_size,
- NGPhysicalSize inner_size) const {
- switch (mode) {
- case WritingMode::kHorizontalTb:
- if (direction == TextDirection::kLtr)
- return NGPhysicalOffset(inline_offset, block_offset);
- else
- return NGPhysicalOffset(
- outer_size.width - inline_offset - inner_size.width, block_offset);
- case WritingMode::kVerticalRl:
- case WritingMode::kSidewaysRl:
- if (direction == TextDirection::kLtr)
- return NGPhysicalOffset(
- outer_size.width - block_offset - inner_size.width, inline_offset);
- else
- return NGPhysicalOffset(
- outer_size.width - block_offset - inner_size.width,
- outer_size.height - inline_offset - inner_size.height);
- case WritingMode::kVerticalLr:
- if (direction == TextDirection::kLtr)
- return NGPhysicalOffset(block_offset, inline_offset);
- else
- return NGPhysicalOffset(
- block_offset,
- outer_size.height - inline_offset - inner_size.height);
- case WritingMode::kSidewaysLr:
- if (direction == TextDirection::kLtr)
- return NGPhysicalOffset(
- block_offset,
- outer_size.height - inline_offset - inner_size.height);
- else
- return NGPhysicalOffset(block_offset, inline_offset);
- default:
- NOTREACHED();
- return NGPhysicalOffset();
- }
-}
-
-bool NGLogicalOffset::operator==(const NGLogicalOffset& other) const {
- return std::tie(other.inline_offset, other.block_offset) ==
- std::tie(inline_offset, block_offset);
-}
-
-bool NGLogicalOffset::operator!=(const NGLogicalOffset& other) const {
- return !operator==(other);
-}
-
-NGLogicalOffset NGLogicalOffset::operator+(const NGLogicalOffset& other) const {
- NGLogicalOffset result;
- result.inline_offset = this->inline_offset + other.inline_offset;
- result.block_offset = this->block_offset + other.block_offset;
- return result;
-}
-
-NGLogicalOffset NGLogicalOffset::operator+(const NGLogicalSize& size) const {
- return {inline_offset + size.inline_size, block_offset + size.block_size};
-}
-
-NGLogicalOffset& NGLogicalOffset::operator+=(const NGLogicalOffset& other) {
- *this = *this + other;
- return *this;
-}
-
-NGLogicalOffset& NGLogicalOffset::operator+=(const NGLogicalSize& size) {
- *this = *this + size;
- return *this;
-}
-
-bool NGLogicalOffset::operator>(const NGLogicalOffset& other) const {
- return inline_offset > other.inline_offset &&
- block_offset > other.block_offset;
-}
-
-bool NGLogicalOffset::operator>=(const NGLogicalOffset& other) const {
- return inline_offset >= other.inline_offset &&
- block_offset >= other.block_offset;
-}
-
-bool NGLogicalOffset::operator<(const NGLogicalOffset& other) const {
- return inline_offset < other.inline_offset &&
- block_offset < other.block_offset;
-}
-
-bool NGLogicalOffset::operator<=(const NGLogicalOffset& other) const {
- return inline_offset <= other.inline_offset &&
- block_offset <= other.block_offset;
-}
-
-NGLogicalDelta NGLogicalOffset::operator-(const NGLogicalOffset& other) const {
- return {inline_offset - other.inline_offset,
- block_offset - other.block_offset};
-}
-
-NGLogicalOffset& NGLogicalOffset::operator-=(const NGLogicalOffset& other) {
- *this = *this - other;
- return *this;
-}
-
-String NGLogicalOffset::ToString() const {
- return String::Format("%d,%d", inline_offset.ToInt(), block_offset.ToInt());
-}
-
-std::ostream& operator<<(std::ostream& os, const NGLogicalOffset& value) {
- return os << value.ToString();
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h
deleted file mode 100644
index 1f3b38af731..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 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 NGLogicalOffset_h
-#define NGLogicalOffset_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
-#include "third_party/blink/renderer/platform/text/text_direction.h"
-#include "third_party/blink/renderer/platform/text/writing_mode.h"
-
-namespace blink {
-
-struct NGLogicalDelta;
-struct NGLogicalSize;
-struct NGPhysicalOffset;
-struct NGPhysicalSize;
-
-// NGLogicalOffset is the position of a rect (typically a fragment) relative to
-// its parent rect in the logical coordinate system.
-struct CORE_EXPORT NGLogicalOffset {
- NGLogicalOffset() = default;
- NGLogicalOffset(LayoutUnit inline_offset, LayoutUnit block_offset)
- : inline_offset(inline_offset), block_offset(block_offset) {}
-
- LayoutUnit inline_offset;
- LayoutUnit block_offset;
-
- // Converts a logical offset to a physical offset. See:
- // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
- // PhysicalOffset will be the physical top left point of the rectangle
- // described by offset + inner_size. Setting inner_size to 0,0 will return
- // the same point.
- // @param outer_size the size of the rect (typically a fragment).
- // @param inner_size the size of the inner rect (typically a child fragment).
- NGPhysicalOffset ConvertToPhysical(WritingMode,
- TextDirection,
- NGPhysicalSize outer_size,
- NGPhysicalSize inner_size) const;
-
- bool operator==(const NGLogicalOffset& other) const;
- bool operator!=(const NGLogicalOffset& other) const;
-
- NGLogicalOffset operator+(const NGLogicalOffset& other) const;
- NGLogicalOffset operator+(const NGLogicalSize& size) const;
- NGLogicalOffset& operator+=(const NGLogicalOffset& other);
- NGLogicalOffset& operator+=(const NGLogicalSize& size);
-
- NGLogicalDelta operator-(const NGLogicalOffset& other) const;
- NGLogicalOffset& operator-=(const NGLogicalOffset& other);
-
- bool operator>(const NGLogicalOffset& other) const;
- bool operator>=(const NGLogicalOffset& other) const;
-
- bool operator<(const NGLogicalOffset& other) const;
- bool operator<=(const NGLogicalOffset& other) const;
-
- String ToString() const;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGLogicalOffset&);
-
-} // namespace blink
-
-#endif // NGLogicalOffset_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset_test.cc
deleted file mode 100644
index 66fd969b333..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset_test.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2016 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/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-TEST(NGGeometryUnitsTest, ConvertLogicalOffsetToPhysicalOffset) {
- NGLogicalOffset logical_offset(LayoutUnit(20), LayoutUnit(30));
- NGPhysicalSize outer_size(LayoutUnit(300), LayoutUnit(400));
- NGPhysicalSize inner_size(LayoutUnit(5), LayoutUnit(65));
- NGPhysicalOffset offset;
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kHorizontalTb, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(20), offset.left);
- EXPECT_EQ(LayoutUnit(30), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kHorizontalTb, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(275), offset.left);
- EXPECT_EQ(LayoutUnit(30), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kVerticalRl, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(265), offset.left);
- EXPECT_EQ(LayoutUnit(20), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kVerticalRl, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(265), offset.left);
- EXPECT_EQ(LayoutUnit(315), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kSidewaysRl, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(265), offset.left);
- EXPECT_EQ(LayoutUnit(20), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kSidewaysRl, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(265), offset.left);
- EXPECT_EQ(LayoutUnit(315), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kVerticalLr, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.left);
- EXPECT_EQ(LayoutUnit(20), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kVerticalLr, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.left);
- EXPECT_EQ(LayoutUnit(315), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kSidewaysLr, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.left);
- EXPECT_EQ(LayoutUnit(315), offset.top);
-
- offset = logical_offset.ConvertToPhysical(
- WritingMode::kSidewaysLr, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.left);
- EXPECT_EQ(LayoutUnit(20), offset.top);
-}
-
-} // namespace
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.cc
deleted file mode 100644
index 0c3b40cc708..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.cc
+++ /dev/null
@@ -1,69 +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/geometry/ng_logical_rect.h"
-
-#include <algorithm>
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-namespace {
-
-inline NGLogicalOffset Min(NGLogicalOffset a, NGLogicalOffset b) {
- return {std::min(a.inline_offset, b.inline_offset),
- std::min(a.block_offset, b.block_offset)};
-}
-
-inline NGLogicalOffset Max(NGLogicalOffset a, NGLogicalOffset b) {
- return {std::max(a.inline_offset, b.inline_offset),
- std::max(a.block_offset, b.block_offset)};
-}
-
-} // namespace
-
-NGLogicalRect::NGLogicalRect(const LayoutRect& source)
- : NGLogicalRect({source.X(), source.Y()},
- {source.Width(), source.Height()}) {}
-
-LayoutRect NGLogicalRect::ToLayoutRect() const {
- return {offset.inline_offset, offset.block_offset, size.inline_size,
- size.block_size};
-}
-
-bool NGLogicalRect::operator==(const NGLogicalRect& other) const {
- return other.offset == offset && other.size == size;
-}
-
-NGLogicalRect NGLogicalRect::operator+(const NGLogicalOffset& offset) const {
- return {this->offset + offset, size};
-}
-
-void NGLogicalRect::Unite(const NGLogicalRect& other) {
- if (other.IsEmpty())
- return;
- if (IsEmpty()) {
- *this = other;
- return;
- }
-
- NGLogicalOffset new_end_offset(Max(EndOffset(), other.EndOffset()));
- offset = Min(offset, other.offset);
- size = new_end_offset - offset;
-}
-
-String NGLogicalRect::ToString() const {
- return String::Format("%s,%s %sx%s",
- offset.inline_offset.ToString().Ascii().data(),
- offset.block_offset.ToString().Ascii().data(),
- size.inline_size.ToString().Ascii().data(),
- size.block_size.ToString().Ascii().data());
-}
-
-std::ostream& operator<<(std::ostream& os, const NGLogicalRect& value) {
- return os << value.ToString();
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h
deleted file mode 100644
index 114b7212b4e..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h
+++ /dev/null
@@ -1,45 +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 NGLogicalRect_h
-#define NGLogicalRect_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-
-namespace blink {
-
-class LayoutRect;
-
-// NGLogicalRect is the position and size of a rect (typically a fragment)
-// relative to the parent.
-struct CORE_EXPORT NGLogicalRect {
- NGLogicalRect() = default;
- NGLogicalRect(const NGLogicalOffset& offset, const NGLogicalSize& size)
- : offset(offset), size(size) {}
-
- explicit NGLogicalRect(const LayoutRect&);
- LayoutRect ToLayoutRect() const;
-
- NGLogicalOffset offset;
- NGLogicalSize size;
-
- NGLogicalOffset EndOffset() const { return offset + size; }
- bool IsEmpty() const { return size.IsEmpty(); }
-
- bool operator==(const NGLogicalRect& other) const;
-
- NGLogicalRect operator+(const NGLogicalOffset&) const;
-
- void Unite(const NGLogicalRect&);
-
- String ToString() const;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGLogicalRect&);
-
-} // namespace blink
-
-#endif // NGLogicalRect_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc
deleted file mode 100644
index b4a4917634c..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc
+++ /dev/null
@@ -1,60 +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/geometry/ng_logical_rect.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-struct LogicalRectUniteTestData {
- const char* test_case;
- NGLogicalRect a;
- NGLogicalRect b;
- NGLogicalRect expected;
-} logical_rect_unite_test_data[] = {
- {"empty", {}, {}, {}},
- {"a empty",
- {},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}}},
- {"b empty",
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}},
- {},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}}},
- {"a larger",
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}},
- {{LayoutUnit(200), LayoutUnit(50)}, {LayoutUnit(200), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}}},
- {"b larger",
- {{LayoutUnit(200), LayoutUnit(50)}, {LayoutUnit(200), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}}},
-};
-
-std::ostream& operator<<(std::ostream& os,
- const LogicalRectUniteTestData& data) {
- return os << "Unite " << data.test_case;
-}
-
-class NGLogicalRectUniteTest
- : public testing::Test,
- public testing::WithParamInterface<LogicalRectUniteTestData> {};
-
-INSTANTIATE_TEST_SUITE_P(NGGeometryUnitsTest,
- NGLogicalRectUniteTest,
- testing::ValuesIn(logical_rect_unite_test_data));
-
-TEST_P(NGLogicalRectUniteTest, Data) {
- const auto& data = GetParam();
- NGLogicalRect actual = data.a;
- actual.Unite(data.b);
- EXPECT_EQ(data.expected, actual);
-}
-
-} // namespace
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.cc
deleted file mode 100644
index ed4c4fac767..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 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/geometry/ng_logical_size.h"
-
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-
-namespace blink {
-
-std::ostream& operator<<(std::ostream& stream, const NGLogicalSize& value) {
- return stream << value.inline_size << "x" << value.block_size;
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h
deleted file mode 100644
index 344170e609e..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2016 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 NGLogicalSize_h
-#define NGLogicalSize_h
-
-#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/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
-#include "third_party/blink/renderer/platform/text/writing_mode.h"
-
-namespace blink {
-
-struct NGLogicalOffset;
-#define NGSizeIndefinite LayoutUnit(-1)
-
-// NGLogicalSize is the size of rect (typically a fragment) in the logical
-// coordinate system.
-struct CORE_EXPORT NGLogicalSize {
- NGLogicalSize() = default;
- NGLogicalSize(LayoutUnit inline_size, LayoutUnit block_size)
- : inline_size(inline_size), block_size(block_size) {}
-
- // Use ToNGPhysicalSize to convert to a physical size.
-
- LayoutUnit inline_size;
- LayoutUnit block_size;
-
- bool operator==(const NGLogicalSize& other) const {
- return std::tie(other.inline_size, other.block_size) ==
- std::tie(inline_size, block_size);
- }
- bool operator!=(const NGLogicalSize& other) const {
- return !(*this == other);
- }
-
- bool IsEmpty() const {
- return inline_size == LayoutUnit() || block_size == LayoutUnit();
- }
-
- void Flip() { std::swap(inline_size, block_size); }
-};
-
-inline NGLogicalSize& operator-=(NGLogicalSize& a, const NGBoxStrut& b) {
- a.inline_size -= b.InlineSum();
- a.block_size -= b.BlockSum();
- return a;
-}
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGLogicalSize&);
-
-// NGLogicalDelta resolves the ambiguity of subtractions.
-//
-// "offset - offset" is ambiguous because both of below are true:
-// offset + offset = offset
-// offset + size = offset
-//
-// NGLogicalDelta resolves this ambiguity by allowing implicit conversions both
-// to NGLogicalOffset and to NGLogicalSize.
-struct CORE_EXPORT NGLogicalDelta : public NGLogicalSize {
- public:
- using NGLogicalSize::NGLogicalSize;
- operator NGLogicalOffset() const { return {inline_size, block_size}; }
-};
-
-} // namespace blink
-
-#endif // NGLogicalSize_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.cc
deleted file mode 100644
index 9dc7146f5f5..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2016 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/geometry/ng_physical_offset.h"
-
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_point.h"
-#include "third_party/blink/renderer/platform/geometry/layout_size.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-NGLogicalOffset NGPhysicalOffset::ConvertToLogical(
- WritingMode mode,
- TextDirection direction,
- NGPhysicalSize outer_size,
- NGPhysicalSize inner_size) const {
- switch (mode) {
- case WritingMode::kHorizontalTb:
- if (direction == TextDirection::kLtr)
- return NGLogicalOffset(left, top);
- else
- return NGLogicalOffset(outer_size.width - left - inner_size.width, top);
- case WritingMode::kVerticalRl:
- case WritingMode::kSidewaysRl:
- if (direction == TextDirection::kLtr)
- return NGLogicalOffset(top, outer_size.width - left - inner_size.width);
- else
- return NGLogicalOffset(outer_size.height - top - inner_size.height,
- outer_size.width - left - inner_size.width);
- case WritingMode::kVerticalLr:
- if (direction == TextDirection::kLtr)
- return NGLogicalOffset(top, left);
- else
- return NGLogicalOffset(outer_size.height - top - inner_size.height,
- left);
- case WritingMode::kSidewaysLr:
- if (direction == TextDirection::kLtr)
- return NGLogicalOffset(outer_size.height - top - inner_size.height,
- left);
- else
- return NGLogicalOffset(top, left);
- default:
- NOTREACHED();
- return NGLogicalOffset();
- }
-}
-
-String NGPhysicalOffset::ToString() const {
- return String::Format("%d,%d", left.ToInt(), top.ToInt());
-}
-
-std::ostream& operator<<(std::ostream& os, const NGPhysicalOffset& value) {
- return os << value.ToString();
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h
deleted file mode 100644
index d08b3aa246a..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 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 NGPhysicalOffset_h
-#define NGPhysicalOffset_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/geometry/layout_point.h"
-#include "third_party/blink/renderer/platform/geometry/layout_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
-#include "third_party/blink/renderer/platform/text/text_direction.h"
-#include "third_party/blink/renderer/platform/text/writing_mode.h"
-
-namespace blink {
-
-class LayoutPoint;
-class LayoutSize;
-struct NGLogicalOffset;
-struct NGPhysicalSize;
-
-// NGPhysicalOffset is the position of a rect (typically a fragment) relative to
-// its parent rect in the physical coordinate system.
-struct CORE_EXPORT NGPhysicalOffset {
- NGPhysicalOffset() = default;
- NGPhysicalOffset(LayoutUnit left, LayoutUnit top) : left(left), top(top) {}
-
- LayoutUnit left;
- LayoutUnit top;
-
- // Converts a physical offset to a logical offset. See:
- // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
- // @param outer_size the size of the rect (typically a fragment).
- // @param inner_size the size of the inner rect (typically a child fragment).
- NGLogicalOffset ConvertToLogical(WritingMode,
- TextDirection,
- NGPhysicalSize outer_size,
- NGPhysicalSize inner_size) const;
-
- NGPhysicalOffset operator+(const NGPhysicalOffset& other) const {
- return NGPhysicalOffset{this->left + other.left, this->top + other.top};
- }
- NGPhysicalOffset& operator+=(const NGPhysicalOffset& other) {
- *this = *this + other;
- return *this;
- }
-
- NGPhysicalOffset operator-(const NGPhysicalOffset& other) const {
- return NGPhysicalOffset{this->left - other.left, this->top - other.top};
- }
- NGPhysicalOffset& operator-=(const NGPhysicalOffset& other) {
- *this = *this - other;
- return *this;
- }
-
- bool operator==(const NGPhysicalOffset& other) const {
- return other.left == left && other.top == top;
- }
-
- bool operator!=(const NGPhysicalOffset& other) const {
- return !(*this == other);
- }
-
- // Conversions from/to existing code. New code prefers type safety for
- // logical/physical distinctions.
- explicit NGPhysicalOffset(const LayoutPoint& point) {
- left = point.X();
- top = point.Y();
- }
- explicit NGPhysicalOffset(const LayoutSize& size) {
- left = size.Width();
- top = size.Height();
- }
-
- // Conversions from/to existing code. New code prefers type safety for
- // logical/physical distinctions.
- LayoutPoint ToLayoutPoint() const { return {left, top}; }
- LayoutSize ToLayoutSize() const { return {left, top}; }
-
- String ToString() const;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalOffset&);
-
-} // namespace blink
-
-#endif // NGPhysicalOffset_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc
deleted file mode 100644
index 0b5ff293af7..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.cc
+++ /dev/null
@@ -1,119 +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/geometry/ng_physical_offset_rect.h"
-
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
-#include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-bool NGPhysicalOffsetRect::operator==(const NGPhysicalOffsetRect& other) const {
- return other.offset == offset && other.size == size;
-}
-
-bool NGPhysicalOffsetRect::Contains(const NGPhysicalOffsetRect& other) const {
- return offset.left <= other.offset.left && offset.top <= other.offset.top &&
- Right() >= other.Right() && Bottom() >= other.Bottom();
-}
-
-NGPhysicalOffsetRect NGPhysicalOffsetRect::operator+(
- const NGPhysicalOffset& offset) const {
- return {this->offset + offset, size};
-}
-
-void NGPhysicalOffsetRect::Unite(const NGPhysicalOffsetRect& other) {
- if (other.IsEmpty())
- return;
- if (IsEmpty()) {
- *this = other;
- return;
- }
-
- UniteEvenIfEmpty(other);
-}
-
-void NGPhysicalOffsetRect::UniteIfNonZero(const NGPhysicalOffsetRect& other) {
- if (other.size.IsZero())
- return;
- if (size.IsZero()) {
- *this = other;
- return;
- }
-
- UniteEvenIfEmpty(other);
-}
-
-void NGPhysicalOffsetRect::UniteEvenIfEmpty(const NGPhysicalOffsetRect& other) {
- LayoutUnit left = std::min(offset.left, other.offset.left);
- LayoutUnit top = std::min(offset.top, other.offset.top);
- LayoutUnit right = std::max(Right(), other.Right());
- LayoutUnit bottom = std::max(Bottom(), other.Bottom());
- offset = {left, top};
- size = {right - left, bottom - top};
-}
-
-void NGPhysicalOffsetRect::Expand(const NGPhysicalBoxStrut& strut) {
- if (strut.top) {
- offset.top -= strut.top;
- size.height += strut.top;
- }
- if (strut.bottom) {
- size.height += strut.bottom;
- }
- if (strut.left) {
- offset.left -= strut.left;
- size.width += strut.left;
- }
- if (strut.right) {
- size.width += strut.right;
- }
-}
-
-void NGPhysicalOffsetRect::ExpandEdgesToPixelBoundaries() {
- int left = FloorToInt(offset.left);
- int top = FloorToInt(offset.top);
- int max_right = (offset.left + size.width).Ceil();
- int max_bottom = (offset.top + size.height).Ceil();
- offset.left = LayoutUnit(left);
- offset.top = LayoutUnit(top);
- size.width = LayoutUnit(max_right - left);
- size.height = LayoutUnit(max_bottom - top);
-}
-
-NGPhysicalOffsetRect::NGPhysicalOffsetRect(const LayoutRect& source)
- : NGPhysicalOffsetRect({source.X(), source.Y()},
- {source.Width(), source.Height()}) {}
-
-LayoutRect NGPhysicalOffsetRect::ToLayoutRect() const {
- return {offset.left, offset.top, size.width, size.height};
-}
-
-LayoutRect NGPhysicalOffsetRect::ToLayoutFlippedRect(
- const ComputedStyle& style,
- const NGPhysicalSize& container_size) const {
- if (!style.IsFlippedBlocksWritingMode())
- return {offset.left, offset.top, size.width, size.height};
- return {container_size.width - offset.left - size.width, offset.top,
- size.width, size.height};
-}
-
-FloatRect NGPhysicalOffsetRect::ToFloatRect() const {
- return {offset.left, offset.top, size.width, size.height};
-}
-
-String NGPhysicalOffsetRect::ToString() const {
- return String::Format("%s,%s %sx%s", offset.left.ToString().Ascii().data(),
- offset.top.ToString().Ascii().data(),
- size.width.ToString().Ascii().data(),
- size.height.ToString().Ascii().data());
-}
-
-std::ostream& operator<<(std::ostream& os, const NGPhysicalOffsetRect& value) {
- return os << value.ToString();
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h
deleted file mode 100644
index 2ef735b6c7c..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h
+++ /dev/null
@@ -1,65 +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 NGPhysicalOffsetRect_h
-#define NGPhysicalOffsetRect_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
-
-namespace blink {
-
-class ComputedStyle;
-class FloatRect;
-class LayoutRect;
-struct NGPhysicalBoxStrut;
-
-// NGPhysicalOffsetRect is the position and size of a rect (typically a
-// fragment) relative to its parent rect in the physical coordinate system.
-struct CORE_EXPORT NGPhysicalOffsetRect {
- NGPhysicalOffsetRect() = default;
- NGPhysicalOffsetRect(const NGPhysicalOffset& offset,
- const NGPhysicalSize& size)
- : offset(offset), size(size) {}
-
- NGPhysicalOffset offset;
- NGPhysicalSize size;
-
- bool IsEmpty() const { return size.IsEmpty(); }
- LayoutUnit Right() const { return offset.left + size.width; }
- LayoutUnit Bottom() const { return offset.top + size.height; }
-
- bool operator==(const NGPhysicalOffsetRect& other) const;
-
- bool Contains(const NGPhysicalOffsetRect& other) const;
-
- NGPhysicalOffsetRect operator+(const NGPhysicalOffset&) const;
-
- void Unite(const NGPhysicalOffsetRect&);
- void UniteIfNonZero(const NGPhysicalOffsetRect&);
- void UniteEvenIfEmpty(const NGPhysicalOffsetRect&);
-
- void Expand(const NGPhysicalBoxStrut&);
- void ExpandEdgesToPixelBoundaries();
-
- // Conversions from/to existing code. New code prefers type safety for
- // logical/physical distinctions.
- explicit NGPhysicalOffsetRect(const LayoutRect&);
- LayoutRect ToLayoutRect() const;
- LayoutRect ToLayoutFlippedRect(const ComputedStyle&,
- const NGPhysicalSize&) const;
-
- FloatRect ToFloatRect() const;
-
- String ToString() const;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&,
- const NGPhysicalOffsetRect&);
-
-} // namespace blink
-
-#endif // NGPhysicalOffsetRect_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc
deleted file mode 100644
index ac7bc56b54b..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.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/geometry/ng_physical_offset_rect.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-struct PhysicalOffsetRectUniteTestData {
- const char* test_case;
- NGPhysicalOffsetRect a;
- NGPhysicalOffsetRect b;
- NGPhysicalOffsetRect expected;
-} physical_offset_rect_unite_test_data[] = {
- {"all_empty", {}, {}, {}},
- {"a empty",
- {},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}}},
- {"b empty",
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}},
- {},
- {{LayoutUnit(1), LayoutUnit(2)}, {LayoutUnit(3), LayoutUnit(4)}}},
- {"a larger",
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}},
- {{LayoutUnit(200), LayoutUnit(50)}, {LayoutUnit(200), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}}},
- {"b larger",
- {{LayoutUnit(200), LayoutUnit(50)}, {LayoutUnit(200), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}},
- {{LayoutUnit(100), LayoutUnit(50)}, {LayoutUnit(300), LayoutUnit(200)}}},
-};
-
-std::ostream& operator<<(std::ostream& os,
- const PhysicalOffsetRectUniteTestData& data) {
- return os << "Unite " << data.test_case;
-}
-
-class NGPhysicalOffsetRectUniteTest
- : public testing::Test,
- public testing::WithParamInterface<PhysicalOffsetRectUniteTestData> {};
-
-INSTANTIATE_TEST_SUITE_P(
- NGGeometryUnitsTest,
- NGPhysicalOffsetRectUniteTest,
- testing::ValuesIn(physical_offset_rect_unite_test_data));
-
-TEST_P(NGPhysicalOffsetRectUniteTest, Data) {
- const auto& data = GetParam();
- NGPhysicalOffsetRect actual = data.a;
- actual.Unite(data.b);
- EXPECT_EQ(data.expected, actual);
-}
-
-} // namespace
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_test.cc
deleted file mode 100644
index 4c85a2dbe0a..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_test.cc
+++ /dev/null
@@ -1,74 +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/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-namespace {
-
-TEST(NGGeometryUnitsTest, ConvertPhysicalOffsetToLogicalOffset) {
- NGPhysicalOffset physical_offset(LayoutUnit(20), LayoutUnit(30));
- NGPhysicalSize outer_size(LayoutUnit(300), LayoutUnit(400));
- NGPhysicalSize inner_size(LayoutUnit(5), LayoutUnit(65));
- NGLogicalOffset offset;
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kHorizontalTb, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(20), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(30), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kHorizontalTb, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(275), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(30), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kVerticalRl, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(275), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kVerticalRl, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(305), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(275), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kSidewaysRl, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(275), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kSidewaysRl, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(305), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(275), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kVerticalLr, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(20), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kVerticalLr, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(305), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(20), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kSidewaysLr, TextDirection::kLtr, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(305), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(20), offset.block_offset);
-
- offset = physical_offset.ConvertToLogical(
- WritingMode::kSidewaysLr, TextDirection::kRtl, outer_size, inner_size);
- EXPECT_EQ(LayoutUnit(30), offset.inline_offset);
- EXPECT_EQ(LayoutUnit(20), offset.block_offset);
-}
-
-} // namespace
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.cc
deleted file mode 100644
index e8661406658..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2016 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/geometry/ng_physical_size.h"
-
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-
-namespace blink {
-
-String NGPhysicalSize::ToString() const {
- return String::Format("%dx%d", width.ToInt(), height.ToInt());
-}
-
-std::ostream& operator<<(std::ostream& os, const NGPhysicalSize& value) {
- return os << value.ToString();
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h
deleted file mode 100644
index cbeb617810c..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 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 NGPhysicalSize_h
-#define NGPhysicalSize_h
-
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_size.h"
-#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
-#include "third_party/blink/renderer/platform/text/writing_mode.h"
-
-namespace blink {
-
-class LayoutSize;
-struct NGLogicalSize;
-
-// NGPhysicalSize is the size of a rect (typically a fragment) in the physical
-// coordinate system.
-struct CORE_EXPORT NGPhysicalSize {
- NGPhysicalSize() = default;
- NGPhysicalSize(LayoutUnit width, LayoutUnit height)
- : width(width), height(height) {}
-
- LayoutUnit width;
- LayoutUnit height;
-
- NGLogicalSize ConvertToLogical(WritingMode mode) const {
- return mode == WritingMode::kHorizontalTb ? NGLogicalSize(width, height)
- : NGLogicalSize(height, width);
- }
-
- bool operator==(const NGPhysicalSize& other) const {
- return std::tie(other.width, other.height) == std::tie(width, height);
- }
- bool operator!=(const NGPhysicalSize& other) const {
- return !(*this == other);
- }
-
- bool IsEmpty() const {
- return width == LayoutUnit() || height == LayoutUnit();
- }
- bool IsZero() const {
- return width == LayoutUnit() && height == LayoutUnit();
- }
-
- // Conversions from/to existing code. New code prefers type safety for
- // logical/physical distinctions.
- LayoutSize ToLayoutSize() const { return {width, height}; }
-
- String ToString() const;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalSize&);
-
-inline NGPhysicalSize ToNGPhysicalSize(const NGLogicalSize& other,
- WritingMode mode) {
- return mode == WritingMode::kHorizontalTb
- ? NGPhysicalSize(other.inline_size, other.block_size)
- : NGPhysicalSize(other.block_size, other.inline_size);
-}
-
-} // namespace blink
-
-#endif // NGPhysicalSize_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
index f80b46bac4a..b90c8e35d33 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.cc
@@ -8,7 +8,7 @@ namespace blink {
NGStaticPosition NGStaticPosition::Create(WritingMode writing_mode,
TextDirection direction,
- NGPhysicalOffset offset) {
+ PhysicalOffset offset) {
NGStaticPosition position;
position.offset = offset;
switch (writing_mode) {
@@ -72,24 +72,4 @@ LayoutUnit NGStaticPosition::BottomInset(LayoutUnit container_size,
return container_size - offset.top;
}
-LayoutUnit NGStaticPosition::Left() const {
- DCHECK(HasLeft());
- return offset.left;
-}
-
-LayoutUnit NGStaticPosition::Right() const {
- DCHECK(!HasLeft());
- return offset.left;
-}
-
-LayoutUnit NGStaticPosition::Top() const {
- DCHECK(HasTop());
- return offset.top;
-}
-
-LayoutUnit NGStaticPosition::Bottom() const {
- DCHECK(!HasTop());
- return offset.top;
-}
-
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
index 7bd2d6cfd2b..68f72b4cd61 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h
@@ -6,7 +6,7 @@
#define NGStaticPosition_h
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
#include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -18,11 +18,11 @@ struct CORE_EXPORT NGStaticPosition {
enum Type { kTopLeft, kTopRight, kBottomLeft, kBottomRight };
Type type; // Logical corner that corresponds to physical top left.
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
// Creates a position with proper type wrt writing mode and direction.
// It expects physical offset of inline_start/block_start vertex.
- static NGStaticPosition Create(WritingMode, TextDirection, NGPhysicalOffset);
+ static NGStaticPosition Create(WritingMode, TextDirection, PhysicalOffset);
// Left/Right/TopPosition functions map static position to inset of
// left/right/top edge wrt container space.
@@ -45,10 +45,25 @@ struct CORE_EXPORT NGStaticPosition {
LayoutUnit margin_top,
LayoutUnit margin_bottom) const;
- LayoutUnit Left() const;
- LayoutUnit Right() const;
- LayoutUnit Top() const;
- LayoutUnit Bottom() const;
+ LayoutUnit Left() const {
+ DCHECK(HasLeft());
+ return offset.left;
+ }
+
+ LayoutUnit Right() const {
+ DCHECK(!HasLeft());
+ return offset.left;
+ }
+
+ LayoutUnit Top() const {
+ DCHECK(HasTop());
+ return offset.top;
+ }
+
+ LayoutUnit Bottom() const {
+ DCHECK(!HasTop());
+ return offset.top;
+ }
bool HasTop() const { return type == kTopLeft || type == kTopRight; }
bool HasLeft() const { return type == kTopLeft || type == kBottomLeft; }
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 c604426d29e..a9b42d7c716 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
@@ -309,11 +309,6 @@ In a bird's‐eye view, it consists of two parts:
content of an inline formatting context (computed in [pre-layout]) and DOM
positions in the context. See [design doc](https://goo.gl/CJbxky) for details.
-[NGCaretNavigator] provides functions for inspecting bidi levels and visual
-ordering of text content, and supports visual left/right caret movements in the
-text. See [design doc](http://bit.ly/2QVAwGq) for details.
-
-
[ICU BiDi]: http://userguide.icu-project.org/transforms/bidi
[UAX#9 Unicode Bidirectional Algorithm]: http://unicode.org/reports/tr9/
[UAX#9 Resolving Embedding Levels]: http://www.unicode.org/reports/tr9/#Resolving_Embedding_Levels
@@ -322,7 +317,6 @@ text. See [design doc](http://bit.ly/2QVAwGq) for details.
[FontBaseline]: ../../../platform/fonts/FontBaseline.h
[NGBaselineAlgorithmType]: ng_baseline.h
[NGBaselineRequest]: ng_baseline.h
-[NGCaretNavigator]: ng_caret_navigator.h
[NGBidiParagraph]: ng_bidi_paragraph.h
[NGBlockNode]: ../ng_block_node.h
[NGBoxFragment]: ../ng_box_fragment.h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h
index d922dbf5684..4e2517fba32 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h
@@ -23,12 +23,6 @@ class CORE_EXPORT LayoutNGText : public LayoutText {
}
bool IsLayoutNGObject() const override { return true; }
- protected:
- void InsertedIntoTree() override {
- valid_ng_items_ = false;
- LayoutText::InsertedIntoTree();
- }
-
private:
const NGInlineItems* GetNGInlineItems() const final { return &inline_items_; }
NGInlineItems* GetNGInlineItems() final { return &inline_items_; }
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
index 0a93d49372b..7fe1900ab55 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
@@ -26,12 +27,16 @@ scoped_refptr<AbstractInlineTextBox> NGAbstractInlineTextBox::GetOrCreate(
new FragmentToNGAbstractInlineTextBoxHashMap();
}
const auto it = g_abstract_inline_text_box_map_->find(&fragment);
- if (it != g_abstract_inline_text_box_map_->end())
+ LayoutText* const layout_text =
+ ToLayoutText(fragment.GetMutableLayoutObject());
+ if (it != g_abstract_inline_text_box_map_->end()) {
+ CHECK(layout_text->HasAbstractInlineTextBox());
return it->value;
- scoped_refptr<AbstractInlineTextBox> obj =
- base::AdoptRef(new NGAbstractInlineTextBox(
- LineLayoutText(ToLayoutText(fragment.GetLayoutObject())), fragment));
+ }
+ scoped_refptr<AbstractInlineTextBox> obj = base::AdoptRef(
+ new NGAbstractInlineTextBox(LineLayoutText(layout_text), fragment));
g_abstract_inline_text_box_map_->Set(&fragment, obj);
+ layout_text->SetHasAbstractInlineTextBox();
return obj;
}
@@ -65,12 +70,6 @@ void NGAbstractInlineTextBox::Detach() {
fragment_ = nullptr;
}
-bool NGAbstractInlineTextBox::HasSoftWrapToNextLine() const {
- return To<NGPhysicalLineBoxFragment>(
- fragment_->ContainerLineBox()->PhysicalFragment())
- .HasSoftWrapToNextLine();
-}
-
const NGPhysicalTextFragment& NGAbstractInlineTextBox::PhysicalTextFragment()
const {
return To<NGPhysicalTextFragment>(fragment_->PhysicalFragment());
@@ -81,13 +80,34 @@ bool NGAbstractInlineTextBox::NeedsLayout() const {
}
bool NGAbstractInlineTextBox::NeedsTrailingSpace() const {
- if (!HasSoftWrapToNextLine())
+ if (!fragment_->Style().CollapseWhiteSpace())
return false;
- const NGPaintFragment* next_fragment = NextTextFragmentForSameLayoutObject();
- if (!next_fragment)
+ const NGPaintFragment& line_box = *fragment_->ContainerLineBox();
+ if (!To<NGPhysicalLineBoxFragment>(line_box.PhysicalFragment())
+ .HasSoftWrapToNextLine())
+ return false;
+ const NGPhysicalTextFragment& text_fragment = PhysicalTextFragment();
+ if (text_fragment.EndOffset() >= text_fragment.TextContent().length())
+ return false;
+ if (text_fragment.TextContent()[text_fragment.EndOffset()] != ' ')
+ return false;
+ const NGInlineBreakToken& break_token = *To<NGInlineBreakToken>(
+ To<NGPhysicalLineBoxFragment>(line_box.PhysicalFragment()).BreakToken());
+ // TODO(yosin): We should support OOF fragments between |fragment_| and
+ // break token.
+ if (break_token.TextOffset() != text_fragment.EndOffset() + 1)
+ return false;
+ // Check a character in text content after |fragment_| comes from same
+ // layout text of |fragment_|.
+ const NGOffsetMapping& mapping =
+ *NGOffsetMapping::GetFor(fragment_->GetLayoutObject());
+ const NGMappingUnitRange& mapping_units =
+ mapping.GetMappingUnitsForTextContentOffsetRange(
+ text_fragment.EndOffset(), text_fragment.EndOffset() + 1);
+ if (mapping_units.begin() == mapping_units.end())
return false;
- return To<NGPhysicalTextFragment>(next_fragment->PhysicalFragment())
- .StartOffset() != PhysicalTextFragment().EndOffset();
+ const NGOffsetMappingUnit* const mapping_unit = mapping_units.begin();
+ return mapping_unit->GetLayoutObject() == fragment_->GetLayoutObject();
}
const NGPaintFragment*
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
index d74e962fc41..fb2368ed230 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h
@@ -32,7 +32,6 @@ class CORE_EXPORT NGAbstractInlineTextBox final : public AbstractInlineTextBox {
NGAbstractInlineTextBox(LineLayoutText line_layout_item,
const NGPaintFragment& fragment);
- bool HasSoftWrapToNextLine() const;
const NGPhysicalTextFragment& PhysicalTextFragment() const;
bool NeedsLayout() const;
bool NeedsTrailingSpace() const;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h
index 22fc93bf751..be83e72efc7 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h
@@ -149,6 +149,17 @@ class CORE_EXPORT NGBaselineList {
void emplace_back(NGBaselineRequest request, LayoutUnit offset);
+#if DCHECK_IS_ON()
+ bool operator==(const NGBaselineList& other) const {
+ for (wtf_size_t i = 0; i < NGBaselineRequest::kTypeIdCount; ++i) {
+ if (offsets_[i] != other.offsets_[i])
+ return false;
+ }
+
+ return true;
+ }
+#endif
+
class const_iterator {
public:
explicit const_iterator(unsigned type_id, const LayoutUnit* offset)
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
deleted file mode 100644
index 5b2f37f4e13..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
+++ /dev/null
@@ -1,426 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
-
-#include "third_party/blink/renderer/core/layout/layout_text_fragment.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"
-#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/ng_physical_box_fragment.h"
-
-namespace blink {
-
-std::ostream& operator<<(std::ostream& out,
- const NGCaretNavigator::Position& position) {
- return out << position.index << "/"
- << (position.IsBeforeCharacter() ? "BeforeCharacter"
- : "AfterCharacter");
-}
-
-NGCaretNavigator::~NGCaretNavigator() = default;
-
-NGCaretNavigator::NGCaretNavigator(const LayoutBlockFlow& context)
- : context_(context),
- disallow_transition_(context.GetDocument().Lifecycle()) {
- DCHECK(RuntimeEnabledFeatures::BidiCaretAffinityEnabled());
- DCHECK(context.IsLayoutNGMixin());
- DCHECK(context.ChildrenInline());
- DCHECK(context.GetNGInlineNodeData());
- DCHECK(!context.GetDocument().NeedsLayoutTreeUpdate());
-}
-
-const NGInlineNodeData& NGCaretNavigator::GetData() const {
- return *context_.GetNGInlineNodeData();
-}
-
-const String& NGCaretNavigator::GetText() const {
- return GetData().text_content;
-}
-
-bool NGCaretNavigator::IsBidiEnabled() const {
- return GetData().IsBidiEnabled();
-}
-
-UBiDiLevel NGCaretNavigator::BidiLevelAt(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- if (!IsBidiEnabled())
- return 0;
- return GetData().FindItemForTextOffset(index).BidiLevel();
-}
-
-TextDirection NGCaretNavigator::TextDirectionAt(unsigned index) const {
- UBiDiLevel level = BidiLevelAt(index);
- return DirectionFromLevel(level);
-}
-
-bool NGCaretNavigator::OffsetIsBidiBoundary(unsigned offset) const {
- DCHECK_LE(offset, GetText().length());
- if (!IsBidiEnabled())
- return false;
- if (!offset || offset == GetText().length())
- return false;
- return BidiLevelAt(offset - 1) != BidiLevelAt(offset);
-}
-
-NGCaretNavigator::Position
-NGCaretNavigator::CaretPositionFromTextContentOffsetAndAffinity(
- unsigned offset,
- TextAffinity affinity) const {
- DCHECK_LE(offset, GetText().length());
- // Callers sometimes pass in (0, upstream) or (length, downstream), which
- // originate from legacy callers. Make sure they are fixed up.
- // TODO(xiaochengh): Catch and eliminate such callers.
- if (affinity == TextAffinity::kUpstream) {
- if (offset)
- return {offset - 1, PositionAnchorType::kAfter};
- return {0, PositionAnchorType::kBefore};
- }
-
- if (offset < GetText().length())
- return {offset, PositionAnchorType::kBefore};
- return {GetText().length() - 1, PositionAnchorType::kAfter};
-}
-
-// static
-NGCaretNavigator::MoveDirection NGCaretNavigator::OppositeDirectionOf(
- MoveDirection direction) {
- if (direction == MoveDirection::kTowardsLeft)
- return MoveDirection::kTowardsRight;
- return MoveDirection::kTowardsLeft;
-}
-
-// static
-bool NGCaretNavigator::TowardsSameDirection(MoveDirection move_direction,
- TextDirection text_direction) {
- if (IsLtr(text_direction))
- return move_direction == MoveDirection::kTowardsRight;
- return move_direction == MoveDirection::kTowardsLeft;
-}
-
-NGCaretNavigator::Line NGCaretNavigator::ContainingLineOf(
- unsigned index) const {
- DCHECK_LT(index, GetText().length());
- // TODO(xiaochengh): Make it work for multi-col
- DCHECK(context_.CurrentFragment());
- unsigned last_line_end = 0;
- for (const auto child : context_.CurrentFragment()->Children()) {
- if (!child->IsLineBox())
- continue;
- const auto* line = To<NGPhysicalLineBoxFragment>(child.get());
- const auto* token = To<NGInlineBreakToken>(line->BreakToken());
- const unsigned line_end =
- token->IsFinished() ? GetText().length() : token->TextOffset();
- if (line_end > index)
- return {last_line_end, line_end, line->BaseDirection()};
- last_line_end = line_end;
- }
- NOTREACHED();
- return {};
-}
-
-bool NGCaretNavigator::IsValidCaretPosition(const Position& position) const {
- unsigned index = position.index;
- if (position.IsAfterCharacter() && IsLineBreak(index))
- return false;
- if (IsCollapsedSpaceByLineWrap(index))
- return false;
- if (IsIgnoredInCaretMovement(index))
- return false;
- return true;
-}
-
-bool NGCaretNavigator::IsCollapsibleWhitespace(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- if (GetText()[index] != kSpaceCharacter)
- return false;
- const NGInlineItem& item = GetData().FindItemForTextOffset(index);
- return item.Style()->CollapseWhiteSpace();
-}
-
-bool NGCaretNavigator::IsLineBreak(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- return GetText()[index] == kNewlineCharacter;
-}
-
-bool NGCaretNavigator::IsCollapsedSpaceByLineWrap(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- if (!IsCollapsibleWhitespace(index))
- return false;
- return index + 1 == ContainingLineOf(index).end_offset;
-}
-
-bool NGCaretNavigator::IsIgnoredInCaretMovement(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- const NGInlineItem& item = GetData().FindItemForTextOffset(index);
-
- // Caret navigation works on text, atomic inlines and non-ZWS controls only.
- switch (item.Type()) {
- case NGInlineItem::kText:
- case NGInlineItem::kAtomicInline:
- break;
- case NGInlineItem::kControl:
- if (GetText()[index] == kZeroWidthSpaceCharacter)
- return true;
- break;
- default:
- return true;
- }
-
- // Ignore CSS generate contents.
- // TODO(xiaochengh): This might be general enough to be merged into
- // |NGInlineItem| as a member function.
- DCHECK(item.GetLayoutObject());
- const LayoutObject* object = item.GetLayoutObject();
- if (const auto* text_fragment = ToLayoutTextFragmentOrNull(object)) {
- // ::first-letter |LayoutTextFragment| returns null for |GetNode()|. Check
- // |AssociatedTextNode()| to see if it's created by a text node.
- if (!text_fragment->AssociatedTextNode())
- return true;
- } else {
- if (!object->NonPseudoNode())
- return true;
- }
-
- // Ignore collapsed whitespaces that not visually at line end due to bidi.
- // Caret movement should move over them as if they don't exist to match the
- // existing behavior.
- return IsCollapsedSpaceByLineWrap(index) &&
- index != VisualLastCharacterOf(ContainingLineOf(index));
-}
-
-bool NGCaretNavigator::IsEnterableChildContext(unsigned index) const {
- DCHECK_LT(index, GetText().length());
- if (GetText()[index] != kObjectReplacementCharacter)
- return false;
-
- const NGInlineItem& item = GetData().FindItemForTextOffset(index);
- if (item.Type() != NGInlineItem::kAtomicInline)
- return false;
- DCHECK(item.GetLayoutObject());
- const LayoutObject* object = item.GetLayoutObject();
- if (!object->IsLayoutBlockFlow())
- return false;
- if (!object->NonPseudoNode() || !object->GetNode()->IsElementNode())
- return false;
- const Element* node = ToElement(object->GetNode());
- return !node->GetShadowRoot() || !node->GetShadowRoot()->IsUserAgent();
-}
-
-NGCaretNavigator::Position NGCaretNavigator::LeftEdgeOf(unsigned index) const {
- return EdgeOfInternal(index, MoveDirection::kTowardsLeft);
-}
-
-NGCaretNavigator::Position NGCaretNavigator::RightEdgeOf(unsigned index) const {
- return EdgeOfInternal(index, MoveDirection::kTowardsRight);
-}
-
-NGCaretNavigator::Position NGCaretNavigator::EdgeOfInternal(
- unsigned index,
- MoveDirection edge_direction) const {
- DCHECK_LT(index, GetText().length());
- const TextDirection character_direction = TextDirectionAt(index);
- return {index, TowardsSameDirection(edge_direction, character_direction)
- ? PositionAnchorType::kAfter
- : PositionAnchorType::kBefore};
-}
-
-NGCaretNavigator::VisualCharacterMovementResult
-NGCaretNavigator::LeftCharacterOf(unsigned index) const {
- return MoveCharacterInternal(index, MoveDirection::kTowardsLeft);
-}
-
-NGCaretNavigator::VisualCharacterMovementResult
-NGCaretNavigator::RightCharacterOf(unsigned index) const {
- return MoveCharacterInternal(index, MoveDirection::kTowardsRight);
-}
-
-Vector<int32_t, 32> NGCaretNavigator::CharacterIndicesInVisualOrder(
- const Line& line) const {
- DCHECK(IsBidiEnabled());
-
- Vector<UBiDiLevel, 32> levels;
- levels.ReserveCapacity(line.end_offset - line.start_offset);
- for (unsigned i = line.start_offset; i < line.end_offset; ++i)
- levels.push_back(BidiLevelAt(i));
-
- Vector<int32_t, 32> indices(levels.size());
- NGBidiParagraph::IndicesInVisualOrder(levels, &indices);
-
- for (auto& index : indices)
- index += line.start_offset;
- return indices;
-}
-
-unsigned NGCaretNavigator::VisualMostForwardCharacterOf(
- const Line& line,
- MoveDirection direction) const {
- if (!IsBidiEnabled()) {
- if (direction == MoveDirection::kTowardsLeft)
- return line.start_offset;
- return line.end_offset - 1;
- }
-
- const auto indices_in_visual_order = CharacterIndicesInVisualOrder(line);
- if (direction == MoveDirection::kTowardsLeft)
- return indices_in_visual_order.front();
- return indices_in_visual_order.back();
-}
-
-unsigned NGCaretNavigator::VisualFirstCharacterOf(const Line& line) const {
- return VisualMostForwardCharacterOf(line, IsLtr(line.base_direction)
- ? MoveDirection::kTowardsLeft
- : MoveDirection::kTowardsRight);
-}
-
-unsigned NGCaretNavigator::VisualLastCharacterOf(const Line& line) const {
- return VisualMostForwardCharacterOf(line, IsLtr(line.base_direction)
- ? MoveDirection::kTowardsRight
- : MoveDirection::kTowardsLeft);
-}
-
-NGCaretNavigator::VisualCharacterMovementResult
-NGCaretNavigator::MoveCharacterInternal(unsigned index,
- MoveDirection move_direction) const {
- const Line line = ContainingLineOf(index);
-
- if (index == VisualMostForwardCharacterOf(line, move_direction)) {
- if (TowardsSameDirection(move_direction, line.base_direction)) {
- if (line.end_offset == GetText().length())
- return {VisualMovementResultType::kAfterContext, base::nullopt};
- const Line next_line = ContainingLineOf(line.end_offset);
- return {VisualMovementResultType::kWithinContext,
- VisualFirstCharacterOf(next_line)};
- }
-
- if (!line.start_offset)
- return {VisualMovementResultType::kBeforeContext, base::nullopt};
- const Line last_line = ContainingLineOf(line.start_offset - 1);
- return {VisualMovementResultType::kWithinContext,
- VisualLastCharacterOf(last_line)};
- }
-
- if (!IsBidiEnabled()) {
- if (move_direction == MoveDirection::kTowardsLeft)
- return {VisualMovementResultType::kWithinContext, index - 1};
- return {VisualMovementResultType::kWithinContext, index + 1};
- }
-
- Vector<int32_t, 32> indices_in_visual_order =
- CharacterIndicesInVisualOrder(line);
- const int32_t* visual_location = std::find(
- indices_in_visual_order.begin(), indices_in_visual_order.end(), index);
- DCHECK_NE(visual_location, indices_in_visual_order.end());
- if (move_direction == MoveDirection::kTowardsLeft) {
- DCHECK_NE(visual_location, indices_in_visual_order.begin());
- return {VisualMovementResultType::kWithinContext,
- *std::prev(visual_location)};
- }
- DCHECK_NE(std::next(visual_location), indices_in_visual_order.end());
- return {VisualMovementResultType::kWithinContext,
- *std::next(visual_location)};
-}
-
-NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::LeftPositionOf(
- const Position& caret_position) const {
- return MoveCaretInternal(caret_position, MoveDirection::kTowardsLeft);
-}
-
-NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::RightPositionOf(
- const Position& caret_position) const {
- return MoveCaretInternal(caret_position, MoveDirection::kTowardsRight);
-}
-
-NGCaretNavigator::UnvalidatedVisualCaretMovementResult
-NGCaretNavigator::MoveCaretWithoutValidation(
- const Position& caret_position,
- MoveDirection move_direction) const {
- const unsigned index = caret_position.index;
- const MoveDirection opposite_direction = OppositeDirectionOf(move_direction);
- if (caret_position == EdgeOfInternal(index, opposite_direction)) {
- // TODO(xiaochengh): Consider grapheme cluster
- return {VisualMovementResultType::kWithinContext,
- EdgeOfInternal(index, move_direction),
- !IsIgnoredInCaretMovement(index)};
- }
-
- VisualCharacterMovementResult forward_character =
- MoveCharacterInternal(index, move_direction);
- if (!forward_character.IsWithinContext())
- return {forward_character.type};
-
- DCHECK(forward_character.index.has_value());
- const Position forward_caret =
- EdgeOfInternal(*forward_character.index, opposite_direction);
- return {VisualMovementResultType::kWithinContext, forward_caret};
-}
-
-NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::MoveCaretInternal(
- const Position& caret_position,
- MoveDirection move_direction) const {
- bool has_passed_character = false;
- base::Optional<Position> last_position;
- for (Position runner = caret_position;
- !has_passed_character || !IsValidCaretPosition(runner);) {
- const UnvalidatedVisualCaretMovementResult next =
- MoveCaretWithoutValidation(runner, move_direction);
- if (next.type != VisualMovementResultType::kWithinContext)
- return {next.type, base::nullopt};
-
- if (next.has_passed_character) {
- has_passed_character = true;
-
- const unsigned last_passed_character = next.position->index;
- if (IsEnterableChildContext(last_passed_character))
- return {VisualMovementResultType::kEnteredChildContext, runner};
- }
-
- runner = *next.position;
- last_position = runner;
-
- // TODO(xiaochengh): Handle the case where we reach a different line with a
- // different base direction, which occurs with 'unicode-bidi: plain-text'.
- }
- DCHECK(last_position.has_value());
- return {VisualMovementResultType::kWithinContext, *last_position};
-}
-
-NGCaretNavigator::Position NGCaretNavigator::LeftmostPositionInFirstLine()
- const {
- Line first_line = ContainingLineOf(0);
- unsigned leftmost_character =
- VisualMostForwardCharacterOf(first_line, MoveDirection::kTowardsLeft);
- // TODO(xiaochengh): Handle if the caret position is invalid.
- return LeftEdgeOf(leftmost_character);
-}
-
-NGCaretNavigator::Position NGCaretNavigator::RightmostPositionInFirstLine()
- const {
- Line first_line = ContainingLineOf(0);
- unsigned rightmost_character =
- VisualMostForwardCharacterOf(first_line, MoveDirection::kTowardsRight);
- // TODO(xiaochengh): Handle if the caret position is invalid.
- return RightEdgeOf(rightmost_character);
-}
-
-NGCaretNavigator::Position NGCaretNavigator::LeftmostPositionInLastLine()
- const {
- Line last_line = ContainingLineOf(GetText().length() - 1);
- unsigned leftmost_character =
- VisualMostForwardCharacterOf(last_line, MoveDirection::kTowardsLeft);
- // TODO(xiaochengh): Handle if the caret position is invalid.
- return LeftEdgeOf(leftmost_character);
-}
-
-NGCaretNavigator::Position NGCaretNavigator::RightmostPositionInLastLine()
- const {
- Line last_line = ContainingLineOf(GetText().length() - 1);
- unsigned rightmost_character =
- VisualMostForwardCharacterOf(last_line, MoveDirection::kTowardsRight);
- // TODO(xiaochengh): Handle if the caret position is invalid.
- return RightEdgeOf(rightmost_character);
-}
-
-} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
deleted file mode 100644
index 6c60d7f9f90..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
-
-#include "base/optional.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
-#include "third_party/blink/renderer/core/editing/text_affinity.h"
-#include "third_party/blink/renderer/platform/text/text_direction.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-#include <unicode/ubidi.h>
-
-namespace blink {
-
-class LayoutBlockFlow;
-struct NGInlineNodeData;
-
-// Hosts the |text_content| of an inline formatting context and provides
-// bidi-related utilities, including checking bidi levels, computing visual
-// left/right characters and visual left/right caret movements.
-// Design doc: http://bit.ly/2QVAwGq
-class CORE_EXPORT NGCaretNavigator {
- STACK_ALLOCATED();
-
- public:
- explicit NGCaretNavigator(const LayoutBlockFlow&);
- ~NGCaretNavigator();
-
- const String& GetText() const;
- bool IsBidiEnabled() const;
-
- // Abstraction of a caret position in |text_|.
- enum class PositionAnchorType { kBefore, kAfter };
- struct Position {
- // |index| is character index the |text_| string.
- unsigned index;
- PositionAnchorType type;
-
- bool IsBeforeCharacter() const {
- return type == PositionAnchorType::kBefore;
- }
-
- bool IsAfterCharacter() const { return type == PositionAnchorType::kAfter; }
-
- bool operator==(const Position& other) const {
- return index == other.index && type == other.type;
- }
- };
-
- // Returns the bidi level or resolved direction of the character at the given
- // logical |index|.
- UBiDiLevel BidiLevelAt(unsigned index) const;
- TextDirection TextDirectionAt(unsigned index) const;
-
- // Returns true if the characters at indexes |offset - 1| and |offset| both
- // exist and are at different bidi levels.
- bool OffsetIsBidiBoundary(unsigned offset) const;
-
- // Converts an (offset, affinity) pair into a |Position| type of this class.
- // Intiontionally long name to indicate the hackiness for handling legacy
- // callers.
- Position CaretPositionFromTextContentOffsetAndAffinity(
- unsigned offset,
- TextAffinity affinity) const;
-
- // Returns the visual left/right edge caret position of the character at the
- // given logical |index|.
- Position LeftEdgeOf(unsigned index) const;
- Position RightEdgeOf(unsigned index) const;
-
- // Left/right visual movements
- // TODO(xiaochengh): Handle the following
- // - Grapheme clusters
-
- enum class VisualMovementResultType {
- kWithinContext,
- kBeforeContext,
- kAfterContext,
- kEnteredChildContext
- };
-
- // Given the character at the logical |index|, returns the logical index of
- // the character at its left/right side.
- struct VisualCharacterMovementResult {
- bool IsWithinContext() const {
- return type == VisualMovementResultType::kWithinContext;
- }
- bool IsBeforeContext() const {
- return type == VisualMovementResultType::kBeforeContext;
- }
- bool IsAfterContext() const {
- return type == VisualMovementResultType::kAfterContext;
- }
-
- VisualMovementResultType type;
- base::Optional<unsigned> index;
- };
- VisualCharacterMovementResult LeftCharacterOf(unsigned index) const;
- VisualCharacterMovementResult RightCharacterOf(unsigned index) const;
-
- // Given a caret position, moves it left/right by one grapheme cluster and
- // returns the result.
- // Note: If we end up entering an inline block, the result |Position| is
- // either before or after the inline block, depending on from which side the
- // inline block is entered. For example:
- // RightPositionOf(abc|<inline-block>def</inline-block>ghi)
- // -> {inline-block, PositionAnchorType::kBefore}
- // LeftPositionOf(abc<inline-block>def</inline-block>|ghi)
- // -> {inline-block, PositionAnchorType::kAfter}
- struct VisualCaretMovementResult {
- bool IsWithinContext() const {
- return type == VisualMovementResultType::kWithinContext;
- }
- bool IsBeforeContext() const {
- return type == VisualMovementResultType::kBeforeContext;
- }
- bool IsAfterContext() const {
- return type == VisualMovementResultType::kAfterContext;
- }
- bool HasEnteredChildContext() const {
- return type == VisualMovementResultType::kEnteredChildContext;
- }
-
- VisualMovementResultType type;
- base::Optional<Position> position;
- };
- VisualCaretMovementResult LeftPositionOf(const Position&) const;
- VisualCaretMovementResult RightPositionOf(const Position&) const;
-
- // TODO(xiaochengh): Specify and implement the behavior in edge cases, e.g.,
- // when the leftmost character of the first line is CSS-generated.
- Position LeftmostPositionInFirstLine() const;
- Position RightmostPositionInFirstLine() const;
- Position LeftmostPositionInLastLine() const;
- Position RightmostPositionInLastLine() const;
-
- private:
- // A caret position is invalid if it is:
- // - kAfter to a line break character.
- // - Anchored to a collapsible space that's removed by line wrap.
- // - Anchored to a character that's ignored in caret movement.
- bool IsValidCaretPosition(const Position&) const;
- bool IsLineBreak(unsigned index) const;
- bool IsCollapsibleWhitespace(unsigned index) const;
- bool IsCollapsedSpaceByLineWrap(unsigned index) const;
- bool IsIgnoredInCaretMovement(unsigned index) const;
-
- // Returns true if the character at |index| represents a child block
- // formatting context that can be entered by caret navigation. Such contexts
- // must be atomic inlines (inline block, inline table, ...) and must not host
- // user agent shadow tree (which excludes, e.g., <input> and image alt text).
- bool IsEnterableChildContext(unsigned index) const;
-
- enum class MoveDirection { kTowardsLeft, kTowardsRight };
- static MoveDirection OppositeDirectionOf(MoveDirection);
- static bool TowardsSameDirection(MoveDirection, TextDirection);
-
- // ------ Line-related functions ------
-
- // A line contains a consecutive substring of |GetText()|. The lines should
- // not overlap, and should together cover the entire |GetText()|.
- struct Line {
- unsigned start_offset;
- unsigned end_offset;
- TextDirection base_direction;
- };
- Line ContainingLineOf(unsigned index) const;
- Vector<int32_t, 32> CharacterIndicesInVisualOrder(const Line&) const;
- unsigned VisualMostForwardCharacterOf(const Line&,
- MoveDirection direction) const;
- unsigned VisualLastCharacterOf(const Line&) const;
- unsigned VisualFirstCharacterOf(const Line&) const;
-
- // ------ Implementation of public visual movement functions ------
-
- Position EdgeOfInternal(unsigned index, MoveDirection) const;
- VisualCharacterMovementResult MoveCharacterInternal(unsigned index,
- MoveDirection) const;
- VisualCaretMovementResult MoveCaretInternal(const Position&,
- MoveDirection) const;
-
- // Performs a "minimal" caret movement to the left/right without validating
- // the result. The result might be invalid due to, e.g., anchored to an
- // unallowed character, being visually the same as the input, etc. It's a
- // subroutine of |MoveCaretInternal|, who keeps calling it until both of the
- // folliwng are satisfied:
- // - We've reached a valid caret position.
- // - During the process, the caret has moved passing a character on which
- // |IsIgnoredInCaretMovement| is false (indicated by |has_passed_character|).
- struct UnvalidatedVisualCaretMovementResult {
- VisualMovementResultType type;
- base::Optional<Position> position;
- bool has_passed_character = false;
- };
- UnvalidatedVisualCaretMovementResult MoveCaretWithoutValidation(
- const Position&,
- MoveDirection) const;
-
- const NGInlineNodeData& GetData() const;
-
- const LayoutBlockFlow& context_;
- DocumentLifecycle::DisallowTransitionScope disallow_transition_;
-};
-
-CORE_EXPORT std::ostream& operator<<(std::ostream&,
- const NGCaretNavigator::Position&);
-
-} // namespace blink
-
-#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
deleted file mode 100644
index 70c99450dd9..00000000000
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
+++ /dev/null
@@ -1,461 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
-
-#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.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 {
-
-class NGCaretNavigatorTest : public RenderingTest,
- private ScopedBidiCaretAffinityForTest {
- public:
- NGCaretNavigatorTest() : ScopedBidiCaretAffinityForTest(true) {}
-
- void SetupHtml(const char* id, String html) {
- SetBodyInnerHTML(html);
-
- block_flow_ = To<LayoutBlockFlow>(GetLayoutObjectByElementId(id));
- DCHECK(block_flow_);
- DCHECK(block_flow_->IsLayoutNGMixin());
- DCHECK(block_flow_->ChildrenInline());
- }
-
- UBiDiLevel BidiLevelAt(unsigned index) const {
- return NGCaretNavigator(*block_flow_).BidiLevelAt(index);
- }
-
- NGCaretNavigator::VisualCharacterMovementResult LeftCharacterOf(
- unsigned index) const {
- return NGCaretNavigator(*block_flow_).LeftCharacterOf(index);
- }
-
- NGCaretNavigator::VisualCharacterMovementResult RightCharacterOf(
- unsigned index) const {
- return NGCaretNavigator(*block_flow_).RightCharacterOf(index);
- }
-
- NGCaretNavigator::Position CaretBefore(unsigned index) const {
- return {index, NGCaretNavigator::PositionAnchorType::kBefore};
- }
-
- NGCaretNavigator::Position CaretAfter(unsigned index) const {
- return {index, NGCaretNavigator::PositionAnchorType::kAfter};
- }
-
- NGCaretNavigator::VisualCaretMovementResult LeftPositionOf(
- const NGCaretNavigator::Position& position) const {
- return NGCaretNavigator(*block_flow_).LeftPositionOf(position);
- }
-
- NGCaretNavigator::VisualCaretMovementResult RightPositionOf(
- const NGCaretNavigator::Position& position) const {
- return NGCaretNavigator(*block_flow_).RightPositionOf(position);
- }
-
- protected:
- const LayoutBlockFlow* block_flow_;
-};
-
-TEST_F(NGCaretNavigatorTest, BidiLevelAtBasic) {
- SetupHtml("container",
- "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
-
- EXPECT_EQ(0u, BidiLevelAt(0));
- EXPECT_EQ(0u, BidiLevelAt(1));
- EXPECT_EQ(0u, BidiLevelAt(2));
- EXPECT_EQ(1u, BidiLevelAt(3));
- EXPECT_EQ(1u, BidiLevelAt(4));
- EXPECT_EQ(1u, BidiLevelAt(5));
- EXPECT_EQ(2u, BidiLevelAt(6));
- EXPECT_EQ(2u, BidiLevelAt(7));
- EXPECT_EQ(2u, BidiLevelAt(8));
-}
-
-TEST_F(NGCaretNavigatorTest, LeftCharacterOfBasic) {
- SetupHtml("container",
- "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
-
- EXPECT_TRUE(LeftCharacterOf(0).IsBeforeContext());
-
- EXPECT_TRUE(LeftCharacterOf(1).IsWithinContext());
- EXPECT_EQ(0u, *LeftCharacterOf(1).index);
-
- EXPECT_TRUE(LeftCharacterOf(2).IsWithinContext());
- EXPECT_EQ(1u, *LeftCharacterOf(2).index);
-
- EXPECT_TRUE(LeftCharacterOf(3).IsWithinContext());
- EXPECT_EQ(4u, *LeftCharacterOf(3).index);
-
- EXPECT_TRUE(LeftCharacterOf(4).IsWithinContext());
- EXPECT_EQ(5u, *LeftCharacterOf(4).index);
-
- EXPECT_TRUE(LeftCharacterOf(5).IsWithinContext());
- EXPECT_EQ(8u, *LeftCharacterOf(5).index);
-
- EXPECT_TRUE(LeftCharacterOf(6).IsWithinContext());
- EXPECT_EQ(2u, *LeftCharacterOf(6).index);
-
- EXPECT_TRUE(LeftCharacterOf(7).IsWithinContext());
- EXPECT_EQ(6u, *LeftCharacterOf(7).index);
-
- EXPECT_TRUE(LeftCharacterOf(8).IsWithinContext());
- EXPECT_EQ(7u, *LeftCharacterOf(8).index);
-}
-
-TEST_F(NGCaretNavigatorTest, RightCharacterOfBasic) {
- SetupHtml("container",
- "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
-
- EXPECT_TRUE(RightCharacterOf(0).IsWithinContext());
- EXPECT_EQ(1u, *RightCharacterOf(0).index);
-
- EXPECT_TRUE(RightCharacterOf(1).IsWithinContext());
- EXPECT_EQ(2u, *RightCharacterOf(1).index);
-
- EXPECT_TRUE(RightCharacterOf(2).IsWithinContext());
- EXPECT_EQ(6u, *RightCharacterOf(2).index);
-
- EXPECT_TRUE(RightCharacterOf(3).IsAfterContext());
-
- EXPECT_TRUE(RightCharacterOf(4).IsWithinContext());
- EXPECT_EQ(3u, *RightCharacterOf(4).index);
-
- EXPECT_TRUE(RightCharacterOf(5).IsWithinContext());
- EXPECT_EQ(4u, *RightCharacterOf(5).index);
-
- EXPECT_TRUE(RightCharacterOf(6).IsWithinContext());
- EXPECT_EQ(7u, *RightCharacterOf(6).index);
-
- EXPECT_TRUE(RightCharacterOf(7).IsWithinContext());
- EXPECT_EQ(8u, *RightCharacterOf(7).index);
-
- EXPECT_TRUE(RightCharacterOf(8).IsWithinContext());
- EXPECT_EQ(5u, *RightCharacterOf(8).index);
-}
-
-TEST_F(NGCaretNavigatorTest, LeftPositionOfBasic) {
- SetupHtml("container",
- "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(0)).IsWithinContext());
- EXPECT_EQ(CaretBefore(0), *LeftPositionOf(CaretAfter(0)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(1)).IsWithinContext());
- EXPECT_EQ(CaretBefore(0), *LeftPositionOf(CaretBefore(1)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(1)).IsWithinContext());
- EXPECT_EQ(CaretBefore(1), *LeftPositionOf(CaretAfter(1)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(2)).IsWithinContext());
- EXPECT_EQ(CaretBefore(1), *LeftPositionOf(CaretBefore(2)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretBefore(2), *LeftPositionOf(CaretAfter(2)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(3)).IsWithinContext());
- EXPECT_EQ(CaretAfter(3), *LeftPositionOf(CaretBefore(3)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(3)).IsWithinContext());
- EXPECT_EQ(CaretAfter(4), *LeftPositionOf(CaretAfter(3)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretAfter(4), *LeftPositionOf(CaretBefore(4)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(4)).IsWithinContext());
- EXPECT_EQ(CaretAfter(5), *LeftPositionOf(CaretAfter(4)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(5)).IsWithinContext());
- EXPECT_EQ(CaretAfter(5), *LeftPositionOf(CaretBefore(5)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(8), *LeftPositionOf(CaretAfter(5)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(6)).IsWithinContext());
- EXPECT_EQ(CaretBefore(2), *LeftPositionOf(CaretBefore(6)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(6)).IsWithinContext());
- EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretAfter(6)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(7)).IsWithinContext());
- EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretBefore(7)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(7)).IsWithinContext());
- EXPECT_EQ(CaretBefore(7), *LeftPositionOf(CaretAfter(7)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(8)).IsWithinContext());
- EXPECT_EQ(CaretBefore(7), *LeftPositionOf(CaretBefore(8)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretAfter(8)).IsWithinContext());
- EXPECT_EQ(CaretBefore(8), *LeftPositionOf(CaretAfter(8)).position);
-}
-
-TEST_F(NGCaretNavigatorTest, RightPositionOfBasic) {
- SetupHtml("container",
- "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(0)).IsWithinContext());
- EXPECT_EQ(CaretAfter(0), *RightPositionOf(CaretBefore(0)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(0)).IsWithinContext());
- EXPECT_EQ(CaretAfter(1), *RightPositionOf(CaretAfter(0)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(1)).IsWithinContext());
- EXPECT_EQ(CaretAfter(1), *RightPositionOf(CaretBefore(1)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(1)).IsWithinContext());
- EXPECT_EQ(CaretAfter(2), *RightPositionOf(CaretAfter(1)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(2)).IsWithinContext());
- EXPECT_EQ(CaretAfter(2), *RightPositionOf(CaretBefore(2)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretAfter(6), *RightPositionOf(CaretAfter(2)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsAfterContext());
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(3)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *RightPositionOf(CaretAfter(3)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *RightPositionOf(CaretBefore(4)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(4)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretBefore(5)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(5), *RightPositionOf(CaretAfter(5)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(6)).IsWithinContext());
- EXPECT_EQ(CaretAfter(6), *RightPositionOf(CaretBefore(6)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsWithinContext());
- EXPECT_EQ(CaretAfter(7), *RightPositionOf(CaretAfter(6)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(7)).IsWithinContext());
- EXPECT_EQ(CaretAfter(7), *RightPositionOf(CaretBefore(7)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(7)).IsWithinContext());
- EXPECT_EQ(CaretAfter(8), *RightPositionOf(CaretAfter(7)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(8)).IsWithinContext());
- EXPECT_EQ(CaretAfter(8), *RightPositionOf(CaretBefore(8)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(8)).IsWithinContext());
- EXPECT_EQ(CaretBefore(5), *RightPositionOf(CaretAfter(8)).position);
-}
-
-// Tests below check caret movement crossing line boundaries
-
-TEST_F(NGCaretNavigatorTest, HardLineBreak) {
- SetupHtml("container", "<div id=container>abc<br>def</div>");
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(2)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretBefore(3)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretBefore(4)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
-}
-
-TEST_F(NGCaretNavigatorTest, SoftLineWrapAtSpace) {
- SetupHtml("container", "<div id=container style=\"width:0\">abc def</div>");
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(2)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretBefore(3)).position);
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretAfter(2), *LeftPositionOf(CaretBefore(4)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
-}
-
-TEST_F(NGCaretNavigatorTest, BidiAndSoftLineWrapAtSpaceLtr) {
- LoadAhem();
- SetupHtml("container",
- "<div id=container style='font: 10px/10px Ahem; width: 100px'>"
- "before &#x05D0;&#x05D1;&#x05D2;&#x05D3; "
- "&#x05D4;&#x05D5;&#x05D6;&#x05D7;&#x05D8;&#x05D9;"
- "&#x05DA;&#x05DB;&#x05DC;&#x05DD;&#x05DE;&#x05DF;"
- "&#x05E0;&#x05E1;&#x05E2;&#x05E3;&#x05E4;&#x05E5;"
- "</div>");
-
- // Moving left from "|before DCBA" should be before context
- EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
-
- // Moving right from "before |DCBA" should yield "before D|CBA"
- EXPECT_TRUE(RightPositionOf(CaretAfter(10)).IsWithinContext());
- EXPECT_EQ(CaretBefore(10), *RightPositionOf(CaretAfter(10)).position);
- EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsWithinContext());
- EXPECT_EQ(CaretBefore(10), *RightPositionOf(CaretAfter(6)).position);
-
- // Moving left from "before |DCBA" should yield "before| DCBA"
- EXPECT_TRUE(LeftPositionOf(CaretAfter(10)).IsWithinContext());
- EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretAfter(10)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(6)).IsWithinContext());
- EXPECT_EQ(CaretBefore(6), *LeftPositionOf(CaretAfter(6)).position);
-
- // Moving right from "before DCBA|" should yield "V|UTSRQPONMLKJIHGFE"
- EXPECT_TRUE(RightPositionOf(CaretBefore(7)).IsWithinContext());
- EXPECT_EQ(CaretBefore(29), *RightPositionOf(CaretBefore(7)).position);
-
- // Moving left from "|VUTSRQPONMLKJIHGFE" should yield "before DCB|A"
- EXPECT_TRUE(LeftPositionOf(CaretAfter(29)).IsWithinContext());
- EXPECT_EQ(CaretAfter(7), *LeftPositionOf(CaretAfter(29)).position);
-
- // Moving right from "VUTSRQPONMLKJIHGFE|" should be after context
- EXPECT_TRUE(RightPositionOf(CaretBefore(12)).IsAfterContext());
-}
-
-TEST_F(NGCaretNavigatorTest, BidiAndSoftLineWrapAtSpaceRtl) {
- LoadAhem();
- SetupHtml(
- "container",
- "<div dir=rtl id=container style='font: 10px/10px Ahem; width: 120px'>"
- "&#x05D0;&#x05D1;&#x05D2;&#x05D3; after encyclopedia"
- "</div>");
-
- // Moving right from "after DCBA|" should be before context
- EXPECT_TRUE(RightPositionOf(CaretBefore(0)).IsBeforeContext());
-
- // Moving left from "after| DCBA" should yield "afte|r DCBA"
- EXPECT_TRUE(LeftPositionOf(CaretAfter(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(9), *LeftPositionOf(CaretAfter(4)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(9)).IsWithinContext());
- EXPECT_EQ(CaretBefore(9), *LeftPositionOf(CaretAfter(9)).position);
-
- // Moving right from "after| DCBA" should yield "after |DCBA"
- EXPECT_TRUE(RightPositionOf(CaretAfter(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(4)).position);
- EXPECT_TRUE(RightPositionOf(CaretAfter(9)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *RightPositionOf(CaretAfter(9)).position);
-
- // Moving left from "|after DCBA" should yield "encyclopedi|a"
- EXPECT_TRUE(LeftPositionOf(CaretBefore(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(22), *LeftPositionOf(CaretBefore(5)).position);
-
- // Moving right from "encyclopedia|" should yield "a|fter DCBA"
- EXPECT_TRUE(RightPositionOf(CaretAfter(22)).IsWithinContext());
- EXPECT_EQ(CaretAfter(5), *RightPositionOf(CaretAfter(22)).position);
-
- // Moving left from "|encyclopedia" should be after context
- EXPECT_TRUE(LeftPositionOf(CaretBefore(11)).IsAfterContext());
-}
-
-TEST_F(NGCaretNavigatorTest, SoftLineWrapAtHyphen) {
- SetupHtml("container", "<div id=container style=\"width:0\">abc-def</div>");
-
- EXPECT_TRUE(LeftPositionOf(CaretBefore(0)).IsBeforeContext());
-
- // 3 -> 4
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretAfter(2)).position);
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
- EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretBefore(3)).position);
-
- // 4 -> 5
- EXPECT_TRUE(RightPositionOf(CaretAfter(3)).IsWithinContext());
- EXPECT_EQ(CaretAfter(4), *RightPositionOf(CaretAfter(3)).position);
- EXPECT_TRUE(RightPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretAfter(4), *RightPositionOf(CaretBefore(4)).position);
-
- // 5 -> 4
- EXPECT_TRUE(LeftPositionOf(CaretBefore(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *LeftPositionOf(CaretBefore(5)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(4), *LeftPositionOf(CaretAfter(4)).position);
-
- // 4 -> 3
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretBefore(4)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(3)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretAfter(3)).position);
-
- EXPECT_TRUE(RightPositionOf(CaretAfter(6)).IsAfterContext());
-}
-
-TEST_F(NGCaretNavigatorTest, MoveOverPseudoElementInBidi) {
- SetupHtml("container",
- "<style>.bidi::before,.bidi::after{content:'a\\05D0 b'}</style>"
- "<div id=container>&#x05D0;&#x05D1; &#x05D2;&#x05D3; "
- "<span class=bidi>&#x05D4;&#x05D5;</span>"
- " &#x05D6;&#x05D7; &#x05D8;&#x05D9;</div>");
-
- // Text: "AB CD aAbEFaAb GH IJ"
- // Rendered as: "DC BA aAbFEaAb JI HG"
-
- // Moving right from "BA |" should arrive at "F|E"
- EXPECT_TRUE(RightPositionOf(CaretAfter(5)).IsWithinContext());
- EXPECT_EQ(CaretBefore(10), *RightPositionOf(CaretAfter(5)).position);
-
- // Moving left from "|FE" should arrive at "BA| "
- EXPECT_TRUE(LeftPositionOf(CaretAfter(10)).IsWithinContext());
- EXPECT_EQ(CaretBefore(5), *LeftPositionOf(CaretAfter(10)).position);
-
- // Moving right from "FE|" should arrive at " |JI"
- EXPECT_TRUE(RightPositionOf(CaretBefore(9)).IsWithinContext());
- EXPECT_EQ(CaretAfter(14), *RightPositionOf(CaretBefore(9)).position);
-
- // Moving left from "| JI" should arrive at "F|E"
- EXPECT_TRUE(LeftPositionOf(CaretBefore(14)).IsWithinContext());
- EXPECT_EQ(CaretAfter(9), *LeftPositionOf(CaretBefore(14)).position);
-}
-
-TEST_F(NGCaretNavigatorTest, EnterableInlineBlock) {
- SetupHtml("container",
- "<div id=container>foo"
- "<span style='display:inline-block'>bar</span>"
- "baz</div>");
-
- // Moving right from "foo|" should enter the span from front.
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).HasEnteredChildContext());
- EXPECT_EQ(CaretBefore(3), *RightPositionOf(CaretAfter(2)).position);
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).HasEnteredChildContext());
- EXPECT_EQ(CaretBefore(3), *RightPositionOf(CaretBefore(3)).position);
-
- // Moving left from "|baz" should enter the span from behind.
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).HasEnteredChildContext());
- EXPECT_EQ(CaretAfter(3), *LeftPositionOf(CaretBefore(4)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(3)).HasEnteredChildContext());
- EXPECT_EQ(CaretAfter(3), *LeftPositionOf(CaretAfter(3)).position);
-}
-
-TEST_F(NGCaretNavigatorTest, UnenterableInlineBlock) {
- SetupHtml("container",
- "<div id=container>foo"
- "<input value=bar>"
- "baz</div>");
-
- // Moving right from "foo|" should reach "<input>|".
- EXPECT_TRUE(RightPositionOf(CaretAfter(2)).IsWithinContext());
- EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretAfter(2)).position);
- EXPECT_TRUE(RightPositionOf(CaretBefore(3)).IsWithinContext());
- EXPECT_EQ(CaretAfter(3), *RightPositionOf(CaretBefore(3)).position);
-
- // Moving left from "|baz" should reach "|<input>".
- EXPECT_TRUE(LeftPositionOf(CaretBefore(4)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretBefore(4)).position);
- EXPECT_TRUE(LeftPositionOf(CaretAfter(3)).IsWithinContext());
- EXPECT_EQ(CaretBefore(3), *LeftPositionOf(CaretAfter(3)).position);
-}
-
-} // 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 00c6d5ee3ed..01ea3fc7cdc 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
@@ -19,16 +19,6 @@ namespace blink {
namespace {
-void AssertValidPositionForCaretPositionComputation(
- const PositionWithAffinity& position) {
-#if DCHECK_IS_ON()
- DCHECK(NGOffsetMapping::AcceptsPosition(position.GetPosition()));
- const LayoutObject* layout_object = position.AnchorNode()->GetLayoutObject();
- DCHECK(layout_object);
- DCHECK(layout_object->IsText() || layout_object->IsAtomicInlineLevel());
-#endif
-}
-
// The calculation takes the following input:
// - An inline formatting context as a |LayoutBlockFlow|
// - An offset in the |text_content_| string of the above context
@@ -97,7 +87,7 @@ CaretPositionResolution TryResolveCaretPositionInTextFragment(
TextAffinity affinity) {
const auto& fragment =
To<NGPhysicalTextFragment>(paint_fragment.PhysicalFragment());
- if (fragment.IsAnonymousText())
+ if (fragment.IsGeneratedText())
return CaretPositionResolution();
const NGOffsetMapping& mapping =
@@ -306,7 +296,6 @@ NGCaretPosition ComputeNGCaretPosition(const LayoutBlockFlow& context,
}
NGCaretPosition ComputeNGCaretPosition(const PositionWithAffinity& position) {
- AssertValidPositionForCaretPositionComputation(position);
LayoutBlockFlow* context =
NGInlineFormattingContextOf(position.GetPosition());
if (!context)
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 97c719d8e96..4149569a048 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
@@ -6,8 +6,8 @@
#include "third_party/blink/renderer/core/editing/local_caret_rect.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
@@ -16,13 +16,12 @@ namespace blink {
namespace {
-NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide(
- const NGPaintFragment& fragment,
- NGCaretPositionType position_type) {
+PhysicalRect ComputeLocalCaretRectByBoxSide(const NGPaintFragment& fragment,
+ NGCaretPositionType position_type) {
const bool is_horizontal = fragment.Style().IsHorizontalWritingMode();
DCHECK(fragment.ContainerLineBox());
const NGPaintFragment& line_box = *fragment.ContainerLineBox();
- const NGPhysicalOffset offset_to_line_box =
+ const PhysicalOffset offset_to_line_box =
fragment.InlineOffsetToContainerBox() -
line_box.InlineOffsetToContainerBox();
LayoutUnit caret_height =
@@ -48,12 +47,12 @@ NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide(
std::swap(caret_width, caret_height);
}
- const NGPhysicalOffset caret_location(caret_left, caret_top);
- const NGPhysicalSize caret_size(caret_width, caret_height);
- return NGPhysicalOffsetRect(caret_location, caret_size);
+ const PhysicalOffset caret_location(caret_left, caret_top);
+ const PhysicalSize caret_size(caret_width, caret_height);
+ return PhysicalRect(caret_location, caret_size);
}
-NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset(
+PhysicalRect ComputeLocalCaretRectAtTextOffset(
const NGPaintFragment& paint_fragment,
unsigned offset) {
const auto& fragment =
@@ -81,16 +80,15 @@ NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset(
}
// Adjust the location to be relative to the inline formatting context.
- NGPhysicalOffset caret_location = NGPhysicalOffset(caret_left, caret_top) +
- paint_fragment.InlineOffsetToContainerBox();
- NGPhysicalSize caret_size(caret_width, caret_height);
+ PhysicalOffset caret_location = PhysicalOffset(caret_left, caret_top) +
+ paint_fragment.InlineOffsetToContainerBox();
+ PhysicalSize caret_size(caret_width, caret_height);
const NGPaintFragment& context_fragment =
*NGPaintFragment::GetForInlineContainer(fragment.GetLayoutObject());
const NGPaintFragment* line_box = paint_fragment.ContainerLineBox();
- const NGPhysicalOffset line_box_offset =
- line_box->InlineOffsetToContainerBox();
- const NGPhysicalOffsetRect line_box_rect(line_box_offset, line_box->Size());
+ const PhysicalOffset line_box_offset = line_box->InlineOffsetToContainerBox();
+ const PhysicalRect line_box_rect(line_box_offset, line_box->Size());
// For horizontal text, adjust the location in the x direction to ensure that
// it completely falls in the union of line box and containing block, and
@@ -102,7 +100,7 @@ NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset(
std::max(context_fragment.Size().width, line_box_rect.Right());
caret_location.left = std::min(caret_location.left, max_x - caret_width);
caret_location.left = LayoutUnit(caret_location.left.Round());
- return NGPhysicalOffsetRect(caret_location, caret_size);
+ return PhysicalRect(caret_location, caret_size);
}
// Similar adjustment and rounding for vertical text.
@@ -112,7 +110,7 @@ NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset(
std::max(context_fragment.Size().height, line_box_rect.Bottom());
caret_location.top = std::min(caret_location.top, max_y - caret_height);
caret_location.top = LayoutUnit(caret_location.top.Round());
- return NGPhysicalOffsetRect(caret_location, caret_size);
+ return PhysicalRect(caret_location, caret_size);
}
LocalCaretRect ComputeLocalCaretRect(const NGCaretPosition& caret_position) {
@@ -125,32 +123,21 @@ LocalCaretRect ComputeLocalCaretRect(const NGCaretPosition& caret_position) {
case NGCaretPositionType::kBeforeBox:
case NGCaretPositionType::kAfterBox: {
DCHECK(fragment.PhysicalFragment().IsBox());
- const NGPhysicalOffsetRect fragment_local_rect =
- ComputeLocalCaretRectByBoxSide(fragment,
- caret_position.position_type);
- return {layout_object, fragment_local_rect.ToLayoutRect()};
+ const PhysicalRect fragment_local_rect = ComputeLocalCaretRectByBoxSide(
+ fragment, caret_position.position_type);
+ return {layout_object, fragment_local_rect};
}
case NGCaretPositionType::kAtTextOffset: {
DCHECK(fragment.PhysicalFragment().IsText());
DCHECK(caret_position.text_offset.has_value());
- const NGPhysicalOffsetRect caret_rect = ComputeLocalCaretRectAtTextOffset(
+ const PhysicalRect caret_rect = ComputeLocalCaretRectAtTextOffset(
fragment, *caret_position.text_offset);
- LayoutRect layout_rect = caret_rect.ToLayoutRect();
-
- // For vertical-rl, convert to "flipped block-flow" coordinates space.
- // See core/layout/README.md#coordinate-spaces for details.
- if (fragment.Style().IsFlippedBlocksWritingMode()) {
- const LayoutBlockFlow* container =
- layout_object->ContainingNGBlockFlow();
- container->FlipForWritingMode(layout_rect);
- }
-
- return {layout_object, layout_rect};
+ return {layout_object, caret_rect};
}
}
NOTREACHED();
- return {layout_object, LayoutRect()};
+ return {layout_object, PhysicalRect()};
}
LocalCaretRect ComputeLocalSelectionRect(
@@ -159,32 +146,21 @@ LocalCaretRect ComputeLocalSelectionRect(
if (!caret_rect.layout_object)
return caret_rect;
- const LayoutObject* layout_object = caret_rect.layout_object;
- const LayoutRect rect = caret_rect.rect;
-
const NGPaintFragment& fragment = *caret_position.fragment;
const NGPaintFragment* line_box = fragment.ContainerLineBox();
// TODO(xiaochengh): We'll hit this DCHECK for caret in empty block if we
// enable LayoutNG in contenteditable.
DCHECK(line_box);
+ PhysicalRect rect = caret_rect.rect;
if (fragment.Style().IsHorizontalWritingMode()) {
- const LayoutUnit line_top = line_box->InlineOffsetToContainerBox().top;
- const LayoutUnit line_height = line_box->Size().height;
- return LocalCaretRect(layout_object, LayoutRect(rect.X(), line_top,
- rect.Width(), line_height));
- }
-
- const LayoutUnit line_top = line_box->InlineOffsetToContainerBox().left;
- const LayoutUnit line_height = line_box->Size().width;
- LayoutRect layout_rect(line_top, rect.Y(), line_height, rect.Height());
- // For vertical-rl, convert to "flipped block-flow" coordinates space.
- // See core/layout/README.md#coordinate-spaces for details.
- if (fragment.Style().IsFlippedBlocksWritingMode()) {
- const LayoutBlockFlow* container = layout_object->ContainingNGBlockFlow();
- container->FlipForWritingMode(layout_rect);
+ rect.SetY(line_box->InlineOffsetToContainerBox().top);
+ rect.SetHeight(line_box->Size().height);
+ } else {
+ rect.SetX(line_box->InlineOffsetToContainerBox().left);
+ rect.SetHeight(line_box->Size().width);
}
- return LocalCaretRect(layout_object, layout_rect);
+ return {caret_rect.layout_object, rect};
}
} // namespace
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.cc
new file mode 100644
index 00000000000..61a0ecf53b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.cc
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.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_physical_line_box_fragment.h"
+
+namespace blink {
+
+void NGDirtyLines::MarkLastFragment() {
+ if (last_fragment_) {
+ // Changes in this LayoutObject may affect the line that contains its
+ // previous object. Mark the line box that contains the last fragment
+ // of the previous object.
+ last_fragment_->LastForSameLayoutObject()->MarkContainingLineBoxDirty();
+ } else {
+ // If there were no fragments so far in this pre-order traversal, mark
+ // the first line box dirty.
+ DCHECK(block_fragment_);
+ if (NGPaintFragment* first_line = block_fragment_->FirstLineBox())
+ first_line->MarkLineBoxDirty();
+ }
+}
+
+void NGDirtyLines::MarkAtTextOffset(unsigned offset) {
+ for (NGPaintFragment* child : block_fragment_->Children()) {
+ // Only the first dirty line is relevant.
+ if (child->IsDirty())
+ break;
+
+ const auto* line =
+ DynamicTo<NGPhysicalLineBoxFragment>(child->PhysicalFragment());
+ if (!line)
+ continue;
+
+ const auto* break_token = To<NGInlineBreakToken>(line->BreakToken());
+ DCHECK(break_token);
+ if (break_token->IsFinished())
+ break;
+
+ if (offset < break_token->TextOffset()) {
+ child->MarkLineBoxDirty();
+ break;
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h
new file mode 100644
index 00000000000..5e97423cd95
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h
@@ -0,0 +1,91 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_DIRTY_LINES_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_DIRTY_LINES_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
+#include "third_party/blink/renderer/core/layout/layout_text.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
+
+namespace blink {
+
+// This class computes dirty line boxes.
+class CORE_EXPORT NGDirtyLines {
+ STACK_ALLOCATED();
+
+ public:
+ explicit NGDirtyLines(const NGPaintFragment* block_fragment)
+ : block_fragment_(block_fragment) {
+ DCHECK(block_fragment_);
+ }
+
+ // Call |Handle*| functions for each object by traversing the LayoutObject
+ // tree in pre-order DFS.
+ //
+ // They return |true| when a dirty line was found. Because only the first
+ // dirty line is relevant, no further calls are necessary.
+ bool HandleText(LayoutText* layout_text) {
+ if (layout_text->SelfNeedsLayout()) {
+ MarkLastFragment();
+ return true;
+ }
+ UpdateLastFragment(layout_text->FirstInlineFragment());
+ return false;
+ }
+
+ bool HandleInlineBox(LayoutInline* layout_inline) {
+ if (layout_inline->SelfNeedsLayout()) {
+ MarkLastFragment();
+ return true;
+ }
+ // Do not keep fragments of LayoutInline unless it's a leaf, because
+ // the last fragment of LayoutInline is not the previous fragment of its
+ // descendants.
+ if (UNLIKELY(!layout_inline->FirstChild()))
+ UpdateLastFragment(layout_inline->FirstInlineFragment());
+ return false;
+ }
+
+ bool HandleAtomicInline(LayoutBox* layout_box) {
+ if (layout_box->NeedsLayout()) {
+ MarkLastFragment();
+ return true;
+ }
+ UpdateLastFragment(layout_box->FirstInlineFragment());
+ return false;
+ }
+
+ bool HandleFloatingOrOutOfFlowPositioned(LayoutObject* layout_object) {
+ DCHECK(layout_object->IsFloatingOrOutOfFlowPositioned());
+ if (layout_object->NeedsLayout()) {
+ MarkLastFragment();
+ return true;
+ }
+ // Don't update last fragment. Floats and OOF are opaque.
+ return false;
+ }
+
+ // Mark the line box at the specified text offset dirty.
+ void MarkAtTextOffset(unsigned offset);
+
+ private:
+ void UpdateLastFragment(NGPaintFragment* fragment) {
+ if (fragment)
+ last_fragment_ = fragment;
+ }
+
+ // Mark the line box that contains |last_fragment_| dirty. If |last_fragment_|
+ // is |nullptr|, the first line box is marked as dirty.
+ void MarkLastFragment();
+
+ const NGPaintFragment* block_fragment_;
+ NGPaintFragment* last_fragment_ = nullptr;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_DIRTY_LINES_H_
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 1899fd16482..3fa122f9709 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
@@ -4,8 +4,8 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.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_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
@@ -254,8 +254,8 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
DCHECK(box->style);
const ComputedStyle& style = *box->style;
- NGLogicalOffset offset;
- NGLogicalSize size;
+ LogicalOffset offset;
+ LogicalSize size;
if (!is_empty_line_) {
// The inline box should have the height of the font metrics without the
// line-height property. Compute from style because |box->metrics| includes
@@ -394,12 +394,15 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
const unsigned box_data_index = start->box_data_index;
if (!box_data_index)
continue;
+ // |box_data_list_[box_data_index - 1]| is the box for |start| child.
+ // Avoid keeping a pointer to the |BoxData| because it maybe invalidated as
+ // we add to |box_data_list_|.
// As |box_data_index| is converted to start/end of BoxData, update
// |box_data_index| to the parent box, or to 0 if no parent boxes.
// This allows including this box to the nested parent box.
- BoxData* box_data = &box_data_list_[box_data_index - 1];
- start->box_data_index = box_data->parent_box_data_index;
+ start->box_data_index =
+ box_data_list_[box_data_index - 1].parent_box_data_index;
// Find the end line box item.
const unsigned start_index = index;
@@ -415,27 +418,33 @@ unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
// because the update is limited only when its |box_data_index| is lower.
while (end->box_data_index && end->box_data_index < box_data_index) {
UpdateBoxDataFragmentRange(line_box, index);
- // Re-compute |box_data| in case |box_data_list_| was reallocated when
- // |UpdateBoxDataFragmentRange| added new fragments.
- box_data = &box_data_list_[box_data_index - 1];
}
if (box_data_index != end->box_data_index)
break;
- end->box_data_index = box_data->parent_box_data_index;
+ end->box_data_index =
+ box_data_list_[box_data_index - 1].parent_box_data_index;
}
// If this is the first range for this BoxData, set it.
- if (!box_data->fragment_end) {
- box_data->fragment_start = start_index;
- box_data->fragment_end = index;
+ if (!box_data_list_[box_data_index - 1].fragment_end) {
+ box_data_list_[box_data_index - 1].SetFragmentRange(start_index, index);
} else {
// This box is fragmented by BiDi reordering. Add a new BoxData for the
// fragmented range.
- box_data->fragmented_box_data_index = box_data_list_.size();
- box_data_list_.emplace_back(*box_data, start_index, index);
+ box_data_list_[box_data_index - 1].fragmented_box_data_index =
+ box_data_list_.size();
+ // Do not use `emplace_back()` here because adding to |box_data_list_| may
+ // reallocate the buffer, but the `BoxData` ctor must run before the
+ // reallocation. Create a new instance and |push_back()| instead.
+ BoxData fragmented_box_data(box_data_list_[box_data_index - 1],
+ start_index, index);
+ box_data_list_.push_back(fragmented_box_data);
}
- return box_data->parent_box_data_index ? start_index : index;
+ // If this box has parent boxes, we need to process it again.
+ if (box_data_list_[box_data_index - 1].parent_box_data_index)
+ return start_index;
+ return index;
}
return index;
}
@@ -572,10 +581,18 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
DCHECK(item);
DCHECK(item->Style());
const ComputedStyle& style = *item->Style();
+
+ NGFragmentGeometry fragment_geometry;
+ fragment_geometry.border_box_size = size;
+ fragment_geometry.border_box_size.inline_size.ClampNegativeToZero();
+ fragment_geometry.padding =
+ NGBoxStrut(padding, IsFlippedLinesWritingMode(style.GetWritingMode()));
+
// 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);
+ box.SetInitialFragmentGeometry(fragment_geometry);
box.SetBoxType(NGPhysicalFragment::kInlineBox);
box.SetStyleVariant(item->StyleVariant());
@@ -583,14 +600,12 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
// was fragmented. Fragmenting a line box in block direction is not
// supported today.
box.SetBorderEdges({true, has_line_right_edge, true, has_line_left_edge});
- box.SetInlineSize(size.inline_size.ClampNegativeToZero());
- box.SetBlockSize(size.block_size);
- box.SetPadding(padding);
for (unsigned i = fragment_start; i < fragment_end; i++) {
NGLineBoxFragmentBuilder::Child& child = (*line_box)[i];
if (child.layout_result) {
- box.AddChild(*child.layout_result, child.offset - offset);
+ box.AddChild(child.layout_result->PhysicalFragment(),
+ child.offset - offset);
child.layout_result.reset();
} else if (child.fragment) {
box.AddChild(std::move(child.fragment), child.offset - offset);
@@ -601,7 +616,7 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
// child.offset is the static position wrt. the linebox. As we are adding
// this as a child of an inline level fragment, we adjust the static
// position to be relative to this fragment.
- NGLogicalOffset static_offset = child.offset - offset;
+ LogicalOffset static_offset = child.offset - offset;
box.AddOutOfFlowChildCandidate(oof_box, static_offset,
child.container_direction);
@@ -609,6 +624,10 @@ NGInlineLayoutStateStack::BoxData::CreateBoxFragment(
}
}
+ // Inline boxes that produce DisplayItemClient should do full paint
+ // invalidations.
+ item->GetLayoutObject()->SetShouldDoFullPaintInvalidation();
+
box.MoveOutOfFlowDescendantCandidatesToDescendants();
return box.ToInlineBoxFragment();
}
@@ -747,10 +766,14 @@ NGLineHeightMetrics NGInlineLayoutStateStack::MetricsForTopAndBottomAlign(
// BoxData contains inline boxes to be created later. Take them into account.
for (const BoxData& box_data : box_data_list_) {
+ // |block_offset| is the top position when the baseline is at 0.
LayoutUnit box_ascent =
-line_box[box_data.fragment_end].offset.block_offset;
- metrics.Unite(
- NGLineHeightMetrics(box_ascent, box_data.size.block_size - box_ascent));
+ LayoutUnit box_descent = box_data.size.block_size - box_ascent;
+ // The top/bottom of inline boxes should not include their paddings.
+ box_ascent -= box_data.padding.line_over;
+ box_descent -= box_data.padding.line_under;
+ metrics.Unite(NGLineHeightMetrics(box_ascent, box_descent));
}
// In quirks mode, metrics is empty if no content.
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 f6ea8e18c77..2c6d9afb49d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_box_state.h
@@ -5,7 +5,7 @@
#ifndef NGInlineBoxState_h
#define NGInlineBoxState_h
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
@@ -106,7 +106,7 @@ struct NGInlineBoxState {
// 2) Performs layout when the positin/size of a box was computed.
// 3) Cache common values for a box.
class CORE_EXPORT NGInlineLayoutStateStack {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
// The box state for the line box.
@@ -204,7 +204,7 @@ class CORE_EXPORT NGInlineLayoutStateStack {
BoxData(unsigned start,
unsigned end,
const NGInlineItem* item,
- NGLogicalSize size)
+ LogicalSize size)
: fragment_start(start), fragment_end(end), item(item), size(size) {}
BoxData(const BoxData& other, unsigned start, unsigned end)
@@ -214,12 +214,17 @@ class CORE_EXPORT NGInlineLayoutStateStack {
size(other.size),
offset(other.offset) {}
+ void SetFragmentRange(unsigned start_index, unsigned end_index) {
+ fragment_start = start_index;
+ fragment_end = end_index;
+ }
+
// The range of child fragments this box contains.
unsigned fragment_start;
unsigned fragment_end;
const NGInlineItem* item;
- NGLogicalSize size;
+ LogicalSize size;
bool has_line_left_edge = false;
bool has_line_right_edge = false;
@@ -230,7 +235,7 @@ class CORE_EXPORT NGInlineLayoutStateStack {
LayoutUnit margin_border_padding_line_left;
LayoutUnit margin_border_padding_line_right;
- NGLogicalOffset offset;
+ LogicalOffset offset;
unsigned parent_box_data_index = 0;
unsigned fragmented_box_data_index = 0;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc
index 23f9cdbfa28..3a9b6f7814d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.cc
@@ -41,7 +41,7 @@ NGInlineBreakToken::NGInlineBreakToken(NGLayoutInputNode node)
NGInlineBreakToken::~NGInlineBreakToken() = default;
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String NGInlineBreakToken::ToString() const {
StringBuilder string_builder;
@@ -55,6 +55,6 @@ String NGInlineBreakToken::ToString() const {
return string_builder.ToString();
}
-#endif // NDEBUG
+#endif // DCHECK_IS_ON()
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
index f457fe494e2..ba6599469b6 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h
@@ -78,9 +78,9 @@ class CORE_EXPORT NGInlineBreakToken final : public NGBreakToken {
void SetIgnoreFloats() { ignore_floats_ = true; }
bool IgnoreFloats() const { return ignore_floats_; }
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String ToString() const override;
-#endif // NDEBUG
+#endif
private:
NGInlineBreakToken(NGInlineNode node,
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 059776134d3..5992007f223 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
@@ -18,7 +18,7 @@ class NGInlineItem;
// Because this context is in initial state for when fragmentation occurs and
// some other cases, do not add things that are too expensive to rebuild.
class NGInlineChildLayoutContext {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
// Returns the NGInlineLayoutStateStack in this context.
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
index 9c4720ab64e..5e20fc2a028 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
@@ -72,7 +72,7 @@ class NGPhysicalFragmentCollectorBase {
for (const auto& child :
To<NGPhysicalContainerFragment>(fragment).Children()) {
- base::AutoReset<NGPhysicalOffset> offset_resetter(
+ base::AutoReset<PhysicalOffset> offset_resetter(
&current_offset_to_root_, current_offset_to_root_ + child.Offset());
base::AutoReset<const NGPhysicalFragment*> fragment_resetter(
&current_fragment_, child.get());
@@ -86,7 +86,7 @@ class NGPhysicalFragmentCollectorBase {
private:
const NGPhysicalFragment* root_fragment_ = nullptr;
const NGPhysicalFragment* current_fragment_ = nullptr;
- NGPhysicalOffset current_offset_to_root_;
+ PhysicalOffset current_offset_to_root_;
Vector<Result> results_;
bool should_stop_traversing_ = false;
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 77cfcafd79e..801aaf305c2 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
@@ -110,6 +110,38 @@ unsigned NGLineInfo::InflowEndOffset() const {
return StartOffset();
}
+bool NGLineInfo::ShouldHangTrailingSpaces() const {
+ DCHECK(HasTrailingSpaces());
+ if (!line_style_->AutoWrap())
+ return false;
+ switch (text_align_) {
+ case ETextAlign::kStart:
+ case ETextAlign::kJustify:
+ return true;
+ case ETextAlign::kEnd:
+ case ETextAlign::kCenter:
+ case ETextAlign::kWebkitCenter:
+ return false;
+ case ETextAlign::kLeft:
+ case ETextAlign::kWebkitLeft:
+ return IsLtr(BaseDirection());
+ case ETextAlign::kRight:
+ case ETextAlign::kWebkitRight:
+ return IsRtl(BaseDirection());
+ }
+ NOTREACHED();
+}
+
+void NGLineInfo::UpdateTextAlign() {
+ text_align_ = line_style_->GetTextAlign(IsLastLine());
+
+ if (HasTrailingSpaces() && ShouldHangTrailingSpaces()) {
+ hang_width_ = ComputeTrailingSpaceWidth(&end_offset_for_justify_);
+ } else if (text_align_ == ETextAlign::kJustify) {
+ end_offset_for_justify_ = InflowEndOffset();
+ }
+}
+
LayoutUnit NGLineInfo::ComputeTrailingSpaceWidth(
unsigned* end_offset_out) const {
if (!has_trailing_spaces_) {
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 9e0253f842b..505bf23e4d6 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
@@ -176,17 +176,22 @@ class CORE_EXPORT NGLineInfo {
void SetTextIndent(LayoutUnit indent) { text_indent_ = indent; }
LayoutUnit TextIndent() const { return text_indent_; }
+ ETextAlign TextAlign() const { return text_align_; }
+ // Update |TextAlign()| and related fields. This depends on |IsLastLine()| and
+ // that must be called after |SetIsLastLine()|.
+ void UpdateTextAlign();
+
NGBfcOffset BfcOffset() const { return bfc_offset_; }
LayoutUnit AvailableWidth() const { return available_width_; }
// The width of this line. Includes trailing spaces if they were preserved.
// Negative width created by negative 'text-indent' is clamped to zero.
LayoutUnit Width() const { return width_.ClampNegativeToZero(); }
- // Same as |Width()| but returns negative value as is.
- LayoutUnit WidthForAlignment() const { return width_; }
- // The width of preserved trailing spaces.
- LayoutUnit ComputeTrailingSpaceWidth(
- unsigned* end_offset_out = nullptr) const;
+ // Same as |Width()| but returns negative value as is. Preserved trailing
+ // spaces may or may not be included, depends on |ShouldHangTrailingSpaces()|.
+ LayoutUnit WidthForAlignment() const { return width_ - hang_width_; }
+ // Width that hangs over the end of the line; e.g., preserved trailing spaces.
+ LayoutUnit HangWidth() const { return hang_width_; }
// Compute |Width()| from |Results()|. Used during line breaking, before
// |Width()| is set. After line breaking, this should match to |Width()|
// without clamping.
@@ -194,6 +199,7 @@ class CORE_EXPORT NGLineInfo {
bool HasTrailingSpaces() const { return has_trailing_spaces_; }
void SetHasTrailingSpaces() { has_trailing_spaces_ = true; }
+ bool ShouldHangTrailingSpaces() const;
// True if this line has overflow, excluding preserved trailing spaces.
bool HasOverflow() const { return has_overflow_; }
@@ -211,6 +217,12 @@ class CORE_EXPORT NGLineInfo {
// End text offset of this line, excluding out-of-flow objects such as
// floating or positioned.
unsigned InflowEndOffset() const;
+ // End text offset for `text-align: justify`. This excludes preserved trailing
+ // spaces. Available only when |TextAlign()| is |kJustify|.
+ unsigned EndOffsetForJustify() const {
+ DCHECK_EQ(text_align_, ETextAlign::kJustify);
+ return end_offset_for_justify_;
+ }
// End item index of this line.
unsigned EndItemIndex() const { return end_item_index_; }
void SetEndItemIndex(unsigned index) { end_item_index_ = index; }
@@ -234,6 +246,10 @@ class CORE_EXPORT NGLineInfo {
private:
bool ComputeNeedsAccurateEndPosition() const;
+ // The width of preserved trailing spaces.
+ LayoutUnit ComputeTrailingSpaceWidth(
+ unsigned* end_offset_out = nullptr) const;
+
const NGInlineItemsData* items_data_ = nullptr;
const ComputedStyle* line_style_ = nullptr;
NGInlineItemResults results_;
@@ -243,11 +259,14 @@ class CORE_EXPORT NGLineInfo {
LayoutUnit available_width_;
LayoutUnit width_;
+ LayoutUnit hang_width_;
LayoutUnit text_indent_;
unsigned start_offset_;
unsigned end_item_index_;
+ unsigned end_offset_for_justify_;
+ ETextAlign text_align_ = ETextAlign::kLeft;
TextDirection base_direction_ = TextDirection::kLtr;
bool use_first_line_style_ = false;
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 d05f2ce5e12..e881df5256a 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
@@ -9,7 +9,9 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_dirty_lines.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
@@ -150,6 +152,8 @@ inline bool IsCollapsibleSpace(UChar c) {
// It makes the line breaker easier to handle.
inline bool IsControlItemCharacter(UChar c) {
return c == kNewlineCharacter || c == kTabulationCharacter ||
+ // Make ZWNJ a control character so that it can prevent kerning.
+ c == kZeroWidthNonJoinerCharacter ||
// Include ignorable character here to avoids shaping/rendering
// these glyphs, and to help the line breaker to ignore them.
ShouldIgnore(c);
@@ -275,7 +279,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
template <typename OffsetMappingBuilder>
bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextReusing(
- const String& original_string,
+ const NGInlineNodeData& original_data,
LayoutText* layout_text) {
DCHECK(layout_text);
const NGInlineItems& items = layout_text->InlineItems();
@@ -283,6 +287,8 @@ bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextReusing(
if (!old_item0.Length())
return false;
+ const String& original_string = original_data.text_content;
+
// Don't reuse existing items if they might be affected by whitespace
// collapsing.
// TODO(layout-dev): This could likely be optimized further.
@@ -311,12 +317,26 @@ bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextReusing(
}
break;
case NGInlineItem::kNotCollapsible: {
- // If the start of the original string was collapsed, it may be
- // restored.
const String& source_text = layout_text->GetText();
- if (source_text.length() && IsCollapsibleSpace(source_text[0]) &&
- original_string[old_item0.StartOffset()] != kSpaceCharacter)
- return false;
+ if (source_text.length() && IsCollapsibleSpace(source_text[0])) {
+ // If the start of the original string was collapsed, it may be
+ // restored.
+ if (original_string[old_item0.StartOffset()] != kSpaceCharacter)
+ return false;
+ // If the start of the original string was not collapsed, and the
+ // collapsible space run contains newline, the newline may be
+ // removed.
+ unsigned offset = 0;
+ UChar c = source_text[0];
+ bool contains_newline =
+ MoveToEndOfCollapsibleSpaces(source_text, &offset, &c);
+ if (contains_newline &&
+ ShouldRemoveNewline(text_, text_.length(), last_item->Style(),
+ StringView(source_text, offset),
+ &new_style)) {
+ return false;
+ }
+ }
break;
}
case NGInlineItem::kCollapsed:
@@ -340,12 +360,22 @@ bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextReusing(
return false;
}
- if (bidi_context_.size() && new_style.PreserveNewline()) {
- // We exit and then re-enter all bidi contexts around a forced breaks. We
+ if (new_style.PreserveNewline()) {
+ // We exit and then re-enter all bidi contexts around a forced break. So, We
// must go through the full pipeline to ensure that we exit and enter the
- // contexts in the same in the re-layout.
- if (layout_text->GetText().Contains(kNewlineCharacter))
- return false;
+ // correct bidi contexts the re-layout.
+ if (bidi_context_.size() || layout_text->HasBidiControlInlineItems()) {
+ if (layout_text->GetText().Contains(kNewlineCharacter))
+ return false;
+ }
+ }
+
+ if (UNLIKELY(old_item0.StartOffset() > 0 &&
+ ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces(
+ layout_text->GetText(), new_style))) {
+ // e.g. <p>abc xyz</p> => <p> xyz</p> where "abc" and " xyz" are different
+ // Text node. |text_| is " \u200Bxyz".
+ return false;
}
for (const NGInlineItem& item : items) {
@@ -398,7 +428,7 @@ bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendTextReusing(
template <>
bool NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::AppendTextReusing(
- const String&,
+ const NGInlineNodeData&,
LayoutText*) {
NOTREACHED();
return false;
@@ -406,6 +436,30 @@ bool NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::AppendTextReusing(
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendText(
+ LayoutText* layout_text,
+ const NGInlineNodeData* previous_data) {
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_ && dirty_lines_->HandleText(layout_text))
+ dirty_lines_ = nullptr;
+
+ // If the LayoutText element hasn't changed, reuse the existing items.
+ if (previous_data && layout_text->HasValidInlineItems()) {
+ if (AppendTextReusing(*previous_data, layout_text)) {
+ return;
+ }
+ }
+
+ // If not create a new item as needed.
+ if (UNLIKELY(layout_text->IsWordBreak())) {
+ AppendBreakOpportunity(layout_text);
+ return;
+ }
+
+ AppendText(layout_text->GetText(), layout_text);
+}
+
+template <typename OffsetMappingBuilder>
+void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendText(
const String& string,
LayoutText* layout_object) {
DCHECK(layout_object);
@@ -623,6 +677,18 @@ void NGInlineItemsBuilderTemplate<
is_empty_inline_ = false; // text item is not empty.
}
+template <typename OffsetMappingBuilder>
+bool NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
+ ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces(
+ const String& string,
+ const ComputedStyle& style) const {
+ return text_.IsEmpty() && string.length() > 0 &&
+ string[0] == kSpaceCharacter && !style.CollapseWhiteSpace() &&
+ style.AutoWrap();
+}
+
+// TODO(yosin): We should remove |style| and |string| parameter because of
+// except for testing, we can get them from |LayoutText|.
// Even when without whitespace collapsing, control characters (newlines and
// tabs) are in their own control items to make the line breaker not special.
template <typename OffsetMappingBuilder>
@@ -638,8 +704,8 @@ void NGInlineItemsBuilderTemplate<
// opportunity after leading preserved spaces needs a special code in the line
// breaker. Generate an opportunity to make it easy.
unsigned start = 0;
- if (UNLIKELY(text_.IsEmpty() && string[start] == kSpaceCharacter &&
- style->AutoWrap())) {
+ if (UNLIKELY(ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces(
+ string, *style))) {
do {
++start;
} while (start < string.length() && string[start] == kSpaceCharacter);
@@ -653,7 +719,9 @@ void NGInlineItemsBuilderTemplate<
if (c == kNewlineCharacter) {
AppendForcedBreak(layout_object);
start++;
- } else if (c == kTabulationCharacter) {
+ continue;
+ }
+ if (c == kTabulationCharacter) {
wtf_size_t end = string.Find(
[](UChar c) { return c != kTabulationCharacter; }, start + 1);
if (end == kNotFound)
@@ -661,11 +729,14 @@ void NGInlineItemsBuilderTemplate<
AppendTextItem(NGInlineItem::kControl,
StringView(string, start, end - start), layout_object);
start = end;
- } else {
+ continue;
+ }
+ // ZWNJ splits item, but it should be text.
+ if (c != kZeroWidthNonJoinerCharacter) {
Append(NGInlineItem::kControl, c, layout_object);
start++;
+ continue;
}
- continue;
}
wtf_size_t end = string.Find(IsControlItemCharacter, start + 1);
@@ -773,6 +844,11 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendAtomicInline(
Append(NGInlineItem::kAtomicInline, kObjectReplacementCharacter,
layout_object);
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_ &&
+ dirty_lines_->HandleAtomicInline(ToLayoutBox(layout_object)))
+ dirty_lines_ = nullptr;
+
// 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
// the inline box creates a box fragment so that its height is available in
@@ -787,17 +863,25 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendAtomicInline(
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::AppendFloating(
LayoutObject* layout_object) {
- changes_may_affect_earlier_lines_ = true;
AppendOpaque(NGInlineItem::kFloating, kObjectReplacementCharacter,
layout_object);
+
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_ &&
+ dirty_lines_->HandleFloatingOrOutOfFlowPositioned(layout_object))
+ dirty_lines_ = nullptr;
}
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::
AppendOutOfFlowPositioned(LayoutObject* layout_object) {
- changes_may_affect_earlier_lines_ = true;
AppendOpaque(NGInlineItem::kOutOfFlowPositioned, kObjectReplacementCharacter,
layout_object);
+
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_ &&
+ dirty_lines_->HandleFloatingOrOutOfFlowPositioned(layout_object))
+ dirty_lines_ = nullptr;
}
template <typename OffsetMappingBuilder>
@@ -857,6 +941,12 @@ void NGInlineItemsBuilderTemplate<
text_.erase(space_offset);
mapping_builder_.CollapseTrailingSpace(space_offset);
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_) {
+ dirty_lines_->MarkAtTextOffset(space_offset);
+ dirty_lines_ = nullptr;
+ }
+
// 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()|.
@@ -972,7 +1062,7 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::EnterBlock(
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::EnterInline(
- LayoutObject* node) {
+ LayoutInline* node) {
DCHECK(node);
// https://drafts.csswg.org/css-writing-modes-3/#bidi-control-codes-injection-table
@@ -1013,6 +1103,10 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::EnterInline(
AppendOpaque(NGInlineItem::kOpenTag, node);
+ // Mark dirty lines. Clear if marked, only the first dirty line is relevant.
+ if (dirty_lines_ && dirty_lines_->HandleInlineBox(node))
+ dirty_lines_ = nullptr;
+
if (!NeedsBoxInfo())
return;
@@ -1072,7 +1166,8 @@ void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::SetIsSymbolMarker(
template <typename OffsetMappingBuilder>
void NGInlineItemsBuilderTemplate<OffsetMappingBuilder>::ClearInlineFragment(
LayoutObject* object) {
- NGInlineNode::ClearInlineFragment(object);
+ object->SetIsInLayoutNGInlineFormattingContext(true);
+ object->SetFirstInlineFragment(nullptr);
}
template <typename OffsetMappingBuilder>
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 9d04bba9549..b7c9530edd1 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
@@ -21,6 +21,8 @@ class ComputedStyle;
class LayoutInline;
class LayoutObject;
class LayoutText;
+struct NGInlineNodeData;
+class NGDirtyLines;
// NGInlineItemsBuilder builds a string and a list of NGInlineItem from inlines.
//
@@ -42,8 +44,13 @@ class NGInlineItemsBuilderTemplate {
STACK_ALLOCATED();
public:
- explicit NGInlineItemsBuilderTemplate(Vector<NGInlineItem>* items)
- : items_(items) {}
+ // Create a builder that appends items to |items|.
+ //
+ // If |dirty_lines| is given, this builder calls its functions to mark lines
+ // dirty.
+ explicit NGInlineItemsBuilderTemplate(Vector<NGInlineItem>* items,
+ NGDirtyLines* dirty_lines = nullptr)
+ : items_(items), dirty_lines_(dirty_lines) {}
~NGInlineItemsBuilderTemplate();
String ToString();
@@ -62,11 +69,19 @@ class NGInlineItemsBuilderTemplate {
return changes_may_affect_earlier_lines_;
}
+ // Append a string from |LayoutText|.
+ //
+ // If |previous_data| is given, reuse existing items if they exist and are
+ // reusable. Otherwise appends new items.
+ void AppendText(LayoutText* layout_text,
+ const NGInlineNodeData* previous_data);
+
// Append existing items from an unchanged LayoutObject.
// Returns whether the existing items could be reused.
// NOTE: The state of the builder remains unchanged if the append operation
// fails (i.e. if it returns false).
- bool AppendTextReusing(const String& previous_text, LayoutText* layout_text);
+ bool AppendTextReusing(const NGInlineNodeData& previous_data,
+ LayoutText* layout_text);
// Append a string.
// When appending, spaces are collapsed according to CSS Text, The white space
@@ -114,7 +129,7 @@ class NGInlineItemsBuilderTemplate {
void EnterBlock(const ComputedStyle*);
void ExitBlock();
- void EnterInline(LayoutObject*);
+ void EnterInline(LayoutInline*);
void ExitInline(LayoutObject*);
OffsetMappingBuilder& GetOffsetMappingBuilder() { return mapping_builder_; }
@@ -134,6 +149,8 @@ class NGInlineItemsBuilderTemplate {
Vector<NGInlineItem>* items_;
StringBuilder text_;
+ NGDirtyLines* dirty_lines_;
+
// |mapping_builder_| builds the whitespace-collapsed offset mapping
// during inline collection. It is updated whenever |text_| is modified or a
// white space is collapsed.
@@ -200,11 +217,17 @@ class NGInlineItemsBuilderTemplate {
void AppendGeneratedBreakOpportunity(LayoutObject*);
void Exit(LayoutObject*);
+
+ bool ShouldInsertBreakOpportunityAfterLeadingPreservedSpaces(
+ const String&,
+ const ComputedStyle&) const;
};
template <>
-CORE_EXPORT bool NGInlineItemsBuilderTemplate<
- NGOffsetMappingBuilder>::AppendTextReusing(const String&, LayoutText*);
+CORE_EXPORT bool
+NGInlineItemsBuilderTemplate<NGOffsetMappingBuilder>::AppendTextReusing(
+ const NGInlineNodeData&,
+ LayoutText*);
template <>
CORE_EXPORT void
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 868bbe90a35..56fa24daadc 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
@@ -7,13 +7,12 @@
#include "testing/gtest/include/gtest/gtest.h"
#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/ng_layout_test.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
-namespace {
-
// The spec turned into a discussion that may change. Put this logic on hold
// until CSSWG resolves the issue.
// https://github.com/w3c/csswg-drafts/issues/337
@@ -79,7 +78,7 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
builder.ExitBlock();
text_ = builder.ToString();
ValidateItems();
- CheckReuseItemsProducesSameResult(inputs);
+ CheckReuseItemsProducesSameResult(inputs, builder.HasBidiControls());
for (LayoutObject* anonymous_object : anonymous_objects)
anonymous_object->Destroy();
return text_;
@@ -111,7 +110,12 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
EXPECT_EQ(current_offset, text_.length());
}
- void CheckReuseItemsProducesSameResult(Vector<Input> inputs) {
+ void CheckReuseItemsProducesSameResult(Vector<Input> inputs,
+ bool has_bidi_controls) {
+ NGInlineNodeData fake_data;
+ fake_data.text_content = text_;
+ fake_data.is_bidi_enabled_ = has_bidi_controls;
+
Vector<NGInlineItem> reuse_items;
NGInlineItemsBuilder reuse_builder(&reuse_items);
for (Input& input : inputs) {
@@ -131,8 +135,9 @@ class NGInlineItemsBuilderTest : public NGLayoutTest {
}
// Try to re-use previous items, or Append if it was not re-usable.
- bool reused = input.layout_text->HasValidInlineItems() &&
- reuse_builder.AppendTextReusing(text_, input.layout_text);
+ bool reused =
+ input.layout_text->HasValidInlineItems() &&
+ reuse_builder.AppendTextReusing(fake_data, input.layout_text);
if (!reused) {
reuse_builder.AppendText(input.text, input.layout_text);
}
@@ -493,6 +498,4 @@ TEST_F(NGInlineItemsBuilderTest, BidiIsolateOverride) {
builder.ToString());
}
-} // namespace
-
} // 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 c8c01e84f7c..f3d4a644b8c 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
@@ -96,8 +96,10 @@ NGInlineBoxState* NGInlineLayoutAlgorithm::HandleCloseTag(
box->EnsureTextMetrics(*item.Style(), baseline_type_);
box = box_states_->OnCloseTag(&line_box_, box, baseline_type_,
item.HasEndEdge());
- item.GetLayoutObject()->SetShouldDoFullPaintInvalidation();
- ClearNeedsLayoutIfNeeded(item.GetLayoutObject());
+ // Just clear |NeedsLayout| flags. Culled inline boxes do not need paint
+ // invalidations. If this object produces box fragments,
+ // |NGInlineBoxStateStack| takes care of invalidations.
+ item.GetLayoutObject()->ClearNeedsLayoutWithoutPaintInvalidation();
return box;
}
@@ -239,7 +241,8 @@ void NGInlineLayoutAlgorithm::CreateLine(
}
line_box_.AddChild(text_builder.ToTextFragment(), box->text_top,
item_result.inline_size, item.BidiLevel());
- ClearNeedsLayoutIfNeeded(item.GetLayoutObject());
+ // Text boxes always need full paint invalidations.
+ item.GetLayoutObject()->ClearNeedsLayoutWithFullPaintInvalidation();
} else if (item.Type() == NGInlineItem::kControl) {
PlaceControlItem(item, *line_info, &item_result, box);
} else if (item.Type() == NGInlineItem::kOpenTag) {
@@ -290,6 +293,14 @@ void NGInlineLayoutAlgorithm::CreateLine(
box_states_->UpdateAfterReorder(&line_box_);
}
LayoutUnit inline_size = box_states_->ComputeInlinePositions(&line_box_);
+ if (LayoutUnit hang_width = line_info->HangWidth()) {
+ inline_size -= hang_width;
+ container_builder_.SetHangInlineSize(hang_width);
+
+ if (IsRtl(line_info->BaseDirection())) {
+ line_box_.MoveInInlineDirection(-hang_width);
+ }
+ }
// Truncate the line if 'text-overflow: ellipsis' is set.
if (UNLIKELY(inline_size > line_info->AvailableWidth() &&
@@ -346,6 +357,7 @@ void NGInlineLayoutAlgorithm::CreateLine(
// 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()) {
+ container_builder_.SetIsSelfCollapsing();
container_builder_.SetIsEmptyLineBox();
container_builder_.SetBaseDirection(line_info->BaseDirection());
container_builder_.AddChildren(line_box_);
@@ -413,7 +425,7 @@ void NGInlineLayoutAlgorithm::PlaceControlItem(const NGInlineItem& item,
// Place a generated content that does not exist in DOM nor in LayoutObject
// tree.
void NGInlineLayoutAlgorithm::PlaceGeneratedContent(
- scoped_refptr<const NGPhysicalFragment> fragment,
+ scoped_refptr<const NGPhysicalTextFragment> fragment,
UBiDiLevel bidi_level,
NGInlineBoxState* box) {
LayoutUnit inline_size = IsHorizontalWritingMode() ? fragment->Size().width
@@ -466,7 +478,7 @@ void NGInlineLayoutAlgorithm::PlaceLayoutResult(NGInlineItemResult* item_result,
NGBoxFragment fragment(ConstraintSpace().GetWritingMode(),
ConstraintSpace().Direction(),
To<NGPhysicalBoxFragment>(
- *item_result->layout_result->PhysicalFragment()));
+ item_result->layout_result->PhysicalFragment()));
NGLineHeightMetrics metrics = fragment.BaselineMetrics(
{NGBaselineAlgorithmType::kAtomicInline, baseline_type_},
ConstraintSpace());
@@ -475,7 +487,7 @@ void NGInlineLayoutAlgorithm::PlaceLayoutResult(NGInlineItemResult* item_result,
LayoutUnit line_top = item_result->margins.line_over - metrics.ascent;
line_box_.AddChild(std::move(item_result->layout_result),
- NGLogicalOffset{inline_offset, line_top},
+ LogicalOffset{inline_offset, line_top},
item_result->inline_size, item.BidiLevel());
}
@@ -486,6 +498,8 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
DCHECK(line_info.IsEmptyLine() || !line_box_metrics.IsEmpty())
<< "Non-empty lines must have a valid set of linebox metrics.";
+ bool is_empty_inline = Node().IsEmptyInline();
+
// All children within the linebox are positioned relative to the baseline,
// then shifted later using NGLineBoxFragmentBuilder::MoveInBlockDirection.
LayoutUnit baseline_adjustment =
@@ -516,25 +530,34 @@ void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
// To correctly determine which "line" block-level out-of-flow positioned
// object is placed on, we need to keep track of if there is any inline-level
// content preceeding it.
- bool has_preceeding_inline_level_content = false;
+ bool has_preceding_inline_level_content = false;
for (NGLineBoxFragmentBuilder::Child& child : line_box_) {
- has_preceeding_inline_level_content |= child.HasInFlowFragment();
+ has_preceding_inline_level_content |= child.HasInFlowFragment();
LayoutObject* box = child.out_of_flow_positioned_box;
if (!box)
continue;
- NGLogicalOffset static_offset(LayoutUnit(), baseline_adjustment);
+ LogicalOffset static_offset(LayoutUnit(), baseline_adjustment);
if (box->StyleRef().IsOriginalDisplayInlineType()) {
// An inline-level OOF element positions itself within the line, at the
// position it would have been if it was in-flow.
static_offset.inline_offset = child.offset.inline_offset;
+
+ // The static-position of inline-level OOF-positioned nodes depends on
+ // previous floats (if any).
+ //
+ // If we are an empty-inline we may not have the correct BFC block-offset
+ // yet. Due to this we need to mark this node as having adjoining
+ // objects, and perform a re-layout if our position shifts.
+ if (is_empty_inline)
+ container_builder_.AddAdjoiningFloatTypes(kAdjoiningInlineOutOfFlow);
} else {
// A block-level OOF element positions itself on the "next" line. However
// only shifts down if there is inline-level content.
static_offset.inline_offset = block_level_inline_offset;
- if (has_preceeding_inline_level_content)
+ if (has_preceding_inline_level_content)
static_offset.block_offset += line_height;
}
@@ -566,9 +589,8 @@ void NGInlineLayoutAlgorithm::PlaceFloatingObjects(
bool is_empty_inline = Node().IsEmptyInline();
LayoutUnit bfc_block_offset = line_info.BfcOffset().block_offset;
- if (is_empty_inline && ConstraintSpace().FloatsBfcBlockOffset()) {
- bfc_block_offset = *ConstraintSpace().FloatsBfcBlockOffset();
- }
+ if (is_empty_inline && ConstraintSpace().ForcedBfcBlockOffset())
+ bfc_block_offset = *ConstraintSpace().ForcedBfcBlockOffset();
LayoutUnit bfc_line_offset = container_builder_.BfcLineOffset();
@@ -587,7 +609,7 @@ void NGInlineLayoutAlgorithm::PlaceFloatingObjects(
// Skip any children which aren't positioned floats.
if (!child.layout_result ||
- !child.layout_result->PhysicalFragment()->IsFloating())
+ !child.layout_result->PhysicalFragment().IsFloating())
continue;
LayoutUnit block_offset =
@@ -595,9 +617,8 @@ void NGInlineLayoutAlgorithm::PlaceFloatingObjects(
// We need to manually account for the flipped-lines writing mode here :(.
if (IsFlippedLinesWritingMode(ConstraintSpace().GetWritingMode())) {
- NGFragment fragment(
- ConstraintSpace().GetWritingMode(),
- To<NGPhysicalBoxFragment>(*child.layout_result->PhysicalFragment()));
+ NGFragment fragment(ConstraintSpace().GetWritingMode(),
+ child.layout_result->PhysicalFragment());
block_offset = -fragment.BlockSize() - block_offset;
}
@@ -629,10 +650,7 @@ bool NGInlineLayoutAlgorithm::ApplyJustify(LayoutUnit space,
return false;
// Justify the end of visible text, ignoring preserved trailing spaces.
- unsigned end_offset;
- LayoutUnit trailing_spaces_width =
- line_info->ComputeTrailingSpaceWidth(&end_offset);
- space += trailing_spaces_width;
+ unsigned end_offset = line_info->EndOffsetForJustify();
// If this line overflows, fallback to 'text-align: start'.
if (space <= 0)
@@ -700,8 +718,7 @@ LayoutUnit NGInlineLayoutAlgorithm::ApplyTextAlign(NGLineInfo* line_info) {
LayoutUnit space =
line_info->AvailableWidth() - line_info->WidthForAlignment();
- const ComputedStyle& line_style = line_info->LineStyle();
- ETextAlign text_align = line_style.GetTextAlign(line_info->IsLastLine());
+ 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|.
@@ -781,16 +798,22 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
unsigned handled_leading_floats_index =
PositionLeadingFloats(&initial_exclusion_space, &leading_floats);
+ // Only empty-inlines should have the "forced" BFC block-offset set.
+ DCHECK(is_empty_inline || !ConstraintSpace().ForcedBfcBlockOffset());
+
// We query all the layout opportunities on the initial exclusion space up
// front, as if the line breaker may add floats and change the opportunities.
const LayoutOpportunityVector opportunities =
initial_exclusion_space.AllLayoutOpportunities(
- ConstraintSpace().BfcOffset(),
+ {ConstraintSpace().BfcOffset().line_offset,
+ ConstraintSpace().ForcedBfcBlockOffset().value_or(
+ ConstraintSpace().BfcOffset().block_offset)},
ConstraintSpace().AvailableSize().inline_size);
NGExclusionSpace exclusion_space;
const NGInlineBreakToken* break_token = BreakToken();
+ bool is_line_created = false;
LayoutUnit line_block_size;
LayoutUnit block_delta;
const auto* opportunities_it = opportunities.begin();
@@ -818,6 +841,7 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
// Reset any state that may have been modified in a previous pass.
container_builder_.Reset();
exclusion_space = initial_exclusion_space;
+ is_line_created = false;
NGLineLayoutOpportunity line_opportunity =
opportunity.ComputeLineLayoutOpportunity(ConstraintSpace(),
@@ -834,8 +858,8 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
// *and* the opportunity is smaller than the available inline-size, and the
// container autowraps, continue to the next opportunity.
if (line_info.HasOverflow() &&
- ConstraintSpace().AvailableSize().inline_size !=
- line_opportunity.AvailableFloatInlineSize() &&
+ !line_opportunity.IsEqualToAvailableFloatInlineSize(
+ ConstraintSpace().AvailableSize().inline_size) &&
Node().Style().AutoWrap()) {
// Shapes are *special*. We need to potentially increment the block-delta
// by 1px each loop to properly test each potential position of the line.
@@ -851,11 +875,15 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
line_block_size = LayoutUnit();
++opportunities_it;
}
+ // There must be at least one more opportunity, or we fail to call
+ // |CreateLine()|.
+ DCHECK_NE(opportunities_it, opportunities.end());
continue;
}
PrepareBoxStates(line_info, break_token);
CreateLine(line_opportunity, &line_info, &exclusion_space);
+ is_line_created = true;
// We now can check the block-size of the fragment, and it fits within the
// opportunity.
@@ -894,10 +922,6 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
continue;
}
- if (opportunity.rect.BlockStartOffset() >
- ConstraintSpace().BfcOffset().block_offset)
- container_builder_.SetIsPushedByFloats();
-
// Success!
container_builder_.SetBreakToken(line_breaker.CreateBreakToken(line_info));
@@ -909,10 +933,20 @@ scoped_refptr<const NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
// TODO(ikilpatrick): Move this into ng_block_layout_algorithm.
container_builder_.SetBlockSize(
ComputeContentSize(line_info, exclusion_space, line_height));
+
+ // As we aren't an empty inline we should have correctly placed all
+ // our adjoining floats, and shouldn't propagate this information
+ // to siblings.
+ container_builder_.ResetAdjoiningFloatTypes();
+
+ if (opportunity.rect.BlockStartOffset() >
+ ConstraintSpace().BfcOffset().block_offset)
+ container_builder_.SetIsPushedByFloats();
}
break;
}
+ CHECK(is_line_created);
container_builder_.SetExclusionSpace(std::move(exclusion_space));
container_builder_.MoveOutOfFlowDescendantCandidatesToDescendants();
return container_builder_.ToLineBoxFragment();
@@ -948,13 +982,13 @@ unsigned NGInlineLayoutAlgorithm::PositionLeadingFloats(
? kFloatTypeLeft
: kFloatTypeRight);
- // If we are an empty inline, and don't have the special floats BFC
+ // If we are an empty inline, and don't have the special forced BFC
// block-offset yet, there is no way to position any floats.
- if (is_empty_inline && !ConstraintSpace().FloatsBfcBlockOffset())
+ if (is_empty_inline && !ConstraintSpace().ForcedBfcBlockOffset())
continue;
LayoutUnit origin_bfc_block_offset =
- is_empty_inline ? *ConstraintSpace().FloatsBfcBlockOffset()
+ is_empty_inline ? *ConstraintSpace().ForcedBfcBlockOffset()
: ConstraintSpace().BfcOffset().block_offset;
NGPositionedFloat positioned_float = PositionFloat(
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 e12d473e761..d93ab75c097 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
@@ -80,7 +80,7 @@ class CORE_EXPORT NGInlineLayoutAlgorithm final
const NGLineInfo&,
NGInlineItemResult*,
NGInlineBoxState*);
- void PlaceGeneratedContent(scoped_refptr<const NGPhysicalFragment>,
+ void PlaceGeneratedContent(scoped_refptr<const NGPhysicalTextFragment>,
UBiDiLevel,
NGInlineBoxState*);
NGInlineBoxState* PlaceAtomicInline(const NGInlineItem&,
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 f1d65e70440..1c01a0a125e 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
@@ -41,7 +41,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) {
auto* block_flow =
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container"));
NGInlineNode inline_node(block_flow);
- NGLogicalSize size(LayoutUnit(50), LayoutUnit(20));
+ LogicalSize size(LayoutUnit(50), LayoutUnit(20));
NGConstraintSpace constraint_space =
NGConstraintSpaceBuilder(
@@ -53,23 +53,20 @@ TEST_F(NGInlineLayoutAlgorithmTest, BreakToken) {
NGInlineChildLayoutContext context;
scoped_refptr<const NGLayoutResult> layout_result =
inline_node.Layout(constraint_space, nullptr, &context);
- auto* line1 =
- To<NGPhysicalLineBoxFragment>(layout_result->PhysicalFragment());
- EXPECT_FALSE(line1->BreakToken()->IsFinished());
+ const auto& line1 = layout_result->PhysicalFragment();
+ EXPECT_FALSE(line1.BreakToken()->IsFinished());
// Perform 2nd layout with the break token from the 1st line.
scoped_refptr<const NGLayoutResult> layout_result2 =
- inline_node.Layout(constraint_space, line1->BreakToken(), &context);
- auto* line2 =
- To<NGPhysicalLineBoxFragment>(layout_result2->PhysicalFragment());
- EXPECT_FALSE(line2->BreakToken()->IsFinished());
+ 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.
scoped_refptr<const NGLayoutResult> layout_result3 =
- inline_node.Layout(constraint_space, line2->BreakToken(), &context);
- auto* line3 =
- To<NGPhysicalLineBoxFragment>(layout_result3->PhysicalFragment());
- EXPECT_TRUE(line3->BreakToken()->IsFinished());
+ inline_node.Layout(constraint_space, line2.BreakToken(), &context);
+ const auto& line3 = layout_result3->PhysicalFragment();
+ EXPECT_TRUE(line3.BreakToken()->IsFinished());
}
TEST_F(NGInlineLayoutAlgorithmTest, GenerateHyphen) {
@@ -168,10 +165,10 @@ TEST_F(NGInlineLayoutAlgorithmTest,
To<NGPhysicalLineBoxFragment>(*container->Children()[0]);
EXPECT_EQ(1u, linebox.Children().size());
- EXPECT_EQ(NGPhysicalSize(), linebox.Size());
+ EXPECT_EQ(PhysicalSize(), linebox.Size());
const auto& oof_container = To<NGPhysicalBoxFragment>(*linebox.Children()[0]);
- EXPECT_EQ(NGPhysicalSize(), oof_container.Size());
+ EXPECT_EQ(PhysicalSize(), oof_container.Size());
}
// This test ensures that if an inline box generates (or does not generate) box
@@ -234,13 +231,12 @@ TEST_F(NGInlineLayoutAlgorithmTest, ContainerBorderPadding) {
NGConstraintSpace::CreateFromLayoutObject(*block_flow);
scoped_refptr<const NGLayoutResult> layout_result = block_node.Layout(space);
- auto* block_box =
- To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment());
EXPECT_TRUE(layout_result->BfcBlockOffset().has_value());
EXPECT_EQ(0, *layout_result->BfcBlockOffset());
EXPECT_EQ(0, layout_result->BfcLineOffset());
- NGPhysicalOffset line_offset = block_box->Children()[0].Offset();
+ PhysicalOffset line_offset =
+ layout_result->PhysicalFragment().Children()[0].Offset();
EXPECT_EQ(5, line_offset.left);
EXPECT_EQ(10, line_offset.top);
}
@@ -270,9 +266,9 @@ TEST_F(NGInlineLayoutAlgorithmTest, MAYBE_VerticalAlignBottomReplaced) {
scoped_refptr<const NGLayoutResult> layout_result =
inline_node.Layout(space, nullptr, &context);
- auto* line = To<NGPhysicalLineBoxFragment>(layout_result->PhysicalFragment());
- EXPECT_EQ(LayoutUnit(96), line->Size().height);
- NGPhysicalOffset img_offset = line->Children()[0].Offset();
+ const auto& line = layout_result->PhysicalFragment();
+ EXPECT_EQ(LayoutUnit(96), line.Size().height);
+ PhysicalOffset img_offset = line.Children()[0].Offset();
EXPECT_EQ(LayoutUnit(0), img_offset.top);
}
@@ -316,7 +312,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, TextFloatsAroundFloatsBefore) {
To<NGPhysicalBoxFragment>(html_fragment->Children()[0].get());
auto* container_fragment =
To<NGPhysicalBoxFragment>(body_fragment->Children()[0].get());
- Vector<NGPhysicalOffset> line_offsets;
+ Vector<PhysicalOffset> line_offsets;
for (const auto& child : container_fragment->Children()) {
if (!child->IsLineBox())
continue;
@@ -368,7 +364,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, TextFloatsAroundInlineFloatThatFitsOnLine) {
// Two lines.
EXPECT_EQ(2u, block_box->Children().size());
- NGPhysicalOffset first_line_offset = block_box->Children()[1].Offset();
+ PhysicalOffset first_line_offset = block_box->Children()[1].Offset();
// 30 == narrow-float's width.
EXPECT_EQ(LayoutUnit(30), first_line_offset.left);
@@ -500,7 +496,7 @@ TEST_F(NGInlineLayoutAlgorithmTest, InkOverflow) {
EXPECT_EQ(LayoutUnit(10), box_fragment.Size().height);
- NGPhysicalOffsetRect ink_overflow = paint_fragment->InkOverflow();
+ PhysicalRect ink_overflow = paint_fragment->InkOverflow();
EXPECT_EQ(LayoutUnit(-5), ink_overflow.offset.top);
EXPECT_EQ(LayoutUnit(20), ink_overflow.size.height);
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 e2e32f01d1b..846eb8c7ef1 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,6 +7,7 @@
#include <algorithm>
#include <memory>
+#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"
@@ -15,6 +16,7 @@
#include "third_party/blink/renderer/core/layout/logical_values.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_dirty_lines.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h"
@@ -62,83 +64,28 @@ unsigned EstimateOffsetMappingItemsCount(const LayoutBlockFlow& block) {
return EstimateInlineItemsCount(block) / 4;
}
-// This class marks appropriate line box fragments as dirty.
-//
-// |CollectInlinesInternal| calls this class when traversing the LayoutObject
-// tree in pre-order DFS
-class NGLineBoxMarker {
- STACK_ALLOCATED();
-
- public:
- NGLineBoxMarker(NGPaintFragment* block_fragment)
- : block_fragment_(block_fragment) {
- DCHECK(block_fragment_);
- }
-
- bool HandleText(LayoutText* layout_text) {
- if (layout_text->SelfNeedsLayout())
- return Mark();
- return UpdateLastFragment(layout_text->FirstInlineFragment());
- }
-
- bool HandleInlineBox(LayoutInline* layout_inline) {
- if (layout_inline->SelfNeedsLayout())
- return Mark();
-
- // Do not keep fragments of LayoutInline unless it's a leaf, because
- // the last fragment of LayoutInline is not the previous fragment of its
- // descendants.
- if (layout_inline->FirstChild())
- return false;
- return UpdateLastFragment(layout_inline->FirstInlineFragment());
- }
-
- bool HandleAtomicInline(LayoutBox* layout_box) {
- if (layout_box->NeedsLayout())
- return Mark();
- return UpdateLastFragment(layout_box->FirstInlineFragment());
- }
-
- private:
- bool Mark() {
- if (last_fragment_) {
- // Changes in this LayoutObject may affect the line that contains its
- // previous object. Mark the line box that contains the last fragment
- // of the previous object.
- last_fragment_->LastForSameLayoutObject()->MarkContainingLineBoxDirty();
- } else {
- // If there were no fragments so far in this pre-order traversal, mark
- // the first line box dirty.
- DCHECK(block_fragment_);
- if (NGPaintFragment* first_line = block_fragment_->FirstLineBox())
- first_line->MarkLineBoxDirty();
- }
- return true;
- }
-
- bool UpdateLastFragment(NGPaintFragment* fragment) {
- if (fragment)
- last_fragment_ = fragment;
- return false;
- }
-
- NGPaintFragment* block_fragment_;
- NGPaintFragment* last_fragment_ = nullptr;
-};
-
// This class has the same interface as NGInlineItemsBuilder but does nothing
// except tracking if floating or out-of-flow objects are added.
//
// |MarkLineBoxesDirty| uses this class to traverse tree without buildling
// |NGInlineItem|.
class ItemsBuilderForMarkLineBoxesDirty {
+ STACK_ALLOCATED();
+
public:
- void AppendText(const String&, LayoutText*) {}
- bool AppendTextReusing(const String&, LayoutText*) { return false; }
+ ItemsBuilderForMarkLineBoxesDirty(NGDirtyLines* dirty_lines)
+ : dirty_lines_(dirty_lines) {}
+ void AppendText(LayoutText* layout_text, const NGInlineItemsData*) {
+ if (dirty_lines_ && dirty_lines_->HandleText(layout_text))
+ dirty_lines_ = nullptr;
+ }
void AppendOpaque(NGInlineItem::NGInlineItemType,
LayoutObject*) {}
- void AppendBreakOpportunity(LayoutObject*) {}
- void AppendAtomicInline(LayoutObject*) {}
+ void AppendAtomicInline(LayoutObject* layout_object) {
+ if (dirty_lines_ &&
+ dirty_lines_->HandleAtomicInline(ToLayoutBox(layout_object)))
+ dirty_lines_ = nullptr;
+ }
void AppendFloating(LayoutObject*) {
has_floating_or_out_of_flow_positioned_ = true;
}
@@ -148,7 +95,10 @@ class ItemsBuilderForMarkLineBoxesDirty {
void SetIsSymbolMarker(bool) {}
void EnterBlock(const ComputedStyle*) {}
void ExitBlock() {}
- void EnterInline(LayoutObject*) {}
+ void EnterInline(LayoutInline* layout_inline) {
+ if (dirty_lines_ && dirty_lines_->HandleInlineBox(layout_inline))
+ dirty_lines_ = nullptr;
+ }
void ExitInline(LayoutObject*) {}
bool ShouldAbort() const {
@@ -164,12 +114,12 @@ class ItemsBuilderForMarkLineBoxesDirty {
}
void ClearInlineFragment(LayoutObject* object) {
- NGInlineNode::ClearInlineFragment(object);
+ DCHECK(object->IsInLayoutNGInlineFormattingContext());
}
void ClearNeedsLayout(LayoutObject* object) {
object->ClearNeedsLayout();
- object->ClearNeedsCollectInlines();
+ DCHECK(!object->NeedsCollectInlines());
ClearInlineFragment(object);
}
@@ -178,6 +128,7 @@ class ItemsBuilderForMarkLineBoxesDirty {
}
private:
+ NGDirtyLines* dirty_lines_;
bool has_floating_or_out_of_flow_positioned_ = false;
};
@@ -194,8 +145,7 @@ class ItemsBuilderForMarkLineBoxesDirty {
template <typename ItemsBuilder>
void CollectInlinesInternal(LayoutBlockFlow* block,
ItemsBuilder* builder,
- String* previous_text,
- NGLineBoxMarker* marker) {
+ const NGInlineNodeData* previous_data) {
builder->EnterBlock(block->Style());
LayoutObject* node = GetLayoutObjectForFirstChildNode(block);
@@ -203,28 +153,11 @@ void CollectInlinesInternal(LayoutBlockFlow* block,
LayoutNGListItem::FindSymbolMarkerLayoutText(block);
while (node) {
if (LayoutText* layout_text = ToLayoutTextOrNull(node)) {
- // If the LayoutText element hasn't changed, reuse the existing items.
-
- // if the last ended with space and this starts with space, do not allow
- // reuse. builder->MightCollapseWithPreceding(*previous_text)
- bool item_reused = false;
- if (previous_text && layout_text->HasValidInlineItems())
- item_reused = builder->AppendTextReusing(*previous_text, layout_text);
-
- // If not create a new item as needed.
- if (!item_reused) {
- if (UNLIKELY(layout_text->IsWordBreak()))
- builder->AppendBreakOpportunity(layout_text);
- else
- builder->AppendText(layout_text->GetText(), layout_text);
- }
+ builder->AppendText(layout_text, previous_data);
if (symbol == layout_text)
builder->SetIsSymbolMarker(true);
- if (marker && marker->HandleText(layout_text))
- marker = nullptr;
-
builder->ClearNeedsLayout(layout_text);
} else if (node->IsFloating()) {
@@ -252,9 +185,6 @@ void CollectInlinesInternal(LayoutBlockFlow* block,
// signal the presence of a non-text object to the unicode bidi
// algorithm.
builder->AppendAtomicInline(node);
-
- if (marker && marker->HandleAtomicInline(ToLayoutBox(node)))
- marker = nullptr;
}
builder->ClearInlineFragment(node);
@@ -268,9 +198,6 @@ void CollectInlinesInternal(LayoutBlockFlow* block,
builder->EnterInline(layout_inline);
- if (marker && marker->HandleInlineBox(layout_inline))
- marker = nullptr;
-
// Traverse to children if they exist.
if (LayoutObject* child = layout_inline->FirstChild()) {
node = child;
@@ -302,7 +229,65 @@ void CollectInlinesInternal(LayoutBlockFlow* block,
builder->ExitBlock();
}
-static bool NeedsShaping(const NGInlineItem& item) {
+// Returns whether this text should break shaping. Even within a box, text runs
+// that have different shaping properties need to break shaping.
+inline bool ShouldBreakShapingBeforeText(const NGInlineItem& item,
+ const NGInlineItem& start_item,
+ const ComputedStyle& start_style,
+ const Font& start_font,
+ TextDirection start_direction) {
+ DCHECK_EQ(item.Type(), NGInlineItem::kText);
+ DCHECK(item.Style());
+ const ComputedStyle& style = *item.Style();
+ if (&style != &start_style) {
+ const Font& font = style.GetFont();
+ if (&font != &start_font && font != start_font)
+ return true;
+ }
+
+ // The resolved direction and run segment properties must match to shape
+ // across for HarfBuzzShaper.
+ return item.Direction() != start_direction ||
+ !item.EqualsRunSegment(start_item);
+}
+
+// Returns whether the start of this box should break shaping.
+inline bool ShouldBreakShapingBeforeBox(const NGInlineItem& item,
+ const Font& start_font) {
+ DCHECK_EQ(item.Type(), NGInlineItem::kOpenTag);
+ DCHECK(item.Style());
+ const ComputedStyle& style = *item.Style();
+
+ // These properties values must break shaping.
+ // https://drafts.csswg.org/css-text-3/#boundary-shaping
+ if ((style.MayHavePadding() && !style.PaddingStart().IsZero()) ||
+ (style.MayHaveMargin() && !style.MarginStart().IsZero()) ||
+ style.BorderStartWidth() ||
+ style.VerticalAlign() != EVerticalAlign::kBaseline)
+ return true;
+
+ return false;
+}
+
+// Returns whether the end of this box should break shaping.
+inline bool ShouldBreakShapingAfterBox(const NGInlineItem& item,
+ const Font& start_font) {
+ DCHECK_EQ(item.Type(), NGInlineItem::kCloseTag);
+ DCHECK(item.Style());
+ const ComputedStyle& style = *item.Style();
+
+ // These properties values must break shaping.
+ // https://drafts.csswg.org/css-text-3/#boundary-shaping
+ if ((style.MayHavePadding() && !style.PaddingEnd().IsZero()) ||
+ (style.MayHaveMargin() && !style.MarginEnd().IsZero()) ||
+ style.BorderEndWidth() ||
+ style.VerticalAlign() != EVerticalAlign::kBaseline)
+ return true;
+
+ return false;
+}
+
+inline bool NeedsShaping(const NGInlineItem& item) {
return item.Type() == NGInlineItem::kText && !item.TextShapeResult();
}
@@ -363,17 +348,31 @@ void NGInlineNode::PrepareLayoutIfNeeded() {
block_flow->ResetNGInlineNodeData();
}
+ if (RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) {
+ if (const NGPaintFragment* fragment = block_flow->PaintFragment()) {
+ NGDirtyLines dirty_lines(fragment);
+ PrepareLayout(std::move(previous_data), &dirty_lines);
+ return;
+ }
+ }
+ PrepareLayout(std::move(previous_data), /* dirty_lines */ nullptr);
+}
+
+void NGInlineNode::PrepareLayout(
+ std::unique_ptr<NGInlineNodeData> previous_data,
+ NGDirtyLines* dirty_lines) {
// 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();
DCHECK(data);
- CollectInlines(data, previous_data.get());
+ CollectInlines(data, previous_data.get(), dirty_lines);
SegmentText(data);
ShapeText(data, previous_data.get());
ShapeTextForFirstLineIfNeeded(data);
AssociateItemsWithInlines(data);
DCHECK_EQ(data, MutableData());
+ LayoutBlockFlow* block_flow = GetLayoutBlockFlow();
block_flow->ClearNeedsCollectInlines();
#if DCHECK_IS_ON()
@@ -418,7 +417,7 @@ void NGInlineNode::ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow,
NGInlineItemsBuilderForOffsetMapping builder(&items);
builder.GetOffsetMappingBuilder().ReserveCapacity(
EstimateOffsetMappingItemsCount(*layout_block_flow));
- CollectInlinesInternal(layout_block_flow, &builder, nullptr, nullptr);
+ CollectInlinesInternal(layout_block_flow, &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
@@ -443,6 +442,14 @@ const NGOffsetMapping* NGInlineNode::GetOffsetMapping(
LayoutBlockFlow* layout_block_flow) {
DCHECK(!layout_block_flow->GetDocument().NeedsLayoutTreeUpdate());
+ if (UNLIKELY(layout_block_flow->NeedsLayout())) {
+ // TODO(kojii): This shouldn't happen, but is not easy to fix all cases.
+ // Return nullptr so that callers can chose to fail gracefully, or
+ // null-deref. crbug.com/946004
+ NOTREACHED();
+ return nullptr;
+ }
+
// If |layout_block_flow| is LayoutNG, compute from |NGInlineNode|.
if (layout_block_flow->IsLayoutNGMixin()) {
NGInlineNode node(layout_block_flow);
@@ -466,23 +473,16 @@ 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,
+ NGDirtyLines* dirty_lines) {
DCHECK(data->text_content.IsNull());
DCHECK(data->items.IsEmpty());
LayoutBlockFlow* block = GetLayoutBlockFlow();
block->WillCollectInlines();
- // If we have PaintFragment, mark line boxes dirty from |NeedsLayout| flag.
- base::Optional<NGLineBoxMarker> marker;
- if (NGPaintFragment* block_fragment = block->PaintFragment())
- marker.emplace(block_fragment);
-
- String* previous_text =
- previous_data ? &previous_data->text_content : nullptr;
data->items.ReserveCapacity(EstimateInlineItemsCount(*block));
- NGInlineItemsBuilder builder(&data->items);
- CollectInlinesInternal(block, &builder, previous_text,
- marker.has_value() ? &*marker : nullptr);
+ NGInlineItemsBuilder builder(&data->items, dirty_lines);
+ CollectInlinesInternal(block, &builder, previous_data);
data->text_content = builder.ToString();
// Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this
@@ -678,22 +678,27 @@ void NGInlineNode::ShapeText(NGInlineItemsData* data,
if (item.Type() == NGInlineItem::kText) {
if (!item.Length())
continue;
- // Shape adjacent items together if the font and direction matches to
- // allow ligatures and kerning to apply.
- // Also run segment properties must match because NGInlineItem gives
- // pre-segmented range to HarfBuzzShaper.
- // TODO(kojii): Figure out the exact conditions under which this
- // behavior is desirable.
- if (font != item.Style()->GetFont() || direction != item.Direction() ||
- !item.EqualsRunSegment(start_item))
+ if (ShouldBreakShapingBeforeText(item, start_item, start_style, font,
+ direction)) {
+ break;
+ }
+ // Break shaping at ZWNJ so that it prevents kerning. ZWNJ is always at
+ // the beginning of an item for this purpose; see NGInlineItemsBuilder.
+ if (text_content[item.StartOffset()] == kZeroWidthNonJoinerCharacter)
break;
end_offset = item.EndOffset();
num_text_items++;
- } else if (item.Type() == NGInlineItem::kOpenTag ||
- item.Type() == NGInlineItem::kCloseTag) {
- // These items are opaque to shaping.
- // Opaque items cannot have text, such as Object Replacement Characters,
- // since such characters can affect shaping.
+ } else if (item.Type() == NGInlineItem::kOpenTag) {
+ if (ShouldBreakShapingBeforeBox(item, font)) {
+ break;
+ }
+ // Should not have any characters to be opaque to shaping.
+ DCHECK_EQ(0u, item.Length());
+ } else if (item.Type() == NGInlineItem::kCloseTag) {
+ if (ShouldBreakShapingAfterBox(item, font)) {
+ break;
+ }
+ // Should not have any characters to be opaque to shaping.
DCHECK_EQ(0u, item.Length());
} else {
break;
@@ -841,22 +846,6 @@ void NGInlineNode::ShapeTextForFirstLineIfNeeded(NGInlineNodeData* data) {
item.SetStyleVariant(NGStyleVariant::kFirstLine);
}
- // Check if we have a first-line anonymous inline box. It is the first
- // open-tag if we have.
- for (auto& item : first_line_items->items) {
- if (item.Type() == NGInlineItem::kOpenTag) {
- if (item.layout_object_->IsAnonymous() &&
- item.layout_object_->IsLayoutInline() &&
- item.layout_object_->Parent() == GetLayoutBox() &&
- ToLayoutInline(item.layout_object_)->IsFirstLineAnonymous()) {
- item.SetShouldCreateBoxFragment();
- }
- break;
- }
- if (item.Type() != NGInlineItem::kBidiControl)
- break;
- }
-
// Re-shape if the font is different.
if (needs_reshape || FirstLineNeedsReshape(*first_line_style, *block_style))
ShapeText(first_line_items.get());
@@ -876,12 +865,18 @@ void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) {
// Items split from a LayoutObject should be consecutive.
DCHECK(associated_objects.insert(object).is_new_entry);
#endif
+ layout_text->ClearHasBidiControlInlineItems();
+ bool has_bidi_control = false;
NGInlineItem* begin = item;
for (++item; item != items.end(); ++item) {
if (item->GetLayoutObject() != object)
break;
+ if (item->Type() == NGInlineItem::kBidiControl)
+ has_bidi_control = true;
}
layout_text->SetInlineItems(begin, item);
+ if (has_bidi_control)
+ layout_text->SetHasBidiControlInlineItems();
continue;
}
++item;
@@ -891,7 +886,7 @@ void NGInlineNode::AssociateItemsWithInlines(NGInlineNodeData* data) {
void NGInlineNode::ClearAssociatedFragments(
const NGPhysicalFragment& fragment,
const NGBlockBreakToken* block_break_token) {
- auto* block_flow = To<LayoutBlockFlow>(fragment.GetLayoutObject());
+ auto* block_flow = To<LayoutBlockFlow>(fragment.GetMutableLayoutObject());
if (!block_flow->ChildrenInline())
return;
NGInlineNode node = NGInlineNode(block_flow);
@@ -949,11 +944,28 @@ scoped_refptr<const NGLayoutResult> NGInlineNode::Layout(
const auto* inline_break_token = To<NGInlineBreakToken>(break_token);
NGInlineLayoutAlgorithm algorithm(*this, constraint_space, inline_break_token,
context);
- return algorithm.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();
+ }
+#endif // defined(OS_ANDROID)
+
+ return layout_result;
}
const NGPaintFragment* NGInlineNode::ReusableLineBoxContainer(
const NGConstraintSpace& constraint_space) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGLineCacheEnabled());
// |SelfNeedsLayout()| is the most common reason that we check it earlier.
LayoutBlockFlow* block_flow = GetLayoutBlockFlow();
DCHECK(!block_flow->SelfNeedsLayout());
@@ -987,7 +999,8 @@ const NGPaintFragment* NGInlineNode::ReusableLineBoxContainer(
return nullptr;
// Propagating OOF needs re-layout.
- if (!cached_layout_result->OutOfFlowPositionedDescendants().IsEmpty())
+ if (cached_layout_result->PhysicalFragment()
+ .HasOutOfFlowPositionedDescendants())
return nullptr;
// Cached fragments are not for intermediate layout.
@@ -1002,11 +1015,9 @@ const NGPaintFragment* NGInlineNode::ReusableLineBoxContainer(
if (!paint_fragment)
return nullptr;
- if (!MarkLineBoxesDirty(block_flow))
+ if (!MarkLineBoxesDirty(block_flow, paint_fragment))
return nullptr;
- PrepareLayoutIfNeeded();
-
if (Data().changes_may_affect_earlier_lines_)
return nullptr;
@@ -1018,10 +1029,19 @@ const NGPaintFragment* NGInlineNode::ReusableLineBoxContainer(
// Removals of LayoutObject already marks relevant line boxes dirty by calling
// |DirtyLinesFromChangedChild()|, but insertions and style changes are not
// marked yet.
-bool NGInlineNode::MarkLineBoxesDirty(LayoutBlockFlow* block_flow) {
- NGLineBoxMarker marker(block_flow->PaintFragment());
- ItemsBuilderForMarkLineBoxesDirty builder;
- CollectInlinesInternal(block_flow, &builder, nullptr, &marker);
+bool NGInlineNode::MarkLineBoxesDirty(LayoutBlockFlow* block_flow,
+ const NGPaintFragment* paint_fragment) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGLineCacheEnabled());
+ NGDirtyLines dirty_lines(paint_fragment);
+ if (block_flow->NeedsCollectInlines()) {
+ std::unique_ptr<NGInlineNodeData> previous_data;
+ previous_data.reset(block_flow->TakeNGInlineNodeData());
+ block_flow->ResetNGInlineNodeData();
+ PrepareLayout(std::move(previous_data), &dirty_lines);
+ return true;
+ }
+ ItemsBuilderForMarkLineBoxesDirty builder(&dirty_lines);
+ CollectInlinesInternal(block_flow, &builder, nullptr);
return !builder.ShouldAbort();
}
@@ -1042,7 +1062,7 @@ static LayoutUnit ComputeContentSize(
/* out_writing_mode */ writing_mode,
/* is_new_fc */ false)
.SetTextDirection(style.Direction())
- .SetAvailableSize({available_inline_size, NGSizeIndefinite})
+ .SetAvailableSize({available_inline_size, kIndefiniteSize})
.SetPercentageResolutionSize({LayoutUnit(), LayoutUnit()})
.SetReplacedPercentageResolutionSize({LayoutUnit(), LayoutUnit()})
.SetIsIntermediateLayout(true)
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 94052b21d64..a3bcb07a50b 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
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -16,10 +17,11 @@ namespace blink {
class NGBlockBreakToken;
class NGConstraintSpace;
+class NGDirtyLines;
class NGInlineChildLayoutContext;
+class NGInlineNodeLegacy;
class NGLayoutResult;
class NGOffsetMapping;
-class NGInlineNodeLegacy;
struct MinMaxSize;
struct NGInlineItemsData;
@@ -59,8 +61,11 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
// Instruct to re-compute |PrepareLayout| on the next layout.
void InvalidatePrepareLayoutForTest() {
- GetLayoutBlockFlow()->ResetNGInlineNodeData();
+ LayoutBlockFlow* block_flow = GetLayoutBlockFlow();
+ block_flow->ResetNGInlineNodeData();
DCHECK(!IsPrepareLayoutFinished());
+ // There shouldn't be paint fragment if NGInlineNodeData does not exist.
+ block_flow->SetPaintFragment(nullptr, nullptr);
}
const NGInlineItemsData& ItemsData(bool is_first_line) const {
@@ -100,21 +105,18 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
String ToString() const;
- // A helper function for NGInlineItemsBuilder.
- static void ClearInlineFragment(LayoutObject* object) {
- object->SetIsInLayoutNGInlineFormattingContext(true);
- object->SetFirstInlineFragment(nullptr);
- }
-
protected:
bool IsPrepareLayoutFinished() const;
// 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,
+ NGDirtyLines* dirty_lines);
void CollectInlines(NGInlineNodeData*,
- NGInlineNodeData* previous_data = nullptr);
+ NGInlineNodeData* previous_data = nullptr,
+ NGDirtyLines* dirty_lines = nullptr);
void SegmentText(NGInlineNodeData*);
void SegmentScriptRuns(NGInlineNodeData*);
void SegmentFontOrientation(NGInlineNodeData*);
@@ -124,7 +126,7 @@ class CORE_EXPORT NGInlineNode : public NGLayoutInputNode {
void ShapeTextForFirstLineIfNeeded(NGInlineNodeData*);
void AssociateItemsWithInlines(NGInlineNodeData*);
- bool MarkLineBoxesDirty(LayoutBlockFlow*);
+ bool MarkLineBoxesDirty(LayoutBlockFlow*, const NGPaintFragment*);
NGInlineNodeData* MutableData() {
return To<LayoutBlockFlow>(box_)->GetNGInlineNodeData();
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 64c19d1af7b..833d63944c7 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
@@ -19,6 +19,8 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
return static_cast<TextDirection>(base_direction_);
}
+ bool IsEmptyInline() const { return is_empty_inline_; }
+
private:
const NGInlineItemsData& ItemsData(bool is_first_line) const {
return !is_first_line || !first_line_items_
@@ -29,6 +31,7 @@ struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
base_direction_ = static_cast<unsigned>(direction);
}
+ friend class NGInlineItemsBuilderTest;
friend class NGInlineNode;
friend class NGInlineNodeLegacy;
friend class NGInlineNodeForTest;
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 2851a3b14ec..4ee6e041cba 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
@@ -21,6 +21,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/svg_names.h"
namespace blink {
@@ -77,7 +78,9 @@ class NGInlineNodeForTest : public NGInlineNode {
void ShapeText() { NGInlineNode::ShapeText(MutableData()); }
bool MarkLineBoxesDirty() {
- return NGInlineNode::MarkLineBoxesDirty(GetLayoutBlockFlow());
+ LayoutBlockFlow* block_flow = GetLayoutBlockFlow();
+ return NGInlineNode::MarkLineBoxesDirty(block_flow,
+ block_flow->PaintFragment());
}
};
@@ -132,9 +135,9 @@ class NGInlineNodeTest : public NGLayoutTest {
nullptr /* break_token */, &context)
.Layout();
- const auto* line =
+ const auto& line =
To<NGPhysicalLineBoxFragment>(result->PhysicalFragment());
- for (const auto& child : line->Children()) {
+ for (const auto& child : line.Children()) {
fragments_out->push_back(To<NGPhysicalTextFragment>(child.get()));
}
}
@@ -546,7 +549,7 @@ TEST_F(NGInlineNodeTest, NeedsCollectInlinesOnSetText) {
Element* container = GetElementById("container");
Element* parent = GetElementById("parent");
- Text* text = ToText(parent->firstChild());
+ auto* text = To<Text>(parent->firstChild());
EXPECT_FALSE(text->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(parent->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(container->GetLayoutObject()->NeedsCollectInlines());
@@ -579,28 +582,35 @@ struct StyleChangeData {
kAll = kText | kParentAndAbove,
};
unsigned needs_collect_inlines;
+ base::Optional<bool> is_line_dirty;
} style_change_data[] = {
// Changing color, text-decoration, etc. should not re-run
// |CollectInlines()|.
- {"#parent.after { color: red; }", StyleChangeData::kNone},
+ {"#parent.after { color: red; }", StyleChangeData::kNone, false},
{"#parent.after { text-decoration-line: underline; }",
- StyleChangeData::kNone},
+ StyleChangeData::kNone, false},
// Changing fonts should re-run |CollectInlines()|.
- {"#parent.after { font-size: 200%; }", StyleChangeData::kAll},
+ {"#parent.after { font-size: 200%; }", StyleChangeData::kAll, true},
+ // Changing from/to out-of-flow should re-rerun |CollectInlines()|.
+ {"#parent.after { position: absolute; }", StyleChangeData::kContainer,
+ true},
+ {"#parent { position: absolute; }"
+ "#parent.after { position: initial; }",
+ StyleChangeData::kContainer, true},
// List markers are captured in |NGInlineItem|.
{"#parent.after { display: list-item; }", StyleChangeData::kContainer},
{"#parent { display: list-item; list-style-type: none; }"
"#parent.after { list-style-type: disc; }",
- StyleChangeData::kTextAndParent},
+ StyleChangeData::kParent},
{"#parent { display: list-item; }"
"#container.after { list-style-type: none; }",
- StyleChangeData::kTextAndParent},
+ StyleChangeData::kParent},
// Changing properties related with bidi resolution should re-run
// |CollectInlines()|.
{"#parent.after { unicode-bidi: bidi-override; }",
- StyleChangeData::kParentAndAbove},
+ StyleChangeData::kParentAndAbove, true},
{"#container.after { unicode-bidi: bidi-override; }",
- StyleChangeData::kContainer},
+ StyleChangeData::kContainer, false},
};
std::ostream& operator<<(std::ostream& os, const StyleChangeData& data) {
@@ -630,7 +640,7 @@ TEST_P(StyleChangeTest, NeedsCollectInlinesOnStyle) {
Element* container = GetElementById("container");
Element* parent = GetElementById("parent");
- Text* text = ToText(parent->firstChild());
+ auto* text = To<Text>(parent->firstChild());
EXPECT_FALSE(text->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(parent->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(container->GetLayoutObject()->NeedsCollectInlines());
@@ -654,6 +664,15 @@ TEST_P(StyleChangeTest, NeedsCollectInlinesOnStyle) {
Element* next = GetElementById("next");
EXPECT_FALSE(previous->GetLayoutObject()->NeedsCollectInlines());
EXPECT_FALSE(next->GetLayoutObject()->NeedsCollectInlines());
+
+ if (data.is_line_dirty &&
+ RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) {
+ layout_block_flow_ = ToLayoutNGBlockFlow(container->GetLayoutObject());
+ auto lines = MarkLineBoxesDirty();
+ EXPECT_EQ(*data.is_line_dirty, lines[0]->IsDirty());
+ }
+
+ ForceLayout(); // Ensure running layout does not crash.
}
using CreateNode = Node* (*)(Document&);
@@ -964,47 +983,88 @@ TEST_F(NGInlineNodeTest, SpaceRestoredByInsertingWord) {
}
// Test marking line boxes when inserting a span before the first child.
-TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnInsert) {
+TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnInsert) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
+ <style>
+ .abspos { position: absolute; }
+ .float { float: left; }
+ </style>
<div id=container style="font-size: 10px; width: 10ch">
12345678
</div>
)HTML");
- Element* span = GetDocument().CreateElementForBinding("span");
+ Node* insert = (*GetParam())(GetDocument());
Element* container = GetElementById("container");
- container->insertBefore(span, container->firstChild());
+ container->insertBefore(insert, container->firstChild());
auto lines = MarkLineBoxesDirty();
EXPECT_TRUE(lines[0]->IsDirty());
}
// Test marking line boxes when appending a span.
-TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnAppend) {
+TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppend) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
+ <style>
+ .abspos { position: absolute; }
+ .float { float: left; }
+ </style>
<div id=container style="font-size: 10px; width: 10ch">
12345678
</div>
)HTML");
- Element* span = GetDocument().CreateElementForBinding("span");
- layout_block_flow_->GetNode()->appendChild(span);
+ Node* insert = (*GetParam())(GetDocument());
+ layout_block_flow_->GetNode()->appendChild(insert);
auto lines = MarkLineBoxesDirty();
EXPECT_TRUE(lines[0]->IsDirty());
}
// Test marking line boxes when appending a span on 2nd line.
-TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnAppend2) {
+TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppend2) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
+ <style>
+ .abspos { position: absolute; }
+ .float { float: left; }
+ </style>
<div id=container style="font-size: 10px; width: 10ch">
12345678
2234
</div>
)HTML");
- Element* span = GetDocument().CreateElementForBinding("span");
- layout_block_flow_->GetNode()->appendChild(span);
+ Node* insert = (*GetParam())(GetDocument());
+ layout_block_flow_->GetNode()->appendChild(insert);
+
+ auto lines = MarkLineBoxesDirty();
+ EXPECT_FALSE(lines[0]->IsDirty());
+ EXPECT_TRUE(lines[1]->IsDirty());
+}
+
+// Test marking line boxes when appending a span on 2nd line.
+TEST_P(NodeInsertTest, MarkLineBoxesDirtyOnAppendAfterBR) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
+ SetupHtml("container", R"HTML(
+ <style>
+ .abspos { position: absolute; }
+ .float { float: left; }
+ </style>
+ <div id=container style="font-size: 10px; width: 10ch">
+ <br>
+ <br>
+ </div>
+ )HTML");
+
+ Node* insert = (*GetParam())(GetDocument());
+ layout_block_flow_->GetNode()->appendChild(insert);
auto lines = MarkLineBoxesDirty();
EXPECT_FALSE(lines[0]->IsDirty());
@@ -1013,6 +1073,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnAppend2) {
// Test marking line boxes when removing a span.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnRemove) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
1234<span id=t>5678</span>
@@ -1028,6 +1090,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnRemove) {
// Test marking line boxes when removing a span.
TEST_P(NodeParameterTest, MarkLineBoxesDirtyOnRemoveFirst) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", String(R"HTML(
<div id=container style="font-size: 10px; width: 10ch">)HTML") +
GetParam() + R"HTML(<span>after</span>
@@ -1045,6 +1109,8 @@ TEST_P(NodeParameterTest, MarkLineBoxesDirtyOnRemoveFirst) {
// Test marking line boxes when removing a span on 2nd line.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnRemove2) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
12345678
@@ -1062,6 +1128,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnRemove2) {
// Test marking line boxes when removing a text node on 2nd line.
TEST_P(NodeParameterTest, MarkLineBoxesDirtyOnRemoveAfterBR) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", String(R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
line 1
@@ -1081,9 +1149,45 @@ TEST_P(NodeParameterTest, MarkLineBoxesDirtyOnRemoveAfterBR) {
ForceLayout(); // Ensure running layout does not crash.
}
+TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnEndSpaceCollapsed) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
+ SetupHtml("container", R"HTML(
+ <style>
+ div {
+ font-size: 10px;
+ width: 8ch;
+ }
+ #empty {
+ background: yellow; /* ensure fragment is created */
+ }
+ #target {
+ display: inline-block;
+ }
+ </style>
+ <div id=container>
+ 1234567890
+ 1234567890
+ <span id=empty> </span>
+ <span id=target></span></div>
+ )HTML");
+
+ // Removing #target makes the spaces before it to be collapsed.
+ Element* target = GetElementById("target");
+ target->remove();
+
+ auto lines = MarkLineBoxesDirty();
+ EXPECT_FALSE(lines[0]->IsDirty());
+ EXPECT_TRUE(lines[1]->IsDirty());
+
+ ForceLayout(); // Ensure running layout does not crash.
+}
+
// Test marking line boxes when the first span has NeedsLayout. The span is
// culled.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutFirst) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
<span id=t>1234</span>5678
@@ -1100,6 +1204,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutFirst) {
// Test marking line boxes when the first span has NeedsLayout. The span has a
// box fragment.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutFirstWithBox) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
<span id=t style="background: blue">1234</span>5678
@@ -1115,6 +1221,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutFirstWithBox) {
// Test marking line boxes when a span has NeedsLayout. The span is culled.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayout) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
12345678
@@ -1133,6 +1241,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayout) {
// Test marking line boxes when a span has NeedsLayout. The span has a box
// fragment.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutWithBox) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px; width: 10ch">
12345678
@@ -1152,6 +1262,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnNeedsLayoutWithBox) {
// The parent span has a box fragment, and wraps, so that its fragment
// is seen earlier in pre-order DFS.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnChildOfWrappedBox) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="font-size: 10px">
<span style="background: yellow">
@@ -1172,6 +1284,8 @@ TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyOnChildOfWrappedBox) {
// Test marking line boxes when a span has NeedsLayout. The span has a box
// fragment.
TEST_F(NGInlineNodeTest, MarkLineBoxesDirtyInInlineBlock) {
+ if (!RuntimeEnabledFeatures::LayoutNGLineCacheEnabled())
+ return;
SetupHtml("container", R"HTML(
<div id=container style="display: inline-block; font-size: 10px">
12345678<br>
@@ -1226,6 +1340,71 @@ TEST_F(NGInlineNodeTest, RemoveInlineNodeDataIfBlockObtainsBlockChild) {
EXPECT_FALSE(layout_block_flow_->HasNGInlineNodeData());
}
+// Test inline objects are initialized when |SplitFlow()| moves them.
+TEST_F(NGInlineNodeTest, ClearFirstInlineFragmentOnSplitFlow) {
+ SetBodyInnerHTML(R"HTML(
+ <div>
+ <span id=outer_span>
+ <span id=inner_span>1234</span>
+ </span>
+ </div>
+ )HTML");
+
+ // 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);
+
+ // Append <div> to <span>. causing SplitFlow().
+ Element* outer_span = GetElementById("outer_span");
+ Element* div = GetDocument().CreateRawElement(html_names::kDivTag);
+ outer_span->appendChild(div);
+
+ // Update tree but do NOT update layout. At this point, there's no guarantee,
+ // but there are some clients (e.g., Schroll Anchor) who try to read
+ // associated fragments.
+ //
+ // NGPaintFragment is owned by LayoutNGBlockFlow. Because the original owner
+ // no longer has an inline formatting context, the NGPaintFragment subtree is
+ // destroyed, and should not be accessible.
+ GetDocument().UpdateStyleAndLayoutTree();
+ scoped_refptr<NGPaintFragment> text_fragment_before_layout =
+ text->GetLayoutObject()->FirstInlineFragment();
+ EXPECT_EQ(text_fragment_before_layout, nullptr);
+
+ // 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);
+
+ // 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);
+}
+
+TEST_F(NGInlineNodeTest, AddChildToSVGRoot) {
+ SetBodyInnerHTML(R"HTML(
+ <div id="container">
+ text
+ <svg id="svg"></svg>
+ </div>
+ )HTML");
+
+ Element* svg = GetElementById("svg");
+ svg->appendChild(GetDocument().CreateRawElement(svg_names::kTextTag));
+ GetDocument().UpdateStyleAndLayoutTree();
+
+ LayoutObject* container = GetLayoutObjectByElementId("container");
+ EXPECT_FALSE(container->NeedsCollectInlines());
+}
+
// https://crbug.com/911220
TEST_F(NGInlineNodeTest, PreservedNewlineWithBidiAndRelayout) {
SetupHtml("container",
@@ -1241,6 +1420,44 @@ TEST_F(NGInlineNodeTest, PreservedNewlineWithBidiAndRelayout) {
EXPECT_EQ(String(u"foo\u2066\u2069\n\u2066\u2069bar\nbaz"), GetText());
}
+TEST_F(NGInlineNodeTest, PreservedNewlineWithRemovedBidiAndRelayout) {
+ SetupHtml("container",
+ "<pre id=container>foo<span dir=rtl>\nbar</span></pre>");
+ EXPECT_EQ(String(u"foo\u2067\u2069\n\u2067bar\u2069"), GetText());
+
+ GetDocument().QuerySelector("span")->removeAttribute(html_names::kDirAttr);
+ UpdateAllLifecyclePhasesForTest();
+
+ // The bidi control characters around '\n' should not preserve
+ EXPECT_EQ("foo\nbar", GetText());
+}
+
+TEST_F(NGInlineNodeTest, PreservedNewlineWithRemovedLtrDirAndRelayout) {
+ SetupHtml("container",
+ "<pre id=container>foo<span dir=ltr>\nbar</span></pre>");
+ EXPECT_EQ(String(u"foo\u2066\u2069\n\u2066bar\u2069"), GetText());
+
+ GetDocument().QuerySelector("span")->removeAttribute(html_names::kDirAttr);
+ UpdateAllLifecyclePhasesForTest();
+
+ // The bidi control characters around '\n' should not preserve
+ EXPECT_EQ("foo\nbar", GetText());
+}
+
+// https://crbug.com/969089
+TEST_F(NGInlineNodeTest, InsertedWBRWithLineBreakInRelayout) {
+ SetupHtml("container", "<div id=container><span>foo</span>\nbar</div>");
+ EXPECT_EQ("foo bar", GetText());
+
+ Element* div = GetElementById("container");
+ Element* wbr = GetDocument().CreateElementForBinding("wbr");
+ div->insertBefore(wbr, div->lastChild());
+ UpdateAllLifecyclePhasesForTest();
+
+ // The '\n' should be collapsed by the inserted <wbr>
+ EXPECT_EQ(String(u"foo\u200Bbar"), GetText());
+}
+
#if SEGMENT_BREAK_TRANSFORMATION_FOR_EAST_ASIAN_WIDTH
// https://crbug.com/879088
TEST_F(NGInlineNodeTest, RemoveSegmentBreakFromJapaneseInRelayout) {
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 8d66c3cc915..13d2b5d6e37 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
@@ -16,7 +16,6 @@ namespace blink {
void NGLineBoxFragmentBuilder::Reset() {
children_.Shrink(0);
- offsets_.Shrink(0);
child_break_tokens_.Shrink(0);
inline_break_tokens_.Shrink(0);
oof_positioned_candidates_.Shrink(0);
@@ -28,7 +27,7 @@ void NGLineBoxFragmentBuilder::Reset() {
has_last_resort_break_ = false;
has_floating_descendants_ = false;
has_orthogonal_flow_roots_ = false;
- has_child_that_depends_on_percentage_block_size_ = false;
+ has_descendant_that_depends_on_percentage_block_size_ = false;
has_block_fragmentation_ = false;
may_have_descendant_above_block_start_ = false;
}
@@ -57,6 +56,12 @@ NGLineBoxFragmentBuilder::ChildList::LastInFlowChild() {
}
void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection(
+ LayoutUnit delta) {
+ for (auto& child : children_)
+ child.offset.inline_offset += delta;
+}
+
+void NGLineBoxFragmentBuilder::ChildList::MoveInInlineDirection(
LayoutUnit delta,
unsigned start,
unsigned end) {
@@ -78,13 +83,12 @@ void NGLineBoxFragmentBuilder::ChildList::MoveInBlockDirection(LayoutUnit delta,
}
void NGLineBoxFragmentBuilder::AddChildren(ChildList& children) {
- offsets_.ReserveCapacity(children.size());
children_.ReserveCapacity(children.size());
for (auto& child : children) {
if (child.layout_result) {
DCHECK(!child.fragment);
- AddChild(*child.layout_result, child.offset);
+ AddChild(child.layout_result->PhysicalFragment(), child.offset);
child.layout_result.reset();
} else if (child.fragment) {
AddChild(std::move(child.fragment), child.offset);
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 573308b00b2..71c7396ed98 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -5,13 +5,15 @@
#ifndef NGLineBoxFragmentBuilder_h
#define NGLineBoxFragmentBuilder_h
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_offset.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_height_metrics.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_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_physical_container_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -19,11 +21,10 @@ namespace blink {
class ComputedStyle;
class NGInlineBreakToken;
-class NGPhysicalFragment;
class CORE_EXPORT NGLineBoxFragmentBuilder final
: public NGContainerFragmentBuilder {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
NGLineBoxFragmentBuilder(NGInlineNode node,
@@ -45,6 +46,14 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
return metrics_.LineHeight().ClampNegativeToZero();
}
+ void SetInlineSize(LayoutUnit inline_size) {
+ size_.inline_size = inline_size;
+ }
+
+ void SetHangInlineSize(LayoutUnit hang_inline_size) {
+ hang_inline_size_ = hang_inline_size;
+ }
+
// Mark this line box is an "empty" line box. See NGLineBoxType.
void SetIsEmptyLineBox();
@@ -67,12 +76,12 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
DISALLOW_NEW();
scoped_refptr<const NGLayoutResult> layout_result;
- scoped_refptr<const NGPhysicalFragment> fragment;
+ scoped_refptr<const NGPhysicalTextFragment> fragment;
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.
- NGLogicalOffset offset;
+ LogicalOffset offset;
// The offset of a positioned float wrt. the root BFC. This should only be
// set for positioned floats.
NGBfcOffset bfc_offset;
@@ -90,29 +99,29 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
Child() = default;
// Create a placeholder. A placeholder does not have a fragment nor a bidi
// level.
- Child(NGLogicalOffset offset) : offset(offset) {}
+ Child(LogicalOffset offset) : offset(offset) {}
// 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,
- NGLogicalOffset offset,
+ LogicalOffset offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
: layout_result(std::move(layout_result)),
offset(offset),
inline_size(inline_size),
bidi_level(bidi_level) {}
- // Create an in-flow |NGPhysicalFragment|.
- Child(scoped_refptr<const NGPhysicalFragment> fragment,
- NGLogicalOffset offset,
+ // Create an in-flow |NGPhysicalTextFragment|.
+ Child(scoped_refptr<const NGPhysicalTextFragment> fragment,
+ LogicalOffset offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
: fragment(std::move(fragment)),
offset(offset),
inline_size(inline_size),
bidi_level(bidi_level) {}
- Child(scoped_refptr<const NGPhysicalFragment> fragment,
+ Child(scoped_refptr<const NGPhysicalTextFragment> fragment,
LayoutUnit block_offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level)
@@ -142,7 +151,7 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
if (fragment)
return true;
- if (layout_result && !layout_result->PhysicalFragment()->IsFloating())
+ if (layout_result && !layout_result->PhysicalFragment().IsFloating())
return true;
return false;
@@ -154,7 +163,9 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
bool HasBidiLevel() const { return bidi_level != 0xff; }
bool IsPlaceholder() const { return !HasFragment() && !HasBidiLevel(); }
const NGPhysicalFragment* PhysicalFragment() const {
- return layout_result ? layout_result->PhysicalFragment() : fragment.get();
+ if (layout_result)
+ return &layout_result->PhysicalFragment();
+ return fragment.get();
}
};
@@ -162,7 +173,7 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
// Unlike the fragment builder, chlidren are mutable.
// Callers can add to the fragment builder in a batch once finalized.
class ChildList {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
ChildList() = default;
@@ -204,13 +215,14 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
}
void InsertChild(unsigned index,
scoped_refptr<const NGLayoutResult> layout_result,
- const NGLogicalOffset& offset,
+ const LogicalOffset& offset,
LayoutUnit inline_size,
UBiDiLevel bidi_level) {
children_.insert(index, Child{std::move(layout_result), offset,
inline_size, bidi_level});
}
+ void MoveInInlineDirection(LayoutUnit);
void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end);
void MoveInBlockDirection(LayoutUnit);
void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end);
@@ -227,6 +239,7 @@ class CORE_EXPORT NGLineBoxFragmentBuilder final
private:
NGLineHeightMetrics metrics_;
+ LayoutUnit hang_inline_size_;
NGPhysicalLineBoxFragment::NGLineBoxType line_box_type_;
TextDirection base_direction_;
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 d36e73f5419..8f60a66ffb1 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
@@ -157,6 +157,14 @@ inline void ClearNeedsLayout(const NGInlineItem& item) {
} // namespace
+LayoutUnit NGLineBreaker::ComputeAvailableWidth() const {
+ LayoutUnit available_width = line_opportunity_.AvailableInlineSize();
+ // Available width must be smaller than |LayoutUnit::Max()| so that the
+ // position can be larger.
+ available_width = std::min(available_width, LayoutUnit::NearlyMax());
+ return available_width;
+}
+
NGLineBreaker::NGLineBreaker(NGInlineNode node,
NGLineBreakerMode mode,
const NGConstraintSpace& space,
@@ -184,6 +192,7 @@ NGLineBreaker::NGLineBreaker(NGInlineNode node,
leading_floats_(leading_floats),
handled_leading_floats_index_(handled_leading_floats_index),
base_direction_(node_.BaseDirection()) {
+ available_width_ = ComputeAvailableWidth();
break_iterator_.SetBreakSpace(BreakSpaceType::kBeforeSpaceRun);
if (break_token) {
@@ -235,20 +244,23 @@ void NGLineBreaker::SetMaxSizeCache(MaxSizeCache* max_size_cache) {
max_size_cache_ = max_size_cache;
}
-void NGLineBreaker::SetLineEndFragment(
+LayoutUnit NGLineBreaker::SetLineEndFragment(
scoped_refptr<const NGPhysicalTextFragment> fragment,
NGLineInfo* line_info) {
+ LayoutUnit inline_size;
bool is_horizontal =
IsHorizontalWritingMode(constraint_space_.GetWritingMode());
if (line_info->LineEndFragment()) {
- const NGPhysicalSize& size = line_info->LineEndFragment()->Size();
- position_ -= is_horizontal ? size.width : size.height;
+ const PhysicalSize& size = line_info->LineEndFragment()->Size();
+ inline_size = is_horizontal ? -size.width : -size.height;
}
if (fragment) {
- const NGPhysicalSize& size = fragment->Size();
- position_ += is_horizontal ? size.width : size.height;
+ const PhysicalSize& size = fragment->Size();
+ inline_size = is_horizontal ? size.width : size.height;
}
line_info->SetLineEndFragment(std::move(fragment));
+ position_ += inline_size;
+ return inline_size;
}
// Compute the base direction for bidi algorithm for this line.
@@ -273,8 +285,8 @@ void NGLineBreaker::ComputeBaseDirection() {
void NGLineBreaker::PrepareNextLine(NGLineInfo* line_info) {
// NGLineInfo is not supposed to be re-used becase it's not much gain and to
// avoid rare code path.
- NGInlineItemResults* item_results = line_info->MutableResults();
- DCHECK(item_results->IsEmpty());
+ const NGInlineItemResults& item_results = line_info->Results();
+ DCHECK(item_results.IsEmpty());
if (item_index_) {
// We're past the first line
@@ -310,6 +322,8 @@ void NGLineBreaker::PrepareNextLine(NGLineInfo* line_info) {
// Use 'text-indent' as the initial position. This lets tab positions to align
// regardless of 'text-indent'.
position_ = line_info->TextIndent();
+
+ overflow_item_index_ = 0;
}
void NGLineBreaker::NextLine(
@@ -326,9 +340,9 @@ void NGLineBreaker::NextLine(
out_floats_for_min_max, line_info);
RemoveTrailingCollapsibleSpace(line_info);
- NGInlineItemResults* item_results = line_info->MutableResults();
+ const NGInlineItemResults& item_results = line_info->Results();
#if DCHECK_IS_ON()
- for (const auto& result : *item_results)
+ for (const auto& result : item_results)
result.CheckConsistency(mode_ == NGLineBreakerMode::kMinContent);
#endif
@@ -340,7 +354,7 @@ void NGLineBreaker::NextLine(
//
// TODO(kojii): There are cases where we need to PlaceItems() without creating
// line boxes. These cases need to be reviewed.
- bool should_create_line_box = ShouldCreateLineBox(*item_results) ||
+ bool should_create_line_box = ShouldCreateLineBox(item_results) ||
(has_list_marker_ && line_info->IsLastLine()) ||
mode_ != NGLineBreakerMode::kContent;
@@ -377,7 +391,7 @@ void NGLineBreaker::BreakLine(
return;
}
- NGInlineItemResults* item_results = line_info->MutableResults();
+ const NGInlineItemResults& item_results = line_info->Results();
// Handle trailable items first. These items may not be break before.
// They (or part of them) may also overhang the available width.
@@ -388,11 +402,16 @@ void NGLineBreaker::BreakLine(
else
HandleEmptyText(item, line_info);
#if DCHECK_IS_ON()
- if (!item_results->IsEmpty())
- item_results->back().CheckConsistency(true);
+ if (!item_results.IsEmpty())
+ item_results.back().CheckConsistency(true);
#endif
continue;
}
+ if (item.Type() == NGInlineItem::kOpenTag) {
+ if (HandleOpenTag(item, line_info))
+ continue;
+ return;
+ }
if (item.Type() == NGInlineItem::kCloseTag) {
HandleCloseTag(item, line_info);
continue;
@@ -413,12 +432,12 @@ void NGLineBreaker::BreakLine(
// Items after this point are not trailable. Break at the earliest break
// opportunity if we're trailing.
if (state_ == LineBreakState::kTrailing &&
- CanBreakAfterLast(*item_results)) {
+ CanBreakAfterLast(item_results)) {
// If the sticky images quirk is enabled, and this is an image that
// follows text that doesn't end with something breakable, we cannot break
// between the two items.
if (sticky_images_quirk_ &&
- IsStickyImage(item, *item_results, trailing_whitespace_, Text())) {
+ IsStickyImage(item, item_results, trailing_whitespace_, Text())) {
HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
line_info);
continue;
@@ -431,8 +450,6 @@ void NGLineBreaker::BreakLine(
if (item.Type() == NGInlineItem::kAtomicInline) {
HandleAtomicInline(item, percentage_resolution_block_size_for_min_max,
line_info);
- } else if (item.Type() == NGInlineItem::kOpenTag) {
- HandleOpenTag(item, line_info);
} else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) {
AddItem(item, line_info);
MoveToNextOf(item);
@@ -465,6 +482,8 @@ void NGLineBreaker::ComputeLineLocation(NGLineInfo* line_info) const {
line_info->SetWidth(available_width, position_);
line_info->SetBfcOffset(
{line_opportunity_.line_left_offset, line_opportunity_.bfc_block_offset});
+ if (mode_ == NGLineBreakerMode::kContent)
+ line_info->UpdateTextAlign();
}
void NGLineBreaker::HandleText(const NGInlineItem& item,
@@ -477,7 +496,7 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// If we're trailing, only trailing spaces can be included in this line.
if (state_ == LineBreakState::kTrailing) {
- if (CanBreakAfterLast(*line_info->MutableResults()))
+ if (CanBreakAfterLast(line_info->Results()))
return HandleTrailingSpaces(item, shape_result, line_info);
// When a run of preserved spaces are across items, |CanBreakAfterLast| is
// false for between spaces. But we still need to handle them as trailing
@@ -519,17 +538,18 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
// Try to break inside of this text item.
LayoutUnit available_width = AvailableWidthToFit();
- BreakText(item_result, item, shape_result, available_width - position_,
- line_info);
-
- LayoutUnit next_position = position_ + item_result->inline_size;
- bool is_overflow = next_position > available_width;
- DCHECK(is_overflow || item_result->shape_result);
- position_ = next_position;
- item_result->may_break_inside = !is_overflow;
+ BreakResult break_result =
+ BreakText(item_result, item, shape_result, available_width - position_,
+ line_info);
+ DCHECK(item_result->shape_result ||
+ (break_result == kOverflow && break_anywhere_if_overflow_ &&
+ !override_break_anywhere_));
+ position_ += item_result->inline_size;
+ DCHECK_EQ(break_result == kSuccess, position_ <= available_width);
+ item_result->may_break_inside = break_result == kSuccess;
MoveToNextOf(*item_result);
- if (!is_overflow ||
+ if (break_result == kSuccess ||
(state_ == LineBreakState::kTrailing && item_result->shape_result)) {
if (item_result->end_offset < item.EndOffset()) {
// The break point found, and text follows. Break here, after trailing
@@ -542,15 +562,27 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
return;
}
+ DCHECK_EQ(break_result, kOverflow);
return HandleOverflow(line_info);
}
- // Add the whole item if !auto_wrap. The previous line should not have wrapped
- // in the middle of nowrap item.
- DCHECK_EQ(item_result->start_offset, item.StartOffset());
+ // 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());
- item_result->inline_size = shape_result.SnappedWidth().ClampNegativeToZero();
- item_result->shape_result = ShapeResultView::Create(&shape_result);
+ if (item_result->start_offset == item.StartOffset()) {
+ item_result->inline_size =
+ shape_result.SnappedWidth().ClampNegativeToZero();
+ item_result->shape_result = ShapeResultView::Create(&shape_result);
+ } else {
+ // <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->shape_result = ShapeResultView::Create(
+ &shape_result, item_result->start_offset, item_result->end_offset);
+ item_result->inline_size =
+ item_result->shape_result->SnappedWidth().ClampNegativeToZero();
+ }
DCHECK(!item_result->may_break_inside);
DCHECK(!item_result->can_break_after);
@@ -559,21 +591,18 @@ void NGLineBreaker::HandleText(const NGInlineItem& item,
MoveToNextOf(item);
}
-void NGLineBreaker::BreakText(NGInlineItemResult* item_result,
- const NGInlineItem& item,
- const ShapeResult& item_shape_result,
- LayoutUnit available_width,
- NGLineInfo* line_info) {
+NGLineBreaker::BreakResult NGLineBreaker::BreakText(
+ NGInlineItemResult* item_result,
+ const NGInlineItem& item,
+ const ShapeResult& item_shape_result,
+ LayoutUnit available_width,
+ NGLineInfo* line_info) {
DCHECK(item.Type() == NGInlineItem::kText ||
(item.Type() == NGInlineItem::kControl &&
Text()[item.StartOffset()] == kTabulationCharacter));
DCHECK(&item_shape_result);
item.AssertOffset(item_result->start_offset);
- // TODO(kojii): We need to instantiate ShapingLineBreaker here because it
- // has item-specific info as context. Should they be part of ShapeLine() to
- // instantiate once, or is this just fine since instatiation is not
- // expensive?
DCHECK_EQ(item_shape_result.StartIndex(), item.StartOffset());
DCHECK_EQ(item_shape_result.EndIndex(), item.EndOffset());
struct ShapeCallbackContext {
@@ -593,7 +622,6 @@ void NGLineBreaker::BreakText(NGInlineItemResult* item_result,
shape_callback, &shape_callback_context);
if (!enable_soft_hyphen_)
breaker.DisableSoftHyphen();
- available_width = std::max(LayoutUnit(0), available_width);
// Use kStartShouldBeSafe if at the beginning of a line.
unsigned options = ShapingLineBreaker::kDefaultOptions;
@@ -612,36 +640,58 @@ void NGLineBreaker::BreakText(NGInlineItemResult* item_result,
if (break_anywhere_if_overflow_ && !override_break_anywhere_)
options |= ShapingLineBreaker::kNoResultIfOverflow;
- ShapingLineBreaker::Result result;
- scoped_refptr<const ShapeResultView> shape_result = breaker.ShapeLine(
- item_result->start_offset, available_width, options, &result);
-
- // If this item overflows and 'break-word' is set, this line will be
- // rewinded. Making this item long enough to overflow is enough.
- if (!shape_result) {
- DCHECK(options & ShapingLineBreaker::kNoResultIfOverflow);
- item_result->inline_size = available_width + 1;
- item_result->end_offset = item.EndOffset();
- return;
- }
- DCHECK_EQ(shape_result->NumCharacters(),
- result.break_offset - item_result->start_offset);
-
- if (result.is_hyphenated) {
- SetLineEndFragment(
- CreateHyphenFragment(node_, constraint_space_.GetWritingMode(), item),
- line_info);
-
- // TODO(kojii): Implement when adding a hyphen caused overflow.
- // crbug.com/714962: Should be removed when switched to NGPaint.
- item_result->text_end_effect = NGTextEndEffect::kHyphen;
+#if DCHECK_IS_ON()
+ unsigned try_count = 0;
+#endif
+ LayoutUnit inline_size;
+ while (true) {
+#if DCHECK_IS_ON()
+ ++try_count;
+ DCHECK_LE(try_count, 2u);
+#endif
+ ShapingLineBreaker::Result result;
+ scoped_refptr<const ShapeResultView> shape_result = breaker.ShapeLine(
+ item_result->start_offset, available_width.ClampNegativeToZero(),
+ options, &result);
+
+ // If this item overflows and 'break-word' is set, this line will be
+ // rewinded. Making this item long enough to overflow is enough.
+ if (!shape_result) {
+ DCHECK(options & ShapingLineBreaker::kNoResultIfOverflow);
+ item_result->inline_size = available_width + 1;
+ item_result->end_offset = item.EndOffset();
+ return kOverflow;
+ }
+ DCHECK_EQ(shape_result->NumCharacters(),
+ result.break_offset - item_result->start_offset);
+ // 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);
+
+ inline_size = shape_result->SnappedWidth().ClampNegativeToZero();
+ item_result->inline_size = inline_size;
+ if (UNLIKELY(result.is_hyphenated)) {
+ const WritingMode writing_mode = constraint_space_.GetWritingMode();
+ scoped_refptr<const NGPhysicalTextFragment> hyphen_fragment =
+ CreateHyphenFragment(node_, writing_mode, item);
+ LayoutUnit space_for_hyphen = available_width - inline_size;
+ LayoutUnit hyphen_inline_size = IsHorizontalWritingMode(writing_mode)
+ ? hyphen_fragment->Size().width
+ : hyphen_fragment->Size().height;
+ // If the hyphen overflows, retry with the reduced available width.
+ if (space_for_hyphen >= 0 && hyphen_inline_size > space_for_hyphen) {
+ available_width -= hyphen_inline_size;
+ continue;
+ }
+ inline_size += SetLineEndFragment(std::move(hyphen_fragment), line_info);
+ item_result->text_end_effect = NGTextEndEffect::kHyphen;
+ }
+ item_result->inline_size =
+ shape_result->SnappedWidth().ClampNegativeToZero();
+ item_result->end_offset = result.break_offset;
+ item_result->shape_result = std::move(shape_result);
+ break;
}
- item_result->inline_size = shape_result->SnappedWidth().ClampNegativeToZero();
- item_result->end_offset = result.break_offset;
- item_result->shape_result = std::move(shape_result);
- // It is critical to move offset forward, or NGLineBreaker may keep adding
- // NGInlineItemResult until all the memory is consumed.
- CHECK_GT(item_result->end_offset, item_result->start_offset) << Text();
// * If width <= available_width:
// * If offset < item.EndOffset(): the break opportunity to fit is found.
@@ -666,6 +716,7 @@ void NGLineBreaker::BreakText(NGInlineItemResult* item_result,
break_iterator_.IsBreakable(item_result->end_offset);
trailing_whitespace_ = WhitespaceState::kUnknown;
}
+ return inline_size <= available_width ? kSuccess : kOverflow;
}
// This function handles text item for min-content. The specialized logic is
@@ -1121,10 +1172,9 @@ void NGLineBreaker::HandleAtomicInline(
.LayoutAtomicInline(constraint_space_, node_.Style(),
line_info->LineStyle().GetFontBaseline(),
line_info->UseFirstLineStyle());
- DCHECK(item_result->layout_result->PhysicalFragment());
item_result->inline_size =
NGFragment(constraint_space_.GetWritingMode(),
- *item_result->layout_result->PhysicalFragment())
+ item_result->layout_result->PhysicalFragment())
.InlineSize();
item_result->margins =
ComputeLineMarginsForVisualContainer(constraint_space_, style);
@@ -1199,7 +1249,7 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item,
// fragmentainer break. In that case the floats associated with this line will
// already have been processed.
NGInlineItemResult* item_result = AddItem(item, line_info);
- ComputeCanBreakAfter(item_result, auto_wrap_, break_iterator_);
+ item_result->can_break_after = auto_wrap_;
MoveToNextOf(item);
// If we are currently computing our min/max-content size simply append to
@@ -1261,7 +1311,7 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item,
// Check if we already have a pending float. That's because a float cannot be
// higher than any block or floated box generated before.
- if (HasUnpositionedFloats(*line_info->MutableResults()) || float_after_line) {
+ if (HasUnpositionedFloats(line_info->Results()) || float_after_line) {
item_result->has_unpositioned_floats = true;
} else {
NGPositionedFloat positioned_float = PositionFloat(
@@ -1276,12 +1326,13 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item,
NGLayoutOpportunity opportunity = exclusion_space_->FindLayoutOpportunity(
{constraint_space_.BfcOffset().line_offset, bfc_block_offset},
- constraint_space_.AvailableSize().inline_size, NGLogicalSize());
+ constraint_space_.AvailableSize().inline_size, LogicalSize());
DCHECK_EQ(bfc_block_offset, opportunity.rect.BlockStartOffset());
line_opportunity_ = opportunity.ComputeLineLayoutOpportunity(
constraint_space_, line_opportunity_.line_block_size, LayoutUnit());
+ available_width_ = ComputeAvailableWidth();
DCHECK_GE(AvailableWidth(), LayoutUnit());
}
@@ -1290,7 +1341,8 @@ void NGLineBreaker::HandleFloat(const NGInlineItem& item,
bool NGLineBreaker::ComputeOpenTagResult(
const NGInlineItem& item,
const NGConstraintSpace& constraint_space,
- NGInlineItemResult* item_result) {
+ NGInlineItemResult* item_result,
+ base::Optional<NGLineBoxStrut> margins) {
DCHECK_EQ(item.Type(), NGInlineItem::kOpenTag);
DCHECK(item.Style());
const ComputedStyle& style = *item.Style();
@@ -1301,7 +1353,9 @@ bool NGLineBreaker::ComputeOpenTagResult(
item_result->borders = ComputeLineBorders(style);
item_result->padding = ComputeLinePadding(constraint_space, style);
if (item_result->has_edge) {
- item_result->margins = ComputeLineMarginsForSelf(constraint_space, style);
+ item_result->margins =
+ margins ? *margins
+ : ComputeLineMarginsForSelf(constraint_space, style);
item_result->inline_size = item_result->margins.inline_start +
item_result->borders.inline_start +
item_result->padding.inline_start;
@@ -1311,11 +1365,39 @@ bool NGLineBreaker::ComputeOpenTagResult(
return false;
}
-void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
+bool NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
NGLineInfo* line_info) {
+ DCHECK_EQ(item.Type(), NGInlineItem::kOpenTag);
+ DCHECK(item.Style());
+ const ComputedStyle& style = *item.Style();
+
+ // OpenTag is not trailable, except when it has negative inline-start margin,
+ // which can bring the position back to inside of the available width.
+ base::Optional<NGLineBoxStrut> margins;
+ if (UNLIKELY(state_ == LineBreakState::kTrailing &&
+ CanBreakAfterLast(line_info->Results()))) {
+ bool can_continue = false;
+ if (UNLIKELY(item_index_ >= overflow_item_index_ &&
+ item.ShouldCreateBoxFragment() && item.HasStartEdge() &&
+ style.MayHaveMargin())) {
+ margins = ComputeLineMarginsForSelf(constraint_space_, style);
+ LayoutUnit inline_start_margin = margins->inline_start;
+ can_continue = inline_start_margin < 0 &&
+ position_ + inline_start_margin < AvailableWidthToFit();
+ }
+ if (!can_continue) {
+ // Not that case. Break the line before this OpenTag.
+ line_info->SetIsLastLine(false);
+ return false;
+ }
+ // The state is back to normal because the position is back to inside of the
+ // available width.
+ state_ = LineBreakState::kContinue;
+ }
+
NGInlineItemResult* item_result = AddItem(item, line_info);
- if (ComputeOpenTagResult(item, constraint_space_, item_result)) {
+ if (ComputeOpenTagResult(item, constraint_space_, item_result, margins)) {
position_ += item_result->inline_size;
// While the spec defines "non-zero margins, padding, or borders" prevents
@@ -1327,16 +1409,15 @@ void NGLineBreaker::HandleOpenTag(const NGInlineItem& item,
}
bool was_auto_wrap = auto_wrap_;
- DCHECK(item.Style());
- const ComputedStyle& style = *item.Style();
SetCurrentStyle(style);
MoveToNextOf(item);
DCHECK(!item_result->can_break_after);
- NGInlineItemResults* item_results = line_info->MutableResults();
- if (UNLIKELY(!was_auto_wrap && auto_wrap_ && item_results->size() >= 2)) {
+ const NGInlineItemResults& item_results = line_info->Results();
+ if (UNLIKELY(!was_auto_wrap && auto_wrap_ && item_results.size() >= 2)) {
ComputeCanBreakAfter(std::prev(item_result), auto_wrap_, break_iterator_);
}
+ return true;
}
void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
@@ -1360,8 +1441,8 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
// If the line can break after the previous item, prohibit it and allow break
// after this close tag instead.
if (was_auto_wrap) {
- NGInlineItemResults* item_results = line_info->MutableResults();
- if (item_results->size() >= 2) {
+ const NGInlineItemResults& item_results = line_info->Results();
+ if (item_results.size() >= 2) {
NGInlineItemResult* last = std::prev(item_result);
item_result->can_break_after = last->can_break_after;
last->can_break_after = false;
@@ -1390,6 +1471,8 @@ void NGLineBreaker::HandleCloseTag(const NGInlineItem& item,
// At this point, item_results does not fit into the current line, and there
// are no break opportunities in item_results.back().
void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
+ overflow_item_index_ = std::max(overflow_item_index_, item_index_);
+
// Compute the width needing to rewind. When |width_to_rewind| goes negative,
// items can fit within the line.
LayoutUnit available_width = AvailableWidthToFit();
@@ -1435,14 +1518,18 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
LayoutUnit item_available_width =
std::min(-width_to_rewind, item_result->inline_size - 1);
SetCurrentStyle(*item.Style());
- BreakText(item_result, item, *item.TextShapeResult(),
- item_available_width, line_info);
+ BreakResult break_result =
+ BreakText(item_result, item, *item.TextShapeResult(),
+ item_available_width, line_info);
#if DCHECK_IS_ON()
item_result->CheckConsistency(true);
#endif
// If BreakText() changed this item small enough to fit, break here.
- if (item_result->inline_size <= item_available_width) {
- DCHECK(item_result->end_offset < item.EndOffset());
+ DCHECK_EQ(break_result == kSuccess,
+ item_result->inline_size <= item_available_width);
+ if (break_result == kSuccess) {
+ DCHECK_LE(item_result->inline_size, item_available_width);
+ DCHECK_LT(item_result->end_offset, item.EndOffset());
DCHECK(item_result->can_break_after);
DCHECK_LE(i + 1, item_results->size());
if (i + 1 == item_results->size()) {
@@ -1478,6 +1565,7 @@ void NGLineBreaker::HandleOverflow(NGLineInfo* line_info) {
if (!item_results->IsEmpty())
Rewind(0, line_info);
state_ = LineBreakState::kContinue;
+ overflow_item_index_ = 0;
return;
}
@@ -1508,6 +1596,8 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) {
// most cases where our support for rewinding positioned floats is not great
// yet (see below.)
while (item_results[new_end].item->Type() == NGInlineItem::kFloating) {
+ // We assume floats can break after, or this may cause an infinite loop.
+ DCHECK(item_results[new_end].can_break_after);
++new_end;
if (new_end == item_results.size()) {
position_ = line_info->ComputeWidth();
@@ -1520,6 +1610,8 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) {
for (unsigned i = item_results.size(); i > new_end;) {
NGInlineItemResult& rewind = item_results[--i];
if (rewind.positioned_float) {
+ // We assume floats can break after, or this may cause an infinite loop.
+ DCHECK(rewind.can_break_after);
// TODO(kojii): We do not have mechanism to remove once positioned floats
// yet, and that rewinding them may lay it out twice. For now, prohibit
// rewinding positioned floats. This may results in incorrect layout, but
@@ -1560,7 +1652,7 @@ void NGLineBreaker::Rewind(unsigned new_end, NGLineInfo* line_info) {
const ComputedStyle& NGLineBreaker::ComputeCurrentStyle(
unsigned item_result_index,
NGLineInfo* line_info) const {
- NGInlineItemResults& item_results = *line_info->MutableResults();
+ const NGInlineItemResults& item_results = line_info->Results();
// Use the current item if it can compute the current style.
const NGInlineItem* item = item_results[item_result_index].item;
@@ -1616,8 +1708,10 @@ void NGLineBreaker::SetCurrentStyle(const ComputedStyle& style) {
line_break_type = LineBreakType::kKeepAll;
break;
}
- if (UNLIKELY(override_break_anywhere_ && break_anywhere_if_overflow_))
+ if (UNLIKELY((override_break_anywhere_ && break_anywhere_if_overflow_) ||
+ style.GetLineBreak() == LineBreak::kAnywhere)) {
line_break_type = LineBreakType::kBreakCharacter;
+ }
break_iterator_.SetBreakType(line_break_type);
enable_soft_hyphen_ = style.GetHyphens() != Hyphens::kNone;
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 1e9baf8885b..17c3d0545a0 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
@@ -47,7 +47,7 @@ class CORE_EXPORT NGLineBreaker {
// Compute the next line break point and produces NGInlineItemResults for
// the line.
inline void NextLine(NGLineInfo* line_info) {
- NextLine(NGSizeIndefinite, nullptr, line_info);
+ NextLine(kIndefiniteSize, nullptr, line_info);
}
// During the min/max size calculation we need a special percentage
@@ -72,9 +72,11 @@ class CORE_EXPORT NGLineBreaker {
// Compute NGInlineItemResult for an open tag item.
// Returns true if this item has edge and may have non-zero inline size.
- static bool ComputeOpenTagResult(const NGInlineItem&,
- const NGConstraintSpace&,
- NGInlineItemResult*);
+ static bool ComputeOpenTagResult(
+ const NGInlineItem&,
+ const NGConstraintSpace&,
+ NGInlineItemResult*,
+ base::Optional<NGLineBoxStrut> margins = base::nullopt);
// This enum is private, except for |WhitespaceStateForTesting()|. See
// |whitespace_| member.
@@ -98,8 +100,8 @@ class CORE_EXPORT NGLineBreaker {
unsigned end_offset,
NGLineInfo*);
NGInlineItemResult* AddItem(const NGInlineItem&, NGLineInfo*);
- void SetLineEndFragment(scoped_refptr<const NGPhysicalTextFragment>,
- NGLineInfo*);
+ LayoutUnit SetLineEndFragment(scoped_refptr<const NGPhysicalTextFragment>,
+ NGLineInfo*);
void BreakLine(LayoutUnit percentage_resolution_block_size_for_min_max,
Vector<LayoutObject*>* out_floats_for_min_max,
@@ -126,11 +128,12 @@ class CORE_EXPORT NGLineBreaker {
HandleText(item, *item.TextShapeResult(), line_info);
}
void HandleText(const NGInlineItem& item, const ShapeResult&, NGLineInfo*);
- void BreakText(NGInlineItemResult*,
- const NGInlineItem&,
- const ShapeResult&,
- LayoutUnit available_width,
- NGLineInfo*);
+ enum BreakResult { kSuccess, kOverflow };
+ BreakResult BreakText(NGInlineItemResult*,
+ const NGInlineItem&,
+ const ShapeResult&,
+ LayoutUnit available_width,
+ NGLineInfo*);
bool HandleTextForFastMinContent(NGInlineItemResult*,
const NGInlineItem&,
const ShapeResult&,
@@ -163,7 +166,7 @@ class CORE_EXPORT NGLineBreaker {
Vector<LayoutObject*>* out_floats_for_min_max,
NGLineInfo*);
- void HandleOpenTag(const NGInlineItem&, NGLineInfo*);
+ bool HandleOpenTag(const NGInlineItem&, NGLineInfo*);
void HandleCloseTag(const NGInlineItem&, NGLineInfo*);
void HandleOverflow(NGLineInfo*);
@@ -179,11 +182,13 @@ class CORE_EXPORT NGLineBreaker {
void ComputeBaseDirection();
LayoutUnit AvailableWidth() const {
- return line_opportunity_.AvailableInlineSize();
+ DCHECK_EQ(available_width_, ComputeAvailableWidth());
+ return available_width_;
}
LayoutUnit AvailableWidthToFit() const {
return AvailableWidth().AddEpsilon();
}
+ LayoutUnit ComputeAvailableWidth() const;
// Represents the current offset of the input.
LineBreakState state_;
@@ -197,6 +202,7 @@ class CORE_EXPORT NGLineBreaker {
// The current position from inline_start. Unlike NGInlineLayoutAlgorithm
// that computes position in visual order, this position in logical order.
LayoutUnit position_;
+ LayoutUnit available_width_;
NGLineLayoutOpportunity line_opportunity_;
NGInlineNode node_;
@@ -261,6 +267,9 @@ class CORE_EXPORT NGLineBreaker {
};
base::Optional<TrailingCollapsibleSpace> trailing_collapsible_space_;
+ // Keep track of item index where overflow occurrred.
+ unsigned overflow_item_index_;
+
// Keep track of handled float items. See HandleFloat().
const NGPositionedFloatVector& leading_floats_;
unsigned leading_floats_index_ = 0u;
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 861fd965e43..778bb892eec 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
@@ -35,10 +35,10 @@ class NGLineBreakerTest : public NGLayoutTest {
node.PrepareLayoutIfNeeded();
NGConstraintSpace space =
- NGConstraintSpaceBuilder(
- WritingMode::kHorizontalTb, WritingMode::kHorizontalTb,
- /* is_new_fc */ false)
- .SetAvailableSize({available_width, NGSizeIndefinite})
+ NGConstraintSpaceBuilder(WritingMode::kHorizontalTb,
+ WritingMode::kHorizontalTb,
+ /* is_new_fc */ false)
+ .SetAvailableSize({available_width, kIndefiniteSize})
.ToConstraintSpace();
scoped_refptr<NGInlineBreakToken> break_token;
@@ -494,8 +494,13 @@ TEST_P(NGTrailingSpaceWidthTest, TrailingSpaceWidth) {
)HTML");
Vector<NGLineInfo> line_infos = BreakToLineInfo(node, LayoutUnit(50));
- EXPECT_EQ(line_infos[0].ComputeTrailingSpaceWidth(),
- LayoutUnit(10) * data.trailing_space_width);
+ const NGLineInfo& line_info = line_infos[0];
+ if (line_info.ShouldHangTrailingSpaces()) {
+ EXPECT_EQ(line_info.HangWidth(),
+ LayoutUnit(10) * data.trailing_space_width);
+ } else {
+ EXPECT_EQ(line_info.HangWidth(), LayoutUnit());
+ }
}
TEST_F(NGLineBreakerTest, MinMaxWithTrailingSpaces) {
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 97a947e04a5..747f3f13a07 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
@@ -13,29 +13,6 @@
namespace blink {
-namespace {
-
-// Create the style to use for the ellipsis characters.
-//
-// The ellipsis is styled according to the line style.
-// https://drafts.csswg.org/css-ui/#ellipsing-details
-scoped_refptr<const ComputedStyle> CreateEllipsisStyle(
- scoped_refptr<const ComputedStyle> line_style) {
- if (line_style->TextDecorationsInEffect() == TextDecoration::kNone)
- return line_style;
-
- // Ellipsis should not have text decorations. Reset if it's set.
- // This is not defined, but 4 impls do this.
- scoped_refptr<ComputedStyle> ellipsis_style =
- ComputedStyle::CreateAnonymousStyleWithDisplay(*line_style,
- EDisplay::kInline);
- ellipsis_style->ResetTextDecoration();
- ellipsis_style->ClearAppliedTextDecorations();
- return ellipsis_style;
-}
-
-} // namespace
-
NGLineTruncator::NGLineTruncator(NGInlineNode& node,
const NGLineInfo& line_info)
: node_(node),
@@ -47,8 +24,9 @@ LayoutUnit NGLineTruncator::TruncateLine(
LayoutUnit line_width,
NGLineBoxFragmentBuilder::ChildList* line_box) {
// Shape the ellipsis and compute its inline size.
- scoped_refptr<const ComputedStyle> ellipsis_style =
- CreateEllipsisStyle(line_style_);
+ // The ellipsis is styled according to the line style.
+ // https://drafts.csswg.org/css-ui/#ellipsing-details
+ const ComputedStyle* ellipsis_style = line_style_.get();
const Font& font = ellipsis_style->GetFont();
const SimpleFontData* font_data = font.PrimaryFont();
DCHECK(font_data);
@@ -101,7 +79,7 @@ LayoutUnit NGLineTruncator::TruncateLine(
NGTextFragmentBuilder builder(node_, line_style_->GetWritingMode());
DCHECK(ellipsized_fragment->GetLayoutObject() &&
ellipsized_fragment->GetLayoutObject()->IsInline());
- builder.SetText(ellipsized_fragment->GetLayoutObject(), ellipsis_text,
+ builder.SetText(ellipsized_fragment->GetMutableLayoutObject(), ellipsis_text,
ellipsis_style, true /* is_ellipsis_style */,
std::move(ellipsis_shape_result));
FontBaseline baseline_type = line_style_->GetFontBaseline();
@@ -109,7 +87,7 @@ LayoutUnit NGLineTruncator::TruncateLine(
baseline_type);
line_box->AddChild(
builder.ToTextFragment(),
- NGLogicalOffset{ellipsis_inline_offset, -ellipsis_metrics.ascent},
+ LogicalOffset{ellipsis_inline_offset, -ellipsis_metrics.ascent},
ellipsis_width, 0);
return std::max(ellipsis_inline_offset + ellipsis_width, line_width);
}
@@ -121,10 +99,9 @@ void NGLineTruncator::HideChild(NGLineBoxFragmentBuilder::Child* child) {
const NGPhysicalFragment* fragment = nullptr;
if (const NGLayoutResult* layout_result = child->layout_result.get()) {
// Need to propagate OOF descendants in this inline-block child.
- if (!layout_result->OutOfFlowPositionedDescendants().IsEmpty()) {
+ if (layout_result->PhysicalFragment().HasOutOfFlowPositionedDescendants())
return;
- }
- fragment = layout_result->PhysicalFragment();
+ fragment = &layout_result->PhysicalFragment();
} else {
fragment = child->fragment.get();
}
@@ -211,6 +188,11 @@ bool NGLineTruncator::TruncateChild(LayoutUnit space_for_child,
if (!child->fragment)
return is_first_child;
auto& fragment = To<NGPhysicalTextFragment>(*child->fragment);
+
+ // No need to truncate empty results.
+ if (!fragment.TextShapeResult())
+ return is_first_child;
+
// TODO(layout-dev): Add support for OffsetToFit to ShapeResultView to avoid
// this copy.
scoped_refptr<blink::ShapeResult> shape_result =
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 fab78c10ead..1e7fb8e2a92 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
@@ -12,7 +12,6 @@
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/position.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/platform/text/character.h"
@@ -27,16 +26,16 @@ bool CanUseNGOffsetMapping(const LayoutObject& object) {
}
Position CreatePositionForOffsetMapping(const Node& node, unsigned dom_offset) {
- if (node.IsTextNode()) {
+ if (auto* text_node = DynamicTo<Text>(node)) {
// 'text-transform' may make the rendered text length longer than the
// original text node, in which case we clamp the offset to avoid crashing.
// TODO(crbug.com/750990): Support 'text-transform' to remove this hack.
#if DCHECK_IS_ON()
// Ensures that the clamping hack kicks in only with text-transform.
if (node.ComputedStyleRef().TextTransform() == ETextTransform::kNone)
- DCHECK_LE(dom_offset, ToText(node).length());
+ DCHECK_LE(dom_offset, text_node->length());
#endif
- const unsigned clamped_offset = std::min(dom_offset, ToText(node).length());
+ const unsigned clamped_offset = std::min(dom_offset, text_node->length());
return Position(&node, clamped_offset);
}
// For non-text-anchored position, the offset must be either 0 or 1.
@@ -46,13 +45,13 @@ Position CreatePositionForOffsetMapping(const Node& node, unsigned dom_offset) {
std::pair<const Node&, unsigned> ToNodeOffsetPair(const Position& position) {
DCHECK(NGOffsetMapping::AcceptsPosition(position)) << position;
- if (position.AnchorNode()->IsTextNode()) {
+ if (auto* text_node = DynamicTo<Text>(position.AnchorNode())) {
if (position.IsOffsetInAnchor())
return {*position.AnchorNode(), position.OffsetInContainerNode()};
if (position.IsBeforeAnchor())
return {*position.AnchorNode(), 0};
DCHECK(position.IsAfterAnchor());
- return {*position.AnchorNode(), ToText(position.AnchorNode())->length()};
+ return {*position.AnchorNode(), text_node->length()};
}
if (position.IsBeforeAnchor())
return {*position.AnchorNode(), 0};
@@ -99,7 +98,29 @@ NGOffsetMappingUnit::NGOffsetMappingUnit(NGOffsetMappingUnitType type,
dom_start_(dom_start),
dom_end_(dom_end),
text_content_start_(text_content_start),
- text_content_end_(text_content_end) {}
+ text_content_end_(text_content_end) {
+ AssertValid();
+}
+
+void NGOffsetMappingUnit::AssertValid() const {
+#if ENABLE_SECURITY_ASSERT
+ SECURITY_DCHECK(dom_start_ <= dom_end_) << dom_start_ << " vs. " << dom_end_;
+ SECURITY_DCHECK(text_content_start_ <= text_content_end_)
+ << text_content_start_ << " vs. " << text_content_end_;
+ if (layout_object_->IsText()) {
+ const LayoutText& layout_text = ToLayoutText(*layout_object_);
+ const unsigned text_start =
+ AssociatedNode() ? layout_text.TextStartOffset() : 0;
+ const unsigned text_end = text_start + layout_text.TextLength();
+ SECURITY_DCHECK(dom_end_ >= text_start)
+ << dom_end_ << " vs. " << text_start;
+ SECURITY_DCHECK(dom_end_ <= text_end) << dom_end_ << " vs. " << text_end;
+ } else {
+ SECURITY_DCHECK(dom_start_ == 0) << dom_start_;
+ SECURITY_DCHECK(dom_end_ == 1) << dom_end_;
+ }
+#endif
+}
NGOffsetMappingUnit::~NGOffsetMappingUnit() = default;
@@ -238,7 +259,23 @@ LayoutBlockFlow* NGOffsetMapping::GetInlineFormattingContextOf(
NGOffsetMapping::NGOffsetMapping(UnitVector&& units,
RangeMap&& ranges,
String text)
- : units_(std::move(units)), ranges_(std::move(ranges)), text_(text) {}
+ : units_(std::move(units)), ranges_(std::move(ranges)), text_(text) {
+#if ENABLE_SECURITY_ASSERT
+ for (const auto& unit : units_) {
+ SECURITY_DCHECK(unit.TextContentStart() <= text.length())
+ << unit.TextContentStart() << "<=" << text.length();
+ SECURITY_DCHECK(unit.TextContentEnd() <= text.length())
+ << unit.TextContentEnd() << "<=" << text.length();
+ unit.AssertValid();
+ }
+ for (const auto& pair : ranges) {
+ SECURITY_DCHECK(pair.value.first < units_.size())
+ << pair.value.first << "<" << units_.size();
+ SECURITY_DCHECK(pair.value.second < units_.size())
+ << pair.value.second << "<" << units_.size();
+ }
+#endif
+}
NGOffsetMapping::~NGOffsetMapping() = default;
@@ -385,7 +422,7 @@ Position NGOffsetMapping::StartOfNextNonCollapsedContent(
const auto node_and_offset = ToNodeOffsetPair(position);
const Node& node = node_and_offset.first;
const unsigned offset = node_and_offset.second;
- while (unit != units_.end() && unit->GetOwner() == node) {
+ while (unit != units_.end() && unit->AssociatedNode() == node) {
if (unit->DOMEnd() > offset &&
unit->GetType() != NGOffsetMappingUnitType::kCollapsed) {
const unsigned result = std::max(offset, unit->DOMStart());
@@ -406,7 +443,7 @@ Position NGOffsetMapping::EndOfLastNonCollapsedContent(
const auto node_and_offset = ToNodeOffsetPair(position);
const Node& node = node_and_offset.first;
const unsigned offset = node_and_offset.second;
- while (unit->GetOwner() == node) {
+ while (unit->AssociatedNode() == node) {
if (unit->DOMStart() < offset &&
unit->GetType() != NGOffsetMappingUnitType::kCollapsed) {
const unsigned result = std::min(offset, unit->DOMEnd());
@@ -509,16 +546,6 @@ Position NGOffsetMapping::GetLastPosition(unsigned offset) const {
return CreatePositionForOffsetMapping(node, dom_offset);
}
-PositionWithAffinity NGOffsetMapping::GetPositionWithAffinity(
- const NGCaretNavigator::Position& position) const {
- if (position.IsBeforeCharacter()) {
- return PositionWithAffinity(GetLastPosition(position.index),
- TextAffinity::kDownstream);
- }
- return PositionWithAffinity(GetFirstPosition(position.index + 1),
- TextAffinity::kUpstream);
-}
-
bool NGOffsetMapping::HasBidiControlCharactersOnly(unsigned start,
unsigned end) const {
DCHECK_LE(start, end);
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 a8b59f75f1e..743dd9d8757 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
@@ -9,7 +9,6 @@
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/node.h"
#include "third_party/blink/renderer/core/editing/forward.h"
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -73,6 +72,8 @@ class CORE_EXPORT NGOffsetMappingUnit {
unsigned ConvertTextContentToFirstDOMOffset(unsigned) const;
unsigned ConvertTextContentToLastDOMOffset(unsigned) const;
+ void AssertValid() const;
+
private:
NGOffsetMappingUnitType type_ = NGOffsetMappingUnitType::kIdentity;
@@ -82,6 +83,10 @@ class CORE_EXPORT NGOffsetMappingUnit {
// offset in |LayoutText::text_| instead of DOM node.
unsigned dom_start_;
unsigned dom_end_;
+
+ // |text_content_start_| and |text_content_end_| are offsets in
+ // |NGOffsetMapping::text_|. These values are in [0, |text_.length()] to
+ // represent collapsed spaces at the end of block.
unsigned text_content_start_;
unsigned text_content_end_;
@@ -219,13 +224,6 @@ class CORE_EXPORT NGOffsetMapping {
Position GetFirstPosition(unsigned) const;
Position GetLastPosition(unsigned) const;
- // Converts the given caret position on text content to a PositionWithAffinity
- // in DOM. If |position| is before a character, the function creates a
- // downstream position before |GetLastPosition()| of the character; otherwise,
- // it returns an upstream position after |GetFirstPosition()| of the character
- PositionWithAffinity GetPositionWithAffinity(
- const NGCaretNavigator::Position& position) const;
-
// Returns all NGOffsetMappingUnits whose text content ranges has non-empty
// (but possibly collapsed) intersection with (start, end). Note that units
// that only "touch" |start| or |end| are excluded.
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
index 8969d67f8f5..9524bfb97ac 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
-#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
namespace blink {
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 f41b357b915..78b74c7b685 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
@@ -1225,6 +1225,33 @@ TEST_F(NGOffsetMappingTest, SoftHyphen) {
TEST_RANGE(mapping.GetRanges(), text, 0u, 1u);
}
+// For http://crbug.com/965353
+TEST_F(NGOffsetMappingTest, PreWrapAndReusing) {
+ // Note: "white-space: break-space" yields same result.
+ SetupHtml("t", "<p id='t' style='white-space: pre-wrap'>abc</p>");
+ Element& target = *GetDocument().getElementById("t");
+
+ // Change to <p id=t>abc xyz</p>
+ Text& text = *Text::Create(GetDocument(), " xyz");
+ target.appendChild(&text);
+ UpdateAllLifecyclePhasesForTest();
+
+ // Change to <p id=t> xyz</p>. We attempt to reuse " xyz".
+ target.firstChild()->remove();
+ UpdateAllLifecyclePhasesForTest();
+
+ const NGOffsetMapping& mapping = GetOffsetMapping();
+ EXPECT_EQ(String(u" \u200Bxyz"), mapping.GetText())
+ << "We have ZWS after leading preserved space.";
+ EXPECT_EQ((Vector<NGOffsetMappingUnit>{
+ NGOffsetMappingUnit(kIdentity, *text.GetLayoutObject(), 0u, 1u,
+ 0u, 1u),
+ NGOffsetMappingUnit(kIdentity, *text.GetLayoutObject(), 1u, 4u,
+ 2u, 5u),
+ }),
+ mapping.GetUnits());
+}
+
TEST_F(NGOffsetMappingTest, TextOverflowEllipsis) {
LoadAhem();
SetupHtml("t",
@@ -1241,6 +1268,41 @@ TEST_F(NGOffsetMappingTest, TextOverflowEllipsis) {
TEST_RANGE(mapping.GetRanges(), text, 0u, 1u);
}
+// https://crbug.com/967106
+TEST_F(NGOffsetMappingTest, StartOfNextNonCollapsedContentWithPseudo) {
+ // The white spaces are necessary for bug repro. Do not remove them.
+ SetupHtml("t", R"HTML(
+ <style>span#quote::before { content: '"'}</style>
+ <div id=t>
+ <span>foo </span>
+ <span id=quote>bar</span>
+ </div>)HTML");
+
+ const Element* quote = GetElementById("quote");
+ const Node* text = quote->previousSibling();
+ const Position position = Position::FirstPositionInNode(*text);
+
+ EXPECT_EQ(Position(),
+ GetOffsetMapping().StartOfNextNonCollapsedContent(position));
+}
+
+// https://crbug.com/967106
+TEST_F(NGOffsetMappingTest, EndOfLastNonCollapsedContentWithPseudo) {
+ // The white spaces are necessary for bug repro. Do not remove them.
+ SetupHtml("t", R"HTML(
+ <style>span#quote::after { content: '" '}</style>
+ <div id=t>
+ <span id=quote>foo</span>
+ <span>bar</span>
+ </div>)HTML");
+
+ const Element* quote = GetElementById("quote");
+ const Node* text = quote->nextSibling();
+ const Position position = Position::LastPositionInNode(*text);
+
+ EXPECT_EQ(Position(),
+ GetOffsetMapping().EndOfLastNonCollapsedContent(position));
+}
// Test |GetOffsetMapping| which is available both for LayoutNG and for legacy.
class NGOffsetMappingGetterTest : public RenderingTest,
public testing::WithParamInterface<bool>,
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 bcbbeb51ba0..08681856ab1 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
@@ -4,7 +4,9 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
@@ -15,7 +17,6 @@ namespace blink {
namespace {
struct SameSizeAsNGPhysicalLineBoxFragment : NGPhysicalContainerFragment {
- void* pointer;
NGLineHeightMetrics metrics;
};
@@ -23,6 +24,18 @@ static_assert(sizeof(NGPhysicalLineBoxFragment) ==
sizeof(SameSizeAsNGPhysicalLineBoxFragment),
"NGPhysicalLineBoxFragment should stay small");
+bool IsInlineLeaf(const NGPhysicalFragment& fragment) {
+ if (fragment.IsText())
+ return true;
+ return fragment.IsBox() && fragment.IsAtomicInline();
+}
+
+bool IsEditableFragment(const NGPhysicalFragment& fragment) {
+ if (!fragment.GetNode())
+ return false;
+ return HasEditableStyle(*fragment.GetNode());
+}
+
} // namespace
scoped_refptr<const NGPhysicalLineBoxFragment>
@@ -34,7 +47,7 @@ NGPhysicalLineBoxFragment::Create(NGLineBoxFragmentBuilder* builder) {
// we pass the buffer as a constructor argument.
void* data = ::WTF::Partitions::FastMalloc(
sizeof(NGPhysicalLineBoxFragment) +
- builder->children_.size() * sizeof(NGLinkStorage),
+ builder->children_.size() * sizeof(NGLink),
::WTF::GetStringWithTypeName<NGPhysicalLineBoxFragment>());
new (data) NGPhysicalLineBoxFragment(builder);
return base::AdoptRef(static_cast<NGPhysicalLineBoxFragment*>(data));
@@ -48,8 +61,13 @@ NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment(
kFragmentLineBox,
builder->line_box_type_),
metrics_(builder->metrics_) {
- style_ = std::move(builder->style_);
+ // A line box must have a metrics unless it's an empty line box.
+ DCHECK(!metrics_.IsEmpty() || IsEmptyLineBox());
base_direction_ = static_cast<unsigned>(builder->base_direction_);
+ has_hanging_ = builder->hang_inline_size_ != 0;
+ has_propagated_descendants_ = has_floating_descendants_ ||
+ HasOutOfFlowPositionedDescendants() ||
+ builder->unpositioned_list_marker_;
}
NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics(
@@ -60,17 +78,35 @@ NGLineHeightMetrics NGPhysicalLineBoxFragment::BaselineMetrics(
return metrics_;
}
-NGPhysicalOffsetRect NGPhysicalLineBoxFragment::ScrollableOverflow(
+PhysicalRect NGPhysicalLineBoxFragment::ScrollableOverflow(
const LayoutObject* container,
const ComputedStyle* container_style,
- NGPhysicalSize container_physical_size) const {
+ PhysicalSize container_physical_size) const {
WritingMode container_writing_mode = container_style->GetWritingMode();
TextDirection container_direction = container_style->Direction();
- NGPhysicalOffsetRect overflow({}, Size());
+ PhysicalRect overflow({}, Size());
for (const auto& child : Children()) {
- NGPhysicalOffsetRect child_scroll_overflow =
+ PhysicalRect child_scroll_overflow =
child->ScrollableOverflowForPropagation(container);
child_scroll_overflow.offset += child.Offset();
+
+ // Chop the hanging part from scrollable overflow. Children overflow in
+ // inline direction should hang, which should not cause scroll.
+ // TODO(kojii): Should move to text fragment to make this more accurate.
+ if (UNLIKELY(has_hanging_ && !child->IsFloatingOrOutOfFlowPositioned())) {
+ if (IsHorizontalWritingMode(container_writing_mode)) {
+ if (child_scroll_overflow.offset.left < 0)
+ child_scroll_overflow.offset.left = LayoutUnit();
+ if (child_scroll_overflow.Right() > Size().width)
+ child_scroll_overflow.ShiftRightEdgeTo(Size().width);
+ } else {
+ if (child_scroll_overflow.offset.top < 0)
+ child_scroll_overflow.offset.top = LayoutUnit();
+ if (child_scroll_overflow.Bottom() > Size().height)
+ child_scroll_overflow.ShiftBottomEdgeTo(Size().height);
+ }
+ }
+
// If child has the same style as parent, parent will compute relative
// offset.
if (&child->Style() != container_style) {
@@ -130,20 +166,63 @@ bool NGPhysicalLineBoxFragment::HasSoftWrapToNextLine() const {
return !break_token.IsFinished() && !break_token.IsForcedBreak();
}
-NGPhysicalOffset NGPhysicalLineBoxFragment::LineStartPoint() const {
- const NGLogicalOffset logical_start; // (0, 0)
- const NGPhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1));
+PhysicalOffset NGPhysicalLineBoxFragment::LineStartPoint() const {
+ const LogicalOffset logical_start; // (0, 0)
+ const PhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1));
return logical_start.ConvertToPhysical(Style().GetWritingMode(),
BaseDirection(), Size(), pixel_size);
}
-NGPhysicalOffset NGPhysicalLineBoxFragment::LineEndPoint() const {
+PhysicalOffset NGPhysicalLineBoxFragment::LineEndPoint() const {
const LayoutUnit inline_size =
NGFragment(Style().GetWritingMode(), *this).InlineSize();
- const NGLogicalOffset logical_end(inline_size, LayoutUnit());
- const NGPhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1));
+ const LogicalOffset logical_end(inline_size, LayoutUnit());
+ const PhysicalSize pixel_size(LayoutUnit(1), LayoutUnit(1));
return logical_end.ConvertToPhysical(Style().GetWritingMode(),
BaseDirection(), Size(), pixel_size);
}
+const LayoutObject* NGPhysicalLineBoxFragment::ClosestLeafChildForPoint(
+ const PhysicalOffset& point,
+ bool only_editable_leaves) const {
+ const PhysicalSize unit_square(LayoutUnit(1), LayoutUnit(1));
+ const LogicalOffset logical_point = point.ConvertToLogical(
+ Style().GetWritingMode(), BaseDirection(), Size(), unit_square);
+ const LayoutUnit inline_offset = logical_point.inline_offset;
+ const NGPhysicalFragment* closest_leaf_child = nullptr;
+ LayoutUnit closest_leaf_distance;
+ for (const auto& descendant :
+ NGInlineFragmentTraversal::DescendantsOf(*this)) {
+ const NGPhysicalFragment& fragment = *descendant.fragment;
+ if (!fragment.GetLayoutObject())
+ continue;
+ if (!IsInlineLeaf(fragment) || fragment.IsListMarker())
+ continue;
+ if (only_editable_leaves && !IsEditableFragment(fragment))
+ continue;
+
+ const LogicalSize fragment_logical_size =
+ fragment.Size().ConvertToLogical(Style().GetWritingMode());
+ const LogicalOffset fragment_logical_offset =
+ descendant.offset_to_container_box.ConvertToLogical(
+ Style().GetWritingMode(), BaseDirection(), Size(), fragment.Size());
+ const LayoutUnit inline_min = fragment_logical_offset.inline_offset;
+ const LayoutUnit inline_max = fragment_logical_offset.inline_offset +
+ fragment_logical_size.inline_size;
+ if (inline_offset >= inline_min && inline_offset < inline_max)
+ return fragment.GetLayoutObject();
+
+ const LayoutUnit distance =
+ inline_offset < inline_min ? inline_min - inline_offset
+ : inline_offset - inline_max + LayoutUnit(1);
+ if (!closest_leaf_child || distance < closest_leaf_distance) {
+ closest_leaf_child = &fragment;
+ closest_leaf_distance = distance;
+ }
+ }
+ if (!closest_leaf_child)
+ return nullptr;
+ return closest_leaf_child->GetLayoutObject();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
index a295aacc7a3..7a453e31e5d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
@@ -32,7 +32,7 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final
NGLineBoxFragmentBuilder* builder);
~NGPhysicalLineBoxFragment() {
- for (const NGLinkStorage& child : Children())
+ for (const NGLink& child : Children())
child.fragment->Release();
}
@@ -41,8 +41,9 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final
}
bool IsEmptyLineBox() const { return LineBoxType() == kEmptyLineBox; }
+ // True if descendants were propagated to outside of this fragment.
+ bool HasPropagatedDescendants() const { return has_propagated_descendants_; }
- const ComputedStyle& Style() const { return *style_; }
const NGLineHeightMetrics& Metrics() const { return metrics_; }
// The base direction of this line. Also known as the paragraph direction.
@@ -59,20 +60,22 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final
// ScrollableOverflow is not precomputed/cached because it cannot be computed
// when LineBox is generated because it needs container dimensions to
// resolve relative position of its children.
- NGPhysicalOffsetRect ScrollableOverflow(
- const LayoutObject* container,
- const ComputedStyle* container_style,
- NGPhysicalSize container_physical_size) const;
+ PhysicalRect ScrollableOverflow(const LayoutObject* container,
+ const ComputedStyle* container_style,
+ PhysicalSize container_physical_size) const;
// Returns the first/last leaf fragment in the line in logical order. Returns
// nullptr if the line box is empty.
const NGPhysicalFragment* FirstLogicalLeaf() const;
const NGPhysicalFragment* LastLogicalLeaf() const;
+ const LayoutObject* ClosestLeafChildForPoint(const PhysicalOffset&,
+ bool only_editable_leaves) const;
+
// Returns a point at the visual start/end of the line.
// Encapsulates the handling of text direction and writing mode.
- NGPhysicalOffset LineStartPoint() const;
- NGPhysicalOffset LineEndPoint() const;
+ PhysicalOffset LineStartPoint() const;
+ PhysicalOffset LineEndPoint() const;
// Whether the content soft-wraps to the next line.
bool HasSoftWrapToNextLine() const;
@@ -80,9 +83,8 @@ class CORE_EXPORT NGPhysicalLineBoxFragment final
private:
NGPhysicalLineBoxFragment(NGLineBoxFragmentBuilder* builder);
- scoped_refptr<const ComputedStyle> style_;
NGLineHeightMetrics metrics_;
- NGLinkStorage children_[];
+ NGLink children_[];
};
template <>
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
index 26ca7c236bf..53167624896 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment_test.cc
@@ -16,7 +16,7 @@ class NGPhysicalLineBoxFragmentTest : public NGLayoutTest {
NGPhysicalLineBoxFragmentTest() : NGLayoutTest() {}
protected:
- const NGPhysicalLineBoxFragment* GetLineBox() const {
+ Vector<const NGPhysicalLineBoxFragment*> GetLineBoxes() const {
const Element* container = GetElementById("root");
DCHECK(container);
const LayoutObject* layout_object = container->GetLayoutObject();
@@ -26,12 +26,21 @@ class NGPhysicalLineBoxFragmentTest : public NGLayoutTest {
To<LayoutBlockFlow>(layout_object)->CurrentFragment();
DCHECK(root_fragment) << container;
+ Vector<const NGPhysicalLineBoxFragment*> lines;
for (const auto& child :
NGInlineFragmentTraversal::DescendantsOf(*root_fragment)) {
- if (child.fragment->IsLineBox())
- return To<NGPhysicalLineBoxFragment>(child.fragment.get());
+ if (const NGPhysicalLineBoxFragment* line =
+ DynamicTo<NGPhysicalLineBoxFragment>(child.fragment.get())) {
+ lines.push_back(line);
+ }
}
- NOTREACHED();
+ return lines;
+ }
+
+ const NGPhysicalLineBoxFragment* GetLineBox() const {
+ Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+ if (!lines.IsEmpty())
+ return lines.front();
return nullptr;
}
};
@@ -47,6 +56,42 @@ class NGPhysicalLineBoxFragmentTest : public NGLayoutTest {
EXPECT_EQ(GetElementById(id), fragment->GetNode()); \
}
+TEST_F(NGPhysicalLineBoxFragmentTest, HasPropagatedDescendantsFloat) {
+ SetBodyInnerHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ div {
+ font-size: 10px;
+ width: 10ch;
+ }
+ .float { float: left; }
+ </style>
+ <div id=root>12345678 12345<div class=float>float</div></div>
+ )HTML");
+ Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+ EXPECT_EQ(lines.size(), 2u);
+ EXPECT_FALSE(lines[0]->HasPropagatedDescendants());
+ EXPECT_TRUE(lines[1]->HasPropagatedDescendants());
+}
+
+TEST_F(NGPhysicalLineBoxFragmentTest, HasPropagatedDescendantsOOF) {
+ SetBodyInnerHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ div {
+ font-size: 10px;
+ width: 10ch;
+ }
+ .abspos { position: absolute; }
+ </style>
+ <div id=root>12345678 12345<div class=abspos>abspos</div></div>
+ )HTML");
+ Vector<const NGPhysicalLineBoxFragment*> lines = GetLineBoxes();
+ EXPECT_EQ(lines.size(), 2u);
+ EXPECT_FALSE(lines[0]->HasPropagatedDescendants());
+ EXPECT_TRUE(lines[1]->HasPropagatedDescendants());
+}
+
TEST_F(NGPhysicalLineBoxFragmentTest, FirstLastLogicalLeafInSimpleText) {
SetBodyInnerHTML(
"<div id=root>"
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 6db5844ca0f..384bb007a72 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
@@ -5,12 +5,13 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_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_text_fragment_builder.h"
+#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
@@ -19,24 +20,15 @@ namespace blink {
namespace {
struct SameSizeAsNGPhysicalTextFragment : NGPhysicalFragment {
- void* pointers[3];
+ void* pointers[2];
unsigned offsets[2];
+ PhysicalRect rect;
};
static_assert(sizeof(NGPhysicalTextFragment) ==
sizeof(SameSizeAsNGPhysicalTextFragment),
"NGPhysicalTextFragment should stay small");
-inline bool IsPhysicalTextFragmentAnonymousText(
- const LayoutObject* layout_object) {
- if (!layout_object)
- return false;
- if (layout_object->IsText() && ToLayoutText(layout_object)->IsTextFragment())
- return !ToLayoutTextFragment(layout_object)->AssociatedTextNode();
- const Node* node = layout_object->GetNode();
- return !node || node->IsPseudoElement();
-}
-
NGLineOrientation ToLineOrientation(WritingMode writing_mode) {
switch (writing_mode) {
case WritingMode::kHorizontalTb:
@@ -59,15 +51,14 @@ NGPhysicalTextFragment::NGPhysicalTextFragment(
unsigned start_offset,
unsigned end_offset,
scoped_refptr<const ShapeResultView> shape_result)
- : NGPhysicalFragment(source.GetLayoutObject(),
- source.StyleVariant(),
- source.IsHorizontal()
- ? NGPhysicalSize{shape_result->SnappedWidth(),
- source.Size().height}
- : NGPhysicalSize{source.Size().width,
- shape_result->SnappedWidth()},
- kFragmentText,
- source.TextType()),
+ : NGPhysicalFragment(
+ source.GetMutableLayoutObject(),
+ source.StyleVariant(),
+ source.IsHorizontal()
+ ? PhysicalSize{shape_result->SnappedWidth(), source.Size().height}
+ : PhysicalSize{source.Size().width, shape_result->SnappedWidth()},
+ kFragmentText,
+ source.TextType()),
text_(source.text_),
start_offset_(start_offset),
end_offset_(end_offset),
@@ -75,11 +66,9 @@ NGPhysicalTextFragment::NGPhysicalTextFragment(
DCHECK_GE(start_offset_, source.StartOffset());
DCHECK_LE(end_offset_, source.EndOffset());
DCHECK(shape_result_ || IsFlowControl()) << ToString();
- DCHECK(!source.rare_data_ || !source.rare_data_->style_);
line_orientation_ = source.line_orientation_;
- is_anonymous_text_ = source.is_anonymous_text_;
-
- UpdateSelfInkOverflow();
+ is_generated_text_ = source.is_generated_text_;
+ ink_overflow_computed_ = false;
}
NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder)
@@ -91,53 +80,25 @@ NGPhysicalTextFragment::NGPhysicalTextFragment(NGTextFragmentBuilder* builder)
DCHECK(shape_result_ || IsFlowControl()) << ToString();
line_orientation_ =
static_cast<unsigned>(ToLineOrientation(builder->GetWritingMode()));
-
- if (UNLIKELY(StyleVariant() == NGStyleVariant::kEllipsis)) {
- EnsureRareData()->style_ = std::move(builder->style_);
- is_anonymous_text_ = true;
- } else {
- is_anonymous_text_ =
- builder->text_type_ == kGeneratedText ||
- IsPhysicalTextFragmentAnonymousText(builder->layout_object_);
- }
-
- UpdateSelfInkOverflow();
-}
-
-NGPhysicalTextFragment::RareData* NGPhysicalTextFragment::EnsureRareData() {
- if (!rare_data_)
- rare_data_ = std::make_unique<RareData>();
- return rare_data_.get();
-}
-
-const ComputedStyle& NGPhysicalTextFragment::Style() const {
- switch (StyleVariant()) {
- case NGStyleVariant::kStandard:
- case NGStyleVariant::kFirstLine:
- return NGPhysicalFragment::Style();
- case NGStyleVariant::kEllipsis:
- DCHECK(rare_data_ && rare_data_->style_);
- return *rare_data_->style_;
- }
- NOTREACHED();
- return NGPhysicalFragment::Style();
+ is_generated_text_ = builder->IsGeneratedText();
+ ink_overflow_computed_ = false;
}
// Convert logical cooridnate to local physical coordinate.
-NGPhysicalOffsetRect NGPhysicalTextFragment::ConvertToLocal(
+PhysicalRect NGPhysicalTextFragment::ConvertToLocal(
const LayoutRect& logical_rect) const {
switch (LineOrientation()) {
case NGLineOrientation::kHorizontal:
- return NGPhysicalOffsetRect(logical_rect);
+ return PhysicalRect(logical_rect);
case NGLineOrientation::kClockWiseVertical:
- return {{size_.width - logical_rect.MaxY(), logical_rect.X()},
- {logical_rect.Height(), logical_rect.Width()}};
+ return {size_.width - logical_rect.MaxY(), logical_rect.X(),
+ logical_rect.Height(), logical_rect.Width()};
case NGLineOrientation::kCounterClockWiseVertical:
- return {{logical_rect.Y(), size_.height - logical_rect.MaxX()},
- {logical_rect.Height(), logical_rect.Width()}};
+ return {logical_rect.Y(), size_.height - logical_rect.MaxX(),
+ logical_rect.Height(), logical_rect.Width()};
}
NOTREACHED();
- return NGPhysicalOffsetRect(logical_rect);
+ return PhysicalRect(logical_rect);
}
// Compute the inline position from text offset, in logical coordinate relative
@@ -190,9 +151,8 @@ NGPhysicalTextFragment::LineLeftAndRightForOffsets(unsigned start_offset,
: std::make_pair(start_position, end_position);
}
-NGPhysicalOffsetRect NGPhysicalTextFragment::LocalRect(
- unsigned start_offset,
- unsigned end_offset) const {
+PhysicalRect NGPhysicalTextFragment::LocalRect(unsigned start_offset,
+ unsigned end_offset) const {
if (start_offset == start_offset_ && end_offset == end_offset_)
return LocalRect();
LayoutUnit start_position, end_position;
@@ -201,34 +161,38 @@ NGPhysicalOffsetRect NGPhysicalTextFragment::LocalRect(
const LayoutUnit inline_size = end_position - start_position;
switch (LineOrientation()) {
case NGLineOrientation::kHorizontal:
- return {{start_position, LayoutUnit()}, {inline_size, Size().height}};
+ return {start_position, LayoutUnit(), inline_size, Size().height};
case NGLineOrientation::kClockWiseVertical:
- return {{LayoutUnit(), start_position}, {Size().width, inline_size}};
+ return {LayoutUnit(), start_position, Size().width, inline_size};
case NGLineOrientation::kCounterClockWiseVertical:
- return {{LayoutUnit(), Size().height - end_position},
- {Size().width, inline_size}};
+ return {LayoutUnit(), Size().height - end_position, Size().width,
+ inline_size};
}
NOTREACHED();
return {};
}
-NGPhysicalOffsetRect NGPhysicalTextFragment::SelfInkOverflow() const {
- return UNLIKELY(rare_data_) ? rare_data_->self_ink_overflow_ : LocalRect();
+PhysicalRect NGPhysicalTextFragment::SelfInkOverflow() const {
+ if (!ink_overflow_computed_)
+ ComputeSelfInkOverflow();
+ return self_ink_overflow_;
}
-void NGPhysicalTextFragment::ClearSelfInkOverflow() {
- if (UNLIKELY(rare_data_))
- rare_data_->self_ink_overflow_ = LocalRect();
+void NGPhysicalTextFragment::ClearSelfInkOverflow() const {
+ self_ink_overflow_ = LocalRect();
}
-void NGPhysicalTextFragment::UpdateSelfInkOverflow() {
+void NGPhysicalTextFragment::ComputeSelfInkOverflow() const {
+ ink_overflow_computed_ = true;
+
if (UNLIKELY(!shape_result_)) {
ClearSelfInkOverflow();
return;
}
// Glyph bounds is in logical coordinate, origin at the alphabetic baseline.
- LayoutRect ink_overflow = EnclosingLayoutRect(shape_result_->Bounds());
+ FloatRect text_ink_bounds = Style().GetFont().TextInkBounds(PaintInfo());
+ LayoutRect ink_overflow = EnclosingLayoutRect(text_ink_bounds);
// Make the origin at the logical top of this fragment.
const ComputedStyle& style = Style();
@@ -269,18 +233,18 @@ void NGPhysicalTextFragment::UpdateSelfInkOverflow() {
// Uniting the frame rect ensures that non-ink spaces such side bearings, or
// even space characters, are included in the visual rect for decorations.
- NGPhysicalOffsetRect local_ink_overflow = ConvertToLocal(ink_overflow);
- NGPhysicalOffsetRect local_rect = LocalRect();
+ PhysicalRect local_ink_overflow = ConvertToLocal(ink_overflow);
+ PhysicalRect local_rect = LocalRect();
if (local_rect.Contains(local_ink_overflow)) {
- ClearSelfInkOverflow();
+ self_ink_overflow_ = local_rect;
return;
}
local_ink_overflow.Unite(local_rect);
local_ink_overflow.ExpandEdgesToPixelBoundaries();
- EnsureRareData()->self_ink_overflow_ = local_ink_overflow;
+ self_ink_overflow_ = local_ink_overflow;
}
-scoped_refptr<const NGPhysicalFragment> NGPhysicalTextFragment::TrimText(
+scoped_refptr<const NGPhysicalTextFragment> NGPhysicalTextFragment::TrimText(
unsigned new_start_offset,
unsigned new_end_offset) const {
DCHECK(shape_result_);
@@ -294,7 +258,7 @@ scoped_refptr<const NGPhysicalFragment> NGPhysicalTextFragment::TrimText(
}
unsigned NGPhysicalTextFragment::TextOffsetForPoint(
- const NGPhysicalOffset& point) const {
+ const PhysicalOffset& point) const {
const ComputedStyle& style = Style();
const LayoutUnit& point_in_line_direction =
style.IsHorizontalWritingMode() ? point.left : point.top;
@@ -311,7 +275,7 @@ unsigned NGPhysicalTextFragment::TextOffsetForPoint(
DCHECK(IsFlowControl());
// Zero-inline-size objects such as newline always return the start offset.
- NGLogicalSize size = Size().ConvertToLogical(style.GetWritingMode());
+ LogicalSize size = Size().ConvertToLogical(style.GetWritingMode());
if (!size.inline_size)
return StartOffset();
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
index c3f7669da89..c46013f2bc8 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h
@@ -17,8 +17,9 @@
namespace blink {
-struct NGPhysicalOffsetRect;
+struct PhysicalRect;
class NGTextFragmentBuilder;
+class NGPhysicalTextFragment;
enum class AdjustMidCluster;
@@ -54,7 +55,7 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
// |text_| holds generated contents instead of |text_content_| in
// |NGNodeInlineData|, e.g. hyphen, and ellipsis.
// Note: Contents generated by CSS pseudo element, e.g. ::before, ::after,
- // are not classified to this. See IsAnonymousText() for them.
+ // are not classified to this. See IsGeneratedText() for them.
kGeneratedText,
// When adding new values, make sure the bit size of |sub_type_| is large
// enough to store.
@@ -63,8 +64,9 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
NGPhysicalTextFragment(NGTextFragmentBuilder*);
NGTextType TextType() const { return static_cast<NGTextType>(sub_type_); }
- // True if this is a generated text.
- bool IsGeneratedText() const { return TextType() == kGeneratedText; }
+ // Returns true if the text is generated (from, e.g., list marker,
+ // pseudo-element, ...) instead of from a DOM text node.
+ bool IsGeneratedText() const { return is_generated_text_; }
// True if this is a forced line break.
bool IsLineBreak() const { return TextType() == kForcedLineBreak; }
// True if this is not for painting; i.e., a forced line break, a tabulation,
@@ -72,8 +74,10 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
bool IsFlowControl() const {
return IsLineBreak() || TextType() == kFlowControl;
}
-
- const ComputedStyle& Style() const;
+ // True if this is an ellpisis generated by `text-overflow: ellipsis`.
+ bool IsEllipsis() const {
+ return StyleVariant() == NGStyleVariant::kEllipsis;
+ }
unsigned Length() const { return end_offset_ - start_offset_; }
StringView Text() const { return StringView(text_, start_offset_, Length()); }
@@ -99,18 +103,18 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
// The layout box of text in (start, end) range in local coordinate.
// Start and end offsets must be between StartOffset() and EndOffset().
- NGPhysicalOffsetRect LocalRect(unsigned start_offset,
- unsigned end_offset) const;
+ PhysicalRect LocalRect(unsigned start_offset, unsigned end_offset) const;
using NGPhysicalFragment::LocalRect;
// The visual bounding box that includes glpyh bounding box and CSS
// properties, in local coordinates.
- NGPhysicalOffsetRect SelfInkOverflow() const;
+ PhysicalRect SelfInkOverflow() const;
// Create a new fragment that has part of the text of this fragment.
// All other properties are the same as this fragment.
- scoped_refptr<const NGPhysicalFragment> TrimText(unsigned start_offset,
- unsigned end_offset) const;
+ scoped_refptr<const NGPhysicalTextFragment> TrimText(
+ unsigned start_offset,
+ unsigned end_offset) const;
scoped_refptr<const NGPhysicalFragment> CloneWithoutOffset() const;
@@ -119,12 +123,8 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
TextShapeResult()};
}
- // Returns true if the text is generated (from, e.g., list marker,
- // pseudo-element, ...) instead of from a DOM text node.
- bool IsAnonymousText() const { return is_anonymous_text_; }
-
// Returns the text offset in the fragment placed closest to the given point.
- unsigned TextOffsetForPoint(const NGPhysicalOffset&) const;
+ unsigned TextOffsetForPoint(const PhysicalOffset&) const;
UBiDiLevel BidiLevel() const;
TextDirection ResolvedDirection() const;
@@ -143,23 +143,14 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
unsigned end_offset,
scoped_refptr<const ShapeResultView> shape_result);
- struct RareData {
- USING_FAST_MALLOC(RareData);
-
- public:
- NGPhysicalOffsetRect self_ink_overflow_;
- scoped_refptr<const ComputedStyle> style_; // Used only for ellipsis.
- };
- RareData* EnsureRareData();
-
LayoutUnit InlinePositionForOffset(unsigned offset,
LayoutUnit (*round)(float),
AdjustMidCluster) const;
- NGPhysicalOffsetRect ConvertToLocal(const LayoutRect&) const;
+ PhysicalRect ConvertToLocal(const LayoutRect&) const;
- void UpdateSelfInkOverflow();
- void ClearSelfInkOverflow();
+ void ComputeSelfInkOverflow() const;
+ void ClearSelfInkOverflow() const;
// The text of NGInlineNode; i.e., of a parent block. The text for this
// fragment is a substring(start_offset_, end_offset_) of this string.
@@ -170,7 +161,10 @@ class CORE_EXPORT NGPhysicalTextFragment final : public NGPhysicalFragment {
const unsigned end_offset_;
const scoped_refptr<const ShapeResultView> shape_result_;
- std::unique_ptr<RareData> rare_data_;
+ // Fragments are immutable but allow certain expensive data, specifically ink
+ // overflow, to be cached as long as it is guaranteed to always recompute to
+ // the same value.
+ mutable PhysicalRect self_ink_overflow_;
};
template <>
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
index ce4035dd595..1867897f0d7 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment_test.cc
@@ -4,7 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.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"
@@ -55,9 +55,7 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRect) {
)HTML");
auto text_fragments = CollectTextFragmentsInContainer("container");
ASSERT_EQ(2u, text_fragments.size());
- EXPECT_EQ(NGPhysicalOffsetRect({LayoutUnit(20), LayoutUnit(0)},
- {LayoutUnit(20), LayoutUnit(10)}),
- text_fragments[1]->LocalRect(8, 10));
+ EXPECT_EQ(PhysicalRect(20, 0, 20, 10), text_fragments[1]->LocalRect(8, 10));
}
TEST_F(NGPhysicalTextFragmentTest, LocalRectRTL) {
@@ -78,8 +76,7 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectRTL) {
// The 2nd line starts at 12, because the div has a bidi-control.
EXPECT_EQ(12u, text_fragments[1]->StartOffset());
// TODO(layout-dev): Investigate whether this is correct.
- // EXPECT_EQ(NGPhysicalOffsetRect({LayoutUnit(50), LayoutUnit(0)},
- // {LayoutUnit(20), LayoutUnit(10)}),
+ // EXPECT_EQ(PhysicalRect(50, 0, 20, 10),
// text_fragments[1]->LocalRect(14, 16));
}
@@ -97,9 +94,7 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectVLR) {
)HTML");
auto text_fragments = CollectTextFragmentsInContainer("container");
ASSERT_EQ(2u, text_fragments.size());
- EXPECT_EQ(NGPhysicalOffsetRect({LayoutUnit(0), LayoutUnit(20)},
- {LayoutUnit(10), LayoutUnit(20)}),
- text_fragments[1]->LocalRect(8, 10));
+ EXPECT_EQ(PhysicalRect(0, 20, 10, 20), text_fragments[1]->LocalRect(8, 10));
}
TEST_F(NGPhysicalTextFragmentTest, LocalRectVRL) {
@@ -116,9 +111,7 @@ TEST_F(NGPhysicalTextFragmentTest, LocalRectVRL) {
)HTML");
auto text_fragments = CollectTextFragmentsInContainer("container");
ASSERT_EQ(2u, text_fragments.size());
- EXPECT_EQ(NGPhysicalOffsetRect({LayoutUnit(0), LayoutUnit(20)},
- {LayoutUnit(10), LayoutUnit(20)}),
- text_fragments[1]->LocalRect(8, 10));
+ EXPECT_EQ(PhysicalRect(0, 20, 10, 20), text_fragments[1]->LocalRect(8, 10));
}
TEST_F(NGPhysicalTextFragmentTest, NormalTextIsNotAnonymousText) {
@@ -128,7 +121,7 @@ TEST_F(NGPhysicalTextFragmentTest, NormalTextIsNotAnonymousText) {
ASSERT_EQ(1u, text_fragments.size());
const NGPhysicalTextFragment& text = *text_fragments[0];
- EXPECT_FALSE(text.IsAnonymousText());
+ EXPECT_FALSE(text.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, FirstLetterIsNotAnonymousText) {
@@ -141,8 +134,8 @@ TEST_F(NGPhysicalTextFragmentTest, FirstLetterIsNotAnonymousText) {
const NGPhysicalTextFragment& first_letter = *text_fragments[0];
const NGPhysicalTextFragment& remaining_text = *text_fragments[1];
- EXPECT_FALSE(first_letter.IsAnonymousText());
- EXPECT_FALSE(remaining_text.IsAnonymousText());
+ EXPECT_FALSE(first_letter.IsGeneratedText());
+ EXPECT_FALSE(remaining_text.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, BeforeAndAfterAreAnonymousText) {
@@ -156,9 +149,9 @@ TEST_F(NGPhysicalTextFragmentTest, BeforeAndAfterAreAnonymousText) {
const NGPhysicalTextFragment& before = *text_fragments[0];
const NGPhysicalTextFragment& text = *text_fragments[1];
const NGPhysicalTextFragment& after = *text_fragments[2];
- EXPECT_TRUE(before.IsAnonymousText());
- EXPECT_FALSE(text.IsAnonymousText());
- EXPECT_TRUE(after.IsAnonymousText());
+ EXPECT_TRUE(before.IsGeneratedText());
+ EXPECT_FALSE(text.IsGeneratedText());
+ EXPECT_TRUE(after.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, Ellipsis) {
@@ -181,15 +174,13 @@ TEST_F(NGPhysicalTextFragmentTest, Ellipsis) {
const NGPhysicalTextFragment& ellipsis = *text_fragments[1];
EXPECT_EQ(NGPhysicalTextFragment::kNormalText, abcdef.TextType());
EXPECT_FALSE(abcdef.IsGeneratedText());
- EXPECT_FALSE(abcdef.IsAnonymousText());
EXPECT_EQ(u8"abc", GetText(abcdef));
EXPECT_EQ(NGPhysicalTextFragment::kGeneratedText, ellipsis.TextType());
EXPECT_TRUE(ellipsis.IsGeneratedText());
- EXPECT_TRUE(ellipsis.IsAnonymousText());
EXPECT_EQ(u8"\u2026", GetText(ellipsis));
}
-TEST_F(NGPhysicalTextFragmentTest, ListMarkerIsAnonymousText) {
+TEST_F(NGPhysicalTextFragmentTest, ListMarkerIsGeneratedText) {
SetBodyInnerHTML(
"<ol style='list-style-position:inside'>"
"<li id=list>text</li>"
@@ -200,8 +191,8 @@ TEST_F(NGPhysicalTextFragmentTest, ListMarkerIsAnonymousText) {
const NGPhysicalTextFragment& marker = *text_fragments[0];
const NGPhysicalTextFragment& text = *text_fragments[1];
- EXPECT_TRUE(marker.IsAnonymousText());
- EXPECT_FALSE(text.IsAnonymousText());
+ EXPECT_TRUE(marker.IsGeneratedText());
+ EXPECT_FALSE(text.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, SoftHyphen) {
@@ -222,17 +213,14 @@ TEST_F(NGPhysicalTextFragmentTest, SoftHyphen) {
const NGPhysicalTextFragment& shy = *text_fragments[1];
const NGPhysicalTextFragment& def = *text_fragments[2];
EXPECT_EQ(NGPhysicalTextFragment::kNormalText, abc.TextType());
- EXPECT_FALSE(abc.IsGeneratedText());
// Note: ShapeResult::RunInfo.width_ == 0 for U+00AD
EXPECT_EQ(u8"abc\u00AD", GetText(abc));
EXPECT_EQ(NGPhysicalTextFragment::kGeneratedText, shy.TextType());
- EXPECT_TRUE(shy.IsGeneratedText());
// Note: |ComputedStyle::HypenString()| returns "-" or U+2010 based on
// glyph availability.
if (GetText(shy) != "-")
EXPECT_EQ(u8"\u2010", GetText(shy));
EXPECT_EQ(NGPhysicalTextFragment::kNormalText, def.TextType());
- EXPECT_FALSE(def.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, QuotationMarksAreAnonymousText) {
@@ -244,9 +232,9 @@ TEST_F(NGPhysicalTextFragmentTest, QuotationMarksAreAnonymousText) {
const NGPhysicalTextFragment& open_quote = *text_fragments[0];
const NGPhysicalTextFragment& text = *text_fragments[1];
const NGPhysicalTextFragment& closed_quote = *text_fragments[2];
- EXPECT_TRUE(open_quote.IsAnonymousText());
- EXPECT_FALSE(text.IsAnonymousText());
- EXPECT_TRUE(closed_quote.IsAnonymousText());
+ EXPECT_TRUE(open_quote.IsGeneratedText());
+ EXPECT_FALSE(text.IsGeneratedText());
+ EXPECT_TRUE(closed_quote.IsGeneratedText());
}
TEST_F(NGPhysicalTextFragmentTest, TextOffsetForPointForTabulation) {
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 07ee69f28b1..65b78d5bd1b 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
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/layout_text_fragment.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_line_height_metrics.h"
@@ -58,6 +59,21 @@ void NGTextFragmentBuilder::SetText(
end_effect_ = NGTextEndEffect::kNone;
}
+bool NGTextFragmentBuilder::IsGeneratedText() const {
+ if (UNLIKELY(style_variant_ == NGStyleVariant::kEllipsis ||
+ text_type_ == NGPhysicalTextFragment::kGeneratedText))
+ return true;
+
+ DCHECK(layout_object_);
+ if (const auto* layout_text_fragment =
+ ToLayoutTextFragmentOrNull(layout_object_)) {
+ return !layout_text_fragment->AssociatedTextNode();
+ }
+
+ const Node* node = layout_object_->GetNode();
+ return !node || node->IsPseudoElement();
+}
+
scoped_refptr<const NGPhysicalTextFragment>
NGTextFragmentBuilder::ToTextFragment() {
scoped_refptr<const NGPhysicalTextFragment> fragment =
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 bf000858f53..b219736b393 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
@@ -5,7 +5,7 @@
#ifndef NGTextFragmentBuilder_h
#define NGTextFragmentBuilder_h
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_text_end_effect.h"
@@ -43,6 +43,10 @@ class CORE_EXPORT NGTextFragmentBuilder final : public NGFragmentBuilder {
scoped_refptr<const NGPhysicalTextFragment> ToTextFragment();
private:
+ // Returns true if the text is generated (from, e.g., list marker,
+ // pseudo-element, ...) instead of from a DOM text node.
+ bool IsGeneratedText() const;
+
NGInlineNode inline_node_;
String text_;
unsigned item_index_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
index fb5a3e5c346..e1c609da844 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
@@ -138,7 +138,7 @@ NGStaticPosition LayoutBoxUtils::ComputeStaticPositionFromLegacy(
.ToConstraintSpace();
NGBoxStrut border_scrollbar =
ComputeBorders(non_anonymous_space, container_node) +
- container_node.GetScrollbarSizes();
+ ComputeScrollbars(non_anonymous_space, container_node);
// Now make it relative to the left or top border edge of the containing
// block.
@@ -170,10 +170,10 @@ NGStaticPosition LayoutBoxUtils::ComputeStaticPositionFromLegacy(
? container_border_box_logical_height - static_block
: static_block;
- NGPhysicalOffset static_location =
+ PhysicalOffset static_location =
container_style->IsHorizontalWritingMode()
- ? NGPhysicalOffset(inline_left_or_top, block_top_or_left)
- : NGPhysicalOffset(block_top_or_left, inline_left_or_top);
+ ? PhysicalOffset(inline_left_or_top, block_top_or_left)
+ : PhysicalOffset(block_top_or_left, inline_left_or_top);
return NGStaticPosition::Create(writing_mode, parent_style->Direction(),
static_location);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
index 5f64e2c3454..ab090971bf4 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
@@ -46,8 +46,10 @@ void LayoutNGBlockFlow::UpdateBlockLayout(bool relayout_children) {
NGBlockNode(this).Layout(constraint_space);
for (const NGOutOfFlowPositionedDescendant& descendant :
- result->OutOfFlowPositionedDescendants())
- descendant.node.UseOldOutOfFlowPositioning();
+ result->PhysicalFragment().OutOfFlowPositionedDescendants())
+ descendant.node.UseLegacyOutOfFlowPositioning();
+
+ UpdateMargins(constraint_space);
}
void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
@@ -78,7 +80,7 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
// borders and scrollbars to compensate.
NGBoxStrut border_scrollbar =
ComputeBorders(constraint_space, container_node) +
- NGBlockNode(container).GetScrollbarSizes();
+ ComputeScrollbars(constraint_space, container_node);
// Calculate the border-box size of the object that's the containing block of
// this out-of-flow positioned descendant. Note that this is not to be used as
@@ -104,27 +106,26 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
container_border_box_logical_height = container->LogicalHeight();
}
- container_builder.SetInlineSize(container_border_box_logical_width);
- container_builder.SetBlockSize(container_border_box_logical_height);
- container_builder.SetBorders(
- ComputeBorders(constraint_space, container_node));
- container_builder.SetPadding(
- ComputePadding(constraint_space, *container_style));
+ NGFragmentGeometry fragment_geometry;
+ fragment_geometry.border_box_size = {container_border_box_logical_width,
+ container_border_box_logical_height};
+ fragment_geometry.border = ComputeBorders(constraint_space, container_node);
+ fragment_geometry.padding =
+ ComputePadding(constraint_space, *container_style);
+ container_builder.SetInitialFragmentGeometry(fragment_geometry);
NGStaticPosition static_position =
LayoutBoxUtils::ComputeStaticPositionFromLegacy(*this);
// Set correct container for inline containing blocks.
container_builder.AddOutOfFlowLegacyCandidate(
- NGBlockNode(this), static_position,
- css_container->IsBox() ? nullptr : css_container);
+ NGBlockNode(this), static_position, ToLayoutInlineOrNull(css_container));
- base::Optional<NGLogicalSize> initial_containing_block_fixed_size;
+ base::Optional<LogicalSize> initial_containing_block_fixed_size;
if (container->IsLayoutView() && !GetDocument().Printing()) {
if (LocalFrameView* frame_view = ToLayoutView(container)->GetFrameView()) {
IntSize size =
frame_view->LayoutViewport()->ExcludeScrollbars(frame_view->Size());
- NGPhysicalSize physical_size =
- NGPhysicalSize(LayoutUnit(size.Width()), LayoutUnit(size.Height()));
+ PhysicalSize physical_size(size);
initial_containing_block_fixed_size =
physical_size.ConvertToLogical(container->Style()->GetWritingMode());
}
@@ -142,21 +143,21 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
container_builder.ToBoxFragment();
// These are the unpositioned OOF descendants of the current OOF block.
for (const NGOutOfFlowPositionedDescendant& descendant :
- result->OutOfFlowPositionedDescendants())
- descendant.node.UseOldOutOfFlowPositioning();
+ result->PhysicalFragment().OutOfFlowPositionedDescendants())
+ descendant.node.UseLegacyOutOfFlowPositioning();
- const auto* fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
- DCHECK_GT(fragment->Children().size(), 0u);
+ const auto& fragment = result->PhysicalFragment();
+ DCHECK_GT(fragment.Children().size(), 0u);
// Copy sizes of all child fragments to Legacy.
// There could be multiple fragments, when this node has descendants whose
// container is this node's container.
// Example: fixed descendant of fixed element.
- for (auto& child : fragment->Children()) {
+ for (auto& child : fragment.Children()) {
const NGPhysicalFragment* child_fragment = child.get();
DCHECK(child_fragment->GetLayoutObject()->IsBox());
LayoutBox* child_legacy_box =
- ToLayoutBox(child_fragment->GetLayoutObject());
- NGPhysicalOffset child_offset = child.Offset();
+ ToLayoutBox(child_fragment->GetMutableLayoutObject());
+ PhysicalOffset child_offset = child.Offset();
if (container_style->IsFlippedBlocksWritingMode()) {
child_legacy_box->SetX(container_border_box_logical_height -
child_offset.left - child_fragment->Size().width);
@@ -165,8 +166,30 @@ void LayoutNGBlockFlow::UpdateOutOfFlowBlockLayout() {
}
child_legacy_box->SetY(child_offset.top);
}
- DCHECK_EQ(fragment->Children()[0]->GetLayoutObject(), this);
+ DCHECK_EQ(fragment.Children()[0]->GetLayoutObject(), this);
SetIsLegacyInitiatedOutOfFlowLayout(true);
}
+void LayoutNGBlockFlow::UpdateMargins(const NGConstraintSpace& space) {
+ const LayoutBlock* containing_block = ContainingBlock();
+ if (!containing_block || !containing_block->IsLayoutBlockFlow())
+ return;
+
+ // In the legacy engine, for regular block container layout, children
+ // calculate and store margins on themselves, while in NG that's done by the
+ // container. Since this object is a LayoutNG entry-point, we'll have to do it
+ // on ourselves, since that's what the legacy container expects.
+ const ComputedStyle& style = StyleRef();
+ const ComputedStyle& cb_style = containing_block->StyleRef();
+ const auto writing_mode = cb_style.GetWritingMode();
+ const auto direction = cb_style.Direction();
+ LayoutUnit percentage_resolution_size =
+ space.PercentageResolutionInlineSizeForParentWritingMode();
+ NGBoxStrut margins = ComputePhysicalMargins(style, percentage_resolution_size)
+ .ConvertToLogical(writing_mode, direction);
+ ResolveInlineMargins(style, cb_style, space.AvailableSize().inline_size,
+ LogicalWidth(), &margins);
+ SetMargin(margins.ConvertToPhysical(writing_mode, direction));
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
index f36533ea284..07792ae7e14 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
@@ -26,6 +26,7 @@ class CORE_EXPORT LayoutNGBlockFlow : public LayoutNGMixin<LayoutBlockFlow> {
private:
void UpdateOutOfFlowBlockLayout();
+ void UpdateMargins(const NGConstraintSpace&);
};
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGBlockFlow, IsLayoutNGBlockFlow());
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
index 6384487adc7..5b1bfac5755 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_flexible_box.cc
@@ -29,8 +29,8 @@ void LayoutNGFlexibleBox::UpdateBlockLayout(bool relayout_children) {
NGBlockNode(this).Layout(constraint_space);
for (NGOutOfFlowPositionedDescendant descendant :
- result->OutOfFlowPositionedDescendants())
- descendant.node.UseOldOutOfFlowPositioning();
+ result->PhysicalFragment().OutOfFlowPositionedDescendants())
+ descendant.node.UseLegacyOutOfFlowPositioning();
}
} // namespace blink
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 45ae19674b9..cd9887971db 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
@@ -46,9 +46,7 @@ void LayoutNGMixin<Base>::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) {
Base::StyleDidChange(diff, old_style);
- const ComputedStyle& new_style = Base::StyleRef();
- if (old_style && Base::ChildrenInline() &&
- new_style.GetUnicodeBidi() != old_style->GetUnicodeBidi()) {
+ if (diff.NeedsCollectInlines()) {
Base::SetNeedsCollectInlines();
}
}
@@ -85,7 +83,7 @@ const NGPhysicalBoxFragment* LayoutNGMixin<Base>::CurrentFragment() const {
if (!cached_layout_result)
return nullptr;
- return To<NGPhysicalBoxFragment>(cached_layout_result->PhysicalFragment());
+ return &To<NGPhysicalBoxFragment>(cached_layout_result->PhysicalFragment());
}
template <typename Base>
@@ -154,7 +152,7 @@ void LayoutNGMixin<Base>::AddScrollingOverflowFromChildren() {
Base::StyleRef().Direction());
}
- NGPhysicalOffsetRect children_overflow;
+ PhysicalRect children_overflow;
// Only add overflow for fragments NG has not reflected into Legacy.
// These fragments are:
@@ -163,8 +161,12 @@ void LayoutNGMixin<Base>::AddScrollingOverflowFromChildren() {
// TODO(layout-dev) Transfroms also need to be applied to compute overflow
// correctly. NG is not yet transform-aware. crbug.com/855965
if (!physical_fragment->Children().IsEmpty()) {
+ LayoutUnit border_inline_start =
+ LayoutUnit(Base::StyleRef().BorderStartWidth());
+ LayoutUnit border_block_start =
+ LayoutUnit(Base::StyleRef().BorderBeforeWidth());
for (const auto& child : physical_fragment->Children()) {
- NGPhysicalOffsetRect child_scrollable_overflow;
+ PhysicalRect child_scrollable_overflow;
if (child->IsOutOfFlowPositioned()) {
child_scrollable_overflow =
child->ScrollableOverflowForPropagation(this);
@@ -179,7 +181,18 @@ void LayoutNGMixin<Base>::AddScrollingOverflowFromChildren() {
continue;
}
child_scrollable_overflow.offset += child.Offset();
- children_overflow.Unite(child_scrollable_overflow);
+
+ // Do not add overflow if fragment is not reachable by scrolling.
+ WritingMode writing_mode = Base::StyleRef().GetWritingMode();
+ LogicalOffset child_logical_end =
+ child_scrollable_overflow.offset.ConvertToLogical(
+ writing_mode, Base::StyleRef().Direction(),
+ physical_fragment->Size(), child_scrollable_overflow.size) +
+ child_scrollable_overflow.size.ConvertToLogical(writing_mode);
+
+ if (child_logical_end.inline_offset > border_inline_start &&
+ child_logical_end.block_offset > border_block_start)
+ children_overflow.Unite(child_scrollable_overflow);
}
}
@@ -191,17 +204,27 @@ void LayoutNGMixin<Base>::AddScrollingOverflowFromChildren() {
template <typename Base>
void LayoutNGMixin<Base>::AddOutlineRects(
- Vector<LayoutRect>& rects,
- const LayoutPoint& additional_offset,
+ Vector<PhysicalRect>& rects,
+ const PhysicalOffset& additional_offset,
NGOutlineType include_block_overflows) const {
if (PaintFragment()) {
- PaintFragment()->AddSelfOutlineRect(&rects, additional_offset,
- include_block_overflows);
+ PaintFragment()->AddSelfOutlineRects(&rects, additional_offset,
+ include_block_overflows);
} else {
Base::AddOutlineRects(rects, additional_offset, include_block_overflows);
}
}
+template <typename Base>
+bool LayoutNGMixin<Base>::PaintedOutputOfObjectHasNoEffectRegardlessOfSize()
+ const {
+ // LayoutNGMixin is in charge of paint invalidation of the first line.
+ if (PaintFragment())
+ return false;
+
+ return Base::PaintedOutputOfObjectHasNoEffectRegardlessOfSize();
+}
+
// Retrieve NGBaseline from the current fragment.
template <typename Base>
base::Optional<LayoutUnit> LayoutNGMixin<Base>::FragmentBaseline(
@@ -249,39 +272,18 @@ void LayoutNGMixin<Base>::SetPaintFragment(
scoped_refptr<NGPaintFragment>* current =
NGPaintFragment::Find(&paint_fragment_, break_token);
DCHECK(current);
- const NGPaintFragment* old = current->get();
if (fragment) {
*current = NGPaintFragment::Create(std::move(fragment), break_token,
std::move(*current));
- } else {
+ // |NGPaintFragment::Create()| calls |SlowSetPaintingLayerNeedsRepaint()|.
+ } else if (*current) {
+ DCHECK_EQ(this, (*current)->GetLayoutObject());
*current = nullptr;
- }
-
- if (old && old != current->get()) {
- // Painting layer needs repaint when a DisplayItemClient is destroyed.
- // TODO(kojii): We need this here for now, but this should be handled
- // differently for better efficiency, in pre-paint tree walk to walk
- // fragment tree, or before layout. crbug.com/941228
ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint();
}
}
template <typename Base>
-void LayoutNGMixin<Base>::InvalidateDisplayItemClients(
- PaintInvalidationReason invalidation_reason) const {
- if (NGPaintFragment* fragment = PaintFragment()) {
- // TODO(koji): Should be in the PaintInvalidator, possibly with more logic
- // ported from BlockFlowPaintInvalidator.
- ObjectPaintInvalidator object_paint_invalidator(*this);
- object_paint_invalidator.InvalidateDisplayItemClient(*fragment,
- invalidation_reason);
- return;
- }
-
- LayoutBlockFlow::InvalidateDisplayItemClients(invalidation_reason);
-}
-
-template <typename Base>
void LayoutNGMixin<Base>::Paint(const PaintInfo& paint_info) const {
if (const NGPaintFragment* paint_fragment = PaintFragment())
NGBoxFragmentPainter(*paint_fragment).Paint(paint_info);
@@ -340,8 +342,10 @@ PositionWithAffinity LayoutNGMixin<Base>::PositionForPoint(
if (!PaintFragment())
return Base::CreatePositionWithAffinity(0);
+ // Flip because |point| is in flipped physical coordinates while
+ // NGPaintFragment::PositionForPoint() requires pure physical coordinates.
const PositionWithAffinity ng_position =
- PaintFragment()->PositionForPoint(NGPhysicalOffset(point));
+ PaintFragment()->PositionForPoint(Base::FlipForWritingMode(point));
if (ng_position.IsNotNull())
return ng_position;
return Base::CreatePositionWithAffinity(0);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 9333fb1cc50..7f45d582c85 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -41,8 +41,6 @@ class LayoutNGMixin : public Base {
LayoutUnit FirstLineBoxBaseline() const final;
LayoutUnit InlineBlockBaseline(LineDirectionMode) const final;
- void InvalidateDisplayItemClients(PaintInvalidationReason) const final;
-
void Paint(const PaintInfo&) const final;
bool NodeAtPoint(HitTestResult&,
@@ -50,9 +48,17 @@ class LayoutNGMixin : public Base {
const LayoutPoint& accumulated_offset,
HitTestAction) final;
- PositionWithAffinity PositionForPoint(const LayoutPoint&) const final;
-
- NGPaintFragment* PaintFragment() const final { return paint_fragment_.get(); }
+ PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
+
+ const NGPaintFragment* PaintFragment() const final {
+ // TODO(layout-dev) crbug.com/963103
+ // Safer option here is to return nullptr only if
+ // Lifecycle > DocumentLifecycle::kAfterPerformLayout, but this breaks
+ // some layout tests.
+ if (Base::NeedsLayout())
+ return nullptr;
+ return paint_fragment_.get();
+ }
void SetPaintFragment(const NGBlockBreakToken*,
scoped_refptr<const NGPhysicalFragment>) final;
@@ -67,10 +73,12 @@ class LayoutNGMixin : public Base {
void AddLayoutOverflowFromChildren() final;
- void AddOutlineRects(Vector<LayoutRect>&,
- const LayoutPoint& additional_offset,
+ void AddOutlineRects(Vector<PhysicalRect>&,
+ const PhysicalOffset& additional_offset,
NGOutlineType) const final;
+ bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final;
+
const NGPhysicalBoxFragment* CurrentFragment() const final;
base::Optional<LayoutUnit> FragmentBaseline(NGBaselineAlgorithmType) const;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
index 6df5ff54c1b..d6756cc09ba 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_caption.cc
@@ -37,8 +37,8 @@ void LayoutNGTableCaption::CalculateAndSetMargins(
LayoutUnit caption_inline_size_in_cb_writing_mode = box_fragment.InlineSize();
LayoutUnit available_inline_size_in_cb_writing_mode =
- ToNGPhysicalSize(constraint_space.AvailableSize(),
- constraint_space.GetWritingMode())
+ ToPhysicalSize(constraint_space.AvailableSize(),
+ constraint_space.GetWritingMode())
.ConvertToLogical(containing_block_style.GetWritingMode())
.inline_size;
@@ -62,7 +62,7 @@ void LayoutNGTableCaption::UpdateBlockLayout(bool relayout_children) {
scoped_refptr<const NGLayoutResult> result =
NGBlockNode(this).Layout(constraint_space);
- CalculateAndSetMargins(constraint_space, *result->PhysicalFragment());
+ CalculateAndSetMargins(constraint_space, result->PhysicalFragment());
// Tell legacy layout there were abspos descendents we couldn't place. We know
// we have to pass up to legacy here because this method is legacy's entry
@@ -70,15 +70,15 @@ void LayoutNGTableCaption::UpdateBlockLayout(bool relayout_children) {
// UpdateBlockLayout, it would have packaged this LayoutObject into
// NGBlockNode and called Layout on that.
for (NGOutOfFlowPositionedDescendant descendant :
- result->OutOfFlowPositionedDescendants())
- descendant.node.UseOldOutOfFlowPositioning();
+ result->PhysicalFragment().OutOfFlowPositionedDescendants())
+ descendant.node.UseLegacyOutOfFlowPositioning();
// The parent table sometimes changes the caption's position after laying it
// out. So there's no point in setting the fragment's offset here;
// NGBoxFragmentPainter::Paint will have to handle it until table layout is
// implemented in NG, in which case that algorithm will set each child's
// offsets. See https://crbug.com/788590 for more info.
- DCHECK(!result->PhysicalFragment()->IsPlacedByLayoutNG())
+ DCHECK(!result->PhysicalFragment().IsPlacedByLayoutNG())
<< "Only a table should be placing table caption fragments and the ng "
"table algorithm doesn't exist yet!";
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
index b1e68657111..d38d1a2bc23 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/layout_ng_table_cell.cc
@@ -29,8 +29,8 @@ void LayoutNGTableCell::UpdateBlockLayout(bool relayout_children) {
NGBlockNode(this).Layout(constraint_space);
for (NGOutOfFlowPositionedDescendant descendant :
- result->OutOfFlowPositionedDescendants())
- descendant.node.UseOldOutOfFlowPositioning();
+ result->PhysicalFragment().OutOfFlowPositionedDescendants())
+ descendant.node.UseLegacyOutOfFlowPositioning();
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc
new file mode 100644
index 00000000000..6938996dcdf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h"
+
+#include "third_party/blink/renderer/core/layout/layout_text.h"
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
+
+namespace blink {
+
+LayoutNGInsideListMarker::LayoutNGInsideListMarker(Element* element)
+ : LayoutInline(element) {}
+
+LayoutNGInsideListMarker* LayoutNGInsideListMarker::CreateAnonymous(
+ Document* document) {
+ LayoutNGInsideListMarker* object = new LayoutNGInsideListMarker(nullptr);
+ object->SetDocumentForAnonymous(document);
+ return object;
+}
+
+bool LayoutNGInsideListMarker::IsOfType(LayoutObjectType type) const {
+ return type == kLayoutObjectNGInsideListMarker ||
+ LayoutInline::IsOfType(type);
+}
+
+PositionWithAffinity LayoutNGInsideListMarker::PositionForPoint(
+ const LayoutPoint&) const {
+ return CreatePositionWithAffinity(0);
+}
+
+} // namespace blink
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
new file mode 100644
index 00000000000..9197de75027
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_INSIDE_LIST_MARKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_INSIDE_LIST_MARKER_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
+
+namespace blink {
+
+class Document;
+
+// A LayoutObject subclass for inside-positioned list markers in LayoutNG.
+class CORE_EXPORT LayoutNGInsideListMarker final : public LayoutInline {
+ public:
+ explicit LayoutNGInsideListMarker(Element*);
+ static LayoutNGInsideListMarker* CreateAnonymous(Document*);
+
+ const char* GetName() const override { return "LayoutNGInsideListMarker"; }
+
+#if DCHECK_IS_ON()
+ void AddChild(LayoutObject* new_child, LayoutObject* before_child) override {
+ // List marker should have at most one child.
+ DCHECK(!FirstChild());
+ LayoutInline::AddChild(new_child, before_child);
+ }
+#endif
+
+ private:
+ bool IsOfType(LayoutObjectType) const override;
+ PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
+};
+
+DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGInsideListMarker,
+ IsLayoutNGInsideListMarker());
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_LIST_LAYOUT_NG_INSIDE_LIST_MARKER_H_
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 1f89a1e9042..7a6416b5e15 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
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_list_marker.h"
#include "third_party/blink/renderer/core/layout/list_marker_text.h"
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker_image.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -115,7 +116,7 @@ void LayoutNGListItem::UpdateMarkerText() {
void LayoutNGListItem::UpdateMarker() {
const ComputedStyle& style = StyleRef();
- if (style.ListStyleType() == EListStyleType::kNone) {
+ if (style.ListStyleType() == EListStyleType::kNone && !IsMarkerImage()) {
DestroyMarker();
marker_type_ = kStatic;
is_marker_text_updated_ = true;
@@ -128,7 +129,7 @@ void LayoutNGListItem::UpdateMarker() {
if (marker_ && !marker_->IsLayoutInline())
DestroyMarker();
if (!marker_)
- marker_ = LayoutInline::CreateAnonymous(&GetDocument());
+ marker_ = LayoutNGInsideListMarker::CreateAnonymous(&GetDocument());
marker_style = ComputedStyle::CreateAnonymousStyleWithDisplay(
style, EDisplay::kInline);
auto margins =
@@ -162,6 +163,21 @@ void LayoutNGListItem::UpdateMarker() {
}
}
+LayoutNGListItem* LayoutNGListItem::FromMarker(const LayoutObject& marker) {
+ DCHECK(marker.IsLayoutNGListMarkerIncludingInside());
+ for (LayoutObject* parent = marker.Parent(); parent;
+ parent = parent->Parent()) {
+ if (parent->IsLayoutNGListItem()) {
+ DCHECK(ToLayoutNGListItem(parent)->Marker() == &marker);
+ return ToLayoutNGListItem(parent);
+ }
+ // These DCHECKs are not critical but to ensure we cover all cases we know.
+ DCHECK(parent->IsAnonymous());
+ DCHECK(parent->IsLayoutBlockFlow() || parent->IsLayoutFlowThread());
+ }
+ return nullptr;
+}
+
int LayoutNGListItem::Value() const {
DCHECK(GetNode());
return ordinal_.Value(*GetNode());
@@ -170,6 +186,12 @@ int LayoutNGListItem::Value() const {
LayoutNGListItem::MarkerType LayoutNGListItem::MarkerText(
StringBuilder* text,
MarkerTextFormat format) const {
+ if (IsMarkerImage()) {
+ if (format == kWithSuffix)
+ text->Append(' ');
+ return kStatic;
+ }
+
const ComputedStyle& style = StyleRef();
switch (style.ListStyleType()) {
case EListStyleType::kNone:
@@ -259,10 +281,20 @@ String LayoutNGListItem::MarkerTextWithoutSuffix() const {
return text.ToString();
}
+String LayoutNGListItem::TextAlternative(const LayoutObject& marker) {
+ // For accessibility, return the marker string in the logical order even in
+ // RTL, reflecting speech order.
+ if (LayoutNGListItem* list_item = FromMarker(marker))
+ return list_item->MarkerTextWithSuffix();
+ return g_empty_string;
+}
+
void LayoutNGListItem::UpdateMarkerContentIfNeeded() {
DCHECK(marker_);
LayoutObject* child = marker_->SlowFirstChild();
+ // There should be at most one child.
+ DCHECK(!child || !child->SlowFirstChild());
if (IsMarkerImage()) {
StyleImage* list_style_image = StyleRef().ListStyleImage();
if (child) {
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
index 204a7ef83a8..c56eaa614e6 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
@@ -22,6 +22,9 @@ class CORE_EXPORT LayoutNGListItem final : public LayoutNGBlockFlow {
String MarkerTextWithSuffix() const;
String MarkerTextWithoutSuffix() const;
+ // Marker text with suffix, e.g. "1. ", for use in accessibility.
+ static String TextAlternative(const LayoutObject& marker);
+
LayoutObject* Marker() const { return marker_; }
bool IsMarkerImage() const {
return StyleRef().ListStyleImage() &&
@@ -40,6 +43,9 @@ class CORE_EXPORT LayoutNGListItem final : public LayoutNGBlockFlow {
LayoutObject* SymbolMarkerLayoutText() const;
static const LayoutObject* FindSymbolMarkerLayoutText(const LayoutObject*);
+ // Find the LayoutNGListItem from a marker.
+ static LayoutNGListItem* FromMarker(const LayoutObject& marker);
+
const char* GetName() const override { return "LayoutNGListItem"; }
private:
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item_test.cc
new file mode 100644
index 00000000000..2c12d7080b3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
+
+#include "third_party/blink/renderer/core/dom/dom_token_list.h"
+#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
+
+namespace blink {
+
+class LayoutNGListItemTest : public NGLayoutTest {};
+
+namespace {
+
+TEST_F(LayoutNGListItemTest, InsideWithFirstLine) {
+ SetBodyInnerHTML(R"HTML(
+ <!DOCTYPE html>
+ <style>
+ li {
+ list-style-position: inside;
+ }
+ .after::first-line {
+ background: yellow;
+ }
+ </style>
+ <div id=container>
+ <ul>
+ <li id=item>test</li>
+ </ul>
+ </div>
+ )HTML");
+
+ Element* container = GetElementById("container");
+ container->classList().Add("after");
+ GetDocument().UpdateStyleAndLayoutTree();
+
+ // The list-item should have a marker.
+ LayoutNGListItem* list_item =
+ ToLayoutNGListItem(GetLayoutObjectByElementId("item"));
+ LayoutObject* marker = list_item->Marker();
+ EXPECT_TRUE(marker);
+ // The marker should have only 1 child.
+ LayoutObject* marker_child = marker->SlowFirstChild();
+ EXPECT_TRUE(marker_child);
+ EXPECT_FALSE(marker_child->NextSibling());
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc
index a7b13fbcd69..bf9f4090fc3 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.cc
@@ -23,54 +23,21 @@ bool LayoutNGListMarker::IsOfType(LayoutObjectType type) const {
LayoutNGMixin<LayoutBlockFlow>::IsOfType(type);
}
-bool LayoutNGListMarker::IsListMarkerWrapperForBlockContent(
- const LayoutObject& object) {
- const auto* block_flow = DynamicTo<LayoutBlockFlow>(object);
- if (!object.IsAnonymous() || !block_flow)
- return false;
- if (const LayoutObject* child = block_flow->FirstChild()) {
- return child->IsLayoutNGListMarker() &&
- // The anonymous box should not have other children.
- // e.g., <li>text<div>block</div></li>
- // In this case, inline layout can handle the list marker.
- !child->NextSibling();
- }
- return false;
-}
-
-// The LayoutNGListItem this marker belongs to.
-LayoutNGListItem* LayoutNGListMarker::ListItem() const {
- for (LayoutObject* parent = Parent(); parent; parent = parent->Parent()) {
- if (parent->IsLayoutNGListItem()) {
- DCHECK(ToLayoutNGListItem(parent)->Marker() == this);
- return ToLayoutNGListItem(parent);
- }
- // These DCHECKs are not critical but to ensure we cover all cases we know.
- DCHECK(parent->IsAnonymous());
- DCHECK(parent->IsLayoutBlockFlow() || parent->IsLayoutFlowThread());
- }
- return nullptr;
-}
-
void LayoutNGListMarker::WillCollectInlines() {
- if (LayoutNGListItem* list_item = ListItem())
+ if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this))
list_item->UpdateMarkerTextIfNeeded();
}
bool LayoutNGListMarker::IsContentImage() const {
- return ListItem()->IsMarkerImage();
+ if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this))
+ return list_item->IsMarkerImage();
+ return false;
}
LayoutObject* LayoutNGListMarker::SymbolMarkerLayoutText() const {
- return ListItem()->SymbolMarkerLayoutText();
-}
-
-String LayoutNGListMarker::TextAlternative() const {
- // Compute from the list item, in the logical order even in RTL, reflecting
- // speech order.
- if (LayoutNGListItem* list_item = ListItem())
- return list_item->MarkerTextWithSuffix();
- return g_empty_string;
+ if (LayoutNGListItem* list_item = LayoutNGListItem::FromMarker(*this))
+ return list_item->SymbolMarkerLayoutText();
+ return nullptr;
}
bool LayoutNGListMarker::NeedsOccupyWholeLine() const {
@@ -86,4 +53,9 @@ bool LayoutNGListMarker::NeedsOccupyWholeLine() const {
return false;
}
+PositionWithAffinity LayoutNGListMarker::PositionForPoint(
+ const LayoutPoint&) const {
+ return CreatePositionWithAffinity(0);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h
index 469de1bebe9..74eb29ed096 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h
@@ -12,40 +12,27 @@
namespace blink {
class Document;
-class LayoutNGListItem;
-// A LayoutObject subclass for list markers in LayoutNG.
+// A LayoutObject subclass for outside-positioned list markers in LayoutNG.
class CORE_EXPORT LayoutNGListMarker final
: public LayoutNGMixin<LayoutBlockFlow> {
public:
explicit LayoutNGListMarker(Element*);
static LayoutNGListMarker* CreateAnonymous(Document*);
- // True if the LayoutObject is a list marker wrapper for block content.
- //
- // Because a list marker in LayoutNG is an inline block, and because CSS
- // defines all children of a box must be either inline level or block level,
- // when the content of an list item is block level, the list marker is wrapped
- // in an anonymous block box. This function determines such an anonymous box.
- static bool IsListMarkerWrapperForBlockContent(const LayoutObject&);
-
void WillCollectInlines() override;
bool IsContentImage() const;
LayoutObject* SymbolMarkerLayoutText() const;
- // Marker text with suffix, e.g. "1. ", for use in accessibility.
- String TextAlternative() const;
-
const char* GetName() const override { return "LayoutNGListMarker"; }
- LayoutNGListItem* ListItem() const;
-
bool NeedsOccupyWholeLine() const;
private:
bool IsOfType(LayoutObjectType) const override;
+ PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
};
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutNGListMarker, IsLayoutNGListMarker());
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 dd01187cac8..0231815b925 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
@@ -47,7 +47,7 @@ scoped_refptr<const NGLayoutResult> NGUnpositionedListMarker::Layout(
scoped_refptr<const NGLayoutResult> marker_layout_result =
marker_node.LayoutAtomicInline(parent_space, parent_style, baseline_type,
parent_space.UseFirstLineStyle());
- DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment());
+ DCHECK(marker_layout_result);
return marker_layout_result;
}
@@ -55,7 +55,7 @@ bool NGUnpositionedListMarker::AddToBox(
const NGConstraintSpace& space,
FontBaseline baseline_type,
const NGPhysicalFragment& content,
- NGLogicalOffset* content_offset,
+ LogicalOffset* content_offset,
NGBoxFragmentBuilder* container_builder,
const NGBoxStrut& border_scrollbar_padding) const {
// Baselines from two different writing-mode cannot be aligned.
@@ -90,16 +90,15 @@ bool NGUnpositionedListMarker::AddToBox(
// Layout the list marker.
scoped_refptr<const NGLayoutResult> marker_layout_result =
Layout(space, container_builder->Style(), baseline_type);
- DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment());
+ DCHECK(marker_layout_result);
const NGPhysicalBoxFragment& marker_physical_fragment =
- To<NGPhysicalBoxFragment>(*marker_layout_result->PhysicalFragment());
+ To<NGPhysicalBoxFragment>(marker_layout_result->PhysicalFragment());
// Compute the inline offset of the marker.
NGBoxFragment marker_fragment(space.GetWritingMode(), space.Direction(),
marker_physical_fragment);
- NGLogicalOffset marker_offset(
- InlineOffset(marker_fragment.Size().inline_size),
- content_offset->block_offset);
+ LogicalOffset marker_offset(InlineOffset(marker_fragment.Size().inline_size),
+ content_offset->block_offset);
// Adjust the block offset to align baselines of the marker and the content.
NGLineHeightMetrics marker_metrics = marker_fragment.BaselineMetrics(
@@ -117,7 +116,7 @@ bool NGUnpositionedListMarker::AddToBox(
marker_offset.block_offset);
DCHECK(container_builder);
- container_builder->AddChild(*marker_layout_result, marker_offset);
+ container_builder->AddChild(marker_physical_fragment, marker_offset);
return true;
}
@@ -129,18 +128,18 @@ LayoutUnit NGUnpositionedListMarker::AddToBoxWithoutLineBoxes(
// Layout the list marker.
scoped_refptr<const NGLayoutResult> marker_layout_result =
Layout(space, container_builder->Style(), baseline_type);
- DCHECK(marker_layout_result && marker_layout_result->PhysicalFragment());
+ DCHECK(marker_layout_result);
const NGPhysicalBoxFragment& marker_physical_fragment =
- To<NGPhysicalBoxFragment>(*marker_layout_result->PhysicalFragment());
+ To<NGPhysicalBoxFragment>(marker_layout_result->PhysicalFragment());
// When there are no line boxes, marker is top-aligned to the list item.
// https://github.com/w3c/csswg-drafts/issues/2417
- NGLogicalSize marker_size =
+ LogicalSize marker_size =
marker_physical_fragment.Size().ConvertToLogical(space.GetWritingMode());
- NGLogicalOffset offset(InlineOffset(marker_size.inline_size), LayoutUnit());
+ LogicalOffset offset(InlineOffset(marker_size.inline_size), LayoutUnit());
DCHECK(container_builder);
- container_builder->AddChild(*marker_layout_result, offset);
+ container_builder->AddChild(marker_physical_fragment, offset);
return marker_size.block_size;
}
@@ -165,7 +164,7 @@ LayoutUnit NGUnpositionedListMarker::ComputeIntrudedFloatOffset(
border_scrollbar_padding.inline_end;
NGLayoutOpportunity opportunity =
space.ExclusionSpace().FindLayoutOpportunity(
- origin_offset, available_size, NGLogicalSize());
+ origin_offset, available_size, LogicalSize());
DCHECK(marker_layout_object_);
const TextDirection direction = marker_layout_object_->StyleRef().Direction();
if (direction == TextDirection::kLtr) {
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
index 3948d11c5b7..166b5b09a4b 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h
@@ -22,7 +22,7 @@ class NGBoxFragmentBuilder;
class NGLayoutResult;
class NGPhysicalFragment;
-struct NGLogicalOffset;
+struct LogicalOffset;
// Represents an unpositioned list marker.
//
@@ -50,7 +50,7 @@ class CORE_EXPORT NGUnpositionedListMarker final {
bool AddToBox(const NGConstraintSpace&,
FontBaseline,
const NGPhysicalFragment& content,
- NGLogicalOffset* content_offset,
+ LogicalOffset* content_offset,
NGBoxFragmentBuilder*,
const NGBoxStrut&) const;
@@ -62,6 +62,10 @@ class CORE_EXPORT NGUnpositionedListMarker final {
NGBoxFragmentBuilder*) const;
LayoutUnit InlineOffset(const LayoutUnit marker_inline_size) const;
+ bool operator==(const NGUnpositionedListMarker& other) const {
+ return marker_layout_object_ == other.marker_layout_object_;
+ }
+
private:
bool IsImage() const;
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 ce2e62a1f08..1a04dc32ac9 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
@@ -19,15 +19,36 @@ namespace blink {
namespace {
+// Tables need special handling, unfortunately. The code in this file assumes
+// that if an element has a height or width specified, that's what its final
+// height/width will be. Tables don't follow this pattern though; they treat
+// specified height/width as a second min-height or min-width.
+bool IsTable(const ComputedStyle& style) {
+ return style.Display() == EDisplay::kTable ||
+ style.Display() == EDisplay::kInlineTable;
+}
+
+inline Length TableAwareHeight(const ComputedStyle& style) {
+ if (IsTable(style))
+ return Length::Auto();
+ return style.Height();
+}
+
+inline Length TableAwareWidth(const ComputedStyle& style) {
+ if (IsTable(style))
+ return Length::Auto();
+ return style.Width();
+}
+
bool AbsoluteHorizontalNeedsEstimate(const ComputedStyle& style) {
- const Length& width = style.Width();
+ const Length& width = TableAwareWidth(style);
return width.IsIntrinsic() || style.MinWidth().IsIntrinsic() ||
style.MaxWidth().IsIntrinsic() ||
(width.IsAuto() && (style.Left().IsAuto() || style.Right().IsAuto()));
}
bool AbsoluteVerticalNeedsEstimate(const ComputedStyle& style) {
- const Length& height = style.Height();
+ const Length& height = TableAwareHeight(style);
return height.IsIntrinsic() || style.MinHeight().IsIntrinsic() ||
style.MaxHeight().IsIntrinsic() ||
(height.IsAuto() && (style.Top().IsAuto() || style.Bottom().IsAuto()));
@@ -56,15 +77,24 @@ bool IsTopDominant(const WritingMode container_writing_mode,
LayoutUnit ResolveMinWidth(const NGConstraintSpace& space,
const ComputedStyle& style,
const NGBoxStrut& border_padding,
- const base::Optional<MinMaxSize>& child_minmax,
- const Length& width) {
+ const base::Optional<MinMaxSize>& child_minmax) {
+ const Length& min_width = style.MinWidth();
if (space.GetWritingMode() == WritingMode::kHorizontalTb) {
- return ResolveMinInlineLength(space, style, border_padding, child_minmax,
- width, LengthResolvePhase::kLayout);
+ LayoutUnit resolved_min_width =
+ ResolveMinInlineLength(space, style, border_padding, child_minmax,
+ min_width, LengthResolvePhase::kLayout);
+ if (!IsTable(style))
+ return resolved_min_width;
+ Length table_width = style.Width();
+ if (table_width.IsAuto())
+ return resolved_min_width;
+ LayoutUnit resolved_width = ResolveMainInlineLength(
+ space, style, border_padding, child_minmax, table_width);
+ return std::max(resolved_min_width, resolved_width);
}
LayoutUnit computed_width =
child_minmax.has_value() ? child_minmax->max_size : LayoutUnit();
- return ResolveMinBlockLength(space, style, border_padding, width,
+ return ResolveMinBlockLength(space, style, border_padding, min_width,
computed_width, LengthResolvePhase::kLayout);
}
@@ -101,15 +131,21 @@ LayoutUnit ResolveMainWidth(const NGConstraintSpace& space,
LayoutUnit ResolveMinHeight(const NGConstraintSpace& space,
const ComputedStyle& style,
const NGBoxStrut& border_padding,
- const base::Optional<MinMaxSize>& child_minmax,
- const Length& height) {
+ const base::Optional<MinMaxSize>& child_minmax) {
+ const Length& min_height = style.MinHeight();
if (space.GetWritingMode() != WritingMode::kHorizontalTb) {
- return ResolveMinInlineLength(space, style, border_padding, child_minmax,
- height, LengthResolvePhase::kLayout);
+ LayoutUnit resolved_min_height =
+ ResolveMinInlineLength(space, style, border_padding, child_minmax,
+ min_height, LengthResolvePhase::kLayout);
+ if (!IsTable(style))
+ return resolved_min_height;
+ LayoutUnit resolved_height = ResolveMainInlineLength(
+ space, style, border_padding, child_minmax, style.Height());
+ return std::max(resolved_min_height, resolved_height);
}
LayoutUnit computed_height =
child_minmax.has_value() ? child_minmax->max_size : LayoutUnit();
- return ResolveMinBlockLength(space, style, border_padding, height,
+ return ResolveMinBlockLength(space, style, border_padding, min_height,
computed_height, LengthResolvePhase::kLayout);
}
@@ -229,9 +265,9 @@ void ComputeAbsoluteHorizontal(const NGConstraintSpace& space,
if (!style.Right().IsAuto())
right = MinimumValueForLength(style.Right(), percentage_width);
base::Optional<LayoutUnit> width = incoming_width;
- NGPhysicalSize container_size =
- ToNGPhysicalSize(space.AvailableSize(), space.GetWritingMode());
- DCHECK_NE(container_size.width, NGSizeIndefinite);
+ PhysicalSize container_size =
+ ToPhysicalSize(space.AvailableSize(), space.GetWritingMode());
+ DCHECK_NE(container_size.width, kIndefiniteSize);
// Solving the equation:
// left + marginLeft + width + marginRight + right = container width
@@ -332,21 +368,25 @@ void ComputeAbsoluteHorizontal(const NGConstraintSpace& space,
container_size.width - *left - *right - *margin_left - *margin_right;
}
+#if DCHECK_IS_ON()
// The DCHECK is useful, but only holds true when not saturated.
if (!(left->MightBeSaturated() || right->MightBeSaturated() ||
width->MightBeSaturated() || margin_left->MightBeSaturated() ||
- margin_right->MightBeSaturated()))
+ margin_right->MightBeSaturated() ||
+ container_size.width.MightBeSaturated())) {
DCHECK_EQ(container_size.width,
*left + *right + *margin_left + *margin_right + *width);
+ }
+#endif // #if DCHECK_IS_ON()
// If calculated width is outside of min/max constraints,
// rerun the algorithm with constrained width.
- LayoutUnit min = ResolveMinWidth(space, style, border_padding, child_minmax,
- style.MinWidth());
+ LayoutUnit min = ResolveMinWidth(space, style, border_padding, child_minmax);
LayoutUnit max = ResolveMaxWidth(space, style, border_padding, child_minmax,
style.MaxWidth());
- if (width != ConstrainByMinMax(*width, min, max)) {
- width = ConstrainByMinMax(*width, min, max);
+ LayoutUnit constrained_width = ConstrainByMinMax(*width, min, max);
+ if (width != constrained_width) {
+ width = constrained_width;
// Because this function only changes "width" when it's not already
// set, it is safe to recursively call ourselves here because on the
// second call it is guaranteed to be within min..max.
@@ -400,9 +440,9 @@ void ComputeAbsoluteVertical(const NGConstraintSpace& space,
bottom = MinimumValueForLength(style.Bottom(), percentage_height);
base::Optional<LayoutUnit> height = incoming_height;
- NGPhysicalSize container_size =
- ToNGPhysicalSize(space.AvailableSize(), space.GetWritingMode());
- DCHECK_NE(container_size.height, NGSizeIndefinite);
+ PhysicalSize container_size =
+ ToPhysicalSize(space.AvailableSize(), space.GetWritingMode());
+ DCHECK_NE(container_size.height, kIndefiniteSize);
// Solving the equation:
// top + marginTop + height + marginBottom + bottom
@@ -493,17 +533,21 @@ void ComputeAbsoluteVertical(const NGConstraintSpace& space,
height =
container_size.height - *top - *bottom - *margin_top - *margin_bottom;
}
+
+#if DCHECK_IS_ON()
// The DCHECK is useful, but only holds true when not saturated.
if (!(top->MightBeSaturated() || bottom->MightBeSaturated() ||
height->MightBeSaturated() || margin_top->MightBeSaturated() ||
- margin_bottom->MightBeSaturated())) {
+ margin_bottom->MightBeSaturated() ||
+ container_size.height.MightBeSaturated())) {
DCHECK_EQ(container_size.height,
*top + *bottom + *margin_top + *margin_bottom + *height);
}
+#endif // #if DCHECK_IS_ON()
+
// If calculated height is outside of min/max constraints,
// rerun the algorithm with constrained width.
- LayoutUnit min = ResolveMinHeight(space, style, border_padding, child_minmax,
- style.MinHeight());
+ LayoutUnit min = ResolveMinHeight(space, style, border_padding, child_minmax);
LayoutUnit max = ResolveMaxHeight(space, style, border_padding, child_minmax,
style.MaxHeight());
if (height != ConstrainByMinMax(*height, min, max)) {
@@ -603,13 +647,13 @@ NGAbsolutePhysicalPosition ComputePartialAbsoluteWithChildInlineSize(
const NGBoxStrut& border_padding,
const NGStaticPosition& static_position,
const base::Optional<MinMaxSize>& child_minmax,
- const base::Optional<NGLogicalSize>& replaced_size,
+ const base::Optional<LogicalSize>& replaced_size,
const WritingMode container_writing_mode,
const TextDirection container_direction) {
NGAbsolutePhysicalPosition position;
if (style.IsHorizontalWritingMode()) {
base::Optional<LayoutUnit> width;
- if (!style.Width().IsAuto()) {
+ if (!TableAwareWidth(style).IsAuto()) {
width = ResolveMainWidth(space, style, border_padding, child_minmax,
style.Width());
} else if (replaced_size.has_value()) {
@@ -620,7 +664,7 @@ NGAbsolutePhysicalPosition ComputePartialAbsoluteWithChildInlineSize(
container_writing_mode, container_direction, &position);
} else {
base::Optional<LayoutUnit> height;
- if (!style.Height().IsAuto()) {
+ if (!TableAwareHeight(style).IsAuto()) {
height = ResolveMainHeight(space, style, border_padding, child_minmax,
style.Height());
} else if (replaced_size.has_value()) {
@@ -639,7 +683,7 @@ void ComputeFullAbsoluteWithChildBlockSize(
const NGBoxStrut& border_padding,
const NGStaticPosition& static_position,
const base::Optional<LayoutUnit>& child_block_size,
- const base::Optional<NGLogicalSize>& replaced_size,
+ const base::Optional<LogicalSize>& replaced_size,
const WritingMode container_writing_mode,
const TextDirection container_direction,
NGAbsolutePhysicalPosition* position) {
@@ -653,7 +697,7 @@ void ComputeFullAbsoluteWithChildBlockSize(
}
if (style.IsHorizontalWritingMode()) {
base::Optional<LayoutUnit> height;
- if (!style.Height().IsAuto()) {
+ if (!TableAwareHeight(style).IsAuto()) {
height = ResolveMainHeight(space, style, border_padding, child_minmax,
style.Height());
} else if (replaced_size.has_value()) {
@@ -664,7 +708,7 @@ void ComputeFullAbsoluteWithChildBlockSize(
container_writing_mode, container_direction, position);
} else {
base::Optional<LayoutUnit> width;
- if (!style.Width().IsAuto()) {
+ if (!TableAwareWidth(style).IsAuto()) {
width = ResolveMainWidth(space, style, border_padding, child_minmax,
style.Width());
} else if (replaced_size.has_value()) {
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
index 03a328c2632..67029cd9917 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h
@@ -7,9 +7,9 @@
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
namespace blink {
@@ -21,7 +21,7 @@ struct NGStaticPosition;
struct CORE_EXPORT NGAbsolutePhysicalPosition {
NGPhysicalBoxStrut inset;
- NGPhysicalSize size;
+ PhysicalSize size;
NGPhysicalBoxStrut margins;
String ToString() const;
};
@@ -65,18 +65,18 @@ ComputePartialAbsoluteWithChildInlineSize(
const NGBoxStrut& border_padding,
const NGStaticPosition&,
const base::Optional<MinMaxSize>& child_minmax,
- const base::Optional<NGLogicalSize>& replaced_size,
+ const base::Optional<LogicalSize>& replaced_size,
const WritingMode container_writing_mode,
const TextDirection container_direction);
-// Compute rest of NGPhysicalRect that depends on child's block_size.
+// Compute rest of PhysicalRect that depends on child's block_size.
CORE_EXPORT void ComputeFullAbsoluteWithChildBlockSize(
const NGConstraintSpace&,
const ComputedStyle&,
const NGBoxStrut& border_padding,
const NGStaticPosition&,
const base::Optional<LayoutUnit>& child_block_size,
- const base::Optional<NGLogicalSize>& replaced_size,
+ const base::Optional<LogicalSize>& replaced_size,
const WritingMode container_writing_mode,
const TextDirection container_direction,
NGAbsolutePhysicalPosition* position);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
index 7d33efb4ba0..20c8a4a4a50 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_absolute_utils_test.cc
@@ -35,7 +35,7 @@ class NGAbsoluteUtilsTest : public testing::Test {
style_->SetBorderTopStyle(EBorderStyle::kSolid);
style_->SetBorderBottomStyle(EBorderStyle::kSolid);
style_->SetBoxSizing(EBoxSizing::kBorderBox);
- container_size_ = NGLogicalSize(LayoutUnit(200), LayoutUnit(300));
+ container_size_ = LogicalSize(LayoutUnit(200), LayoutUnit(300));
ltr_space_ =
CreateConstraintSpace(TextDirection::kLtr, WritingMode::kHorizontalTb);
@@ -90,7 +90,7 @@ class NGAbsoluteUtilsTest : public testing::Test {
}
scoped_refptr<ComputedStyle> style_;
- NGLogicalSize container_size_;
+ LogicalSize container_size_;
NGConstraintSpace ltr_space_;
NGConstraintSpace rtl_space_;
NGConstraintSpace vlr_space_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc
index 5a73fc9d77c..bbc33e3ecf2 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.cc
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
@@ -28,16 +29,34 @@ void NGBaseLayoutAlgorithmTest::AdvanceToLayoutPhase() {
GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInPerformLayout);
}
+scoped_refptr<const NGPhysicalBoxFragment>
+NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(
+ NGBlockNode node,
+ const NGConstraintSpace& space,
+ const NGBreakToken* break_token) {
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(space, node);
+
+ scoped_refptr<const NGLayoutResult> result =
+ NGBlockLayoutAlgorithm(
+ {node, fragment_geometry, space, To<NGBlockBreakToken>(break_token)})
+ .Layout();
+
+ return To<NGPhysicalBoxFragment>(&result->PhysicalFragment());
+}
+
std::pair<scoped_refptr<const NGPhysicalBoxFragment>, NGConstraintSpace>
NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithmForElement(Element* element) {
auto* block_flow = To<LayoutBlockFlow>(element->GetLayoutObject());
NGBlockNode node(block_flow);
NGConstraintSpace space =
NGConstraintSpace::CreateFromLayoutObject(*block_flow);
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(space, node);
scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, space).Layout();
- return std::make_pair(To<NGPhysicalBoxFragment>(result->PhysicalFragment()),
+ NGBlockLayoutAlgorithm({node, fragment_geometry, space}).Layout();
+ return std::make_pair(To<NGPhysicalBoxFragment>(&result->PhysicalFragment()),
std::move(space));
}
@@ -57,7 +76,7 @@ const NGPhysicalBoxFragment* NGBaseLayoutAlgorithmTest::CurrentFragmentFor(
}
const NGPhysicalBoxFragment* FragmentChildIterator::NextChild(
- NGPhysicalOffset* fragment_offset) {
+ PhysicalOffset* fragment_offset) {
if (!parent_)
return nullptr;
if (index_ >= parent_->Children().size())
@@ -77,12 +96,12 @@ const NGPhysicalBoxFragment* FragmentChildIterator::NextChild(
NGConstraintSpace ConstructBlockLayoutTestConstraintSpace(
WritingMode writing_mode,
TextDirection direction,
- NGLogicalSize size,
+ LogicalSize size,
bool shrink_to_fit,
bool is_new_formatting_context,
LayoutUnit fragmentainer_space_available) {
NGFragmentationType block_fragmentation =
- fragmentainer_space_available != NGSizeIndefinite
+ fragmentainer_space_available != kIndefiniteSize
? NGFragmentationType::kFragmentColumn
: NGFragmentationType::kFragmentNone;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h
index b70b1fe114d..8459fe2838a 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h
@@ -6,7 +6,7 @@
#define NG_BASE_LAYOUT_ALGORITHM_TEST_H_
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -18,6 +18,7 @@ namespace blink {
class Element;
class LayoutNGBlockFlow;
+class NGBlockNode;
class NGPhysicalBoxFragment;
// Base class for all LayoutNG Algorithms unit test classes.
@@ -32,6 +33,11 @@ class NGBaseLayoutAlgorithmTest
// RunBlockLayoutAlgorithmForElement.
void AdvanceToLayoutPhase();
+ scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
+ NGBlockNode node,
+ const NGConstraintSpace& space,
+ const NGBreakToken* break_token = nullptr);
+
std::pair<scoped_refptr<const NGPhysicalBoxFragment>, NGConstraintSpace>
RunBlockLayoutAlgorithmForElement(Element* element);
@@ -55,7 +61,7 @@ class FragmentChildIterator {
}
const NGPhysicalBoxFragment* NextChild(
- NGPhysicalOffset* fragment_offset = nullptr);
+ PhysicalOffset* fragment_offset = nullptr);
private:
const NGPhysicalBoxFragment* parent_;
@@ -65,10 +71,10 @@ class FragmentChildIterator {
NGConstraintSpace ConstructBlockLayoutTestConstraintSpace(
WritingMode writing_mode,
TextDirection direction,
- NGLogicalSize size,
+ LogicalSize size,
bool shrink_to_fit = false,
bool is_new_formatting_context = false,
- LayoutUnit fragmentainer_space_available = NGSizeIndefinite);
+ LayoutUnit fragmentainer_space_available = kIndefiniteSize);
} // namespace blink
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 005058185a1..430b8bdde8a 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
@@ -73,7 +73,7 @@ const NGInlineBreakToken* NGBlockBreakToken::InlineBreakTokenFor(
return nullptr;
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String NGBlockBreakToken::ToString() const {
StringBuilder string_builder;
@@ -84,6 +84,6 @@ String NGBlockBreakToken::ToString() const {
return string_builder.ToString();
}
-#endif // NDEBUG
+#endif // DCHECK_IS_ON()
} // namespace blink
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 bf2dd8c06d3..18a44a94fee 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
@@ -114,7 +114,7 @@ class CORE_EXPORT NGBlockBreakToken final : public NGBreakToken {
const NGInlineBreakToken* InlineBreakTokenFor(const NGLayoutInputNode&) const;
const NGInlineBreakToken* InlineBreakTokenFor(const LayoutBox&) const;
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String ToString() const override;
#endif
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 743f78fa150..9cce428b4f7 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
@@ -15,6 +15,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_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"
@@ -35,6 +36,16 @@
namespace blink {
namespace {
+inline scoped_refptr<const NGLayoutResult> LayoutInflow(
+ const NGConstraintSpace& space,
+ const NGBreakToken* break_token,
+ NGLayoutInputNode* node,
+ NGInlineChildLayoutContext* context) {
+ auto* inline_node = DynamicTo<NGInlineNode>(node);
+ return inline_node ? inline_node->Layout(space, break_token, context)
+ : To<NGBlockNode>(node)->Layout(space, break_token);
+}
+
// Return true if a child is to be cleared past adjoining floats. These are
// floats that would otherwise (if 'clear' were 'none') be pulled down by the
// BFC block offset of the child. If the child is to clear floats, though, we
@@ -70,45 +81,13 @@ inline bool HasClearancePastAdjoiningFloats(NGFloatTypes adjoining_floats,
bool ApplyClearance(const NGConstraintSpace& constraint_space,
LayoutUnit* bfc_block_offset) {
if (constraint_space.HasClearanceOffset() &&
- (*bfc_block_offset < constraint_space.ClearanceOffset() ||
- constraint_space.ShouldForceClearance())) {
+ *bfc_block_offset < constraint_space.ClearanceOffset()) {
*bfc_block_offset = constraint_space.ClearanceOffset();
return true;
}
return false;
}
-// Returns if the resulting fragment should be considered an "empty block".
-// There is special casing for fragments like this, e.g. margins "collapse
-// through", etc.
-inline bool IsEmptyBlock(bool is_new_fc, const NGLayoutResult& layout_result) {
- // TODO(ikilpatrick): This should be a DCHECK.
- if (is_new_fc)
- return false;
-
- if (layout_result.BfcBlockOffset())
- return false;
-
-#if DCHECK_IS_ON()
- const NGPhysicalFragment& physical_fragment =
- *layout_result.PhysicalFragment();
- // This just checks that the fragments block size is actually zero. We can
- // assume that its in the same writing mode as its parent, as a different
- // writing mode child will be caught by the is_new_fc check.
- NGFragment fragment(physical_fragment.Style().GetWritingMode(),
- physical_fragment);
- DCHECK_EQ(LayoutUnit(), fragment.BlockSize());
-#endif
-
- return true;
-}
-
-// As above; for convenience if you have a child_space.
-inline bool IsEmptyBlock(const NGConstraintSpace& child_space,
- const NGLayoutResult& layout_result) {
- return IsEmptyBlock(child_space.IsNewFormattingContext(), layout_result);
-}
-
LayoutUnit LogicalFromBfcLineOffset(LayoutUnit child_bfc_line_offset,
LayoutUnit parent_bfc_line_offset,
LayoutUnit child_inline_size,
@@ -127,11 +106,11 @@ LayoutUnit LogicalFromBfcLineOffset(LayoutUnit child_bfc_line_offset,
return inline_offset;
}
-NGLogicalOffset LogicalFromBfcOffsets(const NGBfcOffset& child_bfc_offset,
- const NGBfcOffset& parent_bfc_offset,
- LayoutUnit child_inline_size,
- LayoutUnit parent_inline_size,
- TextDirection direction) {
+LogicalOffset LogicalFromBfcOffsets(const NGBfcOffset& child_bfc_offset,
+ const NGBfcOffset& parent_bfc_offset,
+ LayoutUnit child_inline_size,
+ LayoutUnit parent_inline_size,
+ TextDirection direction) {
LayoutUnit inline_offset = LogicalFromBfcLineOffset(
child_bfc_offset.line_offset, parent_bfc_offset.line_offset,
child_inline_size, parent_inline_size, direction);
@@ -164,13 +143,17 @@ void StopMarginCollapsing(EMarginCollapse collapse_value,
} // namespace
NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm(
- NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBlockBreakToken* break_token)
- : NGLayoutAlgorithm(node, space, break_token),
- is_resuming_(break_token && !break_token->IsBreakBefore()),
- exclusion_space_(space.ExclusionSpace()) {
- container_builder_.SetIsNewFormattingContext(space.IsNewFormattingContext());
+ 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_resuming_(IsResumingLayout(params.break_token)),
+ exclusion_space_(params.space.ExclusionSpace()) {
+ container_builder_.SetIsNewFormattingContext(
+ params.space.IsNewFormattingContext());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
// Define the destructor here, so that we can forward-declare more in the
@@ -183,22 +166,15 @@ void NGBlockLayoutAlgorithm::SetBoxType(NGPhysicalFragment::NGBoxType type) {
base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
- NGBoxStrut border_padding = ComputeBorders(ConstraintSpace(), Node()) +
- ComputePadding(ConstraintSpace(), Style());
- MinMaxSize sizes;
-
- // Size-contained elements don't consider their contents for intrinsic sizing.
- if (node_.ShouldApplySizeContainment()) {
- if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) {
- sizes =
- border_padding.InlineSum() + Node().GetScrollbarSizes().InlineSum();
- }
+ base::Optional<MinMaxSize> sizes = CalculateMinMaxSizesIgnoringChildren(
+ node_, border_scrollbar_padding_, input.size_type);
+ if (sizes)
return sizes;
- }
+ sizes.emplace();
LayoutUnit child_percentage_resolution_block_size =
CalculateChildPercentageBlockSizeForMinMax(
- ConstraintSpace(), Node(), border_padding,
+ ConstraintSpace(), Node(), border_padding_,
input.percentage_resolution_block_size);
const TextDirection direction = Style().Direction();
@@ -225,7 +201,7 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize(
float_left_inline_size + float_right_inline_size;
if (child_clear != EClear::kNone)
- sizes.max_size = std::max(sizes.max_size, float_inline_size);
+ sizes->max_size = std::max(sizes->max_size, float_inline_size);
if (child_clear == EClear::kBoth || child_clear == EClear::kLeft)
float_left_inline_size = LayoutUnit();
@@ -304,13 +280,13 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize(
// This is just a standard inflow child.
max_inline_contribution = child_sizes.max_size + margins.InlineSum();
}
- sizes.max_size = std::max(sizes.max_size, max_inline_contribution);
+ sizes->max_size = std::max(sizes->max_size, max_inline_contribution);
// The min inline contribution just assumes that floats are all on their own
// "line".
LayoutUnit min_inline_contribution =
child_sizes.min_size + margins.InlineSum();
- sizes.min_size = std::max(sizes.min_size, min_inline_contribution);
+ sizes->min_size = std::max(sizes->min_size, min_inline_contribution);
// Anything that isn't a float will create a new "line" resetting the float
// size trackers.
@@ -320,22 +296,22 @@ base::Optional<MinMaxSize> NGBlockLayoutAlgorithm::ComputeMinMaxSize(
}
}
- DCHECK_GE(sizes.min_size, LayoutUnit());
- DCHECK_LE(sizes.min_size, sizes.max_size) << Node().ToString();
+ DCHECK_GE(sizes->min_size, LayoutUnit());
+ DCHECK_LE(sizes->min_size, sizes->max_size) << Node().ToString();
if (input.size_type == NGMinMaxSizeType::kBorderBoxSize)
- sizes += border_padding.InlineSum() + node_.GetScrollbarSizes().InlineSum();
+ *sizes += border_scrollbar_padding_.InlineSum();
return sizes;
}
-NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
+LogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
const NGFragment& fragment,
LayoutUnit child_bfc_line_offset,
const base::Optional<LayoutUnit>& child_bfc_block_offset) {
LayoutUnit inline_size = container_builder_.Size().inline_size;
TextDirection direction = ConstraintSpace().Direction();
- if (child_bfc_block_offset) {
+ if (child_bfc_block_offset && container_builder_.BfcBlockOffset()) {
return LogicalFromBfcOffsets(
{child_bfc_line_offset, *child_bfc_block_offset}, ContainerBfcOffset(),
fragment.InlineSize(), inline_size, direction);
@@ -345,10 +321,9 @@ NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
child_bfc_line_offset, container_builder_.BfcLineOffset(),
fragment.InlineSize(), inline_size, direction);
- // If we've reached here, both the child and the current layout don't have a
- // BFC block offset yet. Children in this situation are always placed at a
- // logical block offset of 0.
- DCHECK(!container_builder_.BfcBlockOffset());
+ // If we've reached here, either the parent, or the child don't have a BFC
+ // block-offset yet. Children in this situation are always placed at a
+ // logical block-offset of zero.
return {inline_offset, LayoutUnit()};
}
@@ -369,34 +344,10 @@ NGBlockLayoutAlgorithm::LayoutWithInlineChildLayoutContext() {
inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
NGInlineChildLayoutContext* inline_child_layout_context) {
- container_builder_.SetBorders(ComputeBorders(ConstraintSpace(), Node()));
- container_builder_.SetPadding(ComputePadding(ConstraintSpace(), Style()));
- border_padding_ = container_builder_.Borders() + container_builder_.Padding();
-
- NGBoxStrut scrollbars = Node().GetScrollbarSizes();
- border_scrollbar_padding_ = ConstraintSpace().IsAnonymous()
- ? NGBoxStrut()
- : border_padding_ + scrollbars;
- NGLogicalSize border_box_size = CalculateBorderBoxSize(
- ConstraintSpace(), Node(), border_padding_,
- CalculateDefaultBlockSize(ConstraintSpace(), Node(),
- border_scrollbar_padding_));
-
+ LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
child_available_size_ =
ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
- // When the content box is smaller than the scrollbar, clamp the scrollbar.
- if (UNLIKELY(!child_available_size_.inline_size && scrollbars.InlineSum() &&
- ClampScrollbarToContentBox(
- &scrollbars,
- border_box_size.inline_size - border_padding_.InlineSum()) &&
- !ConstraintSpace().IsAnonymous())) {
- // Re-compute dependent values if scrollbar size was clamped.
- border_scrollbar_padding_ = border_padding_ + scrollbars;
- child_available_size_ =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
- }
-
child_percentage_size_ = CalculateChildPercentageSize(
ConstraintSpace(), Node(), child_available_size_);
replaced_child_percentage_size_ = CalculateReplacedChildPercentageSize(
@@ -405,13 +356,11 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// All of the above calculations with border_scrollbar_padding_ shouldn't
// include the table cell's intrinsic padding. We can now add this.
- NGBoxStrut intrinsic_padding =
+ border_scrollbar_padding_ +=
ComputeIntrinsicPadding(ConstraintSpace(), Node());
- border_scrollbar_padding_ += intrinsic_padding;
if (ConstraintSpace().HasBlockFragmentation())
container_builder_.SetHasBlockFragmentation();
- container_builder_.SetInlineSize(border_box_size.inline_size);
container_builder_.SetBfcLineOffset(
ConstraintSpace().BfcOffset().line_offset);
@@ -425,7 +374,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// offset is updated.
abort_when_bfc_block_offset_updated_ = true;
- container_builder_.AddAdjoiningFloatTypes(float_types);
+ container_builder_.SetAdjoiningFloatTypes(float_types);
}
// If we are resuming from a break token our start border and padding is
@@ -435,7 +384,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
NGPreviousInflowPosition previous_inflow_position = {
LayoutUnit(), ConstraintSpace().MarginStrut(),
- /* empty_block_affected_by_clearance */ false};
+ /* self_collapsing_child_had_clearance */ false};
// Do not collapse margins between parent and its child if:
//
@@ -485,9 +434,9 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
DCHECK(ConstraintSpace().MarginStrut().IsEmpty());
if (!container_builder_.BfcBlockOffset()) {
- // New formatting contexts, and where we have an empty block affected by
- // clearance should already have their BFC block offset resolved.
- DCHECK(!previous_inflow_position.empty_block_affected_by_clearance);
+ // New formatting-contexts, and when we have a self-collapsing child
+ // affected by clearance must already have their BFC block-offset resolved.
+ DCHECK(!previous_inflow_position.self_collapsing_child_had_clearance);
DCHECK(!ConstraintSpace().IsNewFormattingContext());
}
#endif
@@ -515,7 +464,14 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// When possible, this adds fragments to |container_builder_| and update
// |previous_inflow_position| and |BreakToken()|.
scoped_refptr<const NGInlineBreakToken> previous_inline_break_token;
- NGBlockChildIterator child_iterator(Node().FirstChild(), BreakToken());
+
+ // If this layout is blocked by a display-lock, then we pretend this node has
+ // no children. Due to this, we skip layout on these children.
+ NGBlockChildIterator child_iterator(
+ Node().LayoutBlockedByDisplayLock(DisplayLockContext::kChildren)
+ ? NGBlockNode(nullptr)
+ : Node().FirstChild(),
+ BreakToken());
for (auto entry = child_iterator.NextChild();
NGLayoutInputNode child = entry.node;
entry = child_iterator.NextChild(previous_inline_break_token.get())) {
@@ -537,7 +493,7 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// all the way to the root of the fragmentation context without finding
// any such container, we have no valid class A break point, and if a
// forced break was requested, none will be inserted.
- if (!child.IsInline())
+ if (!child.IsInline() && ConstraintSpace().HasBlockFragmentation())
container_builder_.SetInitialBreakBefore(child.Style().BreakBefore());
bool abort;
@@ -572,12 +528,13 @@ inline scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::Layout(
// function is continued within |FinishLayout|. However it should be read as
// one function.
return FinishLayout(&previous_inflow_position, border_box_size,
- container_builder_.Borders(), scrollbars);
+ container_builder_.Borders(),
+ container_builder_.Scrollbar());
}
scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
NGPreviousInflowPosition* previous_inflow_position,
- NGLogicalSize border_box_size,
+ LogicalSize border_box_size,
const NGBoxStrut& borders,
const NGBoxStrut& scrollbars) {
NGMarginStrut end_margin_strut = previous_inflow_position->margin_strut;
@@ -592,11 +549,11 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
// The end margin strut of an in-flow fragment contributes to the size of the
// current fragment if:
// - There is block-end border/scrollbar/padding.
- // - There was empty block(s) affected by clearance.
+ // - There was a self-collapsing child affected by clearance.
// - We are a new formatting context.
// Additionally this fragment produces no end margin strut.
if (border_scrollbar_padding_.block_end ||
- previous_inflow_position->empty_block_affected_by_clearance ||
+ previous_inflow_position->self_collapsing_child_had_clearance ||
ConstraintSpace().IsNewFormattingContext()) {
// If we are a quirky container, we ignore any quirky margins and
// just consider normal margins to extend our size. Other UAs
@@ -647,24 +604,20 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
intrinsic_block_size_ = std::max(intrinsic_block_size_,
CalculateMinimumBlockSize(end_margin_strut));
- // With contain:size we need to ignore all kinds of intrinsic sizing. If block
- // height was specified as auto, its content-box size will become 0.
- if (Node().ShouldApplySizeContainment())
- intrinsic_block_size_ = border_scrollbar_padding_.BlockSum();
+ // TODO(layout-dev): Is CalculateMinimumBlockSize common to other algorithms,
+ // and should move into ClampIntrinsicBlockSize?
+ intrinsic_block_size_ = ClampIntrinsicBlockSize(
+ Node(), border_scrollbar_padding_, intrinsic_block_size_);
// Recompute the block-axis size now that we know our content size.
- // NOTE: For table cells, the block-size is just the intrinsic block-size.
- border_box_size.block_size =
- Node().IsTableCell()
- ? intrinsic_block_size_
- : ComputeBlockSizeForFragment(ConstraintSpace(), Style(),
- border_padding_, intrinsic_block_size_);
+ border_box_size.block_size = ComputeBlockSizeForFragment(
+ ConstraintSpace(), Node(), border_padding_, intrinsic_block_size_);
container_builder_.SetBlockSize(border_box_size.block_size);
- // If our BFC block offset is still unknown, there's one last thing to take
- // into consideration: Non-empty blocks always know their position in space.
- // If we have a break token, it means that we know the blocks' position even
- // if they're empty; it will be at the very start of the fragmentainer.
+ // If our BFC block-offset is still unknown, we check:
+ // - If we have a non-zero block-size (margins don't collapse through us).
+ // - If we have a break token. (Even if we are self-collapsing we position
+ // ourselves at the very start of the fragmentainer).
if (!container_builder_.BfcBlockOffset() &&
(border_box_size.block_size || BreakToken())) {
if (!ResolveBfcBlockOffset(previous_inflow_position))
@@ -674,9 +627,11 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
if (container_builder_.BfcBlockOffset()) {
// Do not collapse margins between the last in-flow child and bottom margin
- // of its parent if the parent has height != auto.
- if (!Style().LogicalHeight().IsAuto()) {
- // TODO(layout-ng): handle LogicalMinHeight, LogicalMaxHeight.
+ // of its parent if:
+ // - The parent has block-size != auto.
+ // - The block-size differs from the intrinsic size.
+ if (!Style().LogicalHeight().IsAuto() ||
+ border_box_size.block_size != intrinsic_block_size_) {
end_margin_strut = NGMarginStrut();
}
}
@@ -690,6 +645,25 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
container_builder_.SetEndMarginStrut(end_margin_strut);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
+ if (container_builder_.BfcBlockOffset()) {
+ // If we know our BFC block-offset we should have correctly placed all
+ // adjoining floats, and shouldn't propagate this information to siblings.
+ container_builder_.ResetAdjoiningFloatTypes();
+ } else {
+ // If we don't know our BFC block-offset yet, we know that for
+ // margin-collapsing purposes we are self-collapsing.
+ container_builder_.SetIsSelfCollapsing();
+
+ // If we've been forced at a particular BFC block-offset, (either from
+ // clearance past adjoining floats, or a re-layout), we can safely set our
+ // BFC block-offset now.
+ if (ConstraintSpace().ForcedBfcBlockOffset()) {
+ DCHECK(unpositioned_floats_.IsEmpty());
+ container_builder_.SetBfcBlockOffset(
+ *ConstraintSpace().ForcedBfcBlockOffset());
+ }
+ }
+
// We only finalize for fragmentation if the fragment has a BFC block offset.
// This may occur with a zero block size fragment. We need to know the BFC
// block offset to determine where the fragmentation line is relative to us.
@@ -713,6 +687,11 @@ scoped_refptr<const NGLayoutResult> NGBlockLayoutAlgorithm::FinishLayout(
DCHECK(!container_builder_.BfcBlockOffset());
DCHECK(container_builder_.AdjoiningFloatTypes());
}
+
+ // If we're not participating in a fragmentation context, no block
+ // fragmentation related fields should have been set.
+ if (!ConstraintSpace().HasBlockFragmentation())
+ container_builder_.CheckNoBlockFragmentation();
#endif
PropagateBaselinesFromChildren();
@@ -732,6 +711,7 @@ const NGInlineBreakToken* NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache(
NGInlineNode inline_node,
NGPreviousInflowPosition* previous_inflow_position,
bool* aborted_out) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGLineCacheEnabled());
LayoutBox* layout_box = inline_node.GetLayoutBox();
if (layout_box->SelfNeedsLayout())
return nullptr;
@@ -764,58 +744,45 @@ const NGInlineBreakToken* NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache(
TextDirection direction = container_builder_.Direction();
DCHECK_EQ(writing_mode, lineboxes->Style().GetWritingMode());
DCHECK_EQ(direction, lineboxes->Style().Direction());
- const NGPhysicalSize outer_size = lineboxes->Size();
-
- struct FragmentWithLogicalOffset {
- const NGPhysicalFragment& fragment;
- NGLogicalOffset offset;
- };
- // Avoid calling |ReserveInitialCapacity()| because
- // |lineboxes->Children().size()| is O(n), not linear, but it's critical to
- // have enough capacity.
- Vector<FragmentWithLogicalOffset, 64> fragments;
+ const PhysicalSize outer_size = lineboxes->Size();
+
+ LayoutUnit used_block_size = previous_inflow_position->logical_block_offset;
+ NGBreakToken* last_break_token = nullptr;
for (const NGPaintFragment* child : lineboxes->Children()) {
if (child->IsDirty())
break;
- // Abort if there are floats, oof, or list marker. They need re-layout.
- const NGPhysicalFragment& child_fragment = child->PhysicalFragment();
- if (!child_fragment.IsLineBox())
- return nullptr;
-
- NGLogicalOffset logical_offset = child->Offset().ConvertToLogical(
- writing_mode, direction, outer_size, child_fragment.Size());
- fragments.push_back(
- FragmentWithLogicalOffset{child_fragment, logical_offset});
- }
- if (fragments.IsEmpty())
- return nullptr;
+ // Abort if the line propagated its descendants to outside of the line. They
+ // are propagated through NGLayoutResult, which we don't cache.
+ const NGPhysicalLineBoxFragment* line =
+ DynamicTo<NGPhysicalLineBoxFragment>(&child->PhysicalFragment());
+ if (!line || line->HasPropagatedDescendants())
+ break;
- // TODO(kojii): Running the normal layout code at least once for this child
- // helps reducing the code to setup internal states after the reuse. Remove
- // the last fragment if it is the end of the fragmentation to do so, but we
- // should figure out how to setup the states without doing this.
- DCHECK(fragments.back().fragment.BreakToken());
- if (fragments.back().fragment.BreakToken()->IsFinished()) {
- fragments.Shrink(fragments.size() - 1);
- if (fragments.IsEmpty())
- return nullptr;
- }
+ // TODO(kojii): Running the normal layout code at least once for this child
+ // helps reducing the code to setup internal states after the reuse. Remove
+ // the last fragment if it is the end of the fragmentation to do so, but we
+ // should figure out how to setup the states without doing this.
+ NGBreakToken* break_token = line->BreakToken();
+ DCHECK(break_token);
+ if (break_token->IsFinished())
+ break;
- for (const auto& fragment : fragments) {
- container_builder_.AddChild(&fragment.fragment, fragment.offset);
+ last_break_token = break_token;
+ LogicalOffset logical_offset = child->Offset().ConvertToLogical(
+ writing_mode, direction, outer_size, line->Size());
+ container_builder_.AddChild(
+ *line, {logical_offset.inline_offset, used_block_size});
+ used_block_size += line->Size().ConvertToLogical(writing_mode).block_size;
}
+ if (!last_break_token)
+ return nullptr;
// Update the internal states to after the re-used fragments.
- const auto& last_fragment = fragments.back();
- LayoutUnit used_block_size =
- last_fragment.offset.block_offset +
- last_fragment.fragment.Size().ConvertToLogical(writing_mode).block_size;
previous_inflow_position->logical_block_offset = used_block_size;
// In order to layout the rest of lines, return the break token from the last
// reused line box.
- NGBreakToken* last_break_token = last_fragment.fragment.BreakToken();
DCHECK(last_break_token);
DCHECK(!last_break_token->IsFinished());
return To<NGInlineBreakToken>(last_break_token);
@@ -824,57 +791,51 @@ const NGInlineBreakToken* NGBlockLayoutAlgorithm::TryReuseFragmentsFromCache(
void NGBlockLayoutAlgorithm::HandleOutOfFlowPositioned(
const NGPreviousInflowPosition& previous_inflow_position,
NGBlockNode child) {
- const ComputedStyle& child_style = child.Style();
-
- NGLogicalOffset offset = {border_scrollbar_padding_.inline_start,
- previous_inflow_position.logical_block_offset};
+ DCHECK(child.IsOutOfFlowPositioned());
+ LogicalOffset static_offset = {border_scrollbar_padding_.inline_start,
+ previous_inflow_position.logical_block_offset};
// We only include the margin strut in the OOF static-position if we know we
// aren't going to be a zero-block-size fragment.
if (container_builder_.BfcBlockOffset())
- offset.block_offset += previous_inflow_position.margin_strut.Sum();
-
- if (child_style.IsOriginalDisplayInlineType()) {
- // OOF-positioned nodes which were initially inline-level, however are in a
- // block-level context, pretend they are in an inline-level context. E.g.
- // they avoid floats, and respect text-align.
- const TextDirection direction = ConstraintSpace().Direction();
- LayoutUnit child_origin_line_offset =
- container_builder_.BfcLineOffset() +
- border_scrollbar_padding_.LineLeft(direction);
-
- // Find a layout opportunity, this is where we would have placed a
- // zero-sized line.
- NGLayoutOpportunity opportunity = exclusion_space_.FindLayoutOpportunity(
- {child_origin_line_offset, BfcBlockOffset() + offset.block_offset},
- child_available_size_.inline_size, /* minimum_size */ NGLogicalSize());
-
- LayoutUnit child_line_offset = IsLtr(direction)
- ? opportunity.rect.LineStartOffset()
- : opportunity.rect.LineEndOffset();
-
- // Convert back to the logical coordinate system. As the conversion is on
- // an OOF-positioned node, we pretent it has zero inline-size.
- offset.inline_offset = LogicalFromBfcLineOffset(
- child_line_offset, container_builder_.BfcLineOffset(),
- /* child_inline_size */ LayoutUnit(),
- container_builder_.Size().inline_size, direction);
-
- // Adjust for text alignment, within the layout opportunity.
- offset.inline_offset +=
- InlineOffsetForTextAlign(Style(), opportunity.rect.InlineSize());
- }
-
- container_builder_.AddOutOfFlowChildCandidate(child, offset);
+ static_offset.block_offset += previous_inflow_position.margin_strut.Sum();
+
+ if (child.Style().IsOriginalDisplayInlineType()) {
+ // The static-position of inline-level OOF-positioned nodes depends on
+ // previous floats (if any).
+ //
+ // Due to this we need to mark this node as having adjoining objects, and
+ // perform a re-layout if our position shifts.
+ if (!container_builder_.BfcBlockOffset()) {
+ container_builder_.AddAdjoiningFloatTypes(kAdjoiningInlineOutOfFlow);
+ abort_when_bfc_block_offset_updated_ = true;
+ }
+
+ LayoutUnit origin_bfc_block_offset =
+ container_builder_.BfcBlockOffset().value_or(
+ ConstraintSpace().ForcedBfcBlockOffset().value_or(
+ ConstraintSpace().BfcOffset().block_offset)) +
+ static_offset.block_offset;
+
+ NGBfcOffset origin_bfc_offset = {
+ ConstraintSpace().BfcOffset().line_offset +
+ border_scrollbar_padding_.LineLeft(Style().Direction()),
+ origin_bfc_block_offset};
+
+ static_offset.inline_offset += CalculateOutOfFlowStaticInlineLevelOffset(
+ Style(), origin_bfc_offset, exclusion_space_,
+ child_available_size_.inline_size);
+ }
+
+ container_builder_.AddOutOfFlowChildCandidate(child, static_offset);
}
void NGBlockLayoutAlgorithm::HandleFloat(
const NGPreviousInflowPosition& previous_inflow_position,
NGBlockNode child,
const NGBlockBreakToken* child_break_token) {
- // If there is a break token for a float we must be resuming layout, we must
- // always know our position in the BFC.
- DCHECK(!child_break_token || child_break_token->IsBreakBefore() ||
+ // If we're resuming layout, we must always know our position in the BFC.
+ DCHECK(!IsResumingLayout(child_break_token) ||
container_builder_.BfcBlockOffset());
NGUnpositionedFloat unpositioned_float(child, child_break_token);
@@ -892,14 +853,14 @@ void NGBlockLayoutAlgorithm::HandleFloat(
// No need to postpone the positioning if we know the correct offset.
if (container_builder_.BfcBlockOffset() ||
- ConstraintSpace().FloatsBfcBlockOffset()) {
+ ConstraintSpace().ForcedBfcBlockOffset()) {
// Adjust origin point to the margins of the last child.
// Example: <div style="margin-bottom: 20px"><float></div>
// <div style="margin-bottom: 30px"></div>
LayoutUnit origin_block_offset =
container_builder_.BfcBlockOffset()
? NextBorderEdge(previous_inflow_position)
- : *ConstraintSpace().FloatsBfcBlockOffset();
+ : *ConstraintSpace().ForcedBfcBlockOffset();
PositionPendingFloats(origin_block_offset);
}
}
@@ -916,11 +877,9 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
const ComputedStyle& child_style = child.Style();
const TextDirection direction = ConstraintSpace().Direction();
- bool has_clearance_past_adjoining_floats = HasClearancePastAdjoiningFloats(
- container_builder_.AdjoiningFloatTypes(), child_style, Style());
- NGInflowChildData child_data = ComputeChildData(
- *previous_inflow_position, child, child_break_token,
- has_clearance_past_adjoining_floats, /* is_new_fc */ true);
+ NGInflowChildData child_data =
+ ComputeChildData(*previous_inflow_position, child, child_break_token,
+ /* is_new_fc */ true);
LayoutUnit child_origin_line_offset =
ConstraintSpace().BfcOffset().line_offset +
@@ -954,11 +913,22 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
if (!container_builder_.BfcBlockOffset()) {
had_pending_floats = !unpositioned_floats_.IsEmpty();
- if (ConstraintSpace().FloatsBfcBlockOffset()) {
+ // If this node, or an arbitrary ancestor had clearance past adjoining
+ // floats, we consider the margin "separated". We should *never* attempt to
+ // re-resolve the BFC block-offset in this case.
+ bool has_clearance_past_adjoining_floats =
+ ConstraintSpace().AncestorHasClearancePastAdjoiningFloats() ||
+ HasClearancePastAdjoiningFloats(
+ container_builder_.AdjoiningFloatTypes(), child_style, Style());
+
+ if (has_clearance_past_adjoining_floats) {
+ child_bfc_offset_estimate = NextBorderEdge(*previous_inflow_position);
+ child_margin_got_separated = true;
+ } else if (ConstraintSpace().ForcedBfcBlockOffset()) {
// This is not the first time we're here. We already have a suggested BFC
// block offset.
bfc_offset_already_resolved = true;
- child_bfc_offset_estimate = *ConstraintSpace().FloatsBfcBlockOffset();
+ child_bfc_offset_estimate = *ConstraintSpace().ForcedBfcBlockOffset();
// We require that the BFC block offset be the one we'd get with either
// margins adjoining or margins separated. Anything else is a bug.
DCHECK(child_bfc_offset_estimate == adjoining_bfc_offset_estimate ||
@@ -967,9 +937,6 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
// margin strut or not.
child_margin_got_separated =
child_bfc_offset_estimate != adjoining_bfc_offset_estimate;
- } else if (has_clearance_past_adjoining_floats) {
- child_bfc_offset_estimate = NextBorderEdge(*previous_inflow_position);
- child_margin_got_separated = true;
}
// The BFC block offset of this container gets resolved because of this
@@ -1021,8 +988,15 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
// re-resolve the BFC block offset without the child's margin.
LayoutUnit old_offset = *container_builder_.BfcBlockOffset();
container_builder_.ResetBfcBlockOffset();
+
+ // Re-resolving the BFC block-offset with a different "forced" BFC
+ // block-offset is only safe if an ancestor *never* had clearance past
+ // adjoining floats.
+ DCHECK(!ConstraintSpace().AncestorHasClearancePastAdjoiningFloats());
ResolveBfcBlockOffset(previous_inflow_position,
- non_adjoining_bfc_offset_estimate);
+ non_adjoining_bfc_offset_estimate,
+ /* forced_bfc_block_offset */ base::nullopt);
+
if ((bfc_offset_already_resolved || had_pending_floats) &&
old_offset != *container_builder_.BfcBlockOffset()) {
// The first BFC block offset resolution turned out to be wrong, and we
@@ -1047,9 +1021,9 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
{child_origin_line_offset, child_bfc_offset_estimate},
/* abort_if_cleared */ false);
}
- DCHECK(layout_result->PhysicalFragment());
- const auto& physical_fragment = *layout_result->PhysicalFragment();
- NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment);
+
+ NGFragment fragment(ConstraintSpace().GetWritingMode(),
+ layout_result->PhysicalFragment());
// Auto-margins are applied within the layout opportunity which fits. We'll
// pretend that computed margins are 0 here, as they have already been
@@ -1064,8 +1038,14 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
auto_margins.inline_end = opportunity.rect.InlineSize() -
fragment.InlineSize() - auto_margins.inline_start;
} else {
+ LayoutUnit inline_size = fragment.InlineSize();
+ // Negative margins are not used to determine opportunity, but need to take
+ // them into account for positioning.
+ LayoutUnit inline_margin = child_data.margins.InlineSum();
+ if (inline_margin < 0)
+ inline_size += inline_margin;
ResolveInlineMargins(child_style, Style(), opportunity.rect.InlineSize(),
- fragment.InlineSize(), &auto_margins);
+ inline_size, &auto_margins);
}
LayoutUnit child_bfc_line_offset = opportunity.rect.start_offset.line_offset +
@@ -1090,7 +1070,7 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
NGBfcOffset child_bfc_offset(child_bfc_line_offset,
opportunity.rect.start_offset.block_offset);
- NGLogicalOffset logical_offset = LogicalFromBfcOffsets(
+ LogicalOffset logical_offset = LogicalFromBfcOffsets(
child_bfc_offset, ContainerBfcOffset(), fragment.InlineSize(),
container_builder_.Size().inline_size, ConstraintSpace().Direction());
@@ -1109,7 +1089,8 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
PositionOrPropagateListMarker(*layout_result, &logical_offset);
- container_builder_.AddChild(*layout_result, logical_offset);
+ container_builder_.AddChild(layout_result->PhysicalFragment(),
+ logical_offset);
container_builder_.PropagateBreak(*layout_result);
// The margins we store will be used by e.g. getComputedStyle().
@@ -1122,7 +1103,7 @@ bool NGBlockLayoutAlgorithm::HandleNewFormattingContext(
*previous_inflow_position = ComputeInflowPosition(
*previous_inflow_position, child, child_data,
child_bfc_offset.block_offset, logical_offset, *layout_result, fragment,
- /* empty_block_affected_by_clearance */ false);
+ /* self_collapsing_child_had_clearance */ false);
return true;
}
@@ -1192,12 +1173,12 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
// auto inline size by subtracting the inline margins from available inline
// size. We have calculated a layout opportunity without margins in mind,
// since they overlap with adjacent floats. Now we need to add them.
- NGLogicalSize child_available_size = {
+ LogicalSize child_available_size = {
(opportunity.rect.InlineSize() - inline_negative_margin + inline_margin)
.ClampNegativeToZero(),
child_available_size_.block_size};
- NGConstraintSpace child_space =
- CreateConstraintSpaceForChild(child, child_data, child_available_size);
+ NGConstraintSpace child_space = CreateConstraintSpaceForChild(
+ child, child_data, child_available_size, /* is_new_fc */ true);
// All formatting context roots (like this child) should start with an empty
// exclusion space.
@@ -1210,9 +1191,8 @@ NGBlockLayoutAlgorithm::LayoutNewFormattingContext(
// should be returned.
DCHECK(layout_result->ExclusionSpace().IsEmpty());
- DCHECK(layout_result->PhysicalFragment());
NGFragment fragment(ConstraintSpace().GetWritingMode(),
- *layout_result->PhysicalFragment());
+ layout_result->PhysicalFragment());
// Now we can check if the fragment will fit in this layout opportunity.
if ((opportunity.rect.InlineSize() >= fragment.InlineSize() ||
@@ -1237,7 +1217,8 @@ bool NGBlockLayoutAlgorithm::HandleInflow(
DCHECK(!child.CreatesNewFormattingContext());
auto* child_inline_node = DynamicTo<NGInlineNode>(child);
- if (child_inline_node && !child_break_token) {
+ if (child_inline_node && !child_break_token &&
+ RuntimeEnabledFeatures::LayoutNGLineCacheEnabled()) {
DCHECK(!*previous_inline_break_token);
bool aborted = false;
*previous_inline_break_token = TryReuseFragmentsFromCache(
@@ -1251,10 +1232,12 @@ bool NGBlockLayoutAlgorithm::HandleInflow(
bool is_non_empty_inline =
child_inline_node && !child_inline_node->IsEmptyInline();
bool has_clearance_past_adjoining_floats =
- child.IsBlock() &&
+ !container_builder_.BfcBlockOffset() && child.IsBlock() &&
HasClearancePastAdjoiningFloats(container_builder_.AdjoiningFloatTypes(),
child.Style(), Style());
+ base::Optional<LayoutUnit> forced_bfc_block_offset;
+
// If we can separate the previous margin strut from what is to follow, do
// that. Then we're able to resolve *our* BFC block offset and position any
// pending floats. There are two situations where this is necessary:
@@ -1266,21 +1249,33 @@ bool NGBlockLayoutAlgorithm::HandleInflow(
if (has_clearance_past_adjoining_floats || is_non_empty_inline) {
if (!ResolveBfcBlockOffset(previous_inflow_position))
return false;
+
+ // If we had clearance past any adjoining floats, we already know where the
+ // child is going to be (the child's margins won't have any effect).
+ //
+ // Set the forced BFC block-offset to the appropriate clearance offset to
+ // force this placement of this child.
+ if (has_clearance_past_adjoining_floats) {
+ forced_bfc_block_offset = exclusion_space_.ClearanceOffset(
+ ResolvedClear(child.Style(), Style()));
+ }
}
// Perform layout on the child.
- NGInflowChildData child_data = ComputeChildData(
- *previous_inflow_position, child, child_break_token,
- has_clearance_past_adjoining_floats, /* is_new_fc */ false);
- NGConstraintSpace child_space =
- CreateConstraintSpaceForChild(child, child_data, child_available_size_);
- scoped_refptr<const NGLayoutResult> layout_result =
- child.Layout(child_space, child_break_token, inline_child_layout_context);
+ NGInflowChildData child_data =
+ 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);
+ scoped_refptr<const NGLayoutResult> layout_result = LayoutInflow(
+ child_space, child_break_token, &child, inline_child_layout_context);
// To save space of the stack when we recurse into |NGBlockNode::Layout|
// above, the rest of this function is continued within |FinishInflow|.
// However it should be read as one function.
return FinishInflow(child, child_break_token, child_space,
+ has_clearance_past_adjoining_floats,
std::move(layout_result), &child_data,
previous_inflow_position, inline_child_layout_context,
previous_inline_break_token);
@@ -1290,6 +1285,7 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
NGLayoutInputNode child,
const NGBreakToken* child_break_token,
const NGConstraintSpace& child_space,
+ bool has_clearance_past_adjoining_floats,
scoped_refptr<const NGLayoutResult> layout_result,
NGInflowChildData* child_data,
NGPreviousInflowPosition* previous_inflow_position,
@@ -1297,59 +1293,76 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
scoped_refptr<const NGInlineBreakToken>* previous_inline_break_token) {
base::Optional<LayoutUnit> child_bfc_block_offset =
layout_result->BfcBlockOffset();
- // TODO(layout-dev): A more optimal version of this is to set
- // relayout_child_when_bfc_resolved only if the child tree itself _added_ any
- // floats that it failed to position. Currently, we risk relaying out the
- // parent block for no reason, because we're not able to make this
- // distinction.
- bool relayout_child_when_bfc_resolved =
- layout_result->AdjoiningFloatTypes() && !child_bfc_block_offset &&
- !child_space.FloatsBfcBlockOffset();
- bool is_empty_block = IsEmptyBlock(child_space, *layout_result);
-
- bool has_clearance = layout_result->IsPushedByFloats();
-
- // A child may have aborted its layout if it resolved its BFC block offset.
- // If we don't have a BFC block offset yet, we need to propagate the abortion
- // up to our parent.
+
+ bool is_self_collapsing = layout_result->IsSelfCollapsing();
+
+ // Only non self-collapsing children (e.g. "normal children") can be pushed
+ // by floats in this way.
+ bool normal_child_had_clearance = layout_result->IsPushedByFloats();
+ DCHECK(!normal_child_had_clearance || !is_self_collapsing);
+
+ // A child may have aborted its layout if it resolved its BFC block-offset.
+ // If we don't have a BFC block-offset yet, we need to propagate the abort
+ // signal up to our parent.
if (layout_result->Status() == NGLayoutResult::kBfcBlockOffsetResolved &&
!container_builder_.BfcBlockOffset()) {
- // There's no need to do anything apart from resolving the BFC block offset
+ // There's no need to do anything apart from resolving the BFC block-offset
// here, so make sure that it aborts before trying to position floats or
- // anything like that, which would just be waste of time. This is simply
- // propagating an abort up to a node which is able to restart the layout (a
- // node that has resolved its BFC block offset).
+ // anything like that, which would just be waste of time.
+ //
+ // This is simply propagating an abort up to a node which is able to
+ // restart the layout (a node that has resolved its BFC block-offset).
DCHECK(child_bfc_block_offset);
abort_when_bfc_block_offset_updated_ = true;
- ResolveBfcBlockOffset(previous_inflow_position,
- has_clearance
- ? NextBorderEdge(*previous_inflow_position)
- : *child_bfc_block_offset);
- return false;
+
+ LayoutUnit bfc_block_offset = *child_bfc_block_offset;
+
+ if (normal_child_had_clearance) {
+ // If the child has the same clearance-offset as ourselves it means that
+ // we should *also* resolve ourselves at that offset, (and we also have
+ // been pushed by floats).
+ if (ConstraintSpace().ClearanceOffset() == child_space.ClearanceOffset())
+ container_builder_.SetIsPushedByFloats();
+ else
+ bfc_block_offset = NextBorderEdge(*previous_inflow_position);
+ }
+
+ // A new formatting-context may have previously tried to resolve the BFC
+ // block-offset. In this case we'll have a "forced" BFC block-offset
+ // present, but we shouldn't apply it (instead preferring the child's new
+ // BFC block-offset).
+ DCHECK(!ConstraintSpace().AncestorHasClearancePastAdjoiningFloats());
+ if (!ResolveBfcBlockOffset(previous_inflow_position, bfc_block_offset,
+ /* forced_bfc_block_offset */ base::nullopt))
+ return false;
}
- // We have special behaviour for an empty block which gets pushed down due to
- // clearance, see comment inside ComputeInflowPosition.
- bool empty_block_affected_by_clearance = false;
+ // We have special behaviour for a self-collapsing child which gets pushed
+ // down due to clearance, see comment inside |ComputeInflowPosition|.
+ bool self_collapsing_child_had_clearance =
+ is_self_collapsing && has_clearance_past_adjoining_floats;
- // We try and position the child within the block formatting context. This
- // may cause our BFC block offset to be resolved, in which case we should
+ // We try and position the child within the block formatting-context. This
+ // may cause our BFC block-offset to be resolved, in which case we should
// abort our layout if needed.
if (!child_bfc_block_offset) {
- if (!has_clearance && child_space.HasClearanceOffset() &&
+ DCHECK(is_self_collapsing);
+ if (child_space.HasClearanceOffset() &&
child.Style().Clear() != EClear::kNone) {
- // This is an empty block child that we collapsed through, so we have to
- // detect clearance manually. See if the child's hypothetical border edge
- // is past the relevant floats. If it's not, we need to apply clearance
- // before it.
+ // This is a self-collapsing child that we collapsed through, so we have
+ // to detect clearance manually. See if the child's hypothetical border
+ // edge is past the relevant floats. If it's not, we need to apply
+ // clearance before it.
LayoutUnit child_block_offset_estimate =
BfcBlockOffset() + layout_result->EndMarginStrut().Sum();
- if (child_block_offset_estimate < child_space.ClearanceOffset() ||
- child_space.ShouldForceClearance())
- has_clearance = empty_block_affected_by_clearance = true;
+ if (child_block_offset_estimate < child_space.ClearanceOffset())
+ self_collapsing_child_had_clearance = true;
}
}
- if (has_clearance) {
+
+ bool child_had_clearance =
+ self_collapsing_child_had_clearance || normal_child_had_clearance;
+ if (child_had_clearance) {
// The child has clearance. Clearance inhibits margin collapsing and acts as
// spacing before the block-start margin of the child. Our BFC block offset
// is therefore resolvable, and if it hasn't already been resolved, we'll
@@ -1357,31 +1370,50 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
if (!ResolveBfcBlockOffset(previous_inflow_position))
return false;
}
+
+ bool self_collapsing_child_needs_relayout = false;
if (!child_bfc_block_offset) {
- DCHECK(is_empty_block);
- // Layout wasn't able to determine the BFC block offset of the child. This
- // has to mean that the child is empty (block-size-wise).
+ // Layout wasn't able to determine the BFC block-offset of the child. This
+ // has to mean that the child is self-collapsing.
+ DCHECK(is_self_collapsing);
+
if (container_builder_.BfcBlockOffset()) {
- // Since we know our own BFC block offset, though, we can calculate that
+ // Since we know our own BFC block-offset, though, we can calculate that
// of the child as well.
- child_bfc_block_offset = PositionEmptyChildWithParentBfc(
+ child_bfc_block_offset = PositionSelfCollapsingChildWithParentBfc(
child, child_space, *child_data, *layout_result);
+
+ // We may need to relayout this child if it had any objects which were
+ // positioned in the incorrect position.
+ //
+ // TODO(layout-dev): A more optimal version of this is to set this flag
+ // only if the child tree *added* any floats which it failed to position.
+ // Currently, we risk relaying out the sub-tree for no reason, because
+ // we're not able to make this distinction.
+ if (layout_result->AdjoiningFloatTypes() &&
+ !child_space.ForcedBfcBlockOffset())
+ self_collapsing_child_needs_relayout = true;
}
- } else if (!has_clearance) {
+ } else if (!child_had_clearance) {
// We shouldn't have any pending floats here, since an in-flow child found
// its BFC block offset.
DCHECK(unpositioned_floats_.IsEmpty());
- // The child's BFC block offset is known, and since there's no clearance,
- // this container will get the same offset, unless it has already been
- // resolved.
- if (!ResolveBfcBlockOffset(previous_inflow_position,
- *child_bfc_block_offset))
- return false;
+ // Only non self-collapsing children are allowed resolve the BFC
+ // block-offset. We check the BFC block-offset at the end of layout
+ // determine if this fragment is self-collapsing.
+ if (!is_self_collapsing) {
+ // The child's BFC block offset is known, and since there's no clearance,
+ // this container will get the same offset, unless it has already been
+ // resolved.
+ if (!ResolveBfcBlockOffset(previous_inflow_position,
+ *child_bfc_block_offset))
+ return false;
+ }
}
- // We need to re-layout a child if it was affected by clearance in order to
- // produce a new margin strut. For example:
+ // We need to re-layout a self-collapsing child if it was affected by
+ // clearance in order to produce a new margin strut. For example:
// <div style="margin-bottom: 50px;"></div>
// <div id="float" style="height: 50px;"></div>
// <div id="zero" style="clear: left; margin-top: -20px;">
@@ -1395,9 +1427,8 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
// relayout with an empty incoming margin strut.
//
// The resulting margin strut in the above example will be {40, -30}. See
- // ComputeInflowPosition for how this end margin strut is used.
- bool empty_block_affected_by_clearance_needs_relayout = false;
- if (empty_block_affected_by_clearance) {
+ // |ComputeInflowPosition| for how this end margin strut is used.
+ if (self_collapsing_child_had_clearance) {
NGMarginStrut margin_strut;
margin_strut.Append(child_data->margins.block_start,
child.Style().HasMarginBeforeQuirk());
@@ -1406,7 +1437,7 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
// previous one.
if (child_data->margin_strut != margin_strut) {
child_data->margin_strut = margin_strut;
- empty_block_affected_by_clearance_needs_relayout = true;
+ self_collapsing_child_needs_relayout = true;
}
}
@@ -1415,12 +1446,12 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
// - It has some unpositioned floats.
// - It was affected by clearance.
if ((layout_result->Status() == NGLayoutResult::kBfcBlockOffsetResolved ||
- relayout_child_when_bfc_resolved ||
- empty_block_affected_by_clearance_needs_relayout) &&
+ self_collapsing_child_needs_relayout) &&
child_bfc_block_offset) {
NGConstraintSpace new_child_space = CreateConstraintSpaceForChild(
- child, *child_data, child_available_size_, child_bfc_block_offset);
- layout_result = child.Layout(new_child_space, child_break_token,
+ child, *child_data, child_available_size_, /* is_new_fc */ false,
+ child_bfc_block_offset);
+ layout_result = LayoutInflow(new_child_space, child_break_token, &child,
inline_child_layout_context);
if (layout_result->Status() == NGLayoutResult::kBfcBlockOffsetResolved) {
@@ -1432,45 +1463,51 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
child_bfc_block_offset = layout_result->BfcBlockOffset();
DCHECK(child_bfc_block_offset);
new_child_space = CreateConstraintSpaceForChild(
- child, *child_data, child_available_size_, child_bfc_block_offset);
- layout_result = child.Layout(new_child_space, child_break_token,
+ child, *child_data, child_available_size_, /* is_new_fc */ false,
+ child_bfc_block_offset);
+ layout_result = LayoutInflow(new_child_space, child_break_token, &child,
inline_child_layout_context);
}
DCHECK_EQ(layout_result->Status(), NGLayoutResult::kSuccess);
- relayout_child_when_bfc_resolved = false;
}
- // It is now safe to update our version of the exclusion space.
+ // It is now safe to update our version of the exclusion space, and any
+ // propagated adjoining floats.
exclusion_space_ = layout_result->ExclusionSpace();
- // If we don't know our BFC block offset yet, and the child stumbled into
- // something that needs it (unable to position floats when the BFC block
- // offset is unknown), we need abort layout once we manage to resolve it, and
- // relayout. Note that this check is performed after the optional second
- // layout pass above, since we may have been able to resolve our BFC block
- // offset (e.g. due to clearance) and position any descendant floats in the
- // second pass. In particular, when it comes to clearance of empty blocks, if
- // we just applied it and resolved the BFC block offset to separate the
+ // Only self-collapsing children should have adjoining floats.
+ DCHECK(!layout_result->AdjoiningFloatTypes() || is_self_collapsing);
+ container_builder_.SetAdjoiningFloatTypes(
+ layout_result->AdjoiningFloatTypes());
+
+ // If we don't know our BFC block-offset yet, and the child stumbled into
+ // something that needs it (unable to position floats yet), we need abort
+ // layout, and trigger a re-layout once we manage to resolve it.
+ //
+ // NOTE: This check is performed after the optional second layout pass above,
+ // since we may have been able to resolve our BFC block-offset (e.g. due to
+ // clearance) and position any descendant floats in the second pass.
+ // In particular, when it comes to clearance of self-collapsing children, if
+ // we just applied it and resolved the BFC block-offset to separate the
// margins before and after clearance, we cannot abort and re-layout this
- // block, or clearance would be lost.
+ // child, or clearance would be lost.
//
// If we are a new formatting context, the child will get re-laid out once it
// has been positioned.
if (!container_builder_.BfcBlockOffset()) {
- abort_when_bfc_block_offset_updated_ |= relayout_child_when_bfc_resolved;
+ abort_when_bfc_block_offset_updated_ |=
+ layout_result->AdjoiningFloatTypes();
// If our BFC block offset is unknown, and the child got pushed down by
// floats, so will we.
if (layout_result->IsPushedByFloats())
container_builder_.SetIsPushedByFloats();
}
- // We must have an actual fragment at this stage.
- DCHECK(layout_result->PhysicalFragment());
- const auto& physical_fragment = *layout_result->PhysicalFragment();
+ const auto& physical_fragment = layout_result->PhysicalFragment();
NGFragment fragment(ConstraintSpace().GetWritingMode(), physical_fragment);
- NGLogicalOffset logical_offset = CalculateLogicalOffset(
+ LogicalOffset logical_offset = CalculateLogicalOffset(
fragment, layout_result->BfcLineOffset(), child_bfc_block_offset);
if (ConstraintSpace().HasBlockFragmentation()) {
@@ -1485,12 +1522,7 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
PositionOrPropagateListMarker(*layout_result, &logical_offset);
- if (is_empty_block && !container_builder_.BfcBlockOffset()) {
- container_builder_.AddAdjoiningFloatTypes(
- layout_result->AdjoiningFloatTypes());
- }
-
- container_builder_.AddChild(*layout_result, logical_offset);
+ container_builder_.AddChild(physical_fragment, logical_offset);
if (child.IsBlock())
container_builder_.PropagateBreak(*layout_result);
@@ -1512,11 +1544,10 @@ bool NGBlockLayoutAlgorithm::FinishInflow(
*previous_inflow_position = ComputeInflowPosition(
*previous_inflow_position, child, *child_data, child_bfc_block_offset,
logical_offset, *layout_result, fragment,
- empty_block_affected_by_clearance);
+ self_collapsing_child_had_clearance);
*previous_inline_break_token =
- child.IsInline() ? To<NGInlineBreakToken>(
- layout_result->PhysicalFragment()->BreakToken())
+ child.IsInline() ? To<NGInlineBreakToken>(physical_fragment.BreakToken())
: nullptr;
return true;
@@ -1526,7 +1557,6 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData(
const NGPreviousInflowPosition& previous_inflow_position,
NGLayoutInputNode child,
const NGBreakToken* child_break_token,
- bool force_clearance,
bool is_new_fc) {
DCHECK(child);
DCHECK(!child.IsFloating());
@@ -1538,8 +1568,8 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData(
&margins_fully_resolved);
// Append the current margin strut with child's block start margin.
- // Non empty border/padding, and new FC use cases are handled inside of the
- // child's layout
+ // Non empty border/padding, and new formatting-context use cases are handled
+ // inside of the child's layout
NGMarginStrut margin_strut = previous_inflow_position.margin_strut;
LayoutUnit logical_block_offset =
@@ -1561,8 +1591,7 @@ NGInflowChildData NGBlockLayoutAlgorithm::ComputeChildData(
margins.LineLeft(ConstraintSpace().Direction()),
BfcBlockOffset() + logical_block_offset};
- return {child_bfc_offset, margin_strut, margins,
- margins_fully_resolved, force_clearance, is_new_fc};
+ return {child_bfc_offset, margin_strut, margins, margins_fully_resolved};
}
NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
@@ -1570,53 +1599,54 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
const NGLayoutInputNode child,
const NGInflowChildData& child_data,
const base::Optional<LayoutUnit>& child_bfc_block_offset,
- const NGLogicalOffset& logical_offset,
+ const LogicalOffset& logical_offset,
const NGLayoutResult& layout_result,
const NGFragment& fragment,
- bool empty_block_affected_by_clearance) {
+ bool self_collapsing_child_had_clearance) {
// Determine the child's end logical offset, for the next child to use.
LayoutUnit logical_block_offset;
- bool is_empty_block = IsEmptyBlock(child_data.is_new_fc, layout_result);
- if (is_empty_block) {
- // The default behaviour for empty blocks is they just pass through the
- // previous inflow position.
+ bool is_self_collapsing = layout_result.IsSelfCollapsing();
+ if (is_self_collapsing) {
+ // The default behaviour for self-collapsing children is they just pass
+ // through the previous inflow position.
logical_block_offset = previous_inflow_position.logical_block_offset;
- if (empty_block_affected_by_clearance) {
+ if (self_collapsing_child_had_clearance) {
// If there's clearance, we must have applied that by now and thus
- // resolved our BFC block offset.
+ // resolved our BFC block-offset.
DCHECK(container_builder_.BfcBlockOffset());
DCHECK(child_bfc_block_offset.has_value());
- // If an empty block was affected by clearance (that is it got pushed
- // down past a float), we need to do something slightly bizarre.
+ // If a self-collapsing child was affected by clearance (that is it got
+ // pushed down past a float), we need to do something slightly bizarre.
//
// Instead of just passing through the previous inflow position, we make
// the inflow position our new position (which was affected by the
- // float), minus what the margin strut which the empty block produced.
+ // float), minus what the margin strut which the self-collapsing child
+ // produced.
//
// Another way of thinking about this is that when you *add* back the
// margin strut, you end up with the same position as you started with.
//
// This is essentially what the spec refers to as clearance [1], and,
// while we normally don't have to calculate it directly, in the case of
- // an empty cleared child like here, we actually have to.
+ // a self-collapsing cleared child like here, we actually have to.
//
- // We have to calculate clearance for empty cleared children, because we
- // need the margin that's between the clearance and this block to collapse
- // correctly with subsequent content. This is something that needs to take
- // place after the margin strut preceding and following the clearance have
- // been separated. Clearance may be positive, negative or zero, depending
- // on what it takes to (hypothetically) place this child just below the
- // last relevant float. Since the margins before and after the clearance
- // have been separated, we may have to pull the child back, and that's an
- // example of negative clearance.
+ // We have to calculate clearance for self-collapsing cleared children,
+ // because we need the margin that's between the clearance and this block
+ // to collapse correctly with subsequent content. This is something that
+ // needs to take place after the margin strut preceding and following the
+ // clearance have been separated. Clearance may be positive, negative or
+ // zero, depending on what it takes to (hypothetically) place this child
+ // just below the last relevant float. Since the margins before and after
+ // the clearance have been separated, we may have to pull the child back,
+ // and that's an example of negative clearance.
//
- // (In the other case, when a cleared child is non-empty (i.e. when we
- // don't end up here), we don't need to explicitly calculate clearance,
- // because then we just place its border edge where it should be and we're
- // done with it.)
+ // (In the other case, when a cleared child is non self-collapsing (i.e.
+ // when we don't end up here), we don't need to explicitly calculate
+ // clearance, because then we just place its border edge where it should
+ // be and we're done with it.)
//
// [1] https://www.w3.org/TR/CSS22/visuren.html#flow-control
@@ -1646,17 +1676,19 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
child_data.margins.block_end, &logical_block_offset,
&margin_strut);
} else {
- // An empty block's end margin can "inherit" quirkiness from its start
- // margin. E.g.
+ // Self collapsing child's end margin can "inherit" quirkiness from its
+ // start margin. E.g.
// <ol style="margin-bottom: 20px"></ol>
- bool is_quirky = (is_empty_block && child.Style().HasMarginBeforeQuirk()) ||
- child.Style().HasMarginAfterQuirk();
+ bool is_quirky =
+ (is_self_collapsing && child.Style().HasMarginBeforeQuirk()) ||
+ child.Style().HasMarginAfterQuirk();
margin_strut.Append(child_data.margins.block_end, is_quirky);
}
// This flag is subtle, but in order to determine our size correctly we need
- // to check if our last child is an empty block, and it was affected by
- // clearance *or* an adjoining empty sibling was affected by clearance. E.g.
+ // to check if our last child is self-collapsing, and it was affected by
+ // clearance *or* an adjoining self-collapsing sibling was affected by
+ // clearance. E.g.
// <div id="container">
// <div id="float"></div>
// <div id="zero-with-clearance"></div>
@@ -1664,21 +1696,21 @@ NGPreviousInflowPosition NGBlockLayoutAlgorithm::ComputeInflowPosition(
// </div>
// In the above case #container's size will depend on the end margin strut of
// #another-zero, even though usually it wouldn't.
- bool empty_or_sibling_empty_affected_by_clearance =
- empty_block_affected_by_clearance ||
- (previous_inflow_position.empty_block_affected_by_clearance &&
- is_empty_block);
+ bool self_or_sibling_self_collapsing_child_had_clearance =
+ self_collapsing_child_had_clearance ||
+ (previous_inflow_position.self_collapsing_child_had_clearance &&
+ is_self_collapsing);
return {logical_block_offset, margin_strut,
- empty_or_sibling_empty_affected_by_clearance};
+ self_or_sibling_self_collapsing_child_had_clearance};
}
-LayoutUnit NGBlockLayoutAlgorithm::PositionEmptyChildWithParentBfc(
+LayoutUnit NGBlockLayoutAlgorithm::PositionSelfCollapsingChildWithParentBfc(
const NGLayoutInputNode& child,
const NGConstraintSpace& child_space,
const NGInflowChildData& child_data,
const NGLayoutResult& layout_result) const {
- DCHECK(IsEmptyBlock(child_space, layout_result));
+ DCHECK(layout_result.IsSelfCollapsing());
// The child must be an in-flow zero-block-size fragment, use its end margin
// strut for positioning.
@@ -1719,7 +1751,7 @@ void NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
LayoutUnit used_block_size =
BreakToken() ? BreakToken()->UsedBlockSize() : LayoutUnit();
LayoutUnit block_size =
- ComputeBlockSizeForFragment(ConstraintSpace(), Style(), border_padding_,
+ ComputeBlockSizeForFragment(ConstraintSpace(), Node(), border_padding_,
used_block_size + intrinsic_block_size_);
block_size -= used_block_size;
@@ -1802,7 +1834,7 @@ bool NGBlockLayoutAlgorithm::BreakBeforeChild(
// Calculate space shortage: Figure out how much more space would have been
// sufficient to make the child fit right here in the current fragment.
NGFragment fragment(ConstraintSpace().GetWritingMode(),
- *layout_result.PhysicalFragment());
+ layout_result.PhysicalFragment());
LayoutUnit space_left = space_available - block_offset;
space_shortage = fragment.BlockSize() - space_left;
} else {
@@ -1925,8 +1957,8 @@ NGBlockLayoutAlgorithm::BreakType NGBlockLayoutAlgorithm::BreakTypeBeforeChild(
if (!container_builder_.BfcBlockOffset().has_value())
return NoBreak;
- const NGPhysicalFragment& physical_fragment =
- *layout_result.PhysicalFragment();
+ const NGPhysicalContainerFragment& physical_fragment =
+ layout_result.PhysicalFragment();
// If we haven't used any space at all in the fragmentainer yet, we cannot
// break, or there'd be no progress. We'd end up creating an infinite number
@@ -2050,15 +2082,17 @@ NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins(
NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
const NGLayoutInputNode child,
const NGInflowChildData& child_data,
- const NGLogicalSize child_available_size,
- const base::Optional<LayoutUnit> floats_bfc_block_offset) {
+ const LogicalSize child_available_size,
+ bool is_new_fc,
+ const base::Optional<LayoutUnit> forced_bfc_block_offset,
+ bool has_clearance_past_adjoining_floats) {
const ComputedStyle& style = Style();
const ComputedStyle& child_style = child.Style();
WritingMode child_writing_mode =
child.IsInline() ? style.GetWritingMode() : child_style.GetWritingMode();
NGConstraintSpaceBuilder builder(ConstraintSpace(), child_writing_mode,
- child_data.is_new_fc);
+ is_new_fc);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), child, &builder);
if (!IsParallelWritingMode(ConstraintSpace().GetWritingMode(),
@@ -2075,6 +2109,9 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
ConstraintSpace().IsFixedSizeBlock()
? NGTableCellChildLayoutPhase::kLayout
: NGTableCellChildLayoutPhase::kMeasure);
+
+ if (Node().IsRestrictedBlockSizeTableCell())
+ builder.SetIsInRestrictedBlockSizeTableCell();
}
if (NGBaseline::ShouldPropagateBaselines(child))
@@ -2083,13 +2120,22 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
builder.SetBfcOffset(child_data.bfc_offset_estimate)
.SetMarginStrut(child_data.margin_strut);
- if (!container_builder_.BfcBlockOffset() &&
- ConstraintSpace().FloatsBfcBlockOffset()) {
- builder.SetFloatsBfcBlockOffset(*ConstraintSpace().FloatsBfcBlockOffset());
- }
+ bool has_bfc_block_offset = container_builder_.BfcBlockOffset().has_value();
- if (floats_bfc_block_offset)
- builder.SetFloatsBfcBlockOffset(floats_bfc_block_offset);
+ // Propagate the |NGConstraintSpace::ForcedBfcBlockOffset| down to our
+ // children.
+ if (!has_bfc_block_offset && ConstraintSpace().ForcedBfcBlockOffset())
+ builder.SetForcedBfcBlockOffset(*ConstraintSpace().ForcedBfcBlockOffset());
+ if (forced_bfc_block_offset)
+ builder.SetForcedBfcBlockOffset(forced_bfc_block_offset);
+
+ // Propagate the |NGConstraintSpace::AncestorHasClearancePastAdjoiningFloats|
+ // flag down to our children.
+ if (!has_bfc_block_offset &&
+ ConstraintSpace().AncestorHasClearancePastAdjoiningFloats())
+ builder.SetAncestorHasClearancePastAdjoiningFloats();
+ if (has_clearance_past_adjoining_floats)
+ builder.SetAncestorHasClearancePastAdjoiningFloats();
LayoutUnit clearance_offset = ConstraintSpace().IsNewFormattingContext()
? LayoutUnit::Min()
@@ -2109,12 +2155,11 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
builder.SetTextDirection(style.Direction());
}
builder.SetClearanceOffset(clearance_offset);
- if (child_data.force_clearance)
- builder.SetShouldForceClearance(true);
- if (!child_data.is_new_fc) {
+ if (!is_new_fc) {
builder.SetExclusionSpace(exclusion_space_);
- builder.SetAdjoiningFloatTypes(container_builder_.AdjoiningFloatTypes());
+ if (!has_bfc_block_offset)
+ builder.SetAdjoiningFloatTypes(container_builder_.AdjoiningFloatTypes());
}
LayoutUnit space_available;
@@ -2123,9 +2168,9 @@ NGConstraintSpace NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
// If a block establishes a new formatting context we must know our
// position in the formatting context, and are able to adjust the
// fragmentation line.
- if (child_data.is_new_fc) {
+ if (is_new_fc)
space_available -= child_data.bfc_offset_estimate.block_offset;
- }
+
// The policy regarding collapsing block-start margin with the fragmentainer
// block-start is the same throughout the entire fragmentainer (although it
// really only matters at the beginning of each fragmentainer, we don't need
@@ -2172,23 +2217,23 @@ LayoutUnit NGBlockLayoutAlgorithm::ComputeLineBoxBaselineOffset(
// Add a baseline from a child box fragment.
// @return false if the specified child is not a box or is OOF.
bool NGBlockLayoutAlgorithm::AddBaseline(const NGBaselineRequest& request,
- const NGPhysicalFragment* child,
+ const NGPhysicalFragment& child,
LayoutUnit child_offset) {
- if (child->IsLineBox()) {
- const auto* line_box = To<NGPhysicalLineBoxFragment>(child);
+ if (child.IsLineBox()) {
+ const auto& line_box = To<NGPhysicalLineBoxFragment>(child);
// Skip over a line-box which is empty. These don't have any baselines which
// should be added.
- if (line_box->IsEmptyLineBox())
+ if (line_box.IsEmptyLineBox())
return false;
LayoutUnit offset =
- ComputeLineBoxBaselineOffset(request, *line_box, child_offset);
+ ComputeLineBoxBaselineOffset(request, line_box, child_offset);
container_builder_.AddBaseline(request, offset);
return true;
}
- if (child->IsFloatingOrOutOfFlowPositioned())
+ if (child.IsFloatingOrOutOfFlowPositioned())
return false;
if (const auto* box = DynamicTo<NGPhysicalBoxFragment>(child)) {
@@ -2210,21 +2255,20 @@ void NGBlockLayoutAlgorithm::PropagateBaselinesFromChildren() {
for (const auto& request : requests) {
switch (request.AlgorithmType()) {
- case NGBaselineAlgorithmType::kAtomicInline:
+ case NGBaselineAlgorithmType::kAtomicInline: {
if (Node().UseLogicalBottomMarginEdgeForInlineBlockBaseline())
break;
- for (unsigned i = container_builder_.Children().size(); i--;) {
- if (AddBaseline(request, container_builder_.Children()[i].get(),
- container_builder_.Offsets()[i].block_offset))
+ const auto& children = container_builder_.Children();
+ for (auto it = children.rbegin(); it != children.rend(); ++it) {
+ if (AddBaseline(request, *it->fragment, it->offset.block_offset))
break;
}
break;
-
+ }
case NGBaselineAlgorithmType::kFirstLine:
- for (unsigned i = 0; i < container_builder_.Children().size(); i++) {
- if (AddBaseline(request, container_builder_.Children()[i].get(),
- container_builder_.Offsets()[i].block_offset))
+ for (const auto& child : container_builder_.Children()) {
+ if (AddBaseline(request, *child.fragment, child.offset.block_offset))
break;
}
break;
@@ -2234,17 +2278,19 @@ void NGBlockLayoutAlgorithm::PropagateBaselinesFromChildren() {
bool NGBlockLayoutAlgorithm::ResolveBfcBlockOffset(
NGPreviousInflowPosition* previous_inflow_position,
- LayoutUnit bfc_block_offset) {
+ LayoutUnit bfc_block_offset,
+ base::Optional<LayoutUnit> forced_bfc_block_offset) {
if (container_builder_.BfcBlockOffset()) {
DCHECK(unpositioned_floats_.IsEmpty());
return true;
}
+ bfc_block_offset = forced_bfc_block_offset.value_or(bfc_block_offset);
+
if (ApplyClearance(ConstraintSpace(), &bfc_block_offset))
container_builder_.SetIsPushedByFloats();
container_builder_.SetBfcBlockOffset(bfc_block_offset);
- container_builder_.ResetAdjoiningFloatTypes();
if (NeedsAbortOnBfcBlockOffsetChange())
return false;
@@ -2274,22 +2320,19 @@ bool NGBlockLayoutAlgorithm::NeedsAbortOnBfcBlockOffsetChange() const {
if (!abort_when_bfc_block_offset_updated_)
return false;
// If no previous BFC block offset was set, we need to abort.
- if (!ConstraintSpace().FloatsBfcBlockOffset())
+ if (!ConstraintSpace().ForcedBfcBlockOffset())
return true;
+
// If the previous BFC block offset matches the new one, we can continue.
// Otherwise, we need to abort.
- LayoutUnit old_bfc_block_offset = *ConstraintSpace().FloatsBfcBlockOffset();
-
- // In order to determine if the two offsets are equal, we also need to adjust
- // floats offset by the clearance offset.
- ApplyClearance(ConstraintSpace(), &old_bfc_block_offset);
- return *container_builder_.BfcBlockOffset() != old_bfc_block_offset;
+ return *container_builder_.BfcBlockOffset() !=
+ *ConstraintSpace().ForcedBfcBlockOffset();
}
void NGBlockLayoutAlgorithm::PositionPendingFloats(
LayoutUnit origin_block_offset) {
DCHECK(container_builder_.BfcBlockOffset() ||
- ConstraintSpace().FloatsBfcBlockOffset())
+ ConstraintSpace().ForcedBfcBlockOffset())
<< "The parent BFC block offset should be known here.";
NGBfcOffset origin_bfc_offset = {
@@ -2299,7 +2342,7 @@ void NGBlockLayoutAlgorithm::PositionPendingFloats(
LayoutUnit bfc_block_offset = container_builder_.BfcBlockOffset()
? *container_builder_.BfcBlockOffset()
- : *ConstraintSpace().FloatsBfcBlockOffset();
+ : *ConstraintSpace().ForcedBfcBlockOffset();
NGBfcOffset bfc_offset = {ConstraintSpace().BfcOffset().line_offset,
bfc_block_offset};
@@ -2310,16 +2353,17 @@ void NGBlockLayoutAlgorithm::PositionPendingFloats(
replaced_child_percentage_size_, origin_bfc_offset, &unpositioned_float,
ConstraintSpace(), Style(), &exclusion_space_);
- NGFragment child_fragment(
- ConstraintSpace().GetWritingMode(),
- *positioned_float.layout_result->PhysicalFragment());
+ const auto& physical_fragment =
+ positioned_float.layout_result->PhysicalFragment();
+ LayoutUnit float_inline_size =
+ NGFragment(ConstraintSpace().GetWritingMode(), physical_fragment)
+ .InlineSize();
- NGLogicalOffset logical_offset = LogicalFromBfcOffsets(
- positioned_float.bfc_offset, bfc_offset, child_fragment.InlineSize(),
+ LogicalOffset logical_offset = LogicalFromBfcOffsets(
+ positioned_float.bfc_offset, bfc_offset, float_inline_size,
container_builder_.Size().inline_size, ConstraintSpace().Direction());
- container_builder_.AddChild(*positioned_float.layout_result,
- logical_offset);
+ container_builder_.AddChild(physical_fragment, logical_offset);
container_builder_.PropagateBreak(*positioned_float.layout_result);
}
@@ -2329,7 +2373,7 @@ void NGBlockLayoutAlgorithm::PositionPendingFloats(
LayoutUnit NGBlockLayoutAlgorithm::CalculateMinimumBlockSize(
const NGMarginStrut& end_margin_strut) {
if (!Node().GetDocument().InQuirksMode())
- return NGSizeIndefinite;
+ return kIndefiniteSize;
if (Node().IsDocumentElement() && Node().Style().LogicalHeight().IsAuto()) {
return ConstraintSpace().AvailableSize().block_size -
@@ -2353,12 +2397,12 @@ LayoutUnit NGBlockLayoutAlgorithm::CalculateMinimumBlockSize(
ConstraintSpace().AvailableSize().block_size - margin_sum;
return minimum_block_size.ClampNegativeToZero();
}
- return NGSizeIndefinite;
+ return kIndefiniteSize;
}
void NGBlockLayoutAlgorithm::PositionOrPropagateListMarker(
const NGLayoutResult& layout_result,
- NGLogicalOffset* content_offset) {
+ LogicalOffset* content_offset) {
// If this is not a list-item, propagate unpositioned list markers to
// ancestors.
if (!node_.IsListItem()) {
@@ -2379,7 +2423,7 @@ void NGBlockLayoutAlgorithm::PositionOrPropagateListMarker(
container_builder_.SetUnpositionedListMarker(NGUnpositionedListMarker());
}
if (list_marker.AddToBox(ConstraintSpace(), Style().GetFontBaseline(),
- *layout_result.PhysicalFragment(), content_offset,
+ layout_result.PhysicalFragment(), content_offset,
&container_builder_, border_scrollbar_padding_))
return;
@@ -2403,10 +2447,10 @@ void NGBlockLayoutAlgorithm::PositionListMarkerWithoutLineBoxes() {
// well-defined, but 3 out of 4 impls do.
// https://github.com/w3c/csswg-drafts/issues/2418
//
- // TODO(kojii): Since this makes this block non-empty, it's probably better to
- // resolve BFC block offset if not done yet, but that involves additional
- // complexity without knowing how much this is needed. For now, include the
- // marker into the block size only if BFC was resolved.
+ // TODO(kojii): Since this makes this block non self-collapsing, it's
+ // probably better to resolve BFC block-offset if not done yet, but that
+ // involves additional complexity without knowing how much this is needed.
+ // For now, include the marker into the block-size only if BFC was resolved.
if (container_builder_.BfcBlockOffset()) {
intrinsic_block_size_ = std::max(marker_block_size, intrinsic_block_size_);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size_);
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 07dffd9071e..70db5f9ab8a 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
@@ -29,7 +29,7 @@ class NGPhysicalLineBoxFragment;
struct NGPreviousInflowPosition {
LayoutUnit logical_block_offset;
NGMarginStrut margin_strut;
- bool empty_block_affected_by_clearance;
+ bool self_collapsing_child_had_clearance;
};
// This strut holds information for the current inflow child. The data is not
@@ -39,8 +39,6 @@ struct NGInflowChildData {
NGMarginStrut margin_strut;
NGBoxStrut margins;
bool margins_fully_resolved;
- bool force_clearance;
- bool is_new_fc;
};
// A class for general block layout (e.g. a <div> with no special style).
@@ -51,13 +49,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
NGBlockBreakToken> {
public:
// Default constructor.
- // @param node The input node to perform layout upon.
- // @param space The constraint space which the algorithm should generate a
- // fragment within.
- // @param break_token The break token from which the layout should start.
- NGBlockLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBlockBreakToken* break_token = nullptr);
+ NGBlockLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
~NGBlockLayoutAlgorithm() override;
@@ -76,7 +68,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
scoped_refptr<const NGLayoutResult> FinishLayout(
NGPreviousInflowPosition*,
- NGLogicalSize border_box_size,
+ LogicalSize border_box_size,
const NGBoxStrut& borders,
const NGBoxStrut& scrollbars);
@@ -107,14 +99,15 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
NGConstraintSpace CreateConstraintSpaceForChild(
const NGLayoutInputNode child,
const NGInflowChildData& child_data,
- const NGLogicalSize child_available_size,
- const base::Optional<LayoutUnit> floats_bfc_block_offset = base::nullopt);
+ const LogicalSize child_available_size,
+ bool is_new_fc,
+ const base::Optional<LayoutUnit> forced_bfc_block_offset = base::nullopt,
+ bool has_clearance_past_adjoining_floats = false);
// @return Estimated BFC block offset for the "to be layout" child.
NGInflowChildData ComputeChildData(const NGPreviousInflowPosition&,
NGLayoutInputNode,
const NGBreakToken* child_break_token,
- bool force_clearance,
bool is_new_fc);
NGPreviousInflowPosition ComputeInflowPosition(
@@ -122,19 +115,19 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
const NGLayoutInputNode child,
const NGInflowChildData&,
const base::Optional<LayoutUnit>& child_bfc_block_offset,
- const NGLogicalOffset&,
+ const LogicalOffset&,
const NGLayoutResult&,
const NGFragment&,
- bool empty_block_affected_by_clearance);
+ bool self_collapsing_child_had_clearance);
- // Position an empty child using the parent BFC block offset.
+ // Position an self-collapsing child using the parent BFC block-offset.
// The fragment doesn't know its offset, but we can still calculate its BFC
// position because the parent fragment's BFC is known.
// Example:
// BFC Offset is known here because of the padding.
// <div style="padding: 1px">
- // <div id="empty-div" style="margin: 1px"></div>
- LayoutUnit PositionEmptyChildWithParentBfc(
+ // <div id="zero" style="margin: 1px"></div>
+ LayoutUnit PositionSelfCollapsingChildWithParentBfc(
const NGLayoutInputNode& child,
const NGConstraintSpace& child_space,
const NGInflowChildData& child_data,
@@ -198,6 +191,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
NGLayoutInputNode child,
const NGBreakToken* child_break_token,
const NGConstraintSpace&,
+ bool has_clearance_past_adjoining_floats,
scoped_refptr<const NGLayoutResult>,
NGInflowChildData*,
NGPreviousInflowPosition*,
@@ -239,7 +233,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
void PropagateBaselinesFromChildren();
bool AddBaseline(const NGBaselineRequest&,
- const NGPhysicalFragment*,
+ const NGPhysicalFragment&,
LayoutUnit child_offset);
// Compute the baseline offset of a line box from the content box.
@@ -252,18 +246,35 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// If still unresolved, resolve the fragment's BFC block offset.
//
- // This includes applying clearance, so the bfc_block_offset passed won't be
- // the final BFC block offset, if it wasn't large enough to get past all
- // relevant floats. The updated BFC block offset can be read out with
- // ContainerBfcBlockOffset().
+ // This includes applying clearance, so the |bfc_block_offset| passed won't
+ // be the final BFC block-offset, if it wasn't large enough to get past all
+ // relevant floats. The updated BFC block-offset can be read out with
+ // |ContainerBfcBlockOffset()|.
+ //
+ // If the |forced_bfc_block_offset| has a value, it will override the given
+ // |bfc_block_offset|. Typically this comes from the input constraints, when
+ // the current node has clearance past adjoining floats, or has a re-layout
+ // due to a child resolving the BFC block-offset.
//
// In addition to resolving our BFC block offset, this will also position
- // pending floats, and update our in-flow layout state. Returns false if
- // resolving the BFC block offset resulted in needing to abort layout. It
- // will always return true otherwise. If the BFC block offset was already
- // resolved, this method does nothing (and returns true).
- bool ResolveBfcBlockOffset(NGPreviousInflowPosition*,
- LayoutUnit bfc_block_offset);
+ // pending floats, and update our in-flow layout state.
+ //
+ // Returns false if resolving the BFC block-offset resulted in needing to
+ // abort layout. It will always return true otherwise. If the BFC
+ // block-offset was already resolved, this method does nothing (and returns
+ // true).
+ bool ResolveBfcBlockOffset(
+ NGPreviousInflowPosition*,
+ LayoutUnit bfc_block_offset,
+ const base::Optional<LayoutUnit> forced_bfc_block_offset);
+
+ // This passes in the |forced_bfc_block_offset| from the input constraints,
+ // which is almost always desired.
+ bool ResolveBfcBlockOffset(NGPreviousInflowPosition* previous_inflow_position,
+ LayoutUnit bfc_block_offset) {
+ return ResolveBfcBlockOffset(previous_inflow_position, bfc_block_offset,
+ ConstraintSpace().ForcedBfcBlockOffset());
+ }
// A very common way to resolve the BFC block offset is to simply commit the
// pending margin, so here's a convenience overload for that.
@@ -281,7 +292,7 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
void PositionPendingFloats(LayoutUnit origin_block_offset);
// Positions a list marker for the specified block content.
- void PositionOrPropagateListMarker(const NGLayoutResult&, NGLogicalOffset*);
+ void PositionOrPropagateListMarker(const NGLayoutResult&, LogicalOffset*);
// Positions a list marker when the block does not have any line boxes.
void PositionListMarkerWithoutLineBoxes();
@@ -290,21 +301,22 @@ class CORE_EXPORT NGBlockLayoutAlgorithm
// intrinsic_block_size_} when the fragment doesn't know it's offset or
// {@code known_fragment_offset} if the fragment knows it's offset
// @return Fragment's offset relative to the fragment's parent.
- NGLogicalOffset CalculateLogicalOffset(
+ LogicalOffset CalculateLogicalOffset(
const NGFragment& fragment,
LayoutUnit child_bfc_line_offset,
const base::Optional<LayoutUnit>& child_bfc_block_offset);
// Computes minimum size for HTML and BODY elements in quirks mode.
- // Returns NGSizeIndefinite in all other cases.
+ // Returns kIndefiniteSize in all other cases.
LayoutUnit CalculateMinimumBlockSize(const NGMarginStrut& end_margin_strut);
- NGLogicalSize child_available_size_;
- NGLogicalSize child_percentage_size_;
- NGLogicalSize replaced_child_percentage_size_;
-
- NGBoxStrut border_padding_;
+ const NGBoxStrut border_padding_;
NGBoxStrut border_scrollbar_padding_;
+
+ LogicalSize child_available_size_;
+ LogicalSize child_percentage_size_;
+ LogicalSize replaced_child_percentage_size_;
+
LayoutUnit intrinsic_block_size_;
// The line box index at which we ran out of space. This where we'll actually
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 94559f82244..986c3484c6f 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -33,23 +33,16 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
NGBaseLayoutAlgorithmTest::SetUp();
}
- scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
- const NGConstraintSpace& space,
- NGBlockNode node) {
- scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, space).Layout();
-
- return To<NGPhysicalBoxFragment>(result->PhysicalFragment());
- }
-
MinMaxSize RunComputeMinAndMax(NGBlockNode node) {
// The constraint space is not used for min/max computation, but we need
// it to create the algorithm.
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(), LayoutUnit()));
+ LogicalSize(LayoutUnit(), LayoutUnit()));
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(space, node);
- NGBlockLayoutAlgorithm algorithm(node, space);
+ NGBlockLayoutAlgorithm algorithm({node, fragment_geometry, space});
MinMaxSizeInput input(
/* percentage_resolution_block_size */ (LayoutUnit()));
auto min_max = algorithm.ComputeMinMaxSize(input);
@@ -57,6 +50,16 @@ class NGBlockLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
return *min_max;
}
+ scoped_refptr<const NGLayoutResult> RunCachedLayoutResult(
+ const NGConstraintSpace& space,
+ const NGBlockNode& node) {
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> initial_fragment_geometry;
+ return To<LayoutBlockFlow>(node.GetLayoutBox())
+ ->CachedLayoutResult(space, nullptr, &initial_fragment_geometry,
+ &cache_status);
+ }
+
String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
NGPhysicalFragment::DumpFlags flags =
NGPhysicalFragment::DumpHeaderText | NGPhysicalFragment::DumpSubtree |
@@ -83,14 +86,14 @@ TEST_F(NGBlockLayoutAlgorithmTest, FixedSize) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(100), kIndefiniteSize));
NGBlockNode box(ToLayoutBox(GetLayoutObjectByElementId("box")));
- scoped_refptr<const NGPhysicalFragment> frag =
- RunBlockLayoutAlgorithm(space, box);
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(box, space);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(30), LayoutUnit(40)), frag->Size());
+ EXPECT_EQ(PhysicalSize(30, 40), fragment->Size());
}
TEST_F(NGBlockLayoutAlgorithmTest, Caching) {
@@ -102,44 +105,43 @@ TEST_F(NGBlockLayoutAlgorithmTest, Caching) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)));
auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box"));
NGBlockNode node(block_flow);
scoped_refptr<const NGLayoutResult> result(node.Layout(space, nullptr));
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(30), LayoutUnit(40)),
- result->PhysicalFragment()->Size());
+ EXPECT_EQ(PhysicalSize(30, 40), result->PhysicalFragment().Size());
// Test pointer-equal constraint space.
- result = block_flow->CachedLayoutResult(space, nullptr);
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test identical, but not pointer-equal, constraint space.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test different constraint space.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(200), LayoutUnit(100)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(200), LayoutUnit(100)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test a different constraint space that will actually result in a different
// size.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(200), LayoutUnit(200)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(200), LayoutUnit(200)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_EQ(result.get(), nullptr);
// Test layout invalidation
block_flow->SetNeedsLayout("");
- result = block_flow->CachedLayoutResult(space, nullptr);
+ result = RunCachedLayoutResult(space, node);
EXPECT_EQ(result.get(), nullptr);
}
@@ -152,39 +154,38 @@ TEST_F(NGBlockLayoutAlgorithmTest, MinInlineSizeCaching) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)));
auto* block_flow = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box"));
NGBlockNode node(block_flow);
scoped_refptr<const NGLayoutResult> result(node.Layout(space, nullptr));
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(30), LayoutUnit(40)),
- result->PhysicalFragment()->Size());
+ EXPECT_EQ(PhysicalSize(30, 40), result->PhysicalFragment().Size());
// Test pointer-equal constraint space.
- result = block_flow->CachedLayoutResult(space, nullptr);
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test identical, but not pointer-equal, constraint space.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test different constraint space.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(200)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(100), LayoutUnit(200)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_NE(result.get(), nullptr);
// Test a different constraint space that will actually result in a different
// size.
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(200), LayoutUnit(100)));
- result = block_flow->CachedLayoutResult(space, nullptr);
+ LogicalSize(LayoutUnit(200), LayoutUnit(100)));
+ result = RunCachedLayoutResult(space, node);
EXPECT_EQ(result.get(), nullptr);
}
@@ -244,21 +245,22 @@ TEST_F(NGBlockLayoutAlgorithmTest, PercentageBlockSizeQuirkDescendantsCaching) {
};
NGConstraintSpace space100 =
- create_space(NGLogicalSize(LayoutUnit(100), LayoutUnit(100)));
+ create_space(LogicalSize(LayoutUnit(100), LayoutUnit(100)));
NGConstraintSpace space200 =
- create_space(NGLogicalSize(LayoutUnit(100), LayoutUnit(200)));
+ create_space(LogicalSize(LayoutUnit(100), LayoutUnit(200)));
auto run_test = [&](auto id) -> scoped_refptr<const NGLayoutResult> {
// Grab the box under test.
auto* box = To<LayoutBlockFlow>(GetLayoutObjectByElementId(id));
+ NGBlockNode node(box);
// Check that we have a cache hit with space100.
scoped_refptr<const NGLayoutResult> result =
- box->CachedLayoutResult(space100, nullptr);
+ RunCachedLayoutResult(space100, node);
EXPECT_NE(result.get(), nullptr);
// Return the result of the cache with space200.
- return box->CachedLayoutResult(space200, nullptr);
+ return RunCachedLayoutResult(space200, node);
};
// Test 1: No descendants.
@@ -309,8 +311,8 @@ TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFitCaching) {
<div style="display: inline-block; width: 350px;"></div>
<div style="display: inline-block; width: 250px;"></div>
</div>
- <div id="box3" style="float: left; min-width: 50%;">
- <div style="display: inline-block; width: 350px;"></div>
+ <div id="box3" style="float: left; min-width: 80%;">
+ <div style="display: inline-block; width: 150px;"></div>
<div style="display: inline-block; width: 250px;"></div>
</div>
<div id="box4" style="float: left; margin-left: 75px;">
@@ -322,23 +324,23 @@ TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFitCaching) {
NGConstraintSpace space100 = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), LayoutUnit(100)),
+ LogicalSize(LayoutUnit(100), LayoutUnit(100)),
/* shrink_to_fit */ true, /* is_new_formatting_context */ true);
NGConstraintSpace space200 = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(200), LayoutUnit(100)),
+ LogicalSize(LayoutUnit(200), LayoutUnit(100)),
/* shrink_to_fit */ true, /* is_new_formatting_context */ true);
NGConstraintSpace space250 = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(250), LayoutUnit(100)),
+ LogicalSize(LayoutUnit(250), LayoutUnit(100)),
/* shrink_to_fit */ true, /* is_new_formatting_context */ true);
NGConstraintSpace space300 = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(300), LayoutUnit(100)),
+ LogicalSize(LayoutUnit(300), LayoutUnit(100)),
/* shrink_to_fit */ true, /* is_new_formatting_context */ true);
NGConstraintSpace space400 = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(400), LayoutUnit(100)),
+ LogicalSize(LayoutUnit(400), LayoutUnit(100)),
/* shrink_to_fit */ true, /* is_new_formatting_context */ true);
scoped_refptr<const NGLayoutResult> result;
@@ -348,41 +350,41 @@ TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFitCaching) {
auto* box4 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box4"));
// Ensure we cached the result for box1 in the first layout pass.
- result = box1->CachedLayoutResult(space300, nullptr);
+ result = RunCachedLayoutResult(space300, NGBlockNode(box1));
EXPECT_NE(result.get(), nullptr);
// box1 was sized to its max-content size in the first layout pass, passing
// an available size larger than the fragment should hit the cache.
- result = box1->CachedLayoutResult(space400, nullptr);
+ result = RunCachedLayoutResult(space400, NGBlockNode(box1));
EXPECT_NE(result.get(), nullptr);
// Passing an available size smaller than the fragment should miss the cache
// as the fragment may shrink.
- result = box1->CachedLayoutResult(space100, nullptr);
+ result = RunCachedLayoutResult(space100, NGBlockNode(box1));
EXPECT_EQ(result.get(), nullptr);
// Ensure we cached the result for box2 in the first layout pass.
- result = box2->CachedLayoutResult(space300, nullptr);
+ result = RunCachedLayoutResult(space300, NGBlockNode(box2));
EXPECT_NE(result.get(), nullptr);
// box2 was sized to its min-content size in the first layout pass, passing
// an available size smaller than the fragment should hit the cache.
- result = box2->CachedLayoutResult(space200, nullptr);
+ result = RunCachedLayoutResult(space200, NGBlockNode(box2));
EXPECT_NE(result.get(), nullptr);
// Passing an available size larger than the fragment should miss the cache
// as the fragment may shrink.
- result = box2->CachedLayoutResult(space400, nullptr);
+ result = RunCachedLayoutResult(space400, NGBlockNode(box2));
EXPECT_EQ(result.get(), nullptr);
// box3 was sized to its min-content size in the first layout pass, however
// it should miss the cache as it has a %-min-size.
- result = box3->CachedLayoutResult(space200, nullptr);
+ result = RunCachedLayoutResult(space200, NGBlockNode(box3));
EXPECT_EQ(result.get(), nullptr);
// box4 was sized to its max-content size in the first layout pass (the same
// as box1) however it should miss the cache due to its margin.
- result = box4->CachedLayoutResult(space250, nullptr);
+ result = RunCachedLayoutResult(space250, NGBlockNode(box4));
EXPECT_EQ(result.get(), nullptr);
}
@@ -411,14 +413,14 @@ TEST_F(NGBlockLayoutAlgorithmTest, LineOffsetCaching) {
};
NGConstraintSpace space200 =
- create_space(NGLogicalSize(LayoutUnit(300), LayoutUnit(100)),
+ create_space(LogicalSize(LayoutUnit(300), LayoutUnit(100)),
NGBfcOffset(LayoutUnit(50), LayoutUnit()));
scoped_refptr<const NGLayoutResult> result;
auto* box1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("box1"));
// Ensure we get a cached layout result, even if our BFC line-offset changed.
- result = box1->CachedLayoutResult(space200, nullptr);
+ result = RunCachedLayoutResult(space200, NGBlockNode(box1));
EXPECT_NE(result.get(), nullptr);
}
@@ -440,21 +442,22 @@ TEST_F(NGBlockLayoutAlgorithmTest, LayoutBlockChildren) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(100), kIndefiniteSize));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- EXPECT_EQ(LayoutUnit(kWidth), frag->Size().width);
- EXPECT_EQ(LayoutUnit(kHeight1 + kHeight2 + kMarginTop), frag->Size().height);
- EXPECT_EQ(NGPhysicalFragment::kFragmentBox, frag->Type());
- ASSERT_EQ(frag->Children().size(), 2UL);
+ EXPECT_EQ(LayoutUnit(kWidth), fragment->Size().width);
+ EXPECT_EQ(LayoutUnit(kHeight1 + kHeight2 + kMarginTop),
+ fragment->Size().height);
+ EXPECT_EQ(NGPhysicalFragment::kFragmentBox, fragment->Type());
+ ASSERT_EQ(fragment->Children().size(), 2UL);
- const NGLink& first_child = frag->Children()[0];
+ const NGLink& first_child = fragment->Children()[0];
EXPECT_EQ(kHeight1, first_child->Size().height);
EXPECT_EQ(0, first_child.Offset().top);
- const NGLink& second_child = frag->Children()[1];
+ const NGLink& second_child = fragment->Children()[1];
EXPECT_EQ(kHeight2, second_child->Size().height);
EXPECT_EQ(kHeight1 + kMarginTop, second_child.Offset().top);
}
@@ -484,11 +487,11 @@ TEST_F(NGBlockLayoutAlgorithmTest, LayoutBlockChildrenWithWritingMode) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(500), LayoutUnit(500)));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(500), LayoutUnit(500)));
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- const NGLink& child = frag->Children()[0];
+ const NGLink& child = fragment->Children()[0];
const NGLink& child2 =
static_cast<const NGPhysicalBoxFragment*>(child.get())->Children()[0];
@@ -542,7 +545,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase1WithFloats) {
std::tie(fragment, space) = RunBlockLayoutAlgorithmForElement(
GetDocument().getElementsByTagName("html")->item(0));
ASSERT_EQ(fragment->Children().size(), 1UL);
- NGPhysicalOffset body_offset = fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = fragment->Children()[0].Offset();
auto* body_fragment =
To<NGPhysicalBoxFragment>(fragment->Children()[0].get());
// 20 = max(first child's margin top, containers's margin top)
@@ -555,13 +558,13 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase1WithFloats) {
auto* container_fragment =
To<NGPhysicalBoxFragment>(body_fragment->Children()[0].get());
- NGPhysicalOffset container_offset = body_fragment->Children()[0].Offset();
+ PhysicalOffset container_offset = body_fragment->Children()[0].Offset();
// 0 = collapsed with body's margin
EXPECT_THAT(LayoutUnit(0), container_offset.top);
ASSERT_EQ(3UL, container_fragment->Children().size());
- NGPhysicalOffset child_offset = container_fragment->Children()[2].Offset();
+ PhysicalOffset child_offset = container_fragment->Children()[2].Offset();
// 0 = collapsed with container's margin
EXPECT_THAT(LayoutUnit(0), child_offset.top);
@@ -626,13 +629,13 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase2WithFloats) {
auto* body_fragment =
To<NGPhysicalBoxFragment>(fragment->Children()[0].get());
- NGPhysicalOffset body_offset = fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = fragment->Children()[0].Offset();
// -7 = empty1's margin(-15) + body's margin(8)
EXPECT_THAT(LayoutUnit(-7), body_offset.top);
ASSERT_EQ(4UL, body_fragment->Children().size());
FragmentChildIterator iterator(body_fragment);
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
iterator.NextChild(&offset);
EXPECT_THAT(LayoutUnit(), offset.top);
@@ -698,13 +701,13 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase3) {
run_test(Length::Auto());
// Margins are collapsed with the result 200 = std::max(20, 200)
// The fragment size 258 == body's margin 8 + child's height 50 + 200
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(258)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(800, 258), fragment->Size());
// height == fixed
run_test(Length::Fixed(50));
// Margins are not collapsed, so fragment still has margins == 20.
// The fragment size 78 == body's margin 8 + child's height 50 + 20
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(78)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(800, 78), fragment->Size());
}
// Verifies that 2 adjoining margins are not collapsed if there is padding or
@@ -727,9 +730,9 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase4) {
</div>
)HTML");
- NGPhysicalOffset body_offset;
- NGPhysicalOffset container_offset;
- NGPhysicalOffset child_offset;
+ PhysicalOffset body_offset;
+ PhysicalOffset container_offset;
+ PhysicalOffset child_offset;
scoped_refptr<const NGPhysicalBoxFragment> fragment;
auto run_test = [&](const Length& container_padding_top) {
Element* container = GetDocument().getElementById("container");
@@ -752,7 +755,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase4) {
run_test(Length::Fixed(20));
// 500 = child's height 50 + 2xmargin 400 + paddint-top 20 +
// container's margin 30
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(500)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(800, 500), fragment->Size());
// 30 = max(body's margin 8, container margin 30)
EXPECT_EQ(LayoutUnit(30), body_offset.top);
// 220 = container's padding top 20 + child's margin
@@ -762,7 +765,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase4) {
run_test(Length::Fixed(0));
// 450 = 2xmax(body's margin 8, container's margin 30, child's margin 200) +
// child's height 50
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(450)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(800, 450), fragment->Size());
// 200 = (body's margin 8, container's margin 30, child's margin 200)
EXPECT_EQ(LayoutUnit(200), body_offset.top);
// 0 = collapsed margins
@@ -804,7 +807,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase5) {
// body
auto* body_fragment =
To<NGPhysicalBoxFragment>(fragment->Children()[0].get());
- NGPhysicalOffset body_offset = fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = fragment->Children()[0].Offset();
// 10 = std::max(body's margin 8, container's margin top)
int body_top_offset = 10;
EXPECT_THAT(body_offset.top, LayoutUnit(body_top_offset));
@@ -812,27 +815,25 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase5) {
EXPECT_THAT(body_offset.left, LayoutUnit(body_left_offset));
// height = 70. std::max(vertical height's 70, horizontal's height's 60)
- ASSERT_EQ(NGPhysicalSize(LayoutUnit(784), LayoutUnit(70)),
- body_fragment->Size());
+ ASSERT_EQ(PhysicalSize(784, 70), body_fragment->Size());
ASSERT_EQ(1UL, body_fragment->Children().size());
// container
auto* container_fragment =
To<NGPhysicalBoxFragment>(body_fragment->Children()[0].get());
- NGPhysicalOffset container_offset = body_fragment->Children()[0].Offset();
+ PhysicalOffset container_offset = body_fragment->Children()[0].Offset();
// Container's margins are collapsed with body's fragment.
EXPECT_THAT(container_offset.top, LayoutUnit());
EXPECT_THAT(container_offset.left, LayoutUnit());
ASSERT_EQ(2UL, container_fragment->Children().size());
// vertical
- NGPhysicalOffset vertical_offset = container_fragment->Children()[0].Offset();
+ PhysicalOffset vertical_offset = container_fragment->Children()[0].Offset();
EXPECT_THAT(vertical_offset.top, LayoutUnit());
EXPECT_THAT(vertical_offset.left, LayoutUnit());
// horizontal
- NGPhysicalOffset orizontal_offset =
- container_fragment->Children()[1].Offset();
+ PhysicalOffset orizontal_offset = container_fragment->Children()[1].Offset();
EXPECT_THAT(orizontal_offset.top, LayoutUnit());
// 130 = vertical's width 30 +
// std::max(vertical's margin right 90, horizontal's margin-left 100)
@@ -859,13 +860,13 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsWithText) {
const NGPhysicalBoxFragment* body_fragment =
To<NGPhysicalBoxFragment>(html_fragment->Children()[0].get());
- NGPhysicalOffset body_offset = html_fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = html_fragment->Children()[0].Offset();
// 20 = std::max(body's margin, p's margin)
- EXPECT_THAT(body_offset, NGPhysicalOffset(LayoutUnit(10), LayoutUnit(20)));
+ EXPECT_THAT(body_offset, PhysicalOffset(10, 20));
- NGPhysicalOffset p_offset = body_fragment->Children()[0].Offset();
+ PhysicalOffset p_offset = body_fragment->Children()[0].Offset();
// Collapsed margins with result = 0.
- EXPECT_THAT(p_offset, NGPhysicalOffset(LayoutUnit(20), LayoutUnit(0)));
+ EXPECT_THAT(p_offset, PhysicalOffset(20, 0));
}
// Verifies that the margin strut of a child with a different writing mode does
@@ -896,18 +897,18 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase6) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(500), LayoutUnit(500)));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(500), LayoutUnit(500)));
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- ASSERT_EQ(frag->Children().size(), 2UL);
+ ASSERT_EQ(fragment->Children().size(), 2UL);
- const NGPhysicalFragment* child1 = frag->Children()[0].get();
- NGPhysicalOffset child1_offset = frag->Children()[0].Offset();
+ const NGPhysicalFragment* child1 = fragment->Children()[0].get();
+ PhysicalOffset child1_offset = fragment->Children()[0].Offset();
EXPECT_EQ(0, child1_offset.top);
EXPECT_EQ(kHeight, child1->Size().height);
- NGPhysicalOffset child2_offset = frag->Children()[1].Offset();
+ PhysicalOffset child2_offset = fragment->Children()[1].Offset();
EXPECT_EQ(kHeight + std::max(kMarginBottom, kMarginTop), child2_offset.top);
}
@@ -951,21 +952,21 @@ TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase7) {
FragmentChildIterator iterator(fragment.get());
// body
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(8), LayoutUnit(20)), offset);
+ EXPECT_EQ(PhysicalSize(200, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(8, 20), offset);
// #zero
iterator.SetParent(child);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(0)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(200, 0), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
// #inflow
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(200, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
}
// An empty block level element (with margins collapsing through it) has
@@ -1198,15 +1199,15 @@ TEST_F(NGBlockLayoutAlgorithmTest, BorderAndPadding) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- ASSERT_EQ(frag->Children().size(), 1UL);
+ ASSERT_EQ(fragment->Children().size(), 1UL);
// div1
- const NGPhysicalFragment* child = frag->Children()[0].get();
+ const NGPhysicalFragment* child = fragment->Children()[0].get();
EXPECT_EQ(kBorderLeft + kPaddingLeft + kWidth + kPaddingRight + kBorderRight,
child->Size().width);
EXPECT_EQ(kBorderTop + kPaddingTop + kHeight + kPaddingBottom + kBorderBottom,
@@ -1216,7 +1217,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, BorderAndPadding) {
ASSERT_EQ(static_cast<const NGPhysicalBoxFragment*>(child)->Children().size(),
1UL);
- NGPhysicalOffset div2_offset =
+ PhysicalOffset div2_offset =
static_cast<const NGPhysicalBoxFragment*>(child)->Children()[0].Offset();
EXPECT_EQ(kBorderTop + kPaddingTop, div2_offset.top);
EXPECT_EQ(kBorderLeft + kPaddingLeft, div2_offset.left);
@@ -1235,15 +1236,15 @@ TEST_F(NGBlockLayoutAlgorithmTest, PercentageResolutionSize) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(100), kIndefiniteSize));
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), frag->Size().width);
- EXPECT_EQ(NGPhysicalFragment::kFragmentBox, frag->Type());
- ASSERT_EQ(frag->Children().size(), 1UL);
+ EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), fragment->Size().width);
+ EXPECT_EQ(NGPhysicalFragment::kFragmentBox, fragment->Type());
+ ASSERT_EQ(fragment->Children().size(), 1UL);
- const NGPhysicalFragment* child = frag->Children()[0].get();
+ const NGPhysicalFragment* child = fragment->Children()[0].get();
EXPECT_EQ(LayoutUnit(12), child->Size().width);
}
@@ -1267,16 +1268,16 @@ TEST_F(NGBlockLayoutAlgorithmTest, AutoMargin) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), NGSizeIndefinite));
- scoped_refptr<const NGPhysicalBoxFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(100), kIndefiniteSize));
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), frag->Size().width);
- EXPECT_EQ(NGPhysicalFragment::kFragmentBox, frag->Type());
- ASSERT_EQ(1UL, frag->Children().size());
+ EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), fragment->Size().width);
+ EXPECT_EQ(NGPhysicalFragment::kFragmentBox, fragment->Type());
+ ASSERT_EQ(1UL, fragment->Children().size());
- const NGPhysicalFragment* child = frag->Children()[0].get();
- NGPhysicalOffset child_offset = frag->Children()[0].Offset();
+ const NGPhysicalFragment* child = fragment->Children()[0].get();
+ PhysicalOffset child_offset = fragment->Children()[0].Offset();
EXPECT_EQ(LayoutUnit(kChildWidth), child->Size().width);
EXPECT_EQ(LayoutUnit(kPaddingLeft + 10), child_offset.left);
EXPECT_EQ(LayoutUnit(0), child_offset.top);
@@ -1334,7 +1335,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatInsideEmptyBlocks) {
const auto* body_fragment =
To<NGPhysicalBoxFragment>(fragment->Children()[0].get());
- NGPhysicalOffset body_offset = fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = fragment->Children()[0].Offset();
FragmentChildIterator iterator(body_fragment);
// 20 = std::max(empty1's margin, empty2's margin, body's margin)
int body_top_offset = 20;
@@ -1345,7 +1346,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatInsideEmptyBlocks) {
ASSERT_EQ(1UL, container_fragment->Children().size());
iterator.SetParent(container_fragment);
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const auto* empty1_fragment = iterator.NextChild(&offset);
// 0, vertical margins got collapsed
EXPECT_THAT(offset.top, LayoutUnit());
@@ -1364,17 +1365,19 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatInsideEmptyBlocks) {
offset =
To<NGPhysicalLineBoxFragment>(linebox_fragment)->Children()[0].offset;
- // inline 10 = left float's margin(10)
+ // The floats are positioned outside the line-box as the line-box is
+ // "avoiding" these floats.
+ // inline -35 = inline-size of left-float (including margins).
// block 10 = left float's margin
- EXPECT_THAT(offset, NGPhysicalOffset(LayoutUnit(10), LayoutUnit(10)));
+ EXPECT_THAT(offset, PhysicalOffset(-35, 10));
offset =
To<NGPhysicalLineBoxFragment>(linebox_fragment)->Children()[1].offset;
- // inline offset 135 = right float's margin(10) + right float offset(125)
+ // inline offset 90 = right float's margin(10) + right float offset(80)
// block offset 15 = right float's margin
- LayoutUnit right_float_offset = LayoutUnit(125);
- EXPECT_THAT(offset, NGPhysicalOffset(LayoutUnit(10) + right_float_offset,
- LayoutUnit(15)));
+ LayoutUnit right_float_offset = LayoutUnit(80);
+ EXPECT_THAT(offset, PhysicalOffset(LayoutUnit(10) + right_float_offset,
+ LayoutUnit(15)));
// ** Verify layout tree **
Element* left_float = GetDocument().getElementById("left-float");
@@ -1445,7 +1448,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatFragments) {
ASSERT_EQ(1UL, fragment->Children().size());
const auto* body_fragment =
To<NGPhysicalBoxFragment>(fragment->Children()[0].get());
- NGPhysicalOffset body_offset = fragment->Children()[0].Offset();
+ PhysicalOffset body_offset = fragment->Children()[0].Offset();
EXPECT_THAT(LayoutUnit(8), body_offset.top);
FragmentChildIterator iterator(body_fragment);
@@ -1458,7 +1461,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatFragments) {
EXPECT_EQ(8, left_float->OffsetTop());
iterator.SetParent(container_fragment);
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
iterator.NextChild(&offset);
EXPECT_THAT(LayoutUnit(), offset.top);
@@ -1556,11 +1559,11 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionFragmentsWithClear) {
</div>
)HTML");
- NGPhysicalOffset clerance_offset;
- NGPhysicalOffset body_offset;
- NGPhysicalOffset container_offset;
- NGPhysicalOffset block_offset;
- NGPhysicalOffset adjoining_clearance_offset;
+ PhysicalOffset clerance_offset;
+ PhysicalOffset body_offset;
+ PhysicalOffset container_offset;
+ PhysicalOffset block_offset;
+ PhysicalOffset adjoining_clearance_offset;
scoped_refptr<const NGPhysicalBoxFragment> fragment;
auto run_with_clearance = [&](EClear clear_value) {
Element* el_with_clear = GetDocument().getElementById("clearance");
@@ -1770,11 +1773,11 @@ TEST_F(NGBlockLayoutAlgorithmTest, ShrinkToFit) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(100), NGSizeIndefinite), true);
- scoped_refptr<const NGPhysicalFragment> frag =
- RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(100), kIndefiniteSize), true);
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(container, space);
- EXPECT_EQ(LayoutUnit(kWidthChild2), frag->Size().width);
+ EXPECT_EQ(LayoutUnit(kWidthChild2), fragment->Size().width);
}
// Verifies that we position empty blocks and floats correctly inside of the
@@ -1813,17 +1816,15 @@ TEST_F(NGBlockLayoutAlgorithmTest, PositionEmptyBlocksInNewBfc) {
To<NGPhysicalBoxFragment>(html_fragment->Children()[0].get());
auto* container_fragment =
To<NGPhysicalBoxFragment>(body_fragment->Children()[0].get());
- NGPhysicalOffset empty_block1_offset =
+ PhysicalOffset empty_block1_offset =
container_fragment->Children()[1].Offset();
// empty-block1's margin == 8
- EXPECT_THAT(empty_block1_offset,
- NGPhysicalOffset(LayoutUnit(8), LayoutUnit(8)));
+ EXPECT_THAT(empty_block1_offset, PhysicalOffset(8, 8));
- NGPhysicalOffset empty_block2_offset =
+ PhysicalOffset empty_block2_offset =
container_fragment->Children()[2].Offset();
// empty-block2's margin == 50
- EXPECT_THAT(empty_block2_offset,
- NGPhysicalOffset(LayoutUnit(0), LayoutUnit(50)));
+ EXPECT_THAT(empty_block2_offset, PhysicalOffset(0, 50));
}
// Verifies that we can correctly position blocks with clearance and
@@ -1886,11 +1887,9 @@ TEST_F(NGBlockLayoutAlgorithmTest,
// Verify #container-clear block
auto* container_clear_fragment =
To<NGPhysicalBoxFragment>(body_fragment->Children()[3].get());
- NGPhysicalOffset container_clear_offset =
- body_fragment->Children()[3].Offset();
+ PhysicalOffset container_clear_offset = body_fragment->Children()[3].Offset();
// 60 = block1's height 30 + std::max(block1's margin 20, zero's margin 30)
- EXPECT_THAT(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(60)),
- container_clear_offset);
+ EXPECT_THAT(PhysicalOffset(0, 60), container_clear_offset);
Element* container_clear = GetDocument().getElementById("container-clear");
// 190 = block1's margin 130 + block1's height 30 +
// std::max(block1's margin 20, zero's margin 30)
@@ -1898,12 +1897,11 @@ TEST_F(NGBlockLayoutAlgorithmTest,
// Verify #clears-right block
ASSERT_EQ(2UL, container_clear_fragment->Children().size());
- NGPhysicalOffset clears_right_offset =
+ PhysicalOffset clears_right_offset =
container_clear_fragment->Children()[1].Offset();
// 20 = right-float's block end offset (130 + 80) -
// container_clear->offsetTop() 190
- EXPECT_THAT(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)),
- clears_right_offset);
+ EXPECT_THAT(PhysicalOffset(0, 20), clears_right_offset);
}
// Tests that a block won't fragment if it doesn't reach the fragmentation line.
@@ -1924,13 +1922,13 @@ TEST_F(NGBlockLayoutAlgorithmTest, NoFragmentation) {
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
// We should only have one 150x200 fragment with no fragmentation.
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 200), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
}
@@ -1952,19 +1950,16 @@ TEST_F(NGBlockLayoutAlgorithmTest, SimpleFragmentation) {
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 200), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), fragment->Size());
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 100), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
}
@@ -1998,37 +1993,34 @@ TEST_F(NGBlockLayoutAlgorithmTest, InnerChildrenFragmentation) {
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 200), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), offset);
+ EXPECT_EQ(PhysicalSize(150, 180), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 20), offset);
EXPECT_FALSE(iterator.NextChild());
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(140)), fragment->Size());
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 140), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
iterator.SetParent(To<NGPhysicalBoxFragment>(fragment.get()));
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(150, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), offset);
+ EXPECT_EQ(PhysicalSize(150, 100), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 40), offset);
EXPECT_FALSE(iterator.NextChild());
}
@@ -2066,37 +2058,34 @@ TEST_F(NGBlockLayoutAlgorithmTest,
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 200), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), offset);
+ EXPECT_EQ(PhysicalSize(150, 180), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 20), offset);
EXPECT_FALSE(iterator.NextChild());
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(140)), fragment->Size());
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 140), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
iterator.SetParent(To<NGPhysicalBoxFragment>(fragment.get()));
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(150, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), offset);
+ EXPECT_EQ(PhysicalSize(150, 100), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 40), offset);
EXPECT_FALSE(iterator.NextChild());
}
@@ -2132,37 +2121,34 @@ TEST_F(NGBlockLayoutAlgorithmTest, InnerChildrenFragmentationSmallHeight) {
NGBlockNode node(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(70)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 70), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), offset);
+ EXPECT_EQ(PhysicalSize(150, 180), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 20), offset);
EXPECT_FALSE(iterator.NextChild());
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(0)), fragment->Size());
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 0), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
iterator.SetParent(To<NGPhysicalBoxFragment>(fragment.get()));
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(150, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), offset);
+ EXPECT_EQ(PhysicalSize(150, 100), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 40), offset);
EXPECT_FALSE(iterator.NextChild());
}
@@ -2201,49 +2187,47 @@ TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_FloatFragmentationParallelFlows) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(50)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 50), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
// First fragment of float1.
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const auto* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(50), LayoutUnit(150)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(50, 150), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
// First fragment of float2.
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(75), LayoutUnit(150)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(65), LayoutUnit(10)), offset);
+ EXPECT_EQ(PhysicalSize(75, 150), child->Size());
+ EXPECT_EQ(PhysicalOffset(65, 10), offset);
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(0)), fragment->Size());
+
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 0), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
iterator.SetParent(To<NGPhysicalBoxFragment>(fragment.get()));
// Second fragment of float1.
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(50), LayoutUnit(50)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(50, 50), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
// Second fragment of float2.
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(75), LayoutUnit(100)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(65), LayoutUnit()), offset);
+ EXPECT_EQ(PhysicalSize(75, 100), child->Size());
+ EXPECT_EQ(PhysicalOffset(65, 0), offset);
}
// Tests that float children don't fragment if they aren't in the same writing
@@ -2281,14 +2265,14 @@ TEST_F(NGBlockLayoutAlgorithmTest, FloatFragmentationOrthogonalFlows) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, true,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, true,
kFragmentainerSpaceAvailable);
AdvanceToLayoutPhase();
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(60)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 60), fragment->Size());
ASSERT_TRUE(!fragment->BreakToken() || fragment->BreakToken()->IsFinished());
const auto* linebox =
@@ -2297,8 +2281,10 @@ TEST_F(NGBlockLayoutAlgorithmTest, FloatFragmentationOrthogonalFlows) {
To<NGPhysicalLineBoxFragment>(linebox)->Children()[1].fragment;
// float2 should only have one fragment.
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(60), LayoutUnit(200)), float2->Size());
- ASSERT_TRUE(!float2->BreakToken() || float2->BreakToken()->IsFinished());
+ EXPECT_EQ(PhysicalSize(60, 200), float2->Size());
+ ASSERT_TRUE(float2->IsBox());
+ NGBreakToken* break_token = To<NGPhysicalBoxFragment>(float2)->BreakToken();
+ EXPECT_TRUE(!break_token || break_token->IsFinished());
}
// Tests that a float child inside a zero height block fragments correctly.
@@ -2331,12 +2317,12 @@ TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_FloatFragmentationZeroHeight) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(50)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(150, 50), fragment->Size());
ASSERT_FALSE(fragment->BreakToken()->IsFinished());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
@@ -2344,20 +2330,18 @@ TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_FloatFragmentationZeroHeight) {
// First fragment of float.
iterator.SetParent(child);
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(75), LayoutUnit(150)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(10), LayoutUnit(10)), offset);
+ EXPECT_EQ(PhysicalSize(75, 150), child->Size());
+ EXPECT_EQ(PhysicalOffset(10, 10), offset);
space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false,
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false,
node.CreatesNewFormattingContext(), kFragmentainerSpaceAvailable);
- fragment = NGBlockLayoutAlgorithm(
- node, space, To<NGBlockBreakToken>(fragment->BreakToken()))
- .Layout()
- ->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(0)), fragment->Size());
+
+ fragment = RunBlockLayoutAlgorithm(node, space, fragment->BreakToken());
+ EXPECT_EQ(PhysicalSize(150, 0), fragment->Size());
ASSERT_TRUE(fragment->BreakToken()->IsFinished());
iterator.SetParent(To<NGPhysicalBoxFragment>(fragment.get()));
@@ -2366,10 +2350,10 @@ TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_FloatFragmentationZeroHeight) {
// Second fragment of float.
iterator.SetParent(child);
child = iterator.NextChild();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(75), LayoutUnit(50)), child->Size());
+ EXPECT_EQ(PhysicalSize(75, 50), child->Size());
// TODO(ikilpatrick): Don't include the block-start margin of a float which
// has fragmented.
- // EXPECT_EQ(NGPhysicalOffset(LayoutUnit(10), LayoutUnit(0)),
+ // EXPECT_EQ(PhysicalOffset(10, 0),
// child->Offset());
}
@@ -2397,8 +2381,8 @@ TEST_F(NGBlockLayoutAlgorithmTest,
</div>
)HTML");
- NGPhysicalOffset body_offset;
- NGPhysicalOffset new_fc_offset;
+ PhysicalOffset body_offset;
+ PhysicalOffset new_fc_offset;
scoped_refptr<const NGPhysicalBoxFragment> fragment;
auto run_test = [&](const Length& block_width) {
@@ -2420,17 +2404,17 @@ TEST_F(NGBlockLayoutAlgorithmTest,
// #new-fc is small enough to fit on the same line with #float.
run_test(Length::Fixed(80));
// 100 = float's width, 0 = no margin collapsing
- EXPECT_THAT(new_fc_offset, NGPhysicalOffset(LayoutUnit(100), LayoutUnit(0)));
+ EXPECT_THAT(new_fc_offset, PhysicalOffset(100, 0));
// 8 = body's margins, 20 = new-fc's margin top(20) collapses with
// body's margin(8)
- EXPECT_THAT(body_offset, NGPhysicalOffset(LayoutUnit(8), LayoutUnit(20)));
+ EXPECT_THAT(body_offset, PhysicalOffset(8, 20));
// #new-fc is too wide to be positioned on the same line with #float
run_test(Length::Fixed(120));
// 30 = #float's height
- EXPECT_THAT(new_fc_offset, NGPhysicalOffset(LayoutUnit(0), LayoutUnit(30)));
+ EXPECT_THAT(new_fc_offset, PhysicalOffset(0, 30));
// 8 = body's margins, no margin collapsing
- EXPECT_THAT(body_offset, NGPhysicalOffset(LayoutUnit(8), LayoutUnit(8)));
+ EXPECT_THAT(body_offset, PhysicalOffset(8, 8));
}
TEST_F(NGBlockLayoutAlgorithmTest, NewFcAvoidsFloats) {
@@ -2457,22 +2441,22 @@ TEST_F(NGBlockLayoutAlgorithmTest, NewFcAvoidsFloats) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(150)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(200, 150), fragment->Size());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(100), LayoutUnit(30)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), offset);
+ EXPECT_EQ(PhysicalSize(100, 30), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 0), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(120)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(30)), offset);
+ EXPECT_EQ(PhysicalSize(150, 120), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, 30), offset);
}
TEST_F(NGBlockLayoutAlgorithmTest, ZeroBlockSizeAboveEdge) {
@@ -2493,22 +2477,22 @@ TEST_F(NGBlockLayoutAlgorithmTest, ZeroBlockSizeAboveEdge) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, true);
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, true);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(10)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(200, 10), fragment->Size());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(50), LayoutUnit(50)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(-70)), offset);
+ EXPECT_EQ(PhysicalSize(50, 50), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, -70), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(70), LayoutUnit(0)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(-10)), offset);
+ EXPECT_EQ(PhysicalSize(70, 0), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, -10), offset);
}
TEST_F(NGBlockLayoutAlgorithmTest, NewFcFirstChildIsZeroBlockSize) {
@@ -2531,26 +2515,26 @@ TEST_F(NGBlockLayoutAlgorithmTest, NewFcFirstChildIsZeroBlockSize) {
To<LayoutBlockFlow>(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, true);
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize), false, true);
- scoped_refptr<const NGPhysicalFragment> fragment =
- NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(200), LayoutUnit(10)), fragment->Size());
+ scoped_refptr<const NGPhysicalBoxFragment> fragment =
+ RunBlockLayoutAlgorithm(node, space);
+ EXPECT_EQ(PhysicalSize(200, 10), fragment->Size());
FragmentChildIterator iterator(To<NGPhysicalBoxFragment>(fragment.get()));
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
const NGPhysicalBoxFragment* child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(50), LayoutUnit(0)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(-30)), offset);
+ EXPECT_EQ(PhysicalSize(50, 0), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, -30), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(70), LayoutUnit(0)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(-10)), offset);
+ EXPECT_EQ(PhysicalSize(70, 0), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, -10), offset);
child = iterator.NextChild(&offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(90), LayoutUnit(20)), child->Size());
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(-10)), offset);
+ EXPECT_EQ(PhysicalSize(90, 20), child->Size());
+ EXPECT_EQ(PhysicalOffset(0, -10), offset);
}
// This test assumes that tables are not yet implemented in LayoutNG.
@@ -2572,8 +2556,7 @@ TEST_F(NGBlockLayoutAlgorithmTest, RootFragmentOffsetInsideLegacy) {
ASSERT_TRUE(fragment);
// TODO(crbug.com/781241: Re-enable when we calculate inline offset at
// the right time.
- // EXPECT_EQ(NGPhysicalOffset(LayoutUnit(20), LayoutUnit(10)),
- // fragment->Offset());
+ // EXPECT_EQ(PhysicalOffset(20, 10), fragment->Offset());
}
// TODO(dgrogan): Move this to ng_flex_layout_algorithm_test.cc if there ever is
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc
new file mode 100644
index 00000000000..4b3ad59b933
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h"
+
+#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+
+namespace blink {
+
+LayoutUnit CalculateOutOfFlowStaticInlineLevelOffset(
+ const ComputedStyle& container_style,
+ const NGBfcOffset& origin_bfc_offset,
+ const NGExclusionSpace& exclusion_space,
+ LayoutUnit child_available_inline_size) {
+ const TextDirection direction = container_style.Direction();
+
+ // Find a layout opportunity, where we would have placed a zero-sized line.
+ NGLayoutOpportunity opportunity = exclusion_space.FindLayoutOpportunity(
+ origin_bfc_offset, child_available_inline_size,
+ /* minimum_size */ LogicalSize());
+
+ LayoutUnit child_line_offset = IsLtr(direction)
+ ? opportunity.rect.LineStartOffset()
+ : opportunity.rect.LineEndOffset();
+
+ LayoutUnit relative_line_offset =
+ child_line_offset - origin_bfc_offset.line_offset;
+
+ // Convert back to the logical coordinate system. As the conversion is on an
+ // OOF-positioned node, we pretent it has zero inline-size.
+ LayoutUnit inline_offset =
+ IsLtr(direction) ? relative_line_offset
+ : child_available_inline_size - relative_line_offset;
+
+ // Adjust for text alignment, within the layout opportunity.
+ LayoutUnit line_offset = LineOffsetForTextAlign(
+ container_style.GetTextAlign(), direction, opportunity.rect.InlineSize());
+
+ if (IsLtr(direction))
+ inline_offset += line_offset;
+ else
+ inline_offset += opportunity.rect.InlineSize() - line_offset;
+
+ return inline_offset;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h
new file mode 100644
index 00000000000..a95dbb39bd2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_LAYOUT_ALGORITHM_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_LAYOUT_ALGORITHM_UTILS_H_
+
+#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+
+namespace blink {
+
+class ComputedStyle;
+struct NGBfcOffset;
+class NGExclusionSpace;
+
+// OOF-positioned nodes which were initially inline-level, however are in a
+// block-level context, pretend they are in an inline-level context. E.g.
+// they avoid floats, and respect text-align.
+//
+// This function calculates the inline-offset to avoid floats, and respect
+// text-align.
+LayoutUnit CalculateOutOfFlowStaticInlineLevelOffset(
+ const ComputedStyle& container_style,
+ const NGBfcOffset& origin_bfc_offset,
+ const NGExclusionSpace&,
+ LayoutUnit child_available_inline_size);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_LAYOUT_ALGORITHM_UTILS_H_
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 cb72c1725e2..533c2b01271 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
@@ -14,7 +14,10 @@
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
#include "third_party/blink/renderer/core/layout/layout_multi_column_set.h"
+#include "third_party/blink/renderer/core/layout/layout_table.h"
+#include "third_party/blink/renderer/core/layout/layout_table_cell.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/legacy_layout_tree_walking.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
@@ -33,6 +36,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -56,29 +60,14 @@ inline LayoutMultiColumnFlowThread* GetFlowThread(const LayoutBox& box) {
return GetFlowThread(DynamicTo<LayoutBlockFlow>(box));
}
-// Parameters to pass when creating a layout algorithm for a block node.
-struct NGLayoutAlgorithmParams {
- STACK_ALLOCATED();
-
- public:
- NGLayoutAlgorithmParams(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBlockBreakToken* break_token = nullptr)
- : node(node), space(space), break_token(break_token) {}
-
- NGBlockNode node;
- const NGConstraintSpace& space;
- const NGBlockBreakToken* break_token;
-};
-
// The entire purpose of this function is to avoid allocating space on the stack
// for all layout algorithms for each node we lay out. Therefore it must not be
// inline.
template <typename Algorithm, typename Callback>
NOINLINE void CreateAlgorithmAndRun(const NGLayoutAlgorithmParams& params,
const Callback& callback) {
- Algorithm algorithm(params.node, params.space, params.break_token);
- callback(&algorithm);
+ std::unique_ptr<Algorithm> algorithm = std::make_unique<Algorithm>(params);
+ callback(algorithm.get());
}
inline void DetermineAlgorithmAndRun(
@@ -155,7 +144,9 @@ void UpdateLegacyMultiColumnFlowThread(
if (LayoutMultiColumnSet* column_set = flow_thread->FirstMultiColumnSet()) {
NGFragment logical_fragment(writing_mode, fragment);
auto border_scrollbar_padding =
- CalculateBorderScrollbarPadding(constraint_space, node);
+ ComputeBorders(constraint_space, node) +
+ ComputeScrollbars(constraint_space, node) +
+ ComputePadding(constraint_space, node.Style());
column_set->SetLogicalLeft(border_scrollbar_padding.inline_start);
column_set->SetLogicalTop(border_scrollbar_padding.block_start);
@@ -208,16 +199,12 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
const NGBreakToken* break_token) {
// Use the old layout code and synthesize a fragment.
if (!CanUseNewLayout())
- return RunOldLayout(constraint_space);
+ return RunLegacyLayout(constraint_space);
- LayoutBlockFlow* block_flow = ToLayoutBlockFlowOrNull(box_);
+ auto* block_flow = DynamicTo<LayoutBlockFlow>(box_);
if (RuntimeEnabledFeatures::TrackLayoutPassesPerBlockEnabled() && block_flow)
block_flow->IncrementLayoutPassCount();
- NGLayoutInputNode first_child = FirstChild();
- if (block_flow && !first_child)
- block_flow->ClearNGInlineNodeData();
-
// The exclusion space internally is a pointer to a shared vector, and
// equality of exclusion spaces is performed using pointer comparison on this
// internal shared vector.
@@ -228,9 +215,13 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
previous_result->GetConstraintSpaceForCaching().ExclusionSpace());
}
- scoped_refptr<const NGLayoutResult> layout_result =
- box_->CachedLayoutResult(constraint_space, break_token);
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ scoped_refptr<const NGLayoutResult> layout_result = box_->CachedLayoutResult(
+ constraint_space, break_token, &fragment_geometry, &cache_status);
if (layout_result) {
+ DCHECK_EQ(cache_status, NGLayoutCacheStatus::kHit);
+
// We may have to update the margins on box_; we reuse the layout result
// even if a percentage margin may have changed.
if (UNLIKELY(Style().MayHaveMargin() && !IsTableCell()))
@@ -244,16 +235,44 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
return layout_result;
}
- PrepareForLayout();
+ if (!fragment_geometry) {
+ fragment_geometry =
+ CalculateInitialFragmentGeometry(constraint_space, *this);
+ }
- NGBoxStrut old_scrollbars = GetScrollbarSizes();
+ PrepareForLayout();
- NGLayoutAlgorithmParams params(*this, constraint_space,
+ NGLayoutAlgorithmParams params(*this, *fragment_geometry, constraint_space,
To<NGBlockBreakToken>(break_token));
- layout_result = LayoutWithAlgorithm(params);
+
+ // Try to perform "simplified" layout.
+ if (cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout &&
+ block_flow && !GetFlowThread(block_flow)) {
+ // A child may have changed size while performing "simplified" layout (it
+ // may have gained or removed scrollbars, changing its size). In these
+ // cases "simplified" layout will return a null layout-result, indicating
+ // we need to perform a full layout.
+ layout_result = RunSimplifiedLayout(params);
+
+#if DCHECK_IS_ON()
+ if (layout_result) {
+ layout_result->CheckSameForSimplifiedLayout(
+ *box_->GetCachedLayoutResult(), /* check_same_block_size */ false);
+ }
+#endif
+ }
+
+ // Fragment geometry scrollbars are potentially size constrained, and cannot
+ // be used for comparison with their after layout size.
+ NGBoxStrut before_layout_scrollbars =
+ ComputeScrollbars(constraint_space, *this);
+
+ if (!layout_result)
+ layout_result = LayoutWithAlgorithm(params);
FinishLayout(block_flow, constraint_space, break_token, layout_result);
- if (old_scrollbars != GetScrollbarSizes()) {
+
+ if (before_layout_scrollbars != ComputeScrollbars(constraint_space, *this)) {
// If our scrollbars have changed, we need to relayout because either:
// - Our size has changed (if shrinking to fit), or
// - Space available to our children has changed.
@@ -265,11 +284,16 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
// without that check.
PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbars;
+ // Must not call SetNeedsLayout in intermediate layout. If we do,
+ // the NeedsLayout flag might not be cleared. crbug.com/967361
+ DCHECK(!constraint_space.IsIntermediateLayout() || box_->NeedsLayout());
// Scrollbar changes are hard to detect. Make sure everyone gets the
// message.
box_->SetNeedsLayout(layout_invalidation_reason::kScrollbarChanged,
kMarkOnlyThis);
+ fragment_geometry =
+ CalculateInitialFragmentGeometry(constraint_space, *this);
layout_result = LayoutWithAlgorithm(params);
FinishLayout(block_flow, constraint_space, break_token, layout_result);
}
@@ -290,9 +314,39 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::Layout(
return layout_result;
}
+scoped_refptr<const NGLayoutResult> NGBlockNode::SimplifiedLayout() {
+ scoped_refptr<const NGLayoutResult> previous_result =
+ box_->GetCachedLayoutResult();
+ DCHECK(previous_result);
+
+ if (!box_->NeedsLayout())
+ return previous_result;
+
+ DCHECK(box_->NeedsSimplifiedLayoutOnly());
+
+ // Perform layout on ourselves using the previous constraint space.
+ const NGConstraintSpace space(
+ previous_result->GetConstraintSpaceForCaching());
+ scoped_refptr<const NGLayoutResult> result =
+ Layout(space, /* break_token */ nullptr);
+
+ // If we changed size from performing "simplified" layout, we have
+ // added/removed scrollbars. Return null indicating to our parent that it
+ // needs to perform a full layout.
+ if (previous_result->PhysicalFragment().Size() !=
+ result->PhysicalFragment().Size())
+ return nullptr;
+
+#if DCHECK_IS_ON()
+ result->CheckSameForSimplifiedLayout(*previous_result);
+#endif
+
+ return result;
+}
+
scoped_refptr<const NGLayoutResult>
NGBlockNode::CachedLayoutResultForOutOfFlowPositioned(
- NGLogicalSize container_content_size) const {
+ LogicalSize container_content_size) const {
DCHECK(IsOutOfFlowPositioned());
if (box_->NeedsLayout())
@@ -349,25 +403,46 @@ void NGBlockNode::FinishLayout(
if (!IsBlockLayoutComplete(constraint_space, *layout_result))
return;
- DCHECK(layout_result->PhysicalFragment());
-
box_->SetCachedLayoutResult(*layout_result, break_token);
if (block_flow) {
NGLayoutInputNode first_child = FirstChild();
bool has_inline_children = first_child && first_child.IsInline();
- if (has_inline_children || box_->IsLayoutNGFieldset()) {
- if (has_inline_children) {
- CopyFragmentDataToLayoutBoxForInlineChildren(
- To<NGPhysicalBoxFragment>(*layout_result->PhysicalFragment()),
- layout_result->PhysicalFragment()->Size().width,
- Style().IsFlippedBlocksWritingMode());
- }
+ // Don't consider display-locked objects as having any children.
+ if (has_inline_children &&
+ box_->LayoutBlockedByDisplayLock(DisplayLockContext::kChildren)) {
+ has_inline_children = false;
+ // It could be the case that our children are already clean at the time
+ // the lock was acquired. This means that |box_| self dirty bits might be
+ // set, and child dirty bits might not be. We clear the self bits since we
+ // want to treat the |box_| as layout clean, even when locked. However,
+ // here we also skip appending paint fragments for inline children. This
+ // means that we potentially can end up in a situation where |box_| is
+ // completely layout clean, but its inline children didn't append the
+ // paint fragments to it, which causes problems. In order to solve this,
+ // we set a child dirty bit on |box_| ensuring that when the lock
+ // is removed, or update is forced, we will visit this box again and
+ // properly create the paint fragments. See https://crbug.com/962614.
+ box_->SetChildNeedsLayout(kMarkOnlyThis);
+ }
+
+ if (has_inline_children) {
+ const auto& physical_fragment =
+ To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment());
+ CopyFragmentDataToLayoutBoxForInlineChildren(
+ physical_fragment, physical_fragment.Size().width,
+ Style().IsFlippedBlocksWritingMode());
block_flow->SetPaintFragment(To<NGBlockBreakToken>(break_token),
- layout_result->PhysicalFragment());
+ &physical_fragment);
+ } else if (UNLIKELY(box_->IsLayoutNGFieldset())) {
+ // TODO(kojii): NGFieldset should not create PaintFragment.
+ block_flow->ClearNGInlineNodeData();
+ block_flow->SetPaintFragment(To<NGBlockBreakToken>(break_token),
+ &layout_result->PhysicalFragment());
} else {
// We still need to clear paint fragments in case it had inline children,
// and thus had NGPaintFragment.
+ block_flow->ClearNGInlineNodeData();
block_flow->SetPaintFragment(To<NGBlockBreakToken>(break_token), nullptr);
}
}
@@ -413,7 +488,7 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize(
NGBoxFragment fragment(
container_writing_mode,
TextDirection::kLtr, // irrelevant here
- To<NGPhysicalBoxFragment>(*layout_result->PhysicalFragment()));
+ To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()));
sizes.min_size = sizes.max_size = fragment.Size().inline_size;
if (input.size_type == NGMinMaxSizeType::kContentBoxSize) {
sizes -= fragment.Borders().InlineSum() + fragment.Padding().InlineSum() +
@@ -424,8 +499,11 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize(
return sizes;
}
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(*constraint_space, *this);
base::Optional<MinMaxSize> maybe_sizes = ComputeMinMaxSizeWithAlgorithm(
- NGLayoutAlgorithmParams(*this, *constraint_space), input);
+ NGLayoutAlgorithmParams(*this, fragment_geometry, *constraint_space),
+ input);
if (maybe_sizes.has_value()) {
if (UNLIKELY(IsHTMLMarqueeElement(box_->GetNode()) &&
@@ -443,11 +521,9 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize(
// Have to synthesize this value.
scoped_refptr<const NGLayoutResult> layout_result =
Layout(zero_constraint_space);
- NGBoxFragment min_fragment(
- container_writing_mode,
- TextDirection::kLtr, // irrelevant here
- To<NGPhysicalBoxFragment>(*layout_result->PhysicalFragment()));
- sizes.min_size = min_fragment.Size().inline_size;
+ sizes.min_size =
+ NGFragment(container_writing_mode, layout_result->PhysicalFragment())
+ .InlineSize();
// Now, redo with infinite space for max_content
NGConstraintSpace infinite_constraint_space =
@@ -460,7 +536,7 @@ MinMaxSize NGBlockNode::ComputeMinMaxSize(
NGBoxFragment max_fragment(
container_writing_mode,
TextDirection::kLtr, // irrelevant here
- To<NGPhysicalBoxFragment>(*layout_result->PhysicalFragment()));
+ To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment()));
sizes.max_size = max_fragment.Size().inline_size;
if (input.size_type == NGMinMaxSizeType::kContentBoxSize) {
@@ -498,21 +574,6 @@ MinMaxSize NGBlockNode::ComputeMinMaxSizeFromLegacy(
return sizes;
}
-NGBoxStrut NGBlockNode::GetScrollbarSizes() const {
- NGPhysicalBoxStrut sizes;
- const ComputedStyle& style = box_->StyleRef();
- if (!style.IsOverflowVisible()) {
- LayoutUnit vertical = LayoutUnit(box_->VerticalScrollbarWidth());
- LayoutUnit horizontal = LayoutUnit(box_->HorizontalScrollbarHeight());
- sizes.bottom = horizontal;
- if (box_->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
- sizes.left = vertical;
- else
- sizes.right = vertical;
- }
- return sizes.ConvertToLogical(style.GetWritingMode(), style.Direction());
-}
-
NGLayoutInputNode NGBlockNode::NextSibling() const {
LayoutObject* next_sibling = GetLayoutObjectForNextSiblingNode(box_);
if (next_sibling) {
@@ -566,16 +627,15 @@ String NGBlockNode::ToString() const {
void NGBlockNode::CopyFragmentDataToLayoutBox(
const NGConstraintSpace& constraint_space,
const NGLayoutResult& layout_result) {
- DCHECK(layout_result.PhysicalFragment());
if (UNLIKELY(constraint_space.IsIntermediateLayout()))
return;
const auto& physical_fragment =
- To<NGPhysicalBoxFragment>(*layout_result.PhysicalFragment());
+ To<NGPhysicalBoxFragment>(layout_result.PhysicalFragment());
NGBoxFragment fragment(constraint_space.GetWritingMode(),
constraint_space.Direction(), physical_fragment);
- NGLogicalSize fragment_logical_size = fragment.Size();
+ 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
@@ -601,7 +661,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
intrinsic_content_logical_height += layout_result.IntrinsicBlockSize();
NGBoxStrut borders = fragment.Borders();
- NGBoxStrut scrollbars = GetScrollbarSizes();
+ NGBoxStrut scrollbars = ComputeScrollbars(constraint_space, *this);
NGBoxStrut padding = fragment.Padding();
NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
@@ -619,12 +679,12 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
box_->SetMargin(ComputePhysicalMargins(constraint_space, Style()));
}
- LayoutBlockFlow* block_flow = ToLayoutBlockFlowOrNull(box_);
+ auto* block_flow = DynamicTo<LayoutBlockFlow>(box_);
LayoutMultiColumnFlowThread* flow_thread = GetFlowThread(block_flow);
if (UNLIKELY(flow_thread)) {
PlaceChildrenInFlowThread(constraint_space, physical_fragment);
} else {
- NGPhysicalOffset offset_from_start;
+ PhysicalOffset offset_from_start;
if (UNLIKELY(constraint_space.HasBlockFragmentation())) {
// Need to include any block space that this container has used in
// previous fragmentainers. The offset of children will be relative to
@@ -665,7 +725,10 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
}
box_->UpdateAfterLayout();
- box_->ClearNeedsLayout();
+ // We should only clear the child layout bits if display-locking has not
+ // prevented us from laying the children out.
+ box_->ClearNeedsLayout(
+ !box_->LayoutBlockedByDisplayLock(DisplayLockContext::kChildren));
// Overflow computation depends on this being set.
if (LIKELY(block_flow))
@@ -675,7 +738,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBox(
void NGBlockNode::PlaceChildrenInLayoutBox(
const NGConstraintSpace& constraint_space,
const NGPhysicalBoxFragment& physical_fragment,
- const NGPhysicalOffset& offset_from_start) {
+ const PhysicalOffset& offset_from_start) {
LayoutBox* rendered_legend = nullptr;
for (const auto& child_fragment : physical_fragment.Children()) {
// Skip any line-boxes we have as children, this is handled within
@@ -686,7 +749,7 @@ void NGBlockNode::PlaceChildrenInLayoutBox(
const auto& box_fragment = *To<NGPhysicalBoxFragment>(child_fragment.get());
if (IsFirstFragment(constraint_space, box_fragment)) {
if (box_fragment.IsRenderedLegend())
- rendered_legend = ToLayoutBox(box_fragment.GetLayoutObject());
+ rendered_legend = ToLayoutBox(box_fragment.GetMutableLayoutObject());
CopyChildFragmentPosition(box_fragment, child_fragment.Offset(),
offset_from_start);
}
@@ -718,7 +781,7 @@ void NGBlockNode::PlaceChildrenInFlowThread(
DCHECK(child->GetLayoutObject() == box_);
// TODO(mstensho): writing modes
- NGPhysicalOffset offset(LayoutUnit(), flowthread_offset);
+ PhysicalOffset offset(LayoutUnit(), flowthread_offset);
// Position each child node in the first column that they occur, relatively
// to the block-start of the flow thread.
@@ -732,9 +795,9 @@ void NGBlockNode::PlaceChildrenInFlowThread(
// Copies data back to the legacy layout tree for a given child fragment.
void NGBlockNode::CopyChildFragmentPosition(
const NGPhysicalFragment& fragment,
- const NGPhysicalOffset fragment_offset,
- const NGPhysicalOffset additional_offset) {
- LayoutBox* layout_box = ToLayoutBox(fragment.GetLayoutObject());
+ const PhysicalOffset fragment_offset,
+ const PhysicalOffset additional_offset) {
+ LayoutBox* layout_box = ToLayoutBox(fragment.GetMutableLayoutObject());
if (!layout_box)
return;
@@ -772,17 +835,17 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren(
const NGPhysicalContainerFragment& container,
LayoutUnit initial_container_width,
bool initial_container_is_flipped,
- NGPhysicalOffset offset) {
+ PhysicalOffset offset) {
for (const auto& child : container.Children()) {
if (child->IsContainer()) {
- NGPhysicalOffset child_offset = offset + child.Offset();
+ PhysicalOffset child_offset = offset + child.Offset();
// Replaced elements and inline blocks need Location() set relative to
// their block container.
- LayoutObject* layout_object = child->GetLayoutObject();
+ LayoutObject* layout_object = child->GetMutableLayoutObject();
if (layout_object && layout_object->IsBox()) {
LayoutBox& layout_box = ToLayoutBox(*layout_object);
- NGPhysicalOffset maybe_flipped_offset = child_offset;
+ PhysicalOffset maybe_flipped_offset = child_offset;
if (initial_container_is_flipped) {
maybe_flipped_offset.left = initial_container_width -
child->Size().width -
@@ -814,7 +877,7 @@ void NGBlockNode::CopyFragmentDataToLayoutBoxForInlineChildren(
}
bool NGBlockNode::ChildrenInline() const {
- if (const auto* block = ToLayoutBlockFlowOrNull(box_))
+ if (const auto* block = DynamicTo<LayoutBlockFlow>(box_))
return AreNGBlockFlowChildrenInline(block);
return false;
}
@@ -835,6 +898,13 @@ bool NGBlockNode::UseLogicalBottomMarginEdgeForInlineBlockBaseline() const {
layout_box->UseLogicalBottomMarginEdgeForInlineBlockBaseline();
}
+bool NGBlockNode::IsRestrictedBlockSizeTableCell() const {
+ DCHECK(IsTableCell());
+ const LayoutTableCell* cell = ToLayoutTableCell(GetLayoutBox());
+ return !cell->StyleRef().LogicalHeight().IsAuto() ||
+ !cell->Table()->StyleRef().LogicalHeight().IsAuto();
+}
+
scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline(
const NGConstraintSpace& parent_constraint_space,
const ComputedStyle& parent_style,
@@ -871,7 +941,7 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline(
return result;
}
-scoped_refptr<const NGLayoutResult> NGBlockNode::RunOldLayout(
+scoped_refptr<const NGLayoutResult> NGBlockNode::RunLegacyLayout(
const NGConstraintSpace& constraint_space) {
// 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
@@ -880,12 +950,16 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunOldLayout(
To<LayoutBlock>(box_)->CreatesNewFormattingContext());
scoped_refptr<const NGLayoutResult> layout_result =
- box_->GetCachedLayoutResult();
+ box_->IsOutOfFlowPositioned()
+ ? CachedLayoutResultForOutOfFlowPositioned(
+ constraint_space.PercentageResolutionSize())
+ : box_->GetCachedLayoutResult();
// We need to force a layout on the child if the constraint space given will
// change the layout.
bool needs_force_relayout =
- layout_result && !MaySkipLayout(*this, *layout_result, constraint_space);
+ layout_result &&
+ !MaySkipLegacyLayout(*this, *layout_result, constraint_space);
if (box_->NeedsLayout() || !layout_result || needs_force_relayout) {
BoxLayoutExtraInput input(*box_);
@@ -901,8 +975,24 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunOldLayout(
// The sizes should be in the containing block writing mode.
std::swap(input.containing_block_content_block_size,
input.containing_block_content_inline_size);
+
+ // We cannot lay out without a definite containing block inline-size. We
+ // end up here if we're performing a measure pass (as part of resolving
+ // the intrinsic min/max inline-size of some ancestor, for instance).
+ // Legacy layout has a tendency of clamping negative sizes to 0 anyway,
+ // but this is missing when it comes to resolving percentage-based
+ // padding, for instance.
+ if (input.containing_block_content_inline_size == kIndefiniteSize) {
+ DCHECK(constraint_space.IsIntermediateLayout());
+ input.containing_block_content_inline_size = LayoutUnit();
+ }
}
}
+
+ // We need a definite containing block inline-size, or we'd be unable to
+ // resolve percentages.
+ DCHECK_GE(input.containing_block_content_inline_size, LayoutUnit());
+
input.available_inline_size = constraint_space.AvailableSize().inline_size;
if (constraint_space.IsFixedSizeInline())
@@ -920,26 +1010,43 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunOldLayout(
box_->ForceLayout();
// Synthesize a new layout result.
- NGLogicalSize box_size(box_->LogicalWidth(), box_->LogicalHeight());
+ NGFragmentGeometry fragment_geometry;
+ fragment_geometry.border_box_size = {box_->LogicalWidth(),
+ box_->LogicalHeight()};
+ fragment_geometry.border = {box_->BorderStart(), box_->BorderEnd(),
+ box_->BorderBefore(), box_->BorderAfter()};
+ fragment_geometry.scrollbar = ComputeScrollbars(constraint_space, *this);
+ fragment_geometry.padding = {box_->PaddingStart(), box_->PaddingEnd(),
+ box_->PaddingBefore(), box_->PaddingAfter()};
+
// TODO(kojii): Implement use_first_line_style.
NGBoxFragmentBuilder builder(*this, box_->Style(), &constraint_space,
writing_mode, box_->StyleRef().Direction());
builder.SetIsNewFormattingContext(
constraint_space.IsNewFormattingContext());
- builder.SetIsOldLayoutRoot();
- builder.SetInlineSize(box_size.inline_size);
- builder.SetBlockSize(box_size.block_size);
- NGBoxStrut borders(box_->BorderStart(), box_->BorderEnd(),
- box_->BorderBefore(), box_->BorderAfter());
- builder.SetBorders(borders);
- NGBoxStrut padding(box_->PaddingStart(), box_->PaddingEnd(),
- box_->PaddingBefore(), box_->PaddingAfter());
- builder.SetPadding(padding);
-
- CopyBaselinesFromOldLayout(constraint_space, &builder);
+ builder.SetInitialFragmentGeometry(fragment_geometry);
+ builder.SetIsLegacyLayoutRoot();
+
+ CopyBaselinesFromLegacyLayout(constraint_space, &builder);
layout_result = builder.ToBoxFragment();
box_->SetCachedLayoutResult(*layout_result, /* break_token */ nullptr);
+ } else if (layout_result) {
+ // OOF-positioned nodes have a two-tier cache, and their layout results
+ // must always contain the correct percentage resolution size.
+ // See |NGBlockNode::CachedLayoutResultForOutOfFlowPositioned|.
+ const NGConstraintSpace& old_space =
+ layout_result->GetConstraintSpaceForCaching();
+ bool needs_cached_result_update =
+ IsOutOfFlowPositioned() &&
+ constraint_space.PercentageResolutionSize() !=
+ old_space.PercentageResolutionSize();
+ if (needs_cached_result_update) {
+ layout_result = base::AdoptRef(new NGLayoutResult(
+ *layout_result, constraint_space, layout_result->BfcLineOffset(),
+ layout_result->BfcBlockOffset()));
+ box_->SetCachedLayoutResult(*layout_result, /* break_token */ nullptr);
+ }
}
UpdateShapeOutsideInfoIfNeeded(
@@ -948,7 +1055,13 @@ scoped_refptr<const NGLayoutResult> NGBlockNode::RunOldLayout(
return layout_result;
}
-void NGBlockNode::CopyBaselinesFromOldLayout(
+scoped_refptr<const NGLayoutResult> NGBlockNode::RunSimplifiedLayout(
+ const NGLayoutAlgorithmParams& params) const {
+ return NGSimplifiedLayoutAlgorithm(params, *box_->GetCachedLayoutResult())
+ .Layout();
+}
+
+void NGBlockNode::CopyBaselinesFromLegacyLayout(
const NGConstraintSpace& constraint_space,
NGBoxFragmentBuilder* builder) {
const NGBaselineRequestList requests = constraint_space.BaselineRequests();
@@ -962,7 +1075,7 @@ void NGBlockNode::CopyBaselinesFromOldLayout(
switch (request.AlgorithmType()) {
case NGBaselineAlgorithmType::kAtomicInline: {
LayoutUnit position =
- AtomicInlineBaselineFromOldLayout(request, constraint_space);
+ AtomicInlineBaselineFromLegacyLayout(request, constraint_space);
if (position != -1)
builder->AddBaseline(request, position);
break;
@@ -977,7 +1090,7 @@ void NGBlockNode::CopyBaselinesFromOldLayout(
}
}
-LayoutUnit NGBlockNode::AtomicInlineBaselineFromOldLayout(
+LayoutUnit NGBlockNode::AtomicInlineBaselineFromLegacyLayout(
const NGBaselineRequest& request,
const NGConstraintSpace& constraint_space) {
LineDirectionMode line_direction = box_->IsHorizontalWritingMode()
@@ -1015,8 +1128,7 @@ void NGBlockNode::UpdateShapeOutsideInfoIfNeeded(
// The box_ may not have a valid size yet (due to an intermediate layout),
// use the fragment's size instead.
- DCHECK(layout_result.PhysicalFragment());
- LayoutSize box_size = layout_result.PhysicalFragment()->Size().ToLayoutSize();
+ LayoutSize box_size = layout_result.PhysicalFragment().Size().ToLayoutSize();
// TODO(ikilpatrick): Ideally this should be moved to a NGLayoutResult
// computing the shape area. There may be an issue with the new fragmentation
@@ -1030,14 +1142,14 @@ void NGBlockNode::UpdateShapeOutsideInfoIfNeeded(
percentage_resolution_inline_size);
}
-void NGBlockNode::UseOldOutOfFlowPositioning() const {
+void NGBlockNode::UseLegacyOutOfFlowPositioning() const {
DCHECK(box_->IsOutOfFlowPositioned());
box_->ContainingBlock()->InsertPositionedObject(box_);
}
// Save static position for legacy AbsPos layout.
void NGBlockNode::SaveStaticOffsetForLegacy(
- const NGLogicalOffset& offset,
+ const LogicalOffset& offset,
const LayoutObject* offset_container) {
DCHECK(box_->IsOutOfFlowPositioned());
// Only set static position if the current offset container
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 43a54e5fb0b..89b39fa7356 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
@@ -6,7 +6,7 @@
#define NGBlockNode_h
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
#include "third_party/blink/renderer/platform/fonts/font_baseline.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -24,7 +24,8 @@ class NGPhysicalContainerFragment;
class NGPhysicalFragment;
struct MinMaxSize;
struct NGBoxStrut;
-struct NGLogicalOffset;
+struct NGLayoutAlgorithmParams;
+struct LogicalOffset;
// Represents a node to be laid out.
class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
@@ -38,6 +39,13 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
const NGConstraintSpace& constraint_space,
const NGBreakToken* break_token = nullptr);
+ // This method is just for use within the |NGSimplifiedLayoutAlgorithm|.
+ //
+ // If layout is dirty, it will perform layout using the previous constraint
+ // space used to generate the |NGLayoutResult|.
+ // Otherwise it will simply return the previous layout result generated.
+ scoped_refptr<const NGLayoutResult> SimplifiedLayout();
+
// This method is just for use within the |NGOutOfFlowLayoutPart|.
//
// As OOF-positioned objects have their position, and size computed
@@ -47,7 +55,7 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
// If the containing-block size hasn't changed, and we are layout-clean we
// can reuse the previous layout result.
scoped_refptr<const NGLayoutResult> CachedLayoutResultForOutOfFlowPositioned(
- NGLogicalSize container_content_size) const;
+ LogicalSize container_content_size) const;
NGLayoutInputNode NextSibling() const;
@@ -75,8 +83,6 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
MinMaxSize ComputeMinMaxSizeFromLegacy(const MinMaxSizeInput&) const;
- NGBoxStrut GetScrollbarSizes() const;
-
NGLayoutInputNode FirstChild() const;
NGBlockNode GetRenderedLegend() const;
@@ -90,6 +96,10 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
// See comments in UseLogicalBottomMarginEdgeForInlineBlockBaseline().
bool UseLogicalBottomMarginEdgeForInlineBlockBaseline() const;
+ // Return true if the block size of this table cell should be considered
+ // restricted (e.g. height of the cell or its table is non-auto).
+ bool IsRestrictedBlockSizeTableCell() const;
+
// Layout an atomic inline; e.g., inline block.
scoped_refptr<const NGLayoutResult> LayoutAtomicInline(
const NGConstraintSpace& parent_constraint_space,
@@ -97,16 +107,12 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
FontBaseline,
bool use_first_line_style);
- // Runs layout on the underlying LayoutObject and creates a fragment for the
- // resulting geometry.
- scoped_refptr<const NGLayoutResult> RunOldLayout(const NGConstraintSpace&);
-
// Called if this is an out-of-flow block which needs to be
// positioned with legacy layout.
- void UseOldOutOfFlowPositioning() const;
+ void UseLegacyOutOfFlowPositioning() const;
// Save static position for legacy AbsPos layout.
- void SaveStaticOffsetForLegacy(const NGLogicalOffset&,
+ void SaveStaticOffsetForLegacy(const LogicalOffset&,
const LayoutObject* offset_container);
// Write back resolved margins to legacy.
@@ -120,6 +126,13 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
private:
void PrepareForLayout();
+ // 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> RunSimplifiedLayout(
+ const NGLayoutAlgorithmParams&) const;
+
// If this node is a LayoutNGMixin, the caller must pass the layout object for
// this node cast to a LayoutBlockFlow as the first argument.
void FinishLayout(LayoutBlockFlow*,
@@ -135,21 +148,21 @@ class CORE_EXPORT NGBlockNode final : public NGLayoutInputNode {
const NGPhysicalContainerFragment& container,
LayoutUnit initial_container_width,
bool initial_container_is_flipped,
- NGPhysicalOffset offset = {});
+ PhysicalOffset offset = {});
void PlaceChildrenInLayoutBox(const NGConstraintSpace&,
const NGPhysicalBoxFragment&,
- const NGPhysicalOffset& offset_from_start);
+ const PhysicalOffset& offset_from_start);
void PlaceChildrenInFlowThread(const NGConstraintSpace&,
const NGPhysicalBoxFragment&);
void CopyChildFragmentPosition(
const NGPhysicalFragment& fragment,
- const NGPhysicalOffset fragment_offset,
- const NGPhysicalOffset additional_offset = NGPhysicalOffset());
+ const PhysicalOffset fragment_offset,
+ const PhysicalOffset additional_offset = PhysicalOffset());
- void CopyBaselinesFromOldLayout(const NGConstraintSpace&,
- NGBoxFragmentBuilder*);
- LayoutUnit AtomicInlineBaselineFromOldLayout(const NGBaselineRequest&,
- const NGConstraintSpace&);
+ void CopyBaselinesFromLegacyLayout(const NGConstraintSpace&,
+ NGBoxFragmentBuilder*);
+ LayoutUnit AtomicInlineBaselineFromLegacyLayout(const NGBaselineRequest&,
+ const NGConstraintSpace&);
void UpdateShapeOutsideInfoIfNeeded(
const NGLayoutResult&,
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
index 4c553a3d4a4..53be16265c6 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_box_fragment.h
@@ -38,16 +38,23 @@ class CORE_EXPORT NGBoxFragment final : public NGFragment {
const NGConstraintSpace&) const;
NGBoxStrut Borders() const {
- const auto& physical_fragment =
+ const NGPhysicalBoxFragment& physical_box_fragment =
To<NGPhysicalBoxFragment>(physical_fragment_);
- return physical_fragment.Borders().ConvertToLogical(GetWritingMode(),
- direction_);
+ return physical_box_fragment.Borders().ConvertToLogical(GetWritingMode(),
+ direction_);
}
NGBoxStrut Padding() const {
- const auto& physical_fragment =
+ const NGPhysicalBoxFragment& physical_box_fragment =
To<NGPhysicalBoxFragment>(physical_fragment_);
- return physical_fragment.Padding().ConvertToLogical(GetWritingMode(),
- direction_);
+ return physical_box_fragment.Padding().ConvertToLogical(GetWritingMode(),
+ direction_);
+ }
+
+ NGBorderEdges BorderEdges() const {
+ const NGPhysicalBoxFragment& physical_box_fragment =
+ To<NGPhysicalBoxFragment>(physical_fragment_);
+ return NGBorderEdges::FromPhysical(physical_box_fragment.BorderEdges(),
+ GetWritingMode());
}
protected:
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 243fdbf5b94..21b54f6c1d1 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
@@ -17,25 +17,30 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
namespace blink {
namespace {
+// std::pair.first points to the start linebox fragment.
+// std::pair.second points to the end linebox fragment.
using LineBoxPair = std::pair<const NGPhysicalLineBoxFragment*,
const NGPhysicalLineBoxFragment*>;
+
void GatherInlineContainerFragmentsFromLinebox(
NGBoxFragmentBuilder::InlineContainingBlockMap* inline_containing_block_map,
HashMap<const LayoutObject*, LineBoxPair>* containing_linebox_map,
- const NGPhysicalLineBoxFragment* linebox,
- const NGPhysicalOffset linebox_offset) {
- for (auto& descendant : NGInlineFragmentTraversal::DescendantsOf(*linebox)) {
+ const NGPhysicalLineBoxFragment& linebox,
+ const PhysicalOffset linebox_offset) {
+ for (const auto& descendant :
+ NGInlineFragmentTraversal::DescendantsOf(linebox)) {
if (!descendant.fragment->IsBox())
continue;
- LayoutObject* key = descendant.fragment->GetLayoutObject();
+ const LayoutObject* key = descendant.fragment->GetLayoutObject();
// Key for inline is the continuation root if it exists.
if (key->IsLayoutInline() && key->GetNode())
- key = key->GetNode()->GetLayoutObject();
+ key = key->ContinuationRoot();
auto it = inline_containing_block_map->find(key);
if (it == inline_containing_block_map->end()) {
// Default case, not one of the blocks we are looking for.
@@ -51,22 +56,22 @@ void GatherInlineContainerFragmentsFromLinebox(
// |DescendantsOf| returns the offset from the given fragment. Since
// we give it the line box, need to add the |linebox_offset|.
- NGPhysicalOffsetRect fragment_rect(
+ PhysicalRect fragment_rect(
linebox_offset + descendant.offset_to_container_box,
descendant.fragment->Size());
- if (containing_lineboxes.first == linebox) {
+ if (containing_lineboxes.first == &linebox) {
containing_block_geometry->start_fragment_union_rect.Unite(fragment_rect);
} else if (!containing_lineboxes.first) {
- containing_lineboxes.first = linebox;
+ containing_lineboxes.first = &linebox;
containing_block_geometry =
- NGBoxFragmentBuilder::InlineContainingBlockGeometry{
- fragment_rect, NGPhysicalOffsetRect()};
+ NGBoxFragmentBuilder::InlineContainingBlockGeometry{fragment_rect,
+ PhysicalRect()};
}
// Skip fragments within an empty line boxes for the end fragment.
- if (containing_lineboxes.second == linebox) {
+ if (containing_lineboxes.second == &linebox) {
containing_block_geometry->end_fragment_union_rect.Unite(fragment_rect);
- } else if (!containing_lineboxes.second || !linebox->IsEmptyLineBox()) {
- containing_lineboxes.second = linebox;
+ } else if (!containing_lineboxes.second || !linebox.IsEmptyLineBox()) {
+ containing_lineboxes.second = &linebox;
containing_block_geometry->end_fragment_union_rect = fragment_rect;
}
}
@@ -78,7 +83,6 @@ void NGBoxFragmentBuilder::RemoveChildren() {
child_break_tokens_.resize(0);
inline_break_tokens_.resize(0);
children_.resize(0);
- offsets_.resize(0);
}
NGBoxFragmentBuilder& NGBoxFragmentBuilder::AddBreakBeforeChild(
@@ -117,13 +121,12 @@ NGBoxFragmentBuilder& NGBoxFragmentBuilder::AddBreakBeforeLine(
DCHECK_GT(children_.size(), 0UL);
for (int i = children_.size() - 1; i >= 0; i--) {
DCHECK_NE(i, 0);
- if (!children_[i]->IsLineBox())
+ if (!children_[i].fragment->IsLineBox())
continue;
if (!--lines_to_remove) {
// This is the first line that is going to the next fragment. Remove it,
// and everything after it.
children_.resize(i);
- offsets_.resize(i);
break;
}
}
@@ -141,7 +144,7 @@ NGBoxFragmentBuilder& NGBoxFragmentBuilder::PropagateBreak(
if (LIKELY(!has_block_fragmentation_))
return *this;
if (!did_break_)
- PropagateBreak(*child_layout_result.PhysicalFragment());
+ PropagateBreak(child_layout_result.PhysicalFragment());
if (child_layout_result.HasForcedBreak())
SetHasForcedBreak();
else
@@ -150,7 +153,7 @@ NGBoxFragmentBuilder& NGBoxFragmentBuilder::PropagateBreak(
}
NGBoxFragmentBuilder& NGBoxFragmentBuilder::PropagateBreak(
- const NGPhysicalFragment& child_fragment) {
+ const NGPhysicalContainerFragment& child_fragment) {
DCHECK(has_block_fragmentation_);
if (!did_break_) {
const auto* token = child_fragment.BreakToken();
@@ -162,35 +165,37 @@ NGBoxFragmentBuilder& NGBoxFragmentBuilder::PropagateBreak(
void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
NGBlockNode node,
const NGStaticPosition& static_position,
- LayoutObject* inline_container) {
+ const LayoutInline* inline_container) {
DCHECK_GE(InlineSize(), LayoutUnit());
DCHECK_GE(BlockSize(), LayoutUnit());
- NGOutOfFlowPositionedDescendant descendant{node, static_position,
- inline_container};
+ NGOutOfFlowPositionedDescendant descendant(
+ node, static_position,
+ inline_container ? ToLayoutInline(inline_container->ContinuationRoot())
+ : nullptr);
// Need 0,0 physical coordinates as child offset. Because offset
// is stored as logical, must convert physical 0,0 to logical.
- NGLogicalOffset zero_offset;
+ LogicalOffset zero_offset;
switch (GetWritingMode()) {
case WritingMode::kHorizontalTb:
if (IsLtr(Direction()))
- zero_offset = NGLogicalOffset();
+ zero_offset = LogicalOffset();
else
- zero_offset = NGLogicalOffset(InlineSize(), LayoutUnit());
+ zero_offset = LogicalOffset(InlineSize(), LayoutUnit());
break;
case WritingMode::kVerticalRl:
case WritingMode::kSidewaysRl:
if (IsLtr(Direction()))
- zero_offset = NGLogicalOffset(LayoutUnit(), BlockSize());
+ zero_offset = LogicalOffset(LayoutUnit(), BlockSize());
else
- zero_offset = NGLogicalOffset(InlineSize(), BlockSize());
+ zero_offset = LogicalOffset(InlineSize(), BlockSize());
break;
case WritingMode::kVerticalLr:
case WritingMode::kSidewaysLr:
if (IsLtr(Direction()))
- zero_offset = NGLogicalOffset();
+ zero_offset = LogicalOffset();
else
- zero_offset = NGLogicalOffset(InlineSize(), LayoutUnit());
+ zero_offset = LogicalOffset(InlineSize(), LayoutUnit());
break;
}
oof_positioned_candidates_.push_back(
@@ -274,27 +279,27 @@ void NGBoxFragmentBuilder::ComputeInlineContainerFragments(
// and will break if this changes.
DCHECK_GE(InlineSize(), LayoutUnit());
DCHECK_GE(BlockSize(), LayoutUnit());
+#if DCHECK_IS_ON()
+ // Make sure all entries are continuation root.
+ for (const auto& entry : *inline_containing_block_map)
+ DCHECK_EQ(entry.key, entry.key->ContinuationRoot());
+#endif
- // std::pair.first points to the start linebox fragment.
- // std::pair.second points to the end linebox fragment.
- using LineBoxPair = std::pair<const NGPhysicalLineBoxFragment*,
- const NGPhysicalLineBoxFragment*>;
HashMap<const LayoutObject*, LineBoxPair> containing_linebox_map;
-
- for (wtf_size_t i = 0; i < children_.size(); i++) {
- if (children_[i]->IsLineBox()) {
- const auto* linebox = To<NGPhysicalLineBoxFragment>(children_[i].get());
- const NGPhysicalOffset linebox_offset = offsets_[i].ConvertToPhysical(
+ for (const auto& child : children_) {
+ if (child.fragment->IsLineBox()) {
+ const auto& linebox = To<NGPhysicalLineBoxFragment>(*child.fragment);
+ const PhysicalOffset linebox_offset = child.offset.ConvertToPhysical(
GetWritingMode(), Direction(),
- ToNGPhysicalSize(Size(), GetWritingMode()), linebox->Size());
+ ToPhysicalSize(Size(), GetWritingMode()), linebox.Size());
GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
&containing_linebox_map,
linebox, linebox_offset);
- } else if (children_[i]->IsBox()) {
- const auto* box_fragment = To<NGPhysicalBoxFragment>(children_[i].get());
+ } else if (child.fragment->IsBox()) {
+ const auto& box_fragment = To<NGPhysicalBoxFragment>(*child.fragment);
bool is_anonymous_container =
- box_fragment->GetLayoutObject() &&
- box_fragment->GetLayoutObject()->IsAnonymousBlock();
+ box_fragment.GetLayoutObject() &&
+ box_fragment.GetLayoutObject()->IsAnonymousBlock();
if (!is_anonymous_container)
continue;
// If child is an anonymous container, this might be a special case of
@@ -303,15 +308,15 @@ void NGBoxFragmentBuilder::ComputeInlineContainerFragments(
// lineboxes inside anonymous box.
// For more on this special case, see "css container is an inline, with
// inline splitting" comment in NGOutOfFlowLayoutPart::LayoutDescendant.
- const NGPhysicalOffset box_offset = offsets_[i].ConvertToPhysical(
+ const PhysicalOffset box_offset = child.offset.ConvertToPhysical(
GetWritingMode(), Direction(),
- ToNGPhysicalSize(Size(), GetWritingMode()), box_fragment->Size());
+ ToPhysicalSize(Size(), GetWritingMode()), box_fragment.Size());
// Traverse lineboxes of anonymous box.
- for (const auto& child : box_fragment->Children()) {
- if (child->IsLineBox()) {
- const auto* linebox = To<NGPhysicalLineBoxFragment>(child.get());
- const NGPhysicalOffset linebox_offset = child.Offset() + box_offset;
+ for (const auto& box_child : box_fragment.Children()) {
+ if (box_child->IsLineBox()) {
+ const auto& linebox = To<NGPhysicalLineBoxFragment>(*box_child);
+ const PhysicalOffset linebox_offset = box_child.Offset() + box_offset;
GatherInlineContainerFragmentsFromLinebox(inline_containing_block_map,
&containing_linebox_map,
linebox, linebox_offset);
@@ -321,4 +326,17 @@ void NGBoxFragmentBuilder::ComputeInlineContainerFragments(
}
}
+#if DCHECK_IS_ON()
+
+void NGBoxFragmentBuilder::CheckNoBlockFragmentation() const {
+ DCHECK(!did_break_);
+ DCHECK(!has_forced_break_);
+ DCHECK_EQ(used_block_size_, LayoutUnit());
+ DCHECK_EQ(minimal_space_shortage_, LayoutUnit::Max());
+ DCHECK_EQ(initial_break_before_, EBreakBetween::kAuto);
+ DCHECK_EQ(previous_break_after_, EBreakBetween::kAuto);
+}
+
+#endif
+
} // namespace blink
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 73e6b320266..29ffa6702be 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
@@ -5,9 +5,10 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BOX_FRAGMENT_BUILDER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BOX_FRAGMENT_BUILDER_H_
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.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"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h"
@@ -36,9 +37,7 @@ class CORE_EXPORT NGBoxFragmentBuilder final
writing_mode,
direction),
box_type_(NGPhysicalFragment::NGBoxType::kNormalBox),
- did_break_(false) {
- layout_object_ = node.GetLayoutBox();
- }
+ did_break_(false) {}
// Build a fragment for LayoutObject without NGLayoutInputNode. LayoutInline
// has NGInlineItem but does not have corresponding NGLayoutInputNode.
@@ -56,30 +55,35 @@ class CORE_EXPORT NGBoxFragmentBuilder final
layout_object_ = layout_object;
}
+ NGBoxFragmentBuilder& SetInitialFragmentGeometry(
+ const NGFragmentGeometry& initial_fragment_geometry) {
+ initial_fragment_geometry_ = &initial_fragment_geometry;
+ size_ = initial_fragment_geometry_->border_box_size;
+ is_initial_block_size_indefinite_ = size_.block_size == kIndefiniteSize;
+ return *this;
+ }
+
NGBoxFragmentBuilder& SetIntrinsicBlockSize(LayoutUnit intrinsic_block_size) {
intrinsic_block_size_ = intrinsic_block_size;
return *this;
}
- NGBoxFragmentBuilder& SetBorders(const NGBoxStrut& border) {
+ const NGBoxStrut& Borders() const {
+ DCHECK(initial_fragment_geometry_);
DCHECK_NE(BoxType(), NGPhysicalFragment::kInlineBox);
- borders_ = border;
- return *this;
+ return initial_fragment_geometry_->border;
}
- const NGBoxStrut& Borders() const { return borders_; }
- NGBoxFragmentBuilder& SetPadding(const NGBoxStrut& padding) {
- DCHECK_NE(BoxType(), NGPhysicalFragment::kInlineBox);
- padding_ = padding;
- return *this;
+ const NGBoxStrut& Scrollbar() const {
+ DCHECK(initial_fragment_geometry_);
+ return initial_fragment_geometry_->scrollbar;
}
- NGBoxFragmentBuilder& SetPadding(const NGLineBoxStrut& padding) {
- DCHECK_EQ(BoxType(), NGPhysicalFragment::kInlineBox);
- // Convert to flow-relative, because ToInlineBoxFragment() will convert
- // the padding to physical coordinates using flow-relative writing-mode.
- padding_ = NGBoxStrut(padding, IsFlippedLinesWritingMode(GetWritingMode()));
- return *this;
+ const NGBoxStrut& Padding() const {
+ DCHECK(initial_fragment_geometry_);
+ return initial_fragment_geometry_->padding;
+ }
+ const LogicalSize& InitialBorderBoxSize() const {
+ DCHECK(initial_fragment_geometry_);
+ return initial_fragment_geometry_->border_box_size;
}
- const NGBoxStrut& Padding() const { return padding_; }
-
// Remove all children.
void RemoveChildren();
@@ -93,11 +97,11 @@ class CORE_EXPORT NGBoxFragmentBuilder final
// Update if we have fragmented in this flow.
NGBoxFragmentBuilder& PropagateBreak(const NGLayoutResult&);
- NGBoxFragmentBuilder& PropagateBreak(const NGPhysicalFragment&);
+ NGBoxFragmentBuilder& PropagateBreak(const NGPhysicalContainerFragment&);
void AddOutOfFlowLegacyCandidate(NGBlockNode,
const NGStaticPosition&,
- LayoutObject* inline_container);
+ const LayoutInline* inline_container);
// Set how much of the block size we've used so far for this box.
NGBoxFragmentBuilder& SetUsedBlockSize(LayoutUnit used_block_size) {
@@ -168,9 +172,6 @@ class CORE_EXPORT NGBoxFragmentBuilder final
scoped_refptr<const NGLayoutResult> Abort(
NGLayoutResult::NGLayoutResultStatus);
- // A vector of child offsets. Initially set by AddChild().
- const OffsetVector& Offsets() const { return offsets_; }
-
NGPhysicalFragment::NGBoxType BoxType() const;
NGBoxFragmentBuilder& SetBoxType(NGPhysicalFragment::NGBoxType box_type) {
box_type_ = box_type;
@@ -180,8 +181,8 @@ class CORE_EXPORT NGBoxFragmentBuilder final
is_fieldset_container_ = true;
return *this;
}
- NGBoxFragmentBuilder& SetIsOldLayoutRoot() {
- is_old_layout_root_ = true;
+ NGBoxFragmentBuilder& SetIsLegacyLayoutRoot() {
+ is_legacy_layout_root_ = true;
return *this;
}
@@ -213,26 +214,32 @@ class CORE_EXPORT NGBoxFragmentBuilder final
struct InlineContainingBlockGeometry {
DISALLOW_NEW();
// Union of fragments generated on the first line.
- NGPhysicalOffsetRect start_fragment_union_rect;
+ PhysicalRect start_fragment_union_rect;
// Union of fragments generated on the last line.
- NGPhysicalOffsetRect end_fragment_union_rect;
+ PhysicalRect end_fragment_union_rect;
};
using InlineContainingBlockMap =
HashMap<const LayoutObject*,
base::Optional<InlineContainingBlockGeometry>>;
void ComputeInlineContainerFragments(
- InlineContainingBlockMap* inline_container_fragments);
+ InlineContainingBlockMap* inline_containing_block_map);
+
+#if DCHECK_IS_ON()
+ // If we don't participate in a fragmentation context, this method can check
+ // that all block fragmentation related fields have their initial value.
+ void CheckNoBlockFragmentation() const;
+#endif
private:
scoped_refptr<const NGLayoutResult> ToBoxFragment(WritingMode);
+ const NGFragmentGeometry* initial_fragment_geometry_ = nullptr;
LayoutUnit intrinsic_block_size_;
- NGBoxStrut borders_;
- NGBoxStrut padding_;
NGPhysicalFragment::NGBoxType box_type_;
bool is_fieldset_container_ = false;
+ bool is_initial_block_size_indefinite_ = false;
bool did_break_;
bool has_forced_break_ = false;
bool is_new_fc_ = false;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.cc
index 4a2c878514b..3d272e1a1a7 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_break_token.cc
@@ -22,7 +22,7 @@ static_assert(sizeof(NGBreakToken) == sizeof(SameSizeAsNGBreakToken),
} // namespace
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
namespace {
@@ -62,6 +62,6 @@ void NGBreakToken::ShowBreakTokenTree() const {
AppendBreakTokenToString(this, &string_builder);
fprintf(stderr, "%s\n", string_builder.ToString().Utf8().data());
}
-#endif // NDEBUG
+#endif // DCHECK_IS_ON()
} // namespace blink
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 f7f77622110..53c7163688b 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
@@ -58,10 +58,10 @@ class CORE_EXPORT NGBreakToken : public RefCounted<NGBreakToken> {
box_, static_cast<NGLayoutInputNode::NGLayoutInputNodeType>(type_));
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
virtual String ToString() const;
void ShowBreakTokenTree() const;
-#endif // NDEBUG
+#endif
protected:
NGBreakToken(NGBreakTokenType type,
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 911faa1e8c2..147dc0495f2 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
@@ -5,11 +5,13 @@
#include "third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h"
#include <algorithm>
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
@@ -21,87 +23,90 @@ namespace {
inline bool NeedsColumnBalancing(LayoutUnit block_size,
const ComputedStyle& style) {
- return block_size == NGSizeIndefinite ||
+ return block_size == kIndefiniteSize ||
style.GetColumnFill() == EColumnFill::kBalance;
}
-// Constrain a balanced column block size to not overflow the multicol
-// container.
-LayoutUnit ConstrainColumnBlockSize(LayoutUnit size,
- NGBlockNode node,
- const NGConstraintSpace& space) {
- // The {,max-}{height,width} properties are specified on the multicol
- // container, but here we're calculating the column block sizes inside the
- // multicol container, which isn't exactly the same. We may shrink the column
- // block size here, but we'll never stretch it, because the value passed is
- // the perfect balanced block size. Making it taller would only disrupt the
- // balanced output, for no reason. The only thing we need to worry about here
- // is to not overflow the multicol container.
-
- // 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.
- NGBoxStrut border_scrollbar_padding =
- CalculateBorderScrollbarPadding(space, node);
- LayoutUnit extra = border_scrollbar_padding.BlockSum();
- size += extra;
-
- NGBoxStrut border_padding =
- ComputeBorders(space, node) + ComputePadding(space, node.Style());
-
- const ComputedStyle& style = node.Style();
- LayoutUnit max = ResolveMaxBlockLength(space, style, border_padding,
- style.LogicalMaxHeight(), size,
- LengthResolvePhase::kLayout);
- LayoutUnit extent = ResolveMainBlockLength(space, style, border_padding,
- style.LogicalHeight(), size,
- LengthResolvePhase::kLayout);
- if (extent != NGSizeIndefinite) {
- // A specified height/width will just constrain the maximum length.
- max = std::min(max, extent);
- }
-
- // Constrain and convert the value back to content-box.
- size = std::min(size, max);
- return size - extra;
-}
-
} // namespace
NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(
- NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token)
- : NGLayoutAlgorithm(node, space, To<NGBlockBreakToken>(break_token)) {
- container_builder_.SetIsNewFormattingContext(space.IsNewFormattingContext());
+ const NGLayoutAlgorithmParams& params)
+ : NGLayoutAlgorithm(params),
+ border_padding_(params.fragment_geometry.border +
+ params.fragment_geometry.padding),
+ border_scrollbar_padding_(border_padding_ +
+ params.fragment_geometry.scrollbar) {
+ container_builder_.SetIsNewFormattingContext(
+ params.space.IsNewFormattingContext());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
- // TODO(layout-dev): Store some combination of border, scrollbar, padding on
- // this class.
- NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
- NGBoxStrut scrollbars = Node().GetScrollbarSizes();
- NGBoxStrut padding = ComputePadding(ConstraintSpace(), Style()) +
- ComputeIntrinsicPadding(ConstraintSpace(), Node());
- NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
- NGLogicalSize border_box_size =
- CalculateBorderBoxSize(ConstraintSpace(), Node(), borders + padding);
- NGLogicalSize content_box_size =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding);
- NGLogicalSize column_size = CalculateColumnSize(content_box_size);
+ LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
+ LogicalSize content_box_size =
+ ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
+ LogicalSize column_size = CalculateColumnSize(content_box_size);
WritingMode writing_mode = ConstraintSpace().GetWritingMode();
- LayoutUnit column_block_offset(border_scrollbar_padding.block_start);
+
+ // Omit leading border+padding+scrollbar for all fragments but the first.
+ LayoutUnit column_block_offset = IsResumingLayout(BreakToken())
+ ? LayoutUnit()
+ : border_scrollbar_padding_.block_start;
+
+ // 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.
+ LayoutUnit previously_used_block_size;
+ if (const auto* token = BreakToken())
+ previously_used_block_size = token->UsedBlockSize();
+
+ bool needs_more_fragments_in_outer = false;
+ if (ConstraintSpace().HasBlockFragmentation()) {
+ // Subtract the space for content we've processed in previous fragments.
+ column_size.block_size -= previously_used_block_size;
+
+ // Check if we can fit everything (that's remaining), block-wise, within the
+ // current outer fragmentainer. If we can't, we need to adjust the block
+ // size, and allow the multicol container to continue in a subsequent outer
+ // fragmentainer.
+ LayoutUnit available_outer_space =
+ ConstraintSpace().FragmentainerSpaceAtBfcStart() - column_block_offset;
+ if (column_size.block_size > available_outer_space) {
+ column_size.block_size = available_outer_space;
+ needs_more_fragments_in_outer = true;
+ }
+ }
LayoutUnit column_inline_progression =
column_size.inline_size +
ResolveUsedColumnGap(content_box_size.inline_size, Style());
int used_column_count =
ResolveUsedColumnCount(content_box_size.inline_size, Style());
+ if (ConstraintSpace().HasBlockFragmentation())
+ container_builder_.SetHasBlockFragmentation();
+
do {
- scoped_refptr<const NGBlockBreakToken> break_token = BreakToken();
+ scoped_refptr<const NGBlockBreakToken> break_token;
+ if (const auto* token = BreakToken()) {
+ // We're resuming layout of this multicol container after an outer
+ // fragmentation break. Resume at the break token of the last column that
+ // we were able to lay out. Note that in some cases, there may be no child
+ // break tokens. That happens if we weren't able to lay out anything at
+ // all in the previous outer fragmentainer, e.g. due to a forced break
+ // before this multicol container, or e.g. if there was leading
+ // unbreakable content that couldn't fit in the space we were offered back
+ // then. In other words, in that case, we're about to create the first
+ // fragment for this multicol container.
+ const auto child_tokens = token->ChildBreakTokens();
+ if (child_tokens.size()) {
+ break_token =
+ To<NGBlockBreakToken>(child_tokens[child_tokens.size() - 1]);
+ }
+ }
+
LayoutUnit intrinsic_block_size;
- LayoutUnit column_inline_offset(border_scrollbar_padding.inline_start);
+ LayoutUnit column_inline_offset(border_scrollbar_padding_.inline_start);
int actual_column_count = 0;
int forced_break_count = 0;
@@ -119,15 +124,17 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
NGConstraintSpace child_space = CreateConstraintSpaceForColumns(
column_size, separate_leading_margins);
- NGBlockLayoutAlgorithm child_algorithm(Node(), child_space,
- break_token.get());
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(child_space, Node());
+
+ NGBlockLayoutAlgorithm child_algorithm(
+ {Node(), fragment_geometry, child_space, break_token.get()});
child_algorithm.SetBoxType(NGPhysicalFragment::kColumnBox);
scoped_refptr<const NGLayoutResult> result = child_algorithm.Layout();
- const auto* column =
- To<NGPhysicalBoxFragment>(result->PhysicalFragment());
+ const auto& column = result->PhysicalFragment();
- NGLogicalOffset logical_offset(column_inline_offset, column_block_offset);
- container_builder_.AddChild(*result, logical_offset);
+ LogicalOffset logical_offset(column_inline_offset, column_block_offset);
+ container_builder_.AddChild(column, logical_offset);
LayoutUnit space_shortage = result->MinimalSpaceShortage();
if (space_shortage > LayoutUnit()) {
@@ -142,16 +149,51 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
separate_leading_margins = false;
}
- LayoutUnit block_size =
- NGBoxFragment(writing_mode, ConstraintSpace().Direction(), *column)
- .BlockSize();
+ LayoutUnit block_size = NGFragment(writing_mode, column).BlockSize();
intrinsic_block_size =
std::max(intrinsic_block_size, column_block_offset + block_size);
column_inline_offset += column_inline_progression;
- break_token = To<NGBlockBreakToken>(column->BreakToken());
+ break_token = To<NGBlockBreakToken>(column.BreakToken());
+
+ // If we're participating in an outer fragmentation context, we'll only
+ // allow as many columns as the used value of column-count, so that we
+ // don't overflow in the inline direction. There's one important
+ // exception: If we have determined that this is going to be the last
+ // fragment for this multicol container in the outer fragmentation
+ // context, we'll just allow as many columns as needed (and let them
+ // overflow in the inline direction, if necessary). We're not going to
+ // progress into a next outer fragmentainer if the (remaining part of the)
+ // multicol container fits block-wise in the current outer fragmentainer.
+ if (ConstraintSpace().HasBlockFragmentation() && break_token &&
+ !break_token->IsFinished() &&
+ actual_column_count >= used_column_count &&
+ needs_more_fragments_in_outer) {
+ LayoutUnit fragment_block_size = child_space.FragmentainerBlockSize();
+ // Calculate how much block space we've been able to process so far, in
+ // this fragment and all previous fragments generated for this multicol
+ // container.
+ LayoutUnit used_block_size = fragment_block_size;
+ // If this isn't the first fragment, add the amount that we were able to
+ // process in previous fragments. Otherwise, we're the first fragment,
+ // and we have to add leading border+padding+scrollbar to the fragment
+ // size (which would otherwise only be the size of the columns), since
+ // that's put at the start of the first fragment.
+ if (previously_used_block_size)
+ used_block_size += previously_used_block_size;
+ else
+ fragment_block_size += border_scrollbar_padding_.block_start;
+ container_builder_.SetUsedBlockSize(used_block_size);
+ container_builder_.SetBlockSize(fragment_block_size);
+ container_builder_.SetDidBreak();
+ break;
+ }
} while (break_token && !break_token->IsFinished());
+ // TODO(mstensho): Nested column balancing.
+ if (container_builder_.DidBreak())
+ 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.
@@ -180,21 +222,30 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
break;
} while (true);
- NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders + scrollbars,
- &container_builder_)
- .Run();
-
// TODO(mstensho): Propagate baselines.
- if (border_box_size.block_size == NGSizeIndefinite) {
- // Get the block size from the columns if it's auto.
- border_box_size.block_size =
- column_size.block_size + border_scrollbar_padding.BlockSum();
+ // If we need another fragment for this multicol container (because we're
+ // nested inside another fragmentation context), we have already calculated
+ // the block size correctly. Otherwise, do it now.
+ if (!container_builder_.DidBreak()) {
+ LayoutUnit block_size;
+ if (border_box_size.block_size == kIndefiniteSize) {
+ // Get the block size from the columns if it's auto.
+ block_size =
+ column_size.block_size + border_scrollbar_padding_.BlockSum();
+ } 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_used_block_size;
+ }
+ container_builder_.SetBlockSize(block_size);
}
- container_builder_.SetInlineSize(border_box_size.inline_size);
- container_builder_.SetBlockSize(border_box_size.block_size);
- container_builder_.SetBorders(ComputeBorders(ConstraintSpace(), Node()));
- container_builder_.SetPadding(ComputePadding(ConstraintSpace(), Style()));
+
+ NGOutOfFlowLayoutPart(
+ Node(), ConstraintSpace(),
+ container_builder_.Borders() + container_builder_.Scrollbar(),
+ &container_builder_)
+ .Run();
return container_builder_.ToBoxFragment();
}
@@ -202,7 +253,10 @@ scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
// First calculate the min/max sizes of columns.
- NGBlockLayoutAlgorithm algorithm(Node(), ConstraintSpace());
+ NGConstraintSpace space = CreateConstraintSpaceForMinMax();
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(space, Node());
+ NGBlockLayoutAlgorithm algorithm({Node(), fragment_geometry, space});
MinMaxSizeInput child_input(input);
child_input.size_type = NGMinMaxSizeType::kContentBoxSize;
base::Optional<MinMaxSize> min_max_sizes =
@@ -228,17 +282,15 @@ base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
sizes += column_gap * (column_count - 1);
if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) {
- LayoutUnit border_scrollbar_padding =
- CalculateBorderScrollbarPadding(ConstraintSpace(), node_).InlineSum();
- sizes += border_scrollbar_padding;
+ sizes += border_scrollbar_padding_.InlineSum();
}
return sizes;
}
-NGLogicalSize NGColumnLayoutAlgorithm::CalculateColumnSize(
- const NGLogicalSize& content_box_size) {
- NGLogicalSize column_size = content_box_size;
+LogicalSize NGColumnLayoutAlgorithm::CalculateColumnSize(
+ const LogicalSize& content_box_size) {
+ LogicalSize column_size = content_box_size;
DCHECK_GE(column_size.inline_size, LayoutUnit());
column_size.inline_size =
ResolveUsedColumnInlineSize(column_size.inline_size, Style());
@@ -254,7 +306,7 @@ NGLogicalSize NGColumnLayoutAlgorithm::CalculateColumnSize(
}
LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
- const NGLogicalSize& column_size,
+ const LogicalSize& column_size,
int column_count) {
// To calculate a balanced column size, we need to figure out how tall our
// content is. To do that we need to lay out. Create a special constraint
@@ -262,14 +314,18 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
// make us lay out all the multicol content as one single tall strip. When
// we're done with this layout pass, we can examine the result and calculate
// an ideal column block size.
- NGConstraintSpace space = CreateConstaintSpaceForBalancing(column_size);
- NGBlockLayoutAlgorithm balancing_algorithm(Node(), space);
+ NGConstraintSpace space = CreateConstraintSpaceForBalancing(column_size);
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(space, Node());
+ NGBlockLayoutAlgorithm balancing_algorithm(
+ {Node(), fragment_geometry, space});
scoped_refptr<const NGLayoutResult> result = balancing_algorithm.Layout();
// TODO(mstensho): This is where the fun begins. We need to examine the entire
// fragment tree, not just the root.
- NGFragment fragment(space.GetWritingMode(), *result->PhysicalFragment());
- LayoutUnit single_strip_block_size = fragment.BlockSize();
+ LayoutUnit single_strip_block_size =
+ NGFragment(space.GetWritingMode(), result->PhysicalFragment())
+ .BlockSize();
// Some extra care is required the division here. We want a the resulting
// LayoutUnit value to be large enough to prevent overflowing columns. Use
@@ -280,7 +336,7 @@ LayoutUnit NGColumnLayoutAlgorithm::CalculateBalancedColumnBlockSize(
single_strip_block_size.ToFloat() / static_cast<float>(column_count));
// Finally, honor {,min-,max-}{height,width} properties.
- return ConstrainColumnBlockSize(block_size, Node(), ConstraintSpace());
+ return ConstrainColumnBlockSize(block_size);
}
LayoutUnit NGColumnLayoutAlgorithm::StretchColumnBlockSize(
@@ -291,11 +347,46 @@ LayoutUnit NGColumnLayoutAlgorithm::StretchColumnBlockSize(
return current_column_size;
LayoutUnit length = current_column_size + minimal_space_shortage;
// Honor {,min-,max-}{height,width} properties.
- return ConstrainColumnBlockSize(length, Node(), ConstraintSpace());
+ return ConstrainColumnBlockSize(length);
+}
+
+// Constrain a balanced column block size to not overflow the multicol
+// container.
+LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
+ LayoutUnit size) const {
+ // The {,max-}{height,width} properties are specified on the multicol
+ // container, but here we're calculating the column block sizes inside the
+ // multicol container, which isn't exactly the same. We may shrink the column
+ // block size here, but we'll never stretch it, because the value passed is
+ // the perfect balanced block size. Making it taller would only disrupt the
+ // balanced output, for no reason. The only thing we need to worry about here
+ // is to not overflow the multicol container.
+
+ // 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();
+ size += extra;
+
+ const ComputedStyle& style = Style();
+ LayoutUnit max = ResolveMaxBlockLength(
+ ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), size,
+ LengthResolvePhase::kLayout);
+ LayoutUnit extent = ResolveMainBlockLength(
+ ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size,
+ LengthResolvePhase::kLayout);
+ if (extent != kIndefiniteSize) {
+ // A specified height/width will just constrain the maximum length.
+ max = std::min(max, extent);
+ }
+
+ // Constrain and convert the value back to content-box.
+ size = std::min(size, max);
+ return size - extra;
}
NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
- const NGLogicalSize& column_size,
+ const LogicalSize& column_size,
bool separate_leading_margins) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
@@ -321,11 +412,11 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForColumns(
return space_builder.ToConstraintSpace();
}
-NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstaintSpaceForBalancing(
- const NGLogicalSize& column_size) const {
+NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForBalancing(
+ const LogicalSize& column_size) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
- space_builder.SetAvailableSize({column_size.inline_size, NGSizeIndefinite});
+ space_builder.SetAvailableSize({column_size.inline_size, kIndefiniteSize});
space_builder.SetPercentageResolutionSize(column_size);
space_builder.SetIsAnonymous(true);
space_builder.SetIsIntermediateLayout(true);
@@ -333,4 +424,14 @@ NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstaintSpaceForBalancing(
return space_builder.ToConstraintSpace();
}
+NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForMinMax()
+ const {
+ NGConstraintSpaceBuilder space_builder(
+ ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
+ space_builder.SetIsAnonymous(true);
+ space_builder.SetIsIntermediateLayout(true);
+
+ return space_builder.ToConstraintSpace();
+}
+
} // namespace blink
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 d303bb1406b..ae6905dfb6f 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
@@ -12,18 +12,15 @@ namespace blink {
class NGBlockNode;
class NGBlockBreakToken;
-class NGBreakToken;
class NGConstraintSpace;
-struct NGLogicalSize;
+struct LogicalSize;
class CORE_EXPORT NGColumnLayoutAlgorithm
: public NGLayoutAlgorithm<NGBlockNode,
NGBoxFragmentBuilder,
NGBlockBreakToken> {
public:
- NGColumnLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token = nullptr);
+ NGColumnLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
scoped_refptr<const NGLayoutResult> Layout() override;
@@ -31,8 +28,8 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
const MinMaxSizeInput&) const override;
private:
- NGLogicalSize CalculateColumnSize(const NGLogicalSize& content_box_size);
- LayoutUnit CalculateBalancedColumnBlockSize(const NGLogicalSize& column_size,
+ LogicalSize CalculateColumnSize(const LogicalSize& content_box_size);
+ LayoutUnit CalculateBalancedColumnBlockSize(const LogicalSize& column_size,
int column_count);
// Stretch the column length, if allowed. We do this during column balancing,
@@ -43,11 +40,17 @@ class CORE_EXPORT NGColumnLayoutAlgorithm
LayoutUnit current_column_size,
LayoutUnit container_content_box_block_size) const;
+ LayoutUnit ConstrainColumnBlockSize(LayoutUnit size) const;
+
NGConstraintSpace CreateConstraintSpaceForColumns(
- const NGLogicalSize& column_size,
+ const LogicalSize& column_size,
bool separate_leading_margins) const;
- NGConstraintSpace CreateConstaintSpaceForBalancing(
- const NGLogicalSize& column_size) const;
+ NGConstraintSpace CreateConstraintSpaceForBalancing(
+ const LogicalSize& column_size) const;
+ NGConstraintSpace CreateConstraintSpaceForMinMax() const;
+
+ const NGBoxStrut border_padding_;
+ const NGBoxStrut border_scrollbar_padding_;
};
} // namespace blink
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 912dcbc233f..d2d3a04034a 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
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
@@ -28,21 +29,12 @@ class NGColumnLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
}
scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
- const NGConstraintSpace& space,
- NGBlockNode node) {
- scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, space).Layout();
-
- return To<NGPhysicalBoxFragment>(result->PhysicalFragment());
- }
-
- scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
Element* element) {
NGBlockNode container(ToLayoutBox(element->GetLayoutObject()));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
- return RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
+ return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
}
String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
@@ -82,20 +74,20 @@ TEST_F(NGColumnLayoutAlgorithmTest, EmptyMulticol) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
scoped_refptr<const NGPhysicalBoxFragment> parent_fragment =
- RunBlockLayoutAlgorithm(space, container);
+ NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
FragmentChildIterator iterator(parent_fragment.get());
const auto* fragment = iterator.NextChild();
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(210), LayoutUnit(100)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(210, 100), fragment->Size());
EXPECT_FALSE(iterator.NextChild());
// There should be nothing inside the multicol container.
// TODO(mstensho): Get rid of this column fragment. It shouldn't be here.
fragment = FragmentChildIterator(fragment).NextChild();
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(100), LayoutUnit()), fragment->Size());
+ EXPECT_EQ(PhysicalSize(100, 0), fragment->Size());
EXPECT_EQ(0UL, fragment->Children().size());
EXPECT_FALSE(iterator.NextChild());
@@ -123,30 +115,30 @@ TEST_F(NGColumnLayoutAlgorithmTest, EmptyBlock) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
scoped_refptr<const NGPhysicalBoxFragment> parent_fragment =
- RunBlockLayoutAlgorithm(space, container);
+ NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
FragmentChildIterator iterator(parent_fragment.get());
const auto* fragment = iterator.NextChild();
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(210), LayoutUnit(100)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(210, 100), fragment->Size());
ASSERT_TRUE(fragment);
EXPECT_FALSE(iterator.NextChild());
iterator.SetParent(fragment);
// first column fragment
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
fragment = iterator.NextChild(&offset);
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit()), offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(100), LayoutUnit()), fragment->Size());
+ EXPECT_EQ(PhysicalOffset(), offset);
+ EXPECT_EQ(PhysicalSize(100, 0), fragment->Size());
EXPECT_FALSE(iterator.NextChild());
// #child fragment in first column
iterator.SetParent(fragment);
fragment = iterator.NextChild(&offset);
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit()), offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(100), LayoutUnit()), fragment->Size());
+ EXPECT_EQ(PhysicalOffset(), offset);
+ EXPECT_EQ(PhysicalSize(100, 0), fragment->Size());
EXPECT_EQ(0UL, fragment->Children().size());
EXPECT_FALSE(iterator.NextChild());
}
@@ -172,31 +164,31 @@ TEST_F(NGColumnLayoutAlgorithmTest, BlockInOneColumn) {
NGBlockNode container(ToLayoutBox(GetLayoutObjectByElementId("container")));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
scoped_refptr<const NGPhysicalBoxFragment> parent_fragment =
- RunBlockLayoutAlgorithm(space, container);
+ NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
FragmentChildIterator iterator(parent_fragment.get());
const auto* fragment = iterator.NextChild();
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(310), LayoutUnit(100)), fragment->Size());
+ EXPECT_EQ(PhysicalSize(310, 100), fragment->Size());
EXPECT_FALSE(iterator.NextChild());
iterator.SetParent(fragment);
// first column fragment
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
fragment = iterator.NextChild(&offset);
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit()), offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), fragment->Size());
+ EXPECT_EQ(PhysicalOffset(), offset);
+ EXPECT_EQ(PhysicalSize(150, 100), fragment->Size());
EXPECT_FALSE(iterator.NextChild());
// #child fragment in first column
iterator.SetParent(fragment);
fragment = iterator.NextChild(&offset);
ASSERT_TRUE(fragment);
- EXPECT_EQ(NGPhysicalOffset(LayoutUnit(), LayoutUnit()), offset);
- EXPECT_EQ(NGPhysicalSize(LayoutUnit(90), LayoutUnit(100)), fragment->Size());
+ EXPECT_EQ(PhysicalOffset(), offset);
+ EXPECT_EQ(PhysicalSize(90, 100), fragment->Size());
EXPECT_EQ(0UL, fragment->Children().size());
EXPECT_FALSE(iterator.NextChild());
}
@@ -1985,8 +1977,10 @@ TEST_F(NGColumnLayoutAlgorithmTest, MinMax) {
layout_object->SetStyle(style);
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
- NGColumnLayoutAlgorithm algorithm(node, space);
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(space, node);
+ NGColumnLayoutAlgorithm algorithm({node, fragment_geometry, space});
base::Optional<MinMaxSize> size;
MinMaxSizeInput zero_input(
/* percentage_resolution_block_size */ (LayoutUnit()));
@@ -2821,5 +2815,113 @@ TEST_F(NGColumnLayoutAlgorithmTest, ClassCBreakPointBeforeLine) {
EXPECT_EQ(expectation, dump);
}
+TEST_F(NGColumnLayoutAlgorithmTest, Nested) {
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .outer { columns:3; height:50px; column-fill:auto; width:320px; }
+ .inner { columns:2; height:100px; column-fill:auto; padding:1px; }
+ .outer, .inner { column-gap:10px; }
+ .content { break-inside:avoid; height:20px; }
+ </style>
+ <div id="container">
+ <div class="outer">
+ <div class="content" style="width:5px;"></div>
+ <div class="inner">
+ <div class="content" style="width:10px;"></div>
+ <div class="content" style="width:20px;"></div>
+ <div class="content" style="width:30px;"></div>
+ <div class="content" style="width:40px;"></div>
+ <div class="content" style="width:50px;"></div>
+ <div class="content" style="width:60px;"></div>
+ <div class="content" style="width:70px;"></div>
+ </div>
+ </div>
+ </div>
+ )HTML");
+
+ String dump = DumpFragmentTree(GetElementById("container"));
+ String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+ offset:unplaced size:1000x50
+ offset:0,0 size:320x50
+ offset:0,0 size:100x50
+ offset:0,0 size:5x20
+ offset:0,20 size:100x30
+ offset:1,1 size:44x29
+ offset:0,0 size:10x20
+ offset:55,1 size:44x29
+ offset:0,0 size:20x20
+ offset:110,0 size:100x50
+ offset:0,0 size:100x50
+ offset:1,0 size:44x50
+ offset:0,0 size:30x20
+ offset:0,20 size:40x20
+ offset:55,0 size:44x50
+ offset:0,0 size:50x20
+ offset:0,20 size:60x20
+ offset:220,0 size:100x23
+ offset:0,0 size:100x23
+ offset:1,0 size:44x20
+ offset:0,0 size:70x20
+)DUMP";
+ EXPECT_EQ(expectation, dump);
+}
+
+TEST_F(NGColumnLayoutAlgorithmTest, NestedLimitedHeight) {
+ // This tests that we don't advance to the next outer fragmentainer when we've
+ // reached the bottom of an inner multicol container. We should create inner
+ // columns that overflow in the inline direction in that case.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .outer { columns:2; height:50px; column-fill:auto; width:210px; }
+ .inner { columns:2; height:80px; column-fill:auto; }
+ .outer, .inner { column-gap:10px; }
+ .content { break-inside:avoid; height:20px; }
+ </style>
+ <div id="container">
+ <div class="outer">
+ <div class="content" style="width:5px;"></div>
+ <div class="inner">
+ <div class="content" style="width:10px;"></div>
+ <div class="content" style="width:20px;"></div>
+ <div class="content" style="width:30px;"></div>
+ <div class="content" style="width:40px;"></div>
+ <div class="content" style="width:50px;"></div>
+ <div class="content" style="width:60px;"></div>
+ <div class="content" style="width:70px;"></div>
+ <div class="content" style="width:80px;"></div>
+ <div class="content" style="width:90px;"></div>
+ </div>
+ </div>
+ </div>
+ )HTML");
+
+ String dump = DumpFragmentTree(GetElementById("container"));
+ String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+ offset:unplaced size:1000x50
+ offset:0,0 size:210x50
+ offset:0,0 size:100x50
+ offset:0,0 size:5x20
+ offset:0,20 size:100x30
+ offset:0,0 size:45x30
+ offset:0,0 size:10x20
+ offset:55,0 size:45x30
+ offset:0,0 size:20x20
+ offset:110,0 size:100x50
+ offset:0,0 size:100x50
+ offset:0,0 size:45x50
+ offset:0,0 size:30x20
+ offset:0,20 size:40x20
+ offset:55,0 size:45x50
+ offset:0,0 size:50x20
+ offset:0,20 size:60x20
+ offset:110,0 size:45x50
+ offset:0,0 size:70x20
+ offset:0,20 size:80x20
+ offset:165,0 size:45x20
+ offset:0,0 size:90x20
+)DUMP";
+ EXPECT_EQ(expectation, dump);
+}
+
} // anonymous namespace
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
index 6442fffe033..bc8d8bbb5f7 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -19,7 +19,7 @@ namespace blink {
namespace {
struct SameSizeAsNGConstraintSpace {
- NGLogicalSize available_size;
+ LogicalSize available_size;
union {
NGBfcOffset bfc_offset;
void* rare_data;
@@ -44,9 +44,9 @@ NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject(
LayoutBoxUtils::AvailableLogicalWidth(block, cb);
LayoutUnit available_logical_height =
LayoutBoxUtils::AvailableLogicalHeight(block, cb);
- NGLogicalSize percentage_size = {available_logical_width,
- available_logical_height};
- NGLogicalSize available_size = percentage_size;
+ LogicalSize percentage_size = {available_logical_width,
+ available_logical_height};
+ LogicalSize available_size = percentage_size;
bool fixed_inline = false, fixed_block = false;
bool fixed_block_is_definite = true;
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 fd52237443d..0464c66e683 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
@@ -7,11 +7,11 @@
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_baseline.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
@@ -60,20 +60,16 @@ class CORE_EXPORT NGConstraintSpace final {
public:
enum ConstraintSpaceFlags {
kOrthogonalWritingModeRoot = 1 << 0,
- kFixedSizeInline = 1 << 1,
- kFixedSizeBlock = 1 << 2,
- kFixedSizeBlockIsDefinite = 1 << 3,
- kShrinkToFit = 1 << 4,
- kIntermediateLayout = 1 << 5,
- kSeparateLeadingFragmentainerMargins = 1 << 6,
- kNewFormattingContext = 1 << 7,
- kAnonymous = 1 << 8,
- kUseFirstLineStyle = 1 << 9,
- kForceClearance = 1 << 10,
- kHasRareData = 1 << 11,
+ kFixedSizeBlockIsDefinite = 1 << 1,
+ kIntermediateLayout = 1 << 2,
+ kSeparateLeadingFragmentainerMargins = 1 << 3,
+ kNewFormattingContext = 1 << 4,
+ kAnonymous = 1 << 5,
+ kUseFirstLineStyle = 1 << 6,
+ kAncestorHasClearancePastAdjoiningFloats = 1 << 7,
// Size of bitfield used to store the flags.
- kNumberOfConstraintSpaceFlags = 12
+ kNumberOfConstraintSpaceFlags = 8
};
// To ensure that the bfc_offset_, rare_data_ union doesn't get polluted,
@@ -154,7 +150,7 @@ class CORE_EXPORT NGConstraintSpace final {
// The available space size.
// See: https://drafts.csswg.org/css-sizing/#available
- NGLogicalSize AvailableSize() const { return available_size_; }
+ LogicalSize AvailableSize() const { return available_size_; }
// The size to use for percentage resolution.
// See: https://drafts.csswg.org/css-sizing/#percentage-sizing
@@ -169,7 +165,7 @@ class CORE_EXPORT NGConstraintSpace final {
case kZero:
return LayoutUnit();
case kIndefinite:
- return NGSizeIndefinite;
+ return kIndefiniteSize;
case kRareDataPercentage:
DCHECK(HasRareData());
return rare_data_->percentage_resolution_size.inline_size;
@@ -187,14 +183,14 @@ class CORE_EXPORT NGConstraintSpace final {
case kZero:
return LayoutUnit();
case kIndefinite:
- return NGSizeIndefinite;
+ return kIndefiniteSize;
case kRareDataPercentage:
DCHECK(HasRareData());
return rare_data_->percentage_resolution_size.block_size;
}
}
- NGLogicalSize PercentageResolutionSize() const {
+ LogicalSize PercentageResolutionSize() const {
return {PercentageResolutionInlineSize(), PercentageResolutionBlockSize()};
}
@@ -210,7 +206,7 @@ class CORE_EXPORT NGConstraintSpace final {
case kZero:
return LayoutUnit();
case kIndefinite:
- return NGSizeIndefinite;
+ return kIndefiniteSize;
case kRareDataPercentage:
DCHECK(HasRareData());
return rare_data_->replaced_percentage_resolution_block_size;
@@ -222,7 +218,7 @@ class CORE_EXPORT NGConstraintSpace final {
}
// The size to use for percentage resolution of replaced elements.
- NGLogicalSize ReplacedPercentageResolutionSize() const {
+ LogicalSize ReplacedPercentageResolutionSize() const {
return {ReplacedPercentageResolutionInlineSize(),
ReplacedPercentageResolutionBlockSize()};
}
@@ -233,7 +229,7 @@ class CORE_EXPORT NGConstraintSpace final {
LayoutUnit PercentageResolutionInlineSizeForParentWritingMode() const {
if (!IsOrthogonalWritingModeRoot())
return PercentageResolutionInlineSize();
- if (PercentageResolutionBlockSize() != NGSizeIndefinite)
+ if (PercentageResolutionBlockSize() != kIndefiniteSize)
return PercentageResolutionBlockSize();
// TODO(mstensho): Figure out why we get here. It seems wrong, but we do get
// here in some grid layout situations.
@@ -242,7 +238,7 @@ class CORE_EXPORT NGConstraintSpace final {
LayoutUnit FragmentainerBlockSize() const {
return HasRareData() ? rare_data_->fragmentainer_block_size
- : NGSizeIndefinite;
+ : kIndefiniteSize;
}
// Return the block space that was available in the current fragmentainer at
@@ -252,7 +248,7 @@ class CORE_EXPORT NGConstraintSpace final {
LayoutUnit FragmentainerSpaceAtBfcStart() const {
DCHECK(HasBlockFragmentation());
return HasRareData() ? rare_data_->fragmentainer_space_at_bfc_start
- : NGSizeIndefinite;
+ : kIndefiniteSize;
}
// Whether the current constraint space is for the newly established
@@ -280,6 +276,16 @@ class CORE_EXPORT NGConstraintSpace final {
// Also note this is true only when the document has ':first-line' rules.
bool UseFirstLineStyle() const { return HasFlag(kUseFirstLineStyle); }
+ // Returns true if an ancestor had clearance past adjoining floats.
+ //
+ // Typically this can be detected by seeing if a |ForcedBfcBlockOffset| is
+ // set. However new formatting contexts may require additional passes (if
+ // margins are adjoining or not), and without this extra bit of information
+ // can get into a bad state.
+ bool AncestorHasClearancePastAdjoiningFloats() const {
+ return HasFlag(kAncestorHasClearancePastAdjoiningFloats);
+ }
+
// Some layout modes “stretch” their children to a fixed size (e.g. flex,
// grid). These flags represented whether a layout needs to produce a
// fragment that satisfies a fixed constraint in the inline and block
@@ -287,9 +293,9 @@ class CORE_EXPORT NGConstraintSpace final {
//
// If these flags are true, the AvailableSize() is interpreted as the fixed
// border-box size of this box in the respective dimension.
- bool IsFixedSizeInline() const { return HasFlag(kFixedSizeInline); }
+ bool IsFixedSizeInline() const { return bitfields_.is_fixed_size_inline; }
- bool IsFixedSizeBlock() const { return HasFlag(kFixedSizeBlock); }
+ bool IsFixedSizeBlock() const { return bitfields_.is_fixed_size_block; }
// Whether a fixed block size should be considered definite.
bool FixedSizeBlockIsDefinite() const {
@@ -298,7 +304,7 @@ class CORE_EXPORT NGConstraintSpace final {
// Whether an auto inline-size should be interpreted as shrink-to-fit
// (ie. fit-content). This is used for inline-block, floats, etc.
- bool IsShrinkToFit() const { return HasFlag(kShrinkToFit); }
+ bool IsShrinkToFit() const { return bitfields_.is_shrink_to_fit; }
// Whether this constraint space is used for an intermediate layout in a
// multi-pass layout. In such a case, we should not copy back the resulting
@@ -326,6 +332,10 @@ class CORE_EXPORT NGConstraintSpace final {
bitfields_.table_cell_child_layout_phase);
}
+ bool IsInRestrictedBlockSizeTableCell() const {
+ return bitfields_.is_in_restricted_block_size_table_cell;
+ }
+
NGMarginStrut MarginStrut() const {
return HasRareData() ? rare_data_->margin_strut : NGMarginStrut();
}
@@ -350,27 +360,28 @@ class CORE_EXPORT NGConstraintSpace final {
return HasRareData() ? rare_data_->bfc_offset : bfc_offset_;
}
- // If present, and the current layout hasn't resolved its BFC offset yet (see
- // BfcOffset), the layout should position all of its unpositioned floats at
- // this offset. This value is the BFC offset that we calculated in the
- // previous pass, a pass which aborted once the BFC offset got resolved,
- // because we had walked past content (i.e. floats) that depended on it being
- // resolved.
+ // If present, and the current layout hasn't resolved its BFC block-offset
+ // yet (see BfcOffset), the layout should position all of its unpositioned
+ // floats at this offset.
//
- // This value should be propogated to child layouts if the current layout
- // hasn't resolved its BFC offset yet.
+ // This value is present if:
+ // - An ancestor had clearance past adjoining floats. In this case this
+ // value is calculated ahead of time.
+ // - A second layout pass is required as there were unpositioned floats
+ // within the tree, and an arbitrary sibling determined their BFC
+ // block-offset.
//
- // This value is calculated *after* an initial pass of the tree, and should
- // only be present during subsequent passes.
- base::Optional<LayoutUnit> FloatsBfcBlockOffset() const {
- return HasRareData() ? rare_data_->floats_bfc_block_offset : base::nullopt;
+ // This value should be propagated to child layouts if the current layout
+ // hasn't resolved its BFC offset yet.
+ base::Optional<LayoutUnit> ForcedBfcBlockOffset() const {
+ return HasRareData() ? rare_data_->forced_bfc_block_offset : base::nullopt;
}
// Return the types (none, left, right, both) of preceding adjoining
// floats. These are floats that are added while the in-flow BFC offset is
// still unknown. The floats may or may not be unpositioned (pending). That
// depends on which layout pass we're in. They are typically positioned if
- // FloatsBfcOffset() is known. Adjoining floats should be treated differently
+ // ForcedBfcOffset() is known. Adjoining floats should be treated differently
// when calculating clearance on a block with adjoining block-start margin.
// (in such cases we will know up front that the block will need clearance,
// since, if it doesn't, the float will be pulled along with the block, and
@@ -390,28 +401,6 @@ class CORE_EXPORT NGConstraintSpace final {
return HasRareData() ? rare_data_->clearance_offset : LayoutUnit::Min();
}
- // Return true if the fragment needs to have clearance applied to it,
- // regardless of its hypothetical position. The fragment will then go exactly
- // below the relevant floats. This happens when a cleared child gets separated
- // from floats that would otherwise be adjoining; example:
- //
- // <div id="container">
- // <div id="float" style="float:left; width:100px; height:100px;"></div>
- // <div id="clearee" style="clear:left; margin-top:12345px;">text</div>
- // </div>
- //
- // Clearance separates #clearee from #container, and #float is positioned at
- // the block-start content edge of #container. Without clearance, margins
- // would have been adjoining and the large margin on #clearee would have
- // pulled both #container and #float along with it. No margin, no matter how
- // large, would ever be able to pull #clearee below the float then. But we
- // have clearance, the margins are separated, and in this case we know that we
- // have clearance even before we have laid out (because of the adjoining
- // float). So it would just be wrong to check for clearance when we position
- // #clearee. Nothing can prevent clearance here. A large margin on the cleared
- // child will be canceled out with negative clearance.
- bool ShouldForceClearance() const { return HasFlag(kForceClearance); }
-
const NGBaselineRequestList BaselineRequests() const {
return NGBaselineRequestList(bitfields_.baseline_requests);
}
@@ -437,6 +426,12 @@ class CORE_EXPORT NGConstraintSpace final {
return other.rare_data_->IsInitialForMaySkipLayout();
}
+ // Returns true if the size constraints (shrink-to-fit, fixed-inline-size)
+ // are equal.
+ bool AreSizeConstraintsEqual(const NGConstraintSpace& other) const {
+ return bitfields_.AreSizeConstraintsEqual(other.bitfields_);
+ }
+
bool AreSizesEqual(const NGConstraintSpace& other) const {
if (available_size_ != other.available_size_)
return false;
@@ -509,23 +504,23 @@ class CORE_EXPORT NGConstraintSpace final {
RareData(const RareData&) = default;
~RareData() = default;
- NGLogicalSize percentage_resolution_size;
+ LogicalSize percentage_resolution_size;
LayoutUnit replaced_percentage_resolution_block_size;
NGBfcOffset bfc_offset;
NGMarginStrut margin_strut;
- base::Optional<LayoutUnit> floats_bfc_block_offset;
+ base::Optional<LayoutUnit> forced_bfc_block_offset;
LayoutUnit clearance_offset = LayoutUnit::Min();
- LayoutUnit fragmentainer_block_size = NGSizeIndefinite;
- LayoutUnit fragmentainer_space_at_bfc_start = NGSizeIndefinite;
+ LayoutUnit fragmentainer_block_size = kIndefiniteSize;
+ LayoutUnit fragmentainer_space_at_bfc_start = kIndefiniteSize;
unsigned block_direction_fragmentation_type : 2;
bool MaySkipLayout(const RareData& other) const {
return margin_strut == other.margin_strut &&
- floats_bfc_block_offset == other.floats_bfc_block_offset &&
+ forced_bfc_block_offset == other.forced_bfc_block_offset &&
fragmentainer_block_size == other.fragmentainer_block_size &&
fragmentainer_space_at_bfc_start ==
other.fragmentainer_space_at_bfc_start &&
@@ -536,9 +531,9 @@ class CORE_EXPORT NGConstraintSpace final {
// Must be kept in sync with members checked within |MaySkipLayout|.
bool IsInitialForMaySkipLayout() const {
return margin_strut == NGMarginStrut() &&
- floats_bfc_block_offset == base::nullopt &&
- fragmentainer_block_size == NGSizeIndefinite &&
- fragmentainer_space_at_bfc_start == NGSizeIndefinite &&
+ forced_bfc_block_offset == base::nullopt &&
+ fragmentainer_block_size == kIndefiniteSize &&
+ fragmentainer_space_at_bfc_start == kIndefiniteSize &&
block_direction_fragmentation_type == kFragmentNone;
}
};
@@ -550,33 +545,52 @@ class CORE_EXPORT NGConstraintSpace final {
DISALLOW_NEW();
public:
- // We explicitly define a default constructor to ensure the kHasRareData
- // bitfield doesn't accidently get set.
Bitfields() : Bitfields(WritingMode::kHorizontalTb) {}
explicit Bitfields(WritingMode writing_mode)
- : table_cell_child_layout_phase(static_cast<unsigned>(
- NGTableCellChildLayoutPhase::kNotTableCellChild)),
+ : has_rare_data(false),
adjoining_floats(static_cast<unsigned>(kFloatTypeNone)),
writing_mode(static_cast<unsigned>(writing_mode)),
direction(static_cast<unsigned>(TextDirection::kLtr)),
+ is_shrink_to_fit(false),
+ is_fixed_size_inline(false),
+ is_fixed_size_block(false),
+ is_in_restricted_block_size_table_cell(false),
+ table_cell_child_layout_phase(static_cast<unsigned>(
+ NGTableCellChildLayoutPhase::kNotTableCellChild)),
flags(kFixedSizeBlockIsDefinite),
percentage_inline_storage(kSameAsAvailable),
percentage_block_storage(kSameAsAvailable),
replaced_percentage_block_storage(kSameAsAvailable) {}
bool MaySkipLayout(const Bitfields& other) const {
- return table_cell_child_layout_phase ==
- other.table_cell_child_layout_phase &&
- adjoining_floats == other.adjoining_floats &&
+ return adjoining_floats == other.adjoining_floats &&
writing_mode == other.writing_mode && flags == other.flags &&
baseline_requests == other.baseline_requests;
}
- unsigned table_cell_child_layout_phase : 2; // NGTableCellChildLayoutPhase
- unsigned adjoining_floats : 2; // NGFloatTypes
+ bool AreSizeConstraintsEqual(const Bitfields& other) const {
+ return is_shrink_to_fit == other.is_shrink_to_fit &&
+ is_fixed_size_inline == other.is_fixed_size_inline &&
+ is_fixed_size_block == other.is_fixed_size_block &&
+ is_in_restricted_block_size_table_cell ==
+ other.is_in_restricted_block_size_table_cell &&
+ table_cell_child_layout_phase ==
+ other.table_cell_child_layout_phase;
+ }
+
+ unsigned has_rare_data : 1;
+ unsigned adjoining_floats : 3; // NGFloatTypes
unsigned writing_mode : 3;
unsigned direction : 1;
+
+ // Size constraints.
+ unsigned is_shrink_to_fit : 1;
+ unsigned is_fixed_size_inline : 1;
+ unsigned is_fixed_size_block : 1;
+ unsigned is_in_restricted_block_size_table_cell : 1;
+ unsigned table_cell_child_layout_phase : 2; // NGTableCellChildLayoutPhase
+
unsigned flags : kNumberOfConstraintSpaceFlags; // ConstraintSpaceFlags
unsigned baseline_requests : NGBaselineRequestList::kSerializedBits;
@@ -589,21 +603,21 @@ class CORE_EXPORT NGConstraintSpace final {
return bitfields_.flags & static_cast<unsigned>(mask);
}
- inline bool HasRareData() const { return HasFlag(kHasRareData); }
+ inline bool HasRareData() const { return bitfields_.has_rare_data; }
RareData* EnsureRareData() {
if (!HasRareData()) {
rare_data_ = new RareData(bfc_offset_);
- bitfields_.flags |= kHasRareData;
+ bitfields_.has_rare_data = true;
}
return rare_data_;
}
- NGLogicalSize available_size_;
+ LogicalSize available_size_;
// To save a little space, we union these two fields. rare_data_ is valid if
- // the kHasRareData bitfield is set, otherwise bfc_offset_ is valid.
+ // the |has_rare_data| bit is set, otherwise bfc_offset_ is valid.
union {
NGBfcOffset bfc_offset_;
RareData* rare_data_;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc
index 23eed9091e5..c3c57444888 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.cc
@@ -15,7 +15,7 @@ NGPercentageStorage GetPercentageStorage(LayoutUnit percentage_size,
if (percentage_size == available_size)
return kSameAsAvailable;
- if (percentage_size == NGSizeIndefinite)
+ if (percentage_size == kIndefiniteSize)
return kIndefinite;
if (percentage_size == LayoutUnit())
@@ -27,7 +27,7 @@ NGPercentageStorage GetPercentageStorage(LayoutUnit percentage_size,
} // namespace
NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetPercentageResolutionSize(
- NGLogicalSize percentage_resolution_size) {
+ LogicalSize percentage_resolution_size) {
#if DCHECK_IS_ON()
DCHECK(is_available_size_set_);
#endif
@@ -73,7 +73,7 @@ NGConstraintSpaceBuilder& NGConstraintSpaceBuilder::SetPercentageResolutionSize(
NGConstraintSpaceBuilder&
NGConstraintSpaceBuilder::SetReplacedPercentageResolutionSize(
- NGLogicalSize replaced_percentage_resolution_size) {
+ LogicalSize replaced_percentage_resolution_size) {
#if DCHECK_IS_ON()
DCHECK(is_available_size_set_);
#endif
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 41670b6f69b..09a7f0b5770 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
@@ -6,8 +6,8 @@
#define NGConstraintSpaceBuilder_h
#include "base/optional.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
@@ -63,20 +63,20 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
// https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto
void AdjustInlineSizeIfNeeded(LayoutUnit* inline_size) const {
DCHECK(!is_in_parallel_flow_);
- if (*inline_size != NGSizeIndefinite)
+ if (*inline_size != kIndefiniteSize)
return;
- DCHECK_NE(orthogonal_fallback_inline_size_, NGSizeIndefinite);
+ DCHECK_NE(orthogonal_fallback_inline_size_, kIndefiniteSize);
*inline_size = orthogonal_fallback_inline_size_;
}
- NGConstraintSpaceBuilder& SetAvailableSize(NGLogicalSize available_size) {
+ NGConstraintSpaceBuilder& SetAvailableSize(LogicalSize available_size) {
#if DCHECK_IS_ON()
is_available_size_set_ = true;
#endif
space_.available_size_ = available_size;
if (UNLIKELY(!is_in_parallel_flow_)) {
- space_.available_size_.Flip();
+ space_.available_size_.Transpose();
AdjustInlineSizeIfNeeded(&space_.available_size_.inline_size);
}
@@ -84,10 +84,10 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
}
NGConstraintSpaceBuilder& SetPercentageResolutionSize(
- NGLogicalSize percentage_resolution_size);
+ LogicalSize percentage_resolution_size);
NGConstraintSpaceBuilder& SetReplacedPercentageResolutionSize(
- NGLogicalSize replaced_percentage_resolution_size);
+ LogicalSize replaced_percentage_resolution_size);
// Set the fallback available inline-size for an orthogonal child. The size is
// the inline size in the writing mode of the orthogonal child.
@@ -101,7 +101,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
DCHECK(!is_fragmentainer_block_size_set_);
is_fragmentainer_block_size_set_ = true;
#endif
- if (size != NGSizeIndefinite)
+ if (size != kIndefiniteSize)
space_.EnsureRareData()->fragmentainer_block_size = size;
return *this;
}
@@ -111,7 +111,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
DCHECK(!is_fragmentainer_space_at_bfc_start_set_);
is_fragmentainer_space_at_bfc_start_set_ = true;
#endif
- if (space != NGSizeIndefinite)
+ if (space != kIndefiniteSize)
space_.EnsureRareData()->fragmentainer_space_at_bfc_start = space;
return *this;
}
@@ -123,18 +123,18 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
NGConstraintSpaceBuilder& SetIsFixedSizeInline(bool b) {
if (LIKELY(is_in_parallel_flow_))
- SetFlag(NGConstraintSpace::kFixedSizeInline, b);
+ space_.bitfields_.is_fixed_size_inline = b;
else
- SetFlag(NGConstraintSpace::kFixedSizeBlock, b);
+ space_.bitfields_.is_fixed_size_block = b;
return *this;
}
NGConstraintSpaceBuilder& SetIsFixedSizeBlock(bool b) {
if (LIKELY(is_in_parallel_flow_))
- SetFlag(NGConstraintSpace::kFixedSizeBlock, b);
+ space_.bitfields_.is_fixed_size_block = b;
else
- SetFlag(NGConstraintSpace::kFixedSizeInline, b);
+ space_.bitfields_.is_fixed_size_inline = b;
return *this;
}
@@ -147,7 +147,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
}
NGConstraintSpaceBuilder& SetIsShrinkToFit(bool b) {
- SetFlag(NGConstraintSpace::kShrinkToFit, b);
+ space_.bitfields_.is_shrink_to_fit = b;
return *this;
}
@@ -184,6 +184,11 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
return *this;
}
+ NGConstraintSpaceBuilder& SetAncestorHasClearancePastAdjoiningFloats() {
+ SetFlag(NGConstraintSpace::kAncestorHasClearancePastAdjoiningFloats, true);
+ return *this;
+ }
+
NGConstraintSpaceBuilder& SetAdjoiningFloatTypes(NGFloatTypes floats) {
if (!is_new_fc_)
space_.bitfields_.adjoining_floats = static_cast<unsigned>(floats);
@@ -212,15 +217,15 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
return *this;
}
- NGConstraintSpaceBuilder& SetFloatsBfcBlockOffset(
- const base::Optional<LayoutUnit>& floats_bfc_block_offset) {
+ NGConstraintSpaceBuilder& SetForcedBfcBlockOffset(
+ const base::Optional<LayoutUnit>& forced_bfc_block_offset) {
#if DCHECK_IS_ON()
- DCHECK(!is_floats_bfc_block_offset_set_);
- is_floats_bfc_block_offset_set_ = true;
+ DCHECK(!is_forced_bfc_block_offset_set_);
+ is_forced_bfc_block_offset_set_ = true;
#endif
- if (LIKELY(!is_new_fc_ && floats_bfc_block_offset != base::nullopt)) {
- space_.EnsureRareData()->floats_bfc_block_offset =
- floats_bfc_block_offset;
+ if (LIKELY(!is_new_fc_ && forced_bfc_block_offset != base::nullopt)) {
+ space_.EnsureRareData()->forced_bfc_block_offset =
+ forced_bfc_block_offset;
}
return *this;
@@ -237,11 +242,6 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
return *this;
}
- NGConstraintSpaceBuilder& SetShouldForceClearance(bool b) {
- SetFlag(NGConstraintSpace::kForceClearance, b);
- return *this;
- }
-
NGConstraintSpaceBuilder& SetTableCellChildLayoutPhase(
NGTableCellChildLayoutPhase table_cell_child_layout_phase) {
space_.bitfields_.table_cell_child_layout_phase =
@@ -249,6 +249,11 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
return *this;
}
+ NGConstraintSpaceBuilder& SetIsInRestrictedBlockSizeTableCell() {
+ space_.bitfields_.is_in_restricted_block_size_table_cell = true;
+ return *this;
+ }
+
NGConstraintSpaceBuilder& SetExclusionSpace(
const NGExclusionSpace& exclusion_space) {
if (!is_new_fc_)
@@ -300,7 +305,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
// Orthogonal writing mode roots may need a fallback, to prevent available
// inline size from being indefinite, which isn't allowed. This is the
// available inline size in the writing mode of the orthogonal child.
- LayoutUnit orthogonal_fallback_inline_size_ = NGSizeIndefinite;
+ LayoutUnit orthogonal_fallback_inline_size_ = kIndefiniteSize;
bool is_in_parallel_flow_;
bool is_new_fc_;
@@ -312,7 +317,7 @@ class CORE_EXPORT NGConstraintSpaceBuilder final {
bool is_fragmentainer_space_at_bfc_start_set_ = false;
bool is_block_direction_fragmentation_type_set_ = false;
bool is_margin_strut_set_ = false;
- bool is_floats_bfc_block_offset_set_ = false;
+ bool is_forced_bfc_block_offset_set_ = false;
bool is_clearance_offset_set_ = false;
bool to_constraint_space_called_ = false;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder_test.cc
index eb59619ae1d..0f853d9b508 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder_test.cc
@@ -14,13 +14,13 @@ using NGConstraintSpaceBuilderTest = NGLayoutTest;
// Asserts that indefinite inline length becomes initial containing
// block width for horizontal-tb inside vertical document.
TEST(NGConstraintSpaceBuilderTest, AvailableSizeFromHorizontalICB) {
- NGPhysicalSize icb_size{NGSizeIndefinite, LayoutUnit(51)};
+ PhysicalSize icb_size{kIndefiniteSize, LayoutUnit(51)};
NGConstraintSpaceBuilder horizontal_builder(WritingMode::kHorizontalTb,
WritingMode::kHorizontalTb,
/* is_new_fc */ true);
- NGLogicalSize fixed_size{LayoutUnit(100), LayoutUnit(200)};
- NGLogicalSize indefinite_size{NGSizeIndefinite, NGSizeIndefinite};
+ LogicalSize fixed_size{LayoutUnit(100), LayoutUnit(200)};
+ LogicalSize indefinite_size{kIndefiniteSize, kIndefiniteSize};
horizontal_builder.SetOrthogonalFallbackInlineSize(icb_size.height);
horizontal_builder.SetAvailableSize(fixed_size);
@@ -43,13 +43,13 @@ TEST(NGConstraintSpaceBuilderTest, AvailableSizeFromHorizontalICB) {
// Asserts that indefinite inline length becomes initial containing
// block height for vertical-lr inside horizontal document.
TEST(NGConstraintSpaceBuilderTest, AvailableSizeFromVerticalICB) {
- NGPhysicalSize icb_size{LayoutUnit(51), NGSizeIndefinite};
+ PhysicalSize icb_size{LayoutUnit(51), kIndefiniteSize};
NGConstraintSpaceBuilder horizontal_builder(WritingMode::kVerticalLr,
WritingMode::kVerticalLr,
/* is_new_fc */ true);
- NGLogicalSize fixed_size{LayoutUnit(100), LayoutUnit(200)};
- NGLogicalSize indefinite_size{NGSizeIndefinite, NGSizeIndefinite};
+ LogicalSize fixed_size{LayoutUnit(100), LayoutUnit(200)};
+ LogicalSize indefinite_size{kIndefiniteSize, kIndefiniteSize};
horizontal_builder.SetOrthogonalFallbackInlineSize(icb_size.width);
horizontal_builder.SetAvailableSize(fixed_size);
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 39f44dbe3bb..e62ccbc378e 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,48 +7,61 @@
#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_layout_result.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"
namespace blink {
+namespace {
+// This routine returns true if inline_container should replace descendant's
+// inline container.
+bool IsInlineContainerForDescendant(
+ const NGOutOfFlowPositionedDescendant& descendant,
+ const LayoutObject* inline_container) {
+ return !descendant.inline_container && inline_container &&
+ inline_container->IsLayoutInline() &&
+ inline_container->CanContainOutOfFlowPositionedElement(
+ descendant.node.Style().GetPosition());
+}
+} // namespace
+
NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
- const NGLayoutResult& child,
- const NGLogicalOffset& child_offset) {
+ const NGPhysicalContainerFragment& child,
+ const LogicalOffset& child_offset,
+ const LayoutInline* inline_container) {
// Collect the child's out of flow descendants.
// child_offset is offset of inline_start/block_start vertex.
// Candidates need offset of top/left vertex.
- const auto& out_of_flow_descendants = child.OutOfFlowPositionedDescendants();
- if (!out_of_flow_descendants.IsEmpty()) {
- NGLogicalOffset top_left_offset;
- NGPhysicalSize child_size = child.PhysicalFragment()->Size();
+ if (child.HasOutOfFlowPositionedDescendants()) {
+ const auto& out_of_flow_descendants =
+ child.OutOfFlowPositionedDescendants();
+ LogicalOffset top_left_offset;
+ PhysicalSize child_size = child.Size();
switch (GetWritingMode()) {
case WritingMode::kHorizontalTb:
top_left_offset =
(IsRtl(Direction()))
- ? NGLogicalOffset{child_offset.inline_offset + child_size.width,
- child_offset.block_offset}
+ ? LogicalOffset{child_offset.inline_offset + child_size.width,
+ child_offset.block_offset}
: child_offset;
break;
case WritingMode::kVerticalRl:
case WritingMode::kSidewaysRl:
top_left_offset =
(IsRtl(Direction()))
- ? NGLogicalOffset{child_offset.inline_offset +
- child_size.height,
- child_offset.block_offset + child_size.width}
- : NGLogicalOffset{child_offset.inline_offset,
- child_offset.block_offset + child_size.width};
+ ? LogicalOffset{child_offset.inline_offset + child_size.height,
+ child_offset.block_offset + child_size.width}
+ : LogicalOffset{child_offset.inline_offset,
+ child_offset.block_offset + child_size.width};
break;
case WritingMode::kVerticalLr:
case WritingMode::kSidewaysLr:
- top_left_offset = (IsRtl(Direction()))
- ? NGLogicalOffset{child_offset.inline_offset +
- child_size.height,
- child_offset.block_offset}
- : child_offset;
+ top_left_offset =
+ (IsRtl(Direction()))
+ ? LogicalOffset{child_offset.inline_offset + child_size.height,
+ child_offset.block_offset}
+ : child_offset;
break;
}
@@ -62,44 +75,62 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
// </div>
// TODO(layout-dev): This code should eventually be removed once we handle
// relative positioned objects directly in the fragment tree.
- if (LayoutBox* child_box =
- ToLayoutBoxOrNull(child.PhysicalFragment()->GetLayoutObject())) {
- top_left_offset +=
- NGPhysicalOffset(child_box->OffsetForInFlowPosition())
- .ConvertToLogical(GetWritingMode(), Direction(), NGPhysicalSize(),
- NGPhysicalSize());
+ if (const LayoutBox* child_box =
+ ToLayoutBoxOrNull(child.GetLayoutObject())) {
+ top_left_offset += PhysicalOffset(child_box->OffsetForInFlowPosition())
+ .ConvertToLogical(GetWritingMode(), Direction(),
+ PhysicalSize(), PhysicalSize());
}
- for (const NGOutOfFlowPositionedDescendant& descendant :
+ for (NGOutOfFlowPositionedDescendant& descendant :
out_of_flow_descendants) {
+ if (IsInlineContainerForDescendant(descendant, inline_container)) {
+ descendant.inline_container = inline_container;
+ }
oof_positioned_candidates_.push_back(
NGOutOfFlowPositionedCandidate(descendant, top_left_offset));
}
}
- if (child.HasOrthogonalFlowRoots())
+ // 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() ||
+ !IsParallelWritingMode(child.Style().GetWritingMode(),
+ Style().GetWritingMode()))
has_orthogonal_flow_roots_ = true;
// We only need to report if inflow or floating elements depend on the
// percentage resolution block-size. OOF-positioned children resolve their
// percentages against the "final" size of their parent.
- if (child.DependsOnPercentageBlockSize() &&
- !child.PhysicalFragment()->IsOutOfFlowPositioned())
- has_child_that_depends_on_percentage_block_size_ = true;
-
- if (child.MayHaveDescendantAboveBlockStart() &&
- !child.PhysicalFragment()->IsBlockFormattingContextRoot())
+ if (child.DependsOnPercentageBlockSize() && !child.IsOutOfFlowPositioned())
+ has_descendant_that_depends_on_percentage_block_size_ = true;
+
+ // The |may_have_descendant_above_block_start_| flag is used to determine if
+ // a fragment can be re-used when floats are present. We only are about:
+ // - Inflow children who are positioned above our block-start edge.
+ // - Any inflow descendants (within the same formatting-context) that *may*
+ // be positioned above our block-start edge.
+ if ((child_offset.block_offset < LayoutUnit() &&
+ !child.IsOutOfFlowPositioned()) ||
+ (!child.IsBlockFormattingContextRoot() &&
+ child.MayHaveDescendantAboveBlockStart()))
may_have_descendant_above_block_start_ = true;
- return AddChild(child.PhysicalFragment(), child_offset);
-}
+ // Compute |has_floating_descendants_| to optimize tree traversal in paint.
+ if (!has_floating_descendants_) {
+ if (child.IsFloating()) {
+ has_floating_descendants_ = true;
+ } else {
+ if (!child.IsBlockFormattingContextRoot() &&
+ child.HasFloatingDescendants())
+ has_floating_descendants_ = true;
+ }
+ }
-NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
- scoped_refptr<const NGPhysicalFragment> child,
- const NGLogicalOffset& child_offset) {
- NGBreakToken* child_break_token = child->BreakToken();
+ // Collect any (block) break tokens.
+ NGBreakToken* child_break_token = child.BreakToken();
if (child_break_token && has_block_fragmentation_) {
- switch (child->Type()) {
+ switch (child.Type()) {
case NGPhysicalFragment::kFragmentBox:
case NGPhysicalFragment::kFragmentRenderedLegend:
if (To<NGBlockBreakToken>(child_break_token)->HasLastResortBreak())
@@ -119,61 +150,54 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
}
}
- // Compute |has_floating_descendants_| to optimize tree traversal in paint.
- if (!has_floating_descendants_) {
- if (child->IsFloating()) {
- has_floating_descendants_ = true;
- } else {
- auto* child_container = DynamicTo<NGPhysicalContainerFragment>(*child);
- if (child_container && !child->IsBlockFormattingContextRoot() &&
- child_container->HasFloatingDescendants())
- has_floating_descendants_ = true;
- }
- }
-
- if (child_offset.block_offset < LayoutUnit())
- may_have_descendant_above_block_start_ = true;
+ AddChildInternal(&child, child_offset);
+ return *this;
+}
- if (!IsParallelWritingMode(child->Style().GetWritingMode(),
- Style().GetWritingMode()))
- has_orthogonal_flow_roots_ = true;
+void NGContainerFragmentBuilder::AddChildInternal(
+ scoped_refptr<const NGPhysicalFragment> child,
+ const LogicalOffset& child_offset) {
+ // In order to know where list-markers are within the children list (for the
+ // |NGSimplifiedLayoutAlgorithm|) we always place them as the first child.
+ if (child->IsListMarker()) {
+ children_.push_front(ChildWithOffset(child_offset, std::move(child)));
+ return;
+ }
- children_.emplace_back(std::move(child));
- offsets_.push_back(child_offset);
- return *this;
+ children_.emplace_back(child_offset, std::move(child));
}
-NGLogicalOffset NGContainerFragmentBuilder::GetChildOffset(
- const LayoutObject* child) const {
- for (wtf_size_t i = 0; i < children_.size(); ++i) {
- if (children_[i]->GetLayoutObject() == child)
- return offsets_[i];
+LogicalOffset NGContainerFragmentBuilder::GetChildOffset(
+ const LayoutObject* object) const {
+ for (const auto& child : children_) {
+ if (child.fragment->GetLayoutObject() == object)
+ return child.offset;
// TODO(layout-dev): ikilpatrick thinks we may need to traverse
// further than the initial line-box children for a nested inline
// container. We could not come up with a testcase, it would be
// something with split inlines, and nested oof/fixed descendants maybe.
- if (children_[i]->IsLineBox()) {
+ if (child.fragment->IsLineBox()) {
const auto& line_box_fragment =
- To<NGPhysicalLineBoxFragment>(*children_[i]);
+ To<NGPhysicalLineBoxFragment>(*child.fragment);
for (const auto& line_box_child : line_box_fragment.Children()) {
- if (line_box_child->GetLayoutObject() == child) {
- return offsets_[i] + line_box_child.Offset().ConvertToLogical(
- GetWritingMode(), Direction(),
- line_box_fragment.Size(),
- line_box_child->Size());
+ if (line_box_child->GetLayoutObject() == object) {
+ return child.offset + line_box_child.Offset().ConvertToLogical(
+ GetWritingMode(), Direction(),
+ line_box_fragment.Size(),
+ line_box_child->Size());
}
}
}
}
NOTREACHED();
- return NGLogicalOffset();
+ return LogicalOffset();
}
NGContainerFragmentBuilder&
NGContainerFragmentBuilder::AddOutOfFlowChildCandidate(
NGBlockNode child,
- const NGLogicalOffset& child_offset,
+ const LogicalOffset& child_offset,
base::Optional<TextDirection> container_direction) {
DCHECK(child);
DCHECK(layout_object_ && !layout_object_->IsLayoutInline() ||
@@ -188,7 +212,7 @@ NGContainerFragmentBuilder::AddOutOfFlowChildCandidate(
oof_positioned_candidates_.push_back(NGOutOfFlowPositionedCandidate(
NGOutOfFlowPositionedDescendant(
child, NGStaticPosition::Create(GetWritingMode(), direction,
- NGPhysicalOffset())),
+ PhysicalOffset())),
child_offset));
return *this;
@@ -212,12 +236,11 @@ void NGContainerFragmentBuilder::GetAndClearOutOfFlowDescendantCandidates(
DCHECK_GE(InlineSize(), LayoutUnit());
DCHECK_GE(BlockSize(), LayoutUnit());
- NGPhysicalSize builder_physical_size =
- ToNGPhysicalSize(Size(), GetWritingMode());
+ PhysicalSize builder_physical_size = ToPhysicalSize(Size(), GetWritingMode());
for (NGOutOfFlowPositionedCandidate& candidate : oof_positioned_candidates_) {
- NGPhysicalOffset child_offset = candidate.child_offset.ConvertToPhysical(
- GetWritingMode(), Direction(), builder_physical_size, NGPhysicalSize());
+ PhysicalOffset child_offset = candidate.child_offset.ConvertToPhysical(
+ GetWritingMode(), Direction(), builder_physical_size, PhysicalSize());
NGStaticPosition builder_relative_position;
builder_relative_position.type = candidate.descendant.static_position.type;
@@ -230,21 +253,20 @@ void NGContainerFragmentBuilder::GetAndClearOutOfFlowDescendantCandidates(
//
// This checks if the object creating this box will be the container for
// the given descendant.
- const LayoutObject* inline_container =
+ const LayoutInline* inline_container =
candidate.descendant.inline_container;
- if (!inline_container && layout_object_ &&
- layout_object_->IsLayoutInline() &&
- layout_object_->CanContainOutOfFlowPositionedElement(
- candidate.descendant.node.Style().GetPosition()))
- inline_container = layout_object_;
-
+ if (IsInlineContainerForDescendant(candidate.descendant, layout_object_)) {
+ inline_container = ToLayoutInline(layout_object_);
+ }
descendant_candidates->push_back(NGOutOfFlowPositionedDescendant(
candidate.descendant.node, builder_relative_position,
- inline_container));
- NGLogicalOffset container_offset =
+ inline_container ? ToLayoutInline(inline_container->ContinuationRoot())
+ : nullptr));
+
+ LogicalOffset container_offset =
builder_relative_position.offset.ConvertToLogical(
GetWritingMode(), Direction(), builder_physical_size,
- NGPhysicalSize());
+ PhysicalSize());
candidate.descendant.node.SaveStaticOffsetForLegacy(container_offset,
current_container);
}
@@ -259,7 +281,7 @@ void NGContainerFragmentBuilder::GetAndClearOutOfFlowDescendantCandidates(
oof_positioned_candidates_.Shrink(0);
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String NGContainerFragmentBuilder::ToString() const {
StringBuilder builder;
@@ -267,7 +289,7 @@ String NGContainerFragmentBuilder::ToString() const {
InlineSize().ToFloat(), BlockSize().ToFloat(),
children_.size());
for (auto& child : children_) {
- builder.Append(child->DumpFragmentTree(
+ builder.Append(child.fragment->DumpFragmentTree(
NGPhysicalFragment::DumpAll & ~NGPhysicalFragment::DumpHeaderText));
}
return builder.ToString();
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 5db3e92b586..bd692e43df1 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
@@ -7,10 +7,11 @@
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h"
@@ -24,15 +25,24 @@
namespace blink {
class NGExclusionSpace;
-class NGLayoutResult;
class NGPhysicalFragment;
class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
- typedef Vector<scoped_refptr<const NGPhysicalFragment>, 4> ChildrenVector;
- typedef Vector<NGLogicalOffset, 4> OffsetVector;
+ struct ChildWithOffset {
+ DISALLOW_NEW();
+ ChildWithOffset(LogicalOffset offset,
+ scoped_refptr<const NGPhysicalFragment> fragment)
+ : offset(offset), fragment(std::move(fragment)) {}
+
+ // We store logical offsets (instead of the final physical), as we can't
+ // convert into the physical coordinate space until we know our final size.
+ LogicalOffset offset;
+ scoped_refptr<const NGPhysicalFragment> fragment;
+ };
+ typedef Vector<ChildWithOffset, 4> ChildrenVector;
LayoutUnit BfcLineOffset() const { return bfc_line_offset_; }
NGContainerFragmentBuilder& SetBfcLineOffset(LayoutUnit bfc_line_offset) {
@@ -40,8 +50,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
return *this;
}
- // The NGBfcOffset is where this fragment was positioned within the BFC. If
- // it is not set, this fragment may be placed anywhere within the BFC.
+ // The BFC block-offset is where this fragment was positioned within the BFC.
+ // If it is not set, this fragment may be placed anywhere within the BFC.
const base::Optional<LayoutUnit>& BfcBlockOffset() const {
return bfc_block_offset_;
}
@@ -76,19 +86,23 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
return *this;
}
- NGContainerFragmentBuilder& AddChild(const NGLayoutResult&,
- const NGLogicalOffset&);
+ NGContainerFragmentBuilder& AddChild(
+ const NGPhysicalContainerFragment&,
+ const LogicalOffset&,
+ const LayoutInline* inline_container = nullptr);
- // This version of AddChild will not propagate floats/out_of_flow.
- // Use the AddChild(NGLayoutResult) variant if NGLayoutResult is available.
- NGContainerFragmentBuilder& AddChild(scoped_refptr<const NGPhysicalFragment>,
- const NGLogicalOffset&);
+ NGContainerFragmentBuilder& AddChild(
+ scoped_refptr<const NGPhysicalTextFragment> child,
+ const LogicalOffset& offset) {
+ AddChildInternal(child, offset);
+ return *this;
+ }
const ChildrenVector& Children() const { return children_; }
// Returns offset for given child. DCHECK if child not found.
// Warning: Do not call unless necessary.
- NGLogicalOffset GetChildOffset(const LayoutObject* child) const;
+ LogicalOffset GetChildOffset(const LayoutObject* child) const;
// Builder has non-trivial out-of-flow descendant methods.
// These methods are building blocks for implementation of
@@ -120,7 +134,7 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
// Pass in direction if candidates direction does not match.
NGContainerFragmentBuilder& AddOutOfFlowChildCandidate(
NGBlockNode,
- const NGLogicalOffset& child_offset,
+ const LogicalOffset& child_offset,
base::Optional<TextDirection> container_direction = base::nullopt);
NGContainerFragmentBuilder& AddOutOfFlowDescendant(
@@ -146,14 +160,17 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
nullptr);
}
+ NGContainerFragmentBuilder& SetIsSelfCollapsing() {
+ is_self_collapsing_ = true;
+ return *this;
+ }
+
NGContainerFragmentBuilder& SetIsPushedByFloats() {
is_pushed_by_floats_ = true;
return *this;
}
bool IsPushedByFloats() const { return is_pushed_by_floats_; }
- bool HasFloatingDescendants() const { return has_floating_descendants_; }
-
NGContainerFragmentBuilder& ResetAdjoiningFloatTypes() {
adjoining_floats_ = kFloatTypeNone;
return *this;
@@ -162,6 +179,10 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
adjoining_floats_ |= floats;
return *this;
}
+ NGContainerFragmentBuilder& SetAdjoiningFloatTypes(NGFloatTypes floats) {
+ adjoining_floats_ = floats;
+ return *this;
+ }
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
NGContainerFragmentBuilder& SetHasBlockFragmentation() {
@@ -171,7 +192,7 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
const NGConstraintSpace* ConstraintSpace() const { return space_; }
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
String ToString() const;
#endif
@@ -187,7 +208,7 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
// 2. A fragment containing an out-of-flow positioned-descendant. The
// child_offset in this case is the containing fragment's offset.
//
- // The child_offset is stored as a NGLogicalOffset as the physical offset
+ // The child_offset is stored as a LogicalOffset as the physical offset
// cannot be computed until we know the current fragment's size.
//
// When returning the positioned-candidates (from
@@ -196,10 +217,10 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
// physical size the fragment builder.
struct NGOutOfFlowPositionedCandidate {
NGOutOfFlowPositionedDescendant descendant;
- NGLogicalOffset child_offset; // Logical offset of child's top left vertex.
+ LogicalOffset child_offset; // Logical offset of child's top left vertex.
NGOutOfFlowPositionedCandidate(NGOutOfFlowPositionedDescendant descendant,
- NGLogicalOffset child_offset)
+ LogicalOffset child_offset)
: descendant(descendant), child_offset(child_offset) {}
};
@@ -210,7 +231,12 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
TextDirection direction)
: NGFragmentBuilder(std::move(style), writing_mode, direction),
node_(node),
- space_(space) {}
+ space_(space) {
+ layout_object_ = node.GetLayoutBox();
+ }
+
+ void AddChildInternal(scoped_refptr<const NGPhysicalFragment>,
+ const LogicalOffset&);
NGLayoutInputNode node_;
const NGConstraintSpace* space_;
@@ -227,11 +253,6 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
ChildrenVector children_;
- // Logical offsets for the children. Stored as logical offsets as we can't
- // convert to physical offsets until layout of all children has been
- // determined.
- OffsetVector offsets_;
-
// Only used by the NGBoxFragmentBuilder subclass, but defined here to avoid
// a virtual function call.
NGBreakTokenVector child_break_tokens_;
@@ -239,17 +260,21 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
NGFloatTypes adjoining_floats_ = kFloatTypeNone;
+ bool is_self_collapsing_ = false;
bool is_pushed_by_floats_ = false;
- bool is_old_layout_root_ = false;
+ bool is_legacy_layout_root_ = false;
bool has_last_resort_break_ = false;
bool has_floating_descendants_ = false;
bool has_orthogonal_flow_roots_ = false;
- bool has_child_that_depends_on_percentage_block_size_ = false;
+ bool has_descendant_that_depends_on_percentage_block_size_ = false;
bool has_block_fragmentation_ = false;
bool may_have_descendant_above_block_start_ = false;
};
} // namespace blink
+WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
+ blink::NGContainerFragmentBuilder::ChildWithOffset)
+
#endif // NGContainerFragmentBuilder
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 b1d6eefc023..efdee844b7e 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
@@ -19,11 +19,13 @@
namespace blink {
NGFieldsetLayoutAlgorithm::NGFieldsetLayoutAlgorithm(
- NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token)
- : NGLayoutAlgorithm(node, space, To<NGBlockBreakToken>(break_token)) {
- container_builder_.SetIsNewFormattingContext(space.IsNewFormattingContext());
+ const NGLayoutAlgorithmParams& params)
+ : NGLayoutAlgorithm(params),
+ border_padding_(params.fragment_geometry.border +
+ params.fragment_geometry.padding) {
+ container_builder_.SetIsNewFormattingContext(
+ params.space.IsNewFormattingContext());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
@@ -40,37 +42,38 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// with the actual fieldset contents. Since scrollbars are handled by the
// anonymous child box, and since padding is inside the scrollport, padding
// also needs to be handled by the anonymous child.
- NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
- NGBoxStrut padding = ComputePadding(ConstraintSpace(), Style());
- NGBoxStrut border_padding = borders + padding;
- NGLogicalSize border_box_size =
- CalculateBorderBoxSize(ConstraintSpace(), Node(), border_padding);
+ NGBoxStrut borders = container_builder_.Borders();
+ NGBoxStrut padding = container_builder_.Padding();
+ LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
const auto writing_mode = ConstraintSpace().GetWritingMode();
- LayoutUnit block_start_padding_edge = borders.block_start;
+ LayoutUnit block_start_padding_edge =
+ container_builder_.Borders().block_start;
+ // TODO(vmpstr): Skip child (including legend) layout for fieldset elements.
if (NGBlockNode legend = Node().GetRenderedLegend()) {
// Lay out the legend. While the fieldset container normally ignores its
// padding, the legend is laid out within what would have been the content
// box had the fieldset been a regular block with no weirdness.
- NGLogicalSize content_box_size =
- ShrinkAvailableSize(border_box_size, border_padding);
+ LogicalSize content_box_size =
+ ShrinkAvailableSize(border_box_size, border_padding_);
auto legend_space =
CreateConstraintSpaceForLegend(legend, content_box_size);
auto result = legend.Layout(legend_space, BreakToken());
+ const auto& physical_fragment = result->PhysicalFragment();
NGBoxStrut legend_margins =
ComputeMarginsFor(legend_space, legend.Style(), ConstraintSpace());
- NGFragment logical_fragment(writing_mode, *result->PhysicalFragment());
// If the margin box of the legend is at least as tall as the fieldset
// block-start border width, it will start at the block-start border edge of
// the fieldset. As a paint effect, the block-start border will be pushed so
// that the center of the border will be flush with the center of the
// border-box of the legend.
// TODO(mstensho): inline alignment
- NGLogicalOffset legend_offset = NGLogicalOffset(
- border_padding.inline_start + legend_margins.inline_start,
+ LogicalOffset legend_offset = LogicalOffset(
+ border_padding_.inline_start + legend_margins.inline_start,
legend_margins.block_start);
LayoutUnit legend_margin_box_block_size =
- logical_fragment.BlockSize() + legend_margins.BlockSum();
+ NGFragment(writing_mode, physical_fragment).BlockSize() +
+ legend_margins.BlockSum();
LayoutUnit space_left = borders.block_start - legend_margin_box_block_size;
if (space_left > LayoutUnit()) {
// If the border is the larger one, though, it will stay put at the
@@ -87,7 +90,7 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
block_start_padding_edge = legend_margin_box_block_size;
}
- container_builder_.AddChild(*result, legend_offset);
+ container_builder_.AddChild(physical_fragment, legend_offset);
}
NGBoxStrut borders_with_legend = borders;
@@ -97,39 +100,47 @@ scoped_refptr<const NGLayoutResult> NGFieldsetLayoutAlgorithm::Layout() {
// Proceed with normal fieldset children (excluding the rendered legend). They
// all live inside an anonymous child box of the fieldset container.
if (auto fieldset_content = Node().GetFieldsetContent()) {
- NGLogicalSize adjusted_padding_box_size =
+ LogicalSize adjusted_padding_box_size =
ShrinkAvailableSize(border_box_size, borders_with_legend);
auto child_space =
CreateConstraintSpaceForFieldsetContent(adjusted_padding_box_size);
auto result = fieldset_content.Layout(child_space, BreakToken());
- container_builder_.AddChild(*result, borders_with_legend.StartOffset());
+ const auto& physical_fragment = result->PhysicalFragment();
+ container_builder_.AddChild(physical_fragment,
+ borders_with_legend.StartOffset());
- NGFragment logical_fragment(writing_mode, *result->PhysicalFragment());
- intrinsic_block_size += logical_fragment.BlockSize();
+ intrinsic_block_size +=
+ NGFragment(writing_mode, physical_fragment).BlockSize();
} else {
// There was no anonymous child to provide the padding, so we have to add it
// ourselves.
intrinsic_block_size += padding.BlockSum();
}
+ intrinsic_block_size =
+ ClampIntrinsicBlockSize(Node(), border_padding_, intrinsic_block_size);
+
// Recompute the block-axis size now that we know our content size.
border_box_size.block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding, intrinsic_block_size);
+ ConstraintSpace(), Node(), border_padding_, intrinsic_block_size);
// The above computation utility knows nothing about fieldset weirdness. The
// legend may eat from the available content box block size. Make room for
// that if necessary.
- LayoutUnit minimum_border_box_block_size =
- borders_with_legend.BlockSum() + padding.BlockSum();
- border_box_size.block_size =
- std::max(border_box_size.block_size, minimum_border_box_block_size);
+ // Note that in size containment, we have to consider sizing as if we have no
+ // contents, with the conjecture being that legend is part of the contents.
+ // Thus, only do this adjustment if we do not contain size.
+ if (!Node().ShouldApplySizeContainment() &&
+ !Node().DisplayLockInducesSizeContainment()) {
+ LayoutUnit minimum_border_box_block_size =
+ borders_with_legend.BlockSum() + padding.BlockSum();
+ border_box_size.block_size =
+ std::max(border_box_size.block_size, minimum_border_box_block_size);
+ }
container_builder_.SetIsFieldsetContainer();
- container_builder_.SetInlineSize(border_box_size.inline_size);
container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
container_builder_.SetBlockSize(border_box_size.block_size);
- container_builder_.SetBorders(borders);
- container_builder_.SetPadding(padding);
NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders_with_legend,
&container_builder_)
@@ -142,24 +153,38 @@ base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
MinMaxSize sizes;
- // Size-contained elements don't consider their contents for intrinsic sizing.
- if (node_.ShouldApplySizeContainment())
- return sizes;
+ bool apply_size_containment = node_.ShouldApplySizeContainment();
+ if (apply_size_containment) {
+ if (input.size_type == NGMinMaxSizeType::kContentBoxSize)
+ return sizes;
+ } else if (node_.DisplayLockInducesSizeContainment()) {
+ sizes = node_.GetDisplayLockContext().GetLockedContentLogicalWidth();
+ if (input.size_type == NGMinMaxSizeType::kContentBoxSize)
+ return sizes;
+ apply_size_containment = true;
+ }
- if (NGBlockNode legend = Node().GetRenderedLegend()) {
- sizes = ComputeMinAndMaxContentContribution(Style(), legend, input);
- sizes += ComputeMinMaxMargins(Style(), legend).InlineSum();
+ // Size containment does not consider the legend for sizing.
+ if (!apply_size_containment) {
+ if (NGBlockNode legend = Node().GetRenderedLegend()) {
+ sizes = ComputeMinAndMaxContentContribution(Style(), legend, input);
+ sizes += ComputeMinMaxMargins(Style(), legend).InlineSum();
+ }
}
+
// The fieldset content includes the fieldset padding (and any scrollbars),
// while the legend is a regular child and doesn't. We may have a fieldset
// without any content or legend, so add the padding here, on the outside.
sizes += ComputePadding(ConstraintSpace(), node_.Style()).InlineSum();
- if (NGBlockNode content = Node().GetFieldsetContent()) {
- MinMaxSize content_minmax =
- ComputeMinAndMaxContentContribution(Style(), content, input);
- content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum();
- sizes.Encompass(content_minmax);
+ // Size containment does not consider the content for sizing.
+ if (!apply_size_containment) {
+ if (NGBlockNode content = Node().GetFieldsetContent()) {
+ MinMaxSize content_minmax =
+ ComputeMinAndMaxContentContribution(Style(), content, input);
+ content_minmax += ComputeMinMaxMargins(Style(), content).InlineSum();
+ sizes.Encompass(content_minmax);
+ }
}
sizes += ComputeBorders(ConstraintSpace(), node_).InlineSum();
@@ -169,13 +194,13 @@ base::Optional<MinMaxSize> NGFieldsetLayoutAlgorithm::ComputeMinMaxSize(
const NGConstraintSpace
NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend(
NGBlockNode legend,
- NGLogicalSize available_size) {
+ LogicalSize available_size) {
NGConstraintSpaceBuilder builder(
ConstraintSpace(), legend.Style().GetWritingMode(), /* is_new_fc */ true);
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), legend, &builder);
builder.SetAvailableSize(available_size);
- NGLogicalSize percentage_size =
+ LogicalSize percentage_size =
CalculateChildPercentageSize(ConstraintSpace(), Node(), available_size);
builder.SetPercentageResolutionSize(percentage_size);
builder.SetIsShrinkToFit(legend.Style().LogicalWidth().IsAuto());
@@ -185,14 +210,14 @@ NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForLegend(
const NGConstraintSpace
NGFieldsetLayoutAlgorithm::CreateConstraintSpaceForFieldsetContent(
- NGLogicalSize padding_box_size) {
+ LogicalSize padding_box_size) {
NGConstraintSpaceBuilder builder(ConstraintSpace(),
ConstraintSpace().GetWritingMode(),
/* is_new_fc */ true);
builder.SetAvailableSize(padding_box_size);
builder.SetPercentageResolutionSize(
ConstraintSpace().PercentageResolutionSize());
- builder.SetIsFixedSizeBlock(padding_box_size.block_size != NGSizeIndefinite);
+ builder.SetIsFixedSizeBlock(padding_box_size.block_size != kIndefiniteSize);
return builder.ToConstraintSpace();
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
index e804a31f9a2..df5a8d3538b 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
@@ -7,13 +7,12 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
namespace blink {
class NGBlockBreakToken;
-class NGBreakToken;
class NGConstraintSpace;
class CORE_EXPORT NGFieldsetLayoutAlgorithm
@@ -21,9 +20,7 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
NGBoxFragmentBuilder,
NGBlockBreakToken> {
public:
- NGFieldsetLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token = nullptr);
+ NGFieldsetLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
scoped_refptr<const NGLayoutResult> Layout() override;
@@ -32,9 +29,11 @@ class CORE_EXPORT NGFieldsetLayoutAlgorithm
const NGConstraintSpace CreateConstraintSpaceForLegend(
NGBlockNode legend,
- NGLogicalSize available_size);
+ LogicalSize available_size);
const NGConstraintSpace CreateConstraintSpaceForFieldsetContent(
- NGLogicalSize padding_box_size);
+ LogicalSize padding_box_size);
+
+ const NGBoxStrut border_padding_;
};
} // 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 bc6964d2162..d1aae7b5fef 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
@@ -6,6 +6,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_base_layout_algorithm_test.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
@@ -30,29 +31,22 @@ class NGFieldsetLayoutAlgorithmTest : public NGBaseLayoutAlgorithmTest {
}
scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
- const NGConstraintSpace& space,
- NGBlockNode node) {
- scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, space).Layout();
-
- return To<NGPhysicalBoxFragment>(result->PhysicalFragment());
- }
-
- scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
Element* element) {
NGBlockNode container(ToLayoutBox(element->GetLayoutObject()));
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite));
- return RunBlockLayoutAlgorithm(space, container);
+ LogicalSize(LayoutUnit(1000), kIndefiniteSize));
+ return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
}
MinMaxSize RunComputeMinAndMax(NGBlockNode node) {
NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
WritingMode::kHorizontalTb, TextDirection::kLtr,
- NGLogicalSize(LayoutUnit(), LayoutUnit()));
+ LogicalSize(LayoutUnit(), LayoutUnit()));
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(space, node);
- NGFieldsetLayoutAlgorithm algorithm(node, space);
+ NGFieldsetLayoutAlgorithm algorithm({node, fragment_geometry, space});
MinMaxSizeInput input(
/* percentage_resolution_block_size */ (LayoutUnit()));
auto min_max = algorithm.ComputeMinMaxSize(input);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index 2964c16d591..9f7c3d817a2 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -19,17 +19,17 @@
namespace blink {
-NGFlexLayoutAlgorithm::NGFlexLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token)
- : NGLayoutAlgorithm(node, space, To<NGBlockBreakToken>(break_token)),
- border_scrollbar_padding_(
- CalculateBorderScrollbarPadding(ConstraintSpace(), Node())),
- borders_(ComputeBorders(ConstraintSpace(), Node())),
- padding_(ComputePadding(ConstraintSpace(), Style())),
- border_padding_(borders_ + padding_),
+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().IsColumnFlexDirection()) {
- container_builder_.SetIsNewFormattingContext(space.IsNewFormattingContext());
+ container_builder_.SetIsNewFormattingContext(
+ params.space.IsNewFormattingContext());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
bool NGFlexLayoutAlgorithm::MainAxisIsInlineAxis(NGBlockNode child) {
@@ -41,7 +41,7 @@ LayoutUnit NGFlexLayoutAlgorithm::MainAxisContentExtent(
LayoutUnit sum_hypothetical_main_size) {
if (Style().IsColumnFlexDirection()) {
return ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_,
+ ConstraintSpace(), Node(), border_padding_,
sum_hypothetical_main_size + (border_padding_).BlockSum()) -
border_scrollbar_padding_.BlockSum();
}
@@ -84,15 +84,21 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
.SetPercentageResolutionSize(content_box_size_)
.ToConstraintSpace();
- NGBoxStrut border_padding_in_child_writing_mode =
+ NGBoxStrut border_scrollbar_padding_in_child_writing_mode =
ComputeBorders(child_space, child) +
- ComputePadding(child_space, child_style) + child.GetScrollbarSizes();
+ ComputeScrollbars(child_space, child) +
+ ComputePadding(child_space, child_style);
NGPhysicalBoxStrut physical_border_padding(
- border_padding_in_child_writing_mode.ConvertToPhysical(
+ border_scrollbar_padding_in_child_writing_mode.ConvertToPhysical(
child_style.GetWritingMode(), child_style.Direction()));
LayoutUnit main_axis_border_scrollbar_padding =
is_horizontal_flow ? physical_border_padding.HorizontalSum()
: physical_border_padding.VerticalSum();
+ LayoutUnit main_axis_border_and_padding =
+ main_axis_border_scrollbar_padding -
+ (is_horizontal_flow
+ ? child.GetLayoutBox()->VerticalScrollbarWidth()
+ : child.GetLayoutBox()->HorizontalScrollbarHeight());
// We want the child's min/max size in its writing mode, not ours. We'll
// only ever use it if the child's inline axis is our main axis.
@@ -104,7 +110,7 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
scoped_refptr<const NGLayoutResult> layout_result =
child.Layout(child_space, nullptr /*break token*/);
NGFragment fragment_in_child_writing_mode(
- child_style.GetWritingMode(), *layout_result->PhysicalFragment());
+ child_style.GetWritingMode(), layout_result->PhysicalFragment());
LayoutUnit flex_base_border_box;
const Length& specified_length_in_main_axis =
@@ -128,22 +134,26 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
if (MainAxisIsInlineAxis(child)) {
flex_base_border_box = ResolveMainInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
intrinsic_sizes_border_box, length_to_resolve);
} else {
// Flex container's main axis is in child's block direction. Child's
// flex basis is in child's block direction.
flex_base_border_box = ResolveMainBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- length_to_resolve, fragment_in_child_writing_mode.BlockSize(),
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode, length_to_resolve,
+ fragment_in_child_writing_mode.BlockSize(),
LengthResolvePhase::kLayout);
}
}
// Spec calls this "flex base size"
// https://www.w3.org/TR/css-flexbox-1/#algo-main-item
+ // Blink's FlexibleBoxAlgorithm expects it to be content + scrollbar widths,
+ // but no padding or border.
LayoutUnit flex_base_content_size =
- flex_base_border_box - main_axis_border_scrollbar_padding;
+ flex_base_border_box - main_axis_border_and_padding;
NGPhysicalBoxStrut physical_child_margins =
ComputePhysicalMargins(child_space, child_style);
@@ -158,12 +168,14 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
: child.Style().MaxHeight();
if (MainAxisIsInlineAxis(child)) {
min_max_sizes_in_main_axis_direction.max_size = ResolveMaxInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
intrinsic_sizes_border_box, max_property_in_main_axis,
LengthResolvePhase::kLayout);
} else {
min_max_sizes_in_main_axis_direction.max_size = ResolveMaxBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
max_property_in_main_axis, fragment_in_child_writing_mode.BlockSize(),
LengthResolvePhase::kLayout);
}
@@ -192,7 +204,8 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
// resolved specified_length_in_main_axis in the flex basis
// calculation. Reuse that if possible.
specified_size_suggestion = ResolveMainInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
intrinsic_sizes_border_box, specified_length_in_main_axis);
}
} else if (!specified_length_in_main_axis.IsAuto() &&
@@ -200,10 +213,11 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
specified_length_in_main_axis,
LengthResolvePhase::kLayout)) {
specified_size_suggestion = ResolveMainBlockLength(
- child_space, child_style, border_padding_in_child_writing_mode,
+ child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
specified_length_in_main_axis,
layout_result->IntrinsicBlockSize(), LengthResolvePhase::kLayout);
- DCHECK_NE(specified_size_suggestion, NGSizeIndefinite);
+ DCHECK_NE(specified_size_suggestion, kIndefiniteSize);
}
// Spec says to clamp specified_size_suggestion by max size but because
// content_size_suggestion already is, and we take the min of those
@@ -217,17 +231,18 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
}
} else if (MainAxisIsInlineAxis(child)) {
min_max_sizes_in_main_axis_direction.min_size =
- ResolveMinInlineLength(
- child_space, child_style, border_padding_in_child_writing_mode,
- intrinsic_sizes_border_box, min, LengthResolvePhase::kLayout) -
+ ResolveMinInlineLength(child_space, child_style,
+ border_scrollbar_padding_in_child_writing_mode,
+ intrinsic_sizes_border_box, min,
+ LengthResolvePhase::kLayout) -
main_axis_border_scrollbar_padding;
// TODO(dgrogan): No tests changed status as result of subtracting
// main_axis_border_scrollbar_padding. It may be untested.
} else {
min_max_sizes_in_main_axis_direction.min_size =
ResolveMinBlockLength(child_space, child_style,
- border_padding_in_child_writing_mode, min,
- fragment_in_child_writing_mode.BlockSize(),
+ border_scrollbar_padding_in_child_writing_mode,
+ min, fragment_in_child_writing_mode.BlockSize(),
LengthResolvePhase::kLayout) -
main_axis_border_scrollbar_padding;
// TODO(dgrogan): Same as above WRT subtracting
@@ -237,14 +252,13 @@ void NGFlexLayoutAlgorithm::ConstructAndAppendFlexItems() {
algorithm_
->emplace_back(child.GetLayoutBox(), flex_base_content_size,
min_max_sizes_in_main_axis_direction,
- main_axis_border_scrollbar_padding, main_axis_margin)
+ main_axis_border_and_padding, main_axis_margin)
.ng_input_node = child;
}
}
scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
- border_box_size_ =
- CalculateBorderBoxSize(ConstraintSpace(), Node(), border_padding_);
+ border_box_size_ = container_builder_.InitialBorderBoxSize();
content_box_size_ =
ShrinkAvailableSize(border_box_size_, border_scrollbar_padding_);
@@ -281,17 +295,15 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
SetOrthogonalFallbackInlineSizeIfNeeded(Style(), flex_item.ng_input_node,
&space_builder);
- NGLogicalSize available_size;
+ LogicalSize available_size;
if (is_column_) {
available_size.inline_size = content_box_size_.inline_size;
- available_size.block_size =
- flex_item.flexed_content_size +
- flex_item.main_axis_border_scrollbar_padding;
+ available_size.block_size = flex_item.flexed_content_size +
+ flex_item.main_axis_border_and_padding;
space_builder.SetIsFixedSizeBlock(true);
} else {
- available_size.inline_size =
- flex_item.flexed_content_size +
- flex_item.main_axis_border_scrollbar_padding;
+ available_size.inline_size = flex_item.flexed_content_size +
+ flex_item.main_axis_border_and_padding;
available_size.block_size = content_box_size_.block_size;
space_builder.SetIsFixedSizeInline(true);
}
@@ -302,8 +314,8 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
flex_item.ng_input_node.Layout(child_space, nullptr /*break token*/);
flex_item.cross_axis_size =
is_horizontal_flow
- ? flex_item.layout_result->PhysicalFragment()->Size().height
- : flex_item.layout_result->PhysicalFragment()->Size().width;
+ ? flex_item.layout_result->PhysicalFragment().Size().height
+ : flex_item.layout_result->PhysicalFragment().Size().width;
}
// cross_axis_offset is updated in each iteration of the loop, for passing
// in to the next iteration.
@@ -317,21 +329,19 @@ scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
LayoutUnit intrinsic_block_size =
intrinsic_block_content_size + border_scrollbar_padding_.BlockSum();
LayoutUnit block_size = ComputeBlockSizeForFragment(
- ConstraintSpace(), Style(), border_padding_, intrinsic_block_size);
+ ConstraintSpace(), Node(), border_padding_, intrinsic_block_size);
container_builder_.SetBlockSize(block_size);
- container_builder_.SetInlineSize(border_box_size_.inline_size);
- container_builder_.SetBorders(borders_);
- container_builder_.SetPadding(padding_);
container_builder_.SetIntrinsicBlockSize(
algorithm_->IntrinsicContentBlockSize() +
border_scrollbar_padding_.BlockSum());
GiveLinesAndItemsFinalPositionAndSize();
- NGOutOfFlowLayoutPart(Node(), ConstraintSpace(),
- borders_ + Node().GetScrollbarSizes(),
- &container_builder_)
+ NGOutOfFlowLayoutPart(
+ Node(), ConstraintSpace(),
+ container_builder_.Borders() + container_builder_.Scrollbar(),
+ &container_builder_)
.Run();
return container_builder_.ToBoxFragment();
@@ -366,12 +376,11 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
SetOrthogonalFallbackInlineSizeIfNeeded(
Style(), flex_item.ng_input_node, &space_builder);
- NGLogicalSize available_size(
- flex_item.flexed_content_size +
- flex_item.main_axis_border_scrollbar_padding,
- flex_item.cross_axis_size);
+ LogicalSize available_size(flex_item.flexed_content_size +
+ flex_item.main_axis_border_and_padding,
+ flex_item.cross_axis_size);
if (is_column_)
- available_size.Flip();
+ available_size.Transpose();
space_builder.SetAvailableSize(available_size);
space_builder.SetPercentageResolutionSize(content_box_size_);
space_builder.SetIsFixedSizeInline(true);
@@ -388,7 +397,7 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
// non-horizontal flexboxes, like
// LayoutFlexibleBox::SetFlowAwareLocationForChild does?
container_builder_.AddChild(
- *flex_item.layout_result,
+ flex_item.layout_result->PhysicalFragment(),
{flex_item.desired_location.X(), flex_item.desired_location.Y()});
}
}
@@ -396,18 +405,15 @@ void NGFlexLayoutAlgorithm::GiveLinesAndItemsFinalPositionAndSize() {
base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
- MinMaxSize sizes;
- if (Node().ShouldApplySizeContainment()) {
- // TODO(dgrogan): When this code was written it didn't make any more tests
- // pass, so it may be wrong or untested.
- if (input.size_type == NGMinMaxSizeType::kBorderBoxSize)
- sizes = border_scrollbar_padding_.InlineSum();
+ base::Optional<MinMaxSize> sizes = CalculateMinMaxSizesIgnoringChildren(
+ Node(), border_scrollbar_padding_, input.size_type);
+ if (sizes)
return sizes;
- }
+ sizes.emplace();
LayoutUnit child_percentage_resolution_block_size =
CalculateChildPercentageBlockSizeForMinMax(
- ConstraintSpace(), Node(), borders_ + padding_,
+ ConstraintSpace(), Node(), border_padding_,
input.percentage_resolution_block_size);
// Use default MinMaxSizeInput:
@@ -429,24 +435,26 @@ base::Optional<MinMaxSize> NGFlexLayoutAlgorithm::ComputeMinMaxSize(
NGBoxStrut child_margins = ComputeMinMaxMargins(Style(), child);
child_min_max_sizes += child_margins.InlineSum();
if (is_column_) {
- sizes.min_size = std::max(sizes.min_size, child_min_max_sizes.min_size);
- sizes.max_size = std::max(sizes.max_size, child_min_max_sizes.max_size);
+ sizes->min_size = std::max(sizes->min_size, child_min_max_sizes.min_size);
+ sizes->max_size = std::max(sizes->max_size, child_min_max_sizes.max_size);
} else {
- sizes.max_size += child_min_max_sizes.max_size;
- if (IsMultiline())
- sizes.min_size = std::max(sizes.min_size, child_min_max_sizes.min_size);
- else
- sizes.min_size += child_min_max_sizes.min_size;
+ sizes->max_size += child_min_max_sizes.max_size;
+ if (IsMultiline()) {
+ sizes->min_size =
+ std::max(sizes->min_size, child_min_max_sizes.min_size);
+ } else {
+ sizes->min_size += child_min_max_sizes.min_size;
+ }
}
}
- sizes.max_size = std::max(sizes.max_size, sizes.min_size);
+ sizes->max_size = std::max(sizes->max_size, sizes->min_size);
// Due to negative margins, it is possible that we calculated a negative
// intrinsic width. Make sure that we never return a negative width.
- sizes.Encompass(LayoutUnit());
+ sizes->Encompass(LayoutUnit());
if (input.size_type == NGMinMaxSizeType::kBorderBoxSize)
- sizes += border_scrollbar_padding_.InlineSum();
+ *sizes += border_scrollbar_padding_.InlineSum();
return sizes;
}
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
index a85d0418c16..f33c5e0d40d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.h
@@ -14,17 +14,13 @@ namespace blink {
class NGBlockNode;
class NGBlockBreakToken;
-class NGBreakToken;
-class NGConstraintSpace;
class CORE_EXPORT NGFlexLayoutAlgorithm
: public NGLayoutAlgorithm<NGBlockNode,
NGBoxFragmentBuilder,
NGBlockBreakToken> {
public:
- NGFlexLayoutAlgorithm(NGBlockNode,
- const NGConstraintSpace&,
- const NGBreakToken*);
+ NGFlexLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
scoped_refptr<const NGLayoutResult> Layout() override;
@@ -45,13 +41,11 @@ class CORE_EXPORT NGFlexLayoutAlgorithm
// not reimplement.
bool IsMultiline() const;
- const NGBoxStrut border_scrollbar_padding_;
- const NGBoxStrut borders_;
- const NGBoxStrut padding_;
const NGBoxStrut border_padding_;
+ const NGBoxStrut border_scrollbar_padding_;
const bool is_column_;
- NGLogicalSize border_box_size_;
- NGLogicalSize content_box_size_;
+ LogicalSize border_box_size_;
+ LogicalSize content_box_size_;
// This is populated at the top of Layout(), so isn't available in
// ComputeMinMaxSize() or anything it calls.
base::Optional<FlexLayoutAlgorithm> algorithm_;
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 e8c6ba8ebd0..e3127d0e7a9 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
@@ -38,7 +38,7 @@ NGBfcOffset AdjustToTopEdgeAlignmentRule(
}
NGLayoutOpportunity FindLayoutOpportunityForFloat(
- const NGLogicalSize& float_available_size,
+ const LogicalSize& float_available_size,
const NGBfcOffset& origin_bfc_offset,
const NGExclusionSpace& exclusion_space,
const NGUnpositionedFloat& unpositioned_float,
@@ -52,8 +52,8 @@ NGLayoutOpportunity FindLayoutOpportunityForFloat(
AdjustToClearance(clearance_offset, &adjusted_origin_point);
- NGLogicalSize float_size(inline_size + fragment_margins.InlineSum(),
- LayoutUnit());
+ LogicalSize float_size(inline_size + fragment_margins.InlineSum(),
+ LayoutUnit());
return exclusion_space.FindLayoutOpportunity(
adjusted_origin_point, float_available_size.inline_size, float_size);
}
@@ -61,9 +61,9 @@ NGLayoutOpportunity FindLayoutOpportunityForFloat(
// Creates a constraint space for an unpositioned float. origin_block_offset
// should only be set when we want to fragmentation to occur.
NGConstraintSpace CreateConstraintSpaceForFloat(
- const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+ const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGUnpositionedFloat& unpositioned_float,
const NGConstraintSpace& parent_space,
const ComputedStyle& parent_style,
@@ -96,9 +96,9 @@ NGConstraintSpace CreateConstraintSpaceForFloat(
}
std::unique_ptr<NGExclusionShapeData> CreateExclusionShapeData(
- const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+ const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGBoxStrut& margins,
const NGUnpositionedFloat& unpositioned_float,
const NGConstraintSpace& parent_space,
@@ -144,9 +144,9 @@ std::unique_ptr<NGExclusionShapeData> CreateExclusionShapeData(
// Creates an exclusion from the fragment that will be placed in the provided
// layout opportunity.
scoped_refptr<const NGExclusion> CreateExclusion(
- const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+ const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGFragment& fragment,
const NGBfcOffset& float_margin_bfc_offset,
const NGBoxStrut& margins,
@@ -176,9 +176,9 @@ scoped_refptr<const NGExclusion> CreateExclusion(
// Performs layout on a float, without fragmentation, and stores the result on
// the NGUnpositionedFloat data-structure.
void LayoutFloatWithoutFragmentation(
- const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+ const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGConstraintSpace& parent_space,
const ComputedStyle& parent_style,
NGUnpositionedFloat* unpositioned_float) {
@@ -211,19 +211,18 @@ LayoutUnit ComputeMarginBoxInlineSizeForUnpositionedFloat(
parent_style, unpositioned_float);
DCHECK(unpositioned_float->layout_result);
- const auto* fragment = unpositioned_float->layout_result->PhysicalFragment();
- DCHECK(fragment);
- DCHECK(!fragment->BreakToken() || fragment->BreakToken()->IsFinished());
+ const auto& fragment = unpositioned_float->layout_result->PhysicalFragment();
+ DCHECK(!fragment.BreakToken() || fragment.BreakToken()->IsFinished());
- return (NGFragment(parent_space.GetWritingMode(), *fragment).InlineSize() +
+ return (NGFragment(parent_space.GetWritingMode(), fragment).InlineSize() +
unpositioned_float->margins.InlineSum())
.ClampNegativeToZero();
}
NGPositionedFloat PositionFloat(
- const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+ const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGBfcOffset& origin_bfc_offset,
NGUnpositionedFloat* unpositioned_float,
const NGConstraintSpace& parent_space,
@@ -265,15 +264,14 @@ NGPositionedFloat PositionFloat(
unpositioned_float->token.get()))
fragment_margins.block_start = LayoutUnit();
if (const NGBreakToken* break_token =
- layout_result->PhysicalFragment()->BreakToken()) {
+ layout_result->PhysicalFragment().BreakToken()) {
if (!break_token->IsFinished())
fragment_margins.block_end = LayoutUnit();
}
}
- DCHECK(layout_result->PhysicalFragment());
NGFragment float_fragment(parent_space.GetWritingMode(),
- *layout_result->PhysicalFragment());
+ layout_result->PhysicalFragment());
// Find a layout opportunity that will fit our float.
NGLayoutOpportunity opportunity = FindLayoutOpportunityForFloat(
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
index 554f9791270..1b6d9cb3eb2 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_floats_utils.h
@@ -17,17 +17,19 @@ class ComputedStyle;
class NGConstraintSpace;
class NGExclusionSpace;
struct NGBfcOffset;
-struct NGLogicalSize;
+struct LogicalSize;
struct NGPositionedFloat;
struct NGUnpositionedFloat;
typedef Vector<NGPositionedFloat, 8> NGPositionedFloatVector;
+// TODO(ikilpatrick): Rename this to NGAdjoiningObjectTypes.
enum NGFloatTypeValue {
- kFloatTypeNone = 0b00,
- kFloatTypeLeft = 0b01,
- kFloatTypeRight = 0b10,
- kFloatTypeBoth = 0b11
+ kFloatTypeNone = 0b000,
+ kFloatTypeLeft = 0b001,
+ kFloatTypeRight = 0b010,
+ kFloatTypeBoth = 0b011,
+ kAdjoiningInlineOutOfFlow = 0b100
};
typedef int NGFloatTypes;
@@ -41,9 +43,9 @@ LayoutUnit ComputeMarginBoxInlineSizeForUnpositionedFloat(
// Positions {@code unpositioned_float} into {@code new_parent_space}.
// @returns A positioned float.
CORE_EXPORT NGPositionedFloat
-PositionFloat(const NGLogicalSize& float_available_size,
- const NGLogicalSize& float_percentage_size,
- const NGLogicalSize& float_replaced_percentage_size,
+PositionFloat(const LogicalSize& float_available_size,
+ const LogicalSize& float_percentage_size,
+ const LogicalSize& float_replaced_percentage_size,
const NGBfcOffset& origin_bfc_offset,
NGUnpositionedFloat*,
const NGConstraintSpace& parent_space,
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment.h
index e02d39a23e7..cd5ac2aac6d 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment.h
@@ -13,8 +13,7 @@
namespace blink {
-struct NGBorderEdges;
-struct NGLogicalSize;
+struct LogicalSize;
class CORE_EXPORT NGFragment {
STACK_ALLOCATED();
@@ -40,16 +39,11 @@ class CORE_EXPORT NGFragment {
? physical_fragment_.Size().height
: physical_fragment_.Size().width;
}
- NGLogicalSize Size() const {
+ LogicalSize Size() const {
return physical_fragment_.Size().ConvertToLogical(
static_cast<WritingMode>(writing_mode_));
}
- NGBorderEdges BorderEdges() const {
- return NGBorderEdges::FromPhysical(physical_fragment_.BorderEdges(),
- GetWritingMode());
- }
-
NGPhysicalFragment::NGFragmentType Type() const {
return physical_fragment_.Type();
}
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 09a488bdd40..ec3b02fc22d 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
@@ -7,7 +7,7 @@
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#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"
@@ -20,7 +20,7 @@ namespace blink {
class LayoutObject;
class CORE_EXPORT NGFragmentBuilder {
- STACK_ALLOCATED();
+ DISALLOW_NEW();
public:
const ComputedStyle& Style() const {
@@ -44,12 +44,7 @@ class CORE_EXPORT NGFragmentBuilder {
LayoutUnit InlineSize() const { return size_.inline_size; }
LayoutUnit BlockSize() const { return size_.block_size; }
- const NGLogicalSize& Size() const { return size_; }
- NGFragmentBuilder& SetInlineSize(LayoutUnit inline_size) {
- DCHECK_GE(inline_size, LayoutUnit());
- size_.inline_size = inline_size;
- return *this;
- }
+ const LogicalSize& Size() const { return size_; }
void SetBlockSize(LayoutUnit block_size) { size_.block_size = block_size; }
const LayoutObject* GetLayoutObject() const { return layout_object_; }
@@ -72,7 +67,7 @@ class CORE_EXPORT NGFragmentBuilder {
WritingMode writing_mode_;
TextDirection direction_;
NGStyleVariant style_variant_;
- NGLogicalSize size_;
+ LogicalSize size_;
LayoutObject* layout_object_ = nullptr;
scoped_refptr<NGBreakToken> break_token_;
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 e61ae6a5e5e..9c556e3468b 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
@@ -17,12 +17,13 @@ LayoutUnit PreviouslyUsedBlockSpace(const NGConstraintSpace& constraint_space,
const NGPhysicalFragment& fragment) {
if (!fragment.IsBox())
return LayoutUnit();
- const auto* break_token = To<NGBlockBreakToken>(fragment.BreakToken());
+ const NGPhysicalBoxFragment& box_fragment =
+ To<NGPhysicalBoxFragment>(fragment);
+ const auto* break_token = To<NGBlockBreakToken>(box_fragment.BreakToken());
if (!break_token)
return LayoutUnit();
NGBoxFragment logical_fragment(constraint_space.GetWritingMode(),
- constraint_space.Direction(),
- To<NGPhysicalBoxFragment>(fragment));
+ constraint_space.Direction(), box_fragment);
return break_token->UsedBlockSize() - logical_fragment.BlockSize();
}
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 6231e4e5572..31ead905d04 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
@@ -5,9 +5,9 @@
#ifndef NGFragmentationUtils_h
#define NGFragmentationUtils_h
-#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
@@ -34,7 +34,10 @@ inline bool IsFirstFragment(const NGConstraintSpace& constraint_space,
// Return true if the specified fragment is the final fragment of some node.
inline bool IsLastFragment(const NGPhysicalFragment& fragment) {
- const auto* break_token = fragment.BreakToken();
+ if (!fragment.IsContainer())
+ return false;
+ const auto* break_token =
+ To<NGPhysicalContainerFragment>(fragment).BreakToken();
return !break_token || break_token->IsFinished();
}
@@ -63,6 +66,11 @@ bool ShouldIgnoreBlockStartMargin(const NGConstraintSpace&,
NGLayoutInputNode,
const NGBreakToken*);
+// Return true if we're resuming layout after a previous break.
+inline bool IsResumingLayout(const NGBlockBreakToken* token) {
+ return token && !token->IsBreakBefore();
+}
+
} // namespace blink
#endif // NGFragmentationUtils_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
index 17487fa66c7..23072c83512 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_inline_layout_test.cc
@@ -8,6 +8,7 @@
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
@@ -21,12 +22,11 @@ namespace blink {
class NGInlineLayoutTest : public SimTest {
public:
NGConstraintSpace ConstraintSpaceForElement(LayoutBlockFlow* block_flow) {
- return NGConstraintSpaceBuilder(
- block_flow->Style()->GetWritingMode(),
- block_flow->Style()->GetWritingMode(),
- /* is_new_fc */ false)
- .SetAvailableSize(NGLogicalSize(LayoutUnit(), LayoutUnit()))
- .SetPercentageResolutionSize(NGLogicalSize(LayoutUnit(), LayoutUnit()))
+ return NGConstraintSpaceBuilder(block_flow->Style()->GetWritingMode(),
+ block_flow->Style()->GetWritingMode(),
+ /* is_new_fc */ false)
+ .SetAvailableSize(LogicalSize(LayoutUnit(), LayoutUnit()))
+ .SetPercentageResolutionSize(LogicalSize(LayoutUnit(), LayoutUnit()))
.SetTextDirection(block_flow->Style()->Direction())
.ToConstraintSpace();
}
@@ -48,8 +48,11 @@ TEST_F(NGInlineLayoutTest, BlockWithSingleTextNode) {
NGConstraintSpace constraint_space = ConstraintSpaceForElement(block_flow);
NGBlockNode node(block_flow);
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(constraint_space, node);
scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, constraint_space).Layout();
+ NGBlockLayoutAlgorithm({node, fragment_geometry, constraint_space})
+ .Layout();
EXPECT_TRUE(result);
String expected_text("Hello World!");
@@ -73,8 +76,11 @@ TEST_F(NGInlineLayoutTest, BlockWithTextAndAtomicInline) {
NGConstraintSpace constraint_space = ConstraintSpaceForElement(block_flow);
NGBlockNode node(block_flow);
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(constraint_space, node);
scoped_refptr<const NGLayoutResult> result =
- NGBlockLayoutAlgorithm(node, constraint_space).Layout();
+ NGBlockLayoutAlgorithm({node, fragment_geometry, constraint_space})
+ .Layout();
EXPECT_TRUE(result);
StringBuilder expected_text;
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 aa4aa8ba2a7..003277b57ef 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
@@ -8,6 +8,7 @@
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -37,12 +38,33 @@ class NGLayoutAlgorithmOperations {
}
};
+// Parameters to pass when creating a layout algorithm for a block node.
+struct NGLayoutAlgorithmParams {
+ STACK_ALLOCATED();
+
+ public:
+ NGLayoutAlgorithmParams(NGBlockNode node,
+ const NGFragmentGeometry& fragment_geometry,
+ const NGConstraintSpace& space,
+ const NGBlockBreakToken* break_token = nullptr)
+ : node(node),
+ fragment_geometry(fragment_geometry),
+ space(space),
+ break_token(break_token) {}
+
+ NGBlockNode node;
+ const NGFragmentGeometry& fragment_geometry;
+ const NGConstraintSpace& space;
+ const NGBlockBreakToken* break_token;
+};
+
// Base class for all LayoutNG algorithms.
template <typename NGInputNodeType,
typename NGBoxFragmentBuilderType,
typename NGBreakTokenType>
class CORE_EXPORT NGLayoutAlgorithm : public NGLayoutAlgorithmOperations {
- STACK_ALLOCATED();
+ USING_FAST_MALLOC(NGLayoutAlgorithm);
+
public:
NGLayoutAlgorithm(NGInputNodeType node,
scoped_refptr<const ComputedStyle> style,
@@ -57,14 +79,12 @@ class CORE_EXPORT NGLayoutAlgorithm : public NGLayoutAlgorithmOperations {
space.GetWritingMode(),
direction) {}
- NGLayoutAlgorithm(NGInputNodeType node,
- const NGConstraintSpace& space,
- const NGBreakTokenType* break_token)
- : NGLayoutAlgorithm(node,
- &node.Style(),
- space,
- space.Direction(),
- break_token) {}
+ NGLayoutAlgorithm(const NGLayoutAlgorithmParams& params)
+ : NGLayoutAlgorithm(params.node,
+ &params.node.Style(),
+ params.space,
+ params.space.Direction(),
+ params.break_token) {}
virtual ~NGLayoutAlgorithm() = default;
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 f44232ed371..c6cc9856596 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
@@ -4,12 +4,12 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
#include "third_party/blink/renderer/core/layout/layout_replaced.h"
#include "third_party/blink/renderer/core/layout/layout_table_cell.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
@@ -19,7 +19,7 @@
namespace blink {
namespace {
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
void AppendNodeToString(NGLayoutInputNode node,
StringBuilder* string_builder,
unsigned indent = 2) {
@@ -62,15 +62,6 @@ void AppendNodeToString(NGLayoutInputNode node,
} // namespace
-scoped_refptr<const NGLayoutResult> NGLayoutInputNode::Layout(
- const NGConstraintSpace& space,
- const NGBreakToken* break_token,
- NGInlineChildLayoutContext* context) {
- auto* inline_node = DynamicTo<NGInlineNode>(this);
- return inline_node ? inline_node->Layout(space, break_token, context)
- : To<NGBlockNode>(*this).Layout(space, break_token);
-}
-
MinMaxSize NGLayoutInputNode::ComputeMinMaxSize(
WritingMode writing_mode,
const MinMaxSizeInput& input,
@@ -83,12 +74,20 @@ MinMaxSize NGLayoutInputNode::ComputeMinMaxSize(
void NGLayoutInputNode::IntrinsicSize(
base::Optional<LayoutUnit>* computed_inline_size,
base::Optional<LayoutUnit>* computed_block_size,
- NGLogicalSize* aspect_ratio) const {
+ LogicalSize* aspect_ratio) const {
DCHECK(IsReplaced());
if (ShouldApplySizeContainment()) {
*computed_inline_size = LayoutUnit();
*computed_block_size = LayoutUnit();
- *aspect_ratio = NGLogicalSize(LayoutUnit(), LayoutUnit());
+ *aspect_ratio = LogicalSize(LayoutUnit(), LayoutUnit());
+ return;
+ }
+ if (DisplayLockInducesSizeContainment()) {
+ *computed_inline_size =
+ GetDisplayLockContext().GetLockedContentLogicalWidth();
+ *computed_block_size =
+ GetDisplayLockContext().GetLockedContentLogicalHeight();
+ *aspect_ratio = LogicalSize(**computed_inline_size, **computed_block_size);
return;
}
IntrinsicSizingInfo legacy_sizing_info;
@@ -99,8 +98,8 @@ void NGLayoutInputNode::IntrinsicSize(
if (legacy_sizing_info.has_height)
*computed_block_size = LayoutUnit(legacy_sizing_info.size.Height());
*aspect_ratio =
- NGLogicalSize(LayoutUnit(legacy_sizing_info.aspect_ratio.Width()),
- LayoutUnit(legacy_sizing_info.aspect_ratio.Height()));
+ LogicalSize(LayoutUnit(legacy_sizing_info.aspect_ratio.Width()),
+ LayoutUnit(legacy_sizing_info.aspect_ratio.Height()));
}
LayoutUnit NGLayoutInputNode::IntrinsicPaddingBlockStart() const {
@@ -119,11 +118,10 @@ NGLayoutInputNode NGLayoutInputNode::NextSibling() {
: To<NGBlockNode>(*this).NextSibling();
}
-NGPhysicalSize NGLayoutInputNode::InitialContainingBlockSize() const {
+PhysicalSize NGLayoutInputNode::InitialContainingBlockSize() const {
IntSize icb_size =
GetDocument().GetLayoutView()->GetLayoutSize(kExcludeScrollbars);
- return NGPhysicalSize{LayoutUnit(icb_size.Width()),
- LayoutUnit(icb_size.Height())};
+ return PhysicalSize(icb_size);
}
const NGPaintFragment* NGLayoutInputNode::PaintFragment() const {
@@ -136,7 +134,7 @@ String NGLayoutInputNode::ToString() const {
: To<NGBlockNode>(*this).ToString();
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
void NGLayoutInputNode::ShowNodeTree() const {
StringBuilder string_builder;
string_builder.Append(".:: LayoutNG Node Tree ::.\n");
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 c65f36812b8..0c1c0d40168 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
@@ -7,8 +7,9 @@
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
#include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h"
#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
@@ -17,17 +18,15 @@
namespace blink {
class ComputedStyle;
+class DisplayLockContext;
class Document;
class LayoutObject;
class LayoutBox;
-class NGBreakToken;
class NGConstraintSpace;
-class NGInlineChildLayoutContext;
-class NGLayoutResult;
class NGPaintFragment;
struct MinMaxSize;
-struct NGLogicalSize;
-struct NGPhysicalSize;
+struct LogicalSize;
+struct PhysicalSize;
enum class NGMinMaxSizeType { kContentBoxSize, kBorderBoxSize };
@@ -82,6 +81,7 @@ class CORE_EXPORT NGLayoutInputNode {
bool IsInline() const { return type_ == kInline; }
bool IsBlock() const { return type_ == kBlock; }
+ bool IsBlockFlow() const { return IsBlock() && box_->IsLayoutBlockFlow(); }
bool IsColumnSpanAll() const { return IsBlock() && box_->IsColumnSpanAll(); }
bool IsFloating() const { return IsBlock() && Style().IsFloating(); }
bool IsOutOfFlowPositioned() const {
@@ -97,6 +97,9 @@ class CORE_EXPORT NGLayoutInputNode {
bool IsBody() const { return IsBlock() && box_->IsBody(); }
bool IsDocumentElement() const { return box_->IsDocumentElement(); }
bool IsFlexItem() const { return IsBlock() && box_->IsFlexItemIncludingNG(); }
+ bool IsFlexibleBox() const {
+ return IsBlock() && box_->IsFlexibleBoxIncludingNG();
+ }
bool ShouldBeConsideredAsReplaced() const {
return box_->ShouldBeConsideredAsReplaced();
}
@@ -153,11 +156,6 @@ class CORE_EXPORT NGLayoutInputNode {
return false;
}
- // Performs layout on this input node, will return the layout result.
- scoped_refptr<const NGLayoutResult> Layout(const NGConstraintSpace&,
- const NGBreakToken*,
- NGInlineChildLayoutContext*);
-
// Returns border box.
MinMaxSize ComputeMinMaxSize(WritingMode,
const MinMaxSizeInput&,
@@ -168,7 +166,7 @@ class CORE_EXPORT NGLayoutInputNode {
// Corresponds to Legacy's LayoutReplaced::IntrinsicSizingInfo.
void IntrinsicSize(base::Optional<LayoutUnit>* computed_inline_size,
base::Optional<LayoutUnit>* computed_block_size,
- NGLogicalSize* aspect_ratio) const;
+ LogicalSize* aspect_ratio) const;
LayoutUnit IntrinsicPaddingBlockStart() const;
LayoutUnit IntrinsicPaddingBlockEnd() const;
@@ -178,7 +176,7 @@ class CORE_EXPORT NGLayoutInputNode {
Document& GetDocument() const { return box_->GetDocument(); }
- NGPhysicalSize InitialContainingBlockSize() const;
+ PhysicalSize InitialContainingBlockSize() const;
// Returns the LayoutObject which is associated with this node.
LayoutBox* GetLayoutBox() const { return box_; }
@@ -189,6 +187,19 @@ class CORE_EXPORT NGLayoutInputNode {
return box_->ShouldApplySizeContainment();
}
+ // Display locking functionality.
+ const DisplayLockContext& GetDisplayLockContext() const {
+ DCHECK(box_->GetDisplayLockContext());
+ return *box_->GetDisplayLockContext();
+ }
+ bool DisplayLockInducesSizeContainment() const {
+ return box_->DisplayLockInducesSizeContainment();
+ }
+ bool LayoutBlockedByDisplayLock(
+ DisplayLockContext::LifecycleTarget target) const {
+ return box_->LayoutBlockedByDisplayLock(target);
+ }
+
// Returns the first NGPaintFragment for this node. When block fragmentation
// occurs, there will be multiple NGPaintFragment for a node.
const NGPaintFragment* PaintFragment() const;
@@ -205,7 +216,7 @@ class CORE_EXPORT NGLayoutInputNode {
return !(*this == other);
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
void ShowNodeTree() const;
#endif
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 b6c86d4b949..f2d5ddb46c2 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
@@ -10,37 +10,40 @@
#include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
namespace blink {
NGLayoutResult::NGLayoutResult(
- scoped_refptr<const NGPhysicalFragment> physical_fragment,
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGBoxFragmentBuilder* builder)
- : NGLayoutResult(builder, /* cache_space */ true) {
+ : NGLayoutResult(std::move(physical_fragment),
+ builder,
+ /* cache_space */ true) {
+ is_initial_block_size_indefinite_ =
+ builder->is_initial_block_size_indefinite_;
intrinsic_block_size_ = builder->intrinsic_block_size_;
minimal_space_shortage_ = builder->minimal_space_shortage_;
initial_break_before_ = builder->initial_break_before_;
final_break_after_ = builder->previous_break_after_;
has_forced_break_ = builder->has_forced_break_;
- DCHECK(physical_fragment) << "Use the other constructor for aborting layout";
- physical_fragment_ = std::move(physical_fragment);
- oof_positioned_descendants_ = std::move(builder->oof_positioned_descendants_);
}
NGLayoutResult::NGLayoutResult(
- scoped_refptr<const NGPhysicalFragment> physical_fragment,
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
NGLineBoxFragmentBuilder* builder)
- : NGLayoutResult(builder, /* cache_space */ false) {
- physical_fragment_ = std::move(physical_fragment);
- oof_positioned_descendants_ = std::move(builder->oof_positioned_descendants_);
-}
+ : NGLayoutResult(std::move(physical_fragment),
+ builder,
+ /* cache_space */ false) {}
NGLayoutResult::NGLayoutResult(NGLayoutResultStatus status,
NGBoxFragmentBuilder* builder)
- : NGLayoutResult(builder, /* cache_space */ false) {
+ : NGLayoutResult(/* physical_fragment */ nullptr,
+ builder,
+ /* cache_space */ false) {
adjoining_floats_ = kFloatTypeNone;
- depends_on_percentage_block_size_ = false;
+ has_descendant_that_depends_on_percentage_block_size_ = false;
status_ = status;
DCHECK_NE(status, kSuccess)
<< "Use the other constructor for successful layout";
@@ -52,7 +55,6 @@ NGLayoutResult::NGLayoutResult(const NGLayoutResult& other,
base::Optional<LayoutUnit> bfc_block_offset)
: space_(new_space),
physical_fragment_(other.physical_fragment_),
- oof_positioned_descendants_(other.oof_positioned_descendants_),
unpositioned_list_marker_(other.unpositioned_list_marker_),
exclusion_space_(MergeExclusionSpaces(other,
space_.ExclusionSpace(),
@@ -67,20 +69,23 @@ NGLayoutResult::NGLayoutResult(const NGLayoutResult& other,
final_break_after_(other.final_break_after_),
has_valid_space_(other.has_valid_space_),
has_forced_break_(other.has_forced_break_),
+ is_self_collapsing_(other.is_self_collapsing_),
is_pushed_by_floats_(other.is_pushed_by_floats_),
adjoining_floats_(other.adjoining_floats_),
- has_orthogonal_flow_roots_(other.has_orthogonal_flow_roots_),
- may_have_descendant_above_block_start_(
- other.may_have_descendant_above_block_start_),
- depends_on_percentage_block_size_(
- other.depends_on_percentage_block_size_),
+ is_initial_block_size_indefinite_(
+ other.is_initial_block_size_indefinite_),
+ has_descendant_that_depends_on_percentage_block_size_(
+ other.has_descendant_that_depends_on_percentage_block_size_),
status_(other.status_) {}
-NGLayoutResult::NGLayoutResult(NGContainerFragmentBuilder* builder,
- bool cache_space)
+NGLayoutResult::NGLayoutResult(
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
+ NGContainerFragmentBuilder* builder,
+ bool cache_space)
: space_(cache_space && builder->space_
? NGConstraintSpace(*builder->space_)
: NGConstraintSpace()),
+ physical_fragment_(std::move(physical_fragment)),
unpositioned_list_marker_(builder->unpositioned_list_marker_),
exclusion_space_(std::move(builder->exclusion_space_)),
bfc_line_offset_(builder->bfc_line_offset_),
@@ -88,56 +93,30 @@ NGLayoutResult::NGLayoutResult(NGContainerFragmentBuilder* builder,
end_margin_strut_(builder->end_margin_strut_),
has_valid_space_(cache_space && builder->space_),
has_forced_break_(false),
+ is_self_collapsing_(builder->is_self_collapsing_),
is_pushed_by_floats_(builder->is_pushed_by_floats_),
adjoining_floats_(builder->adjoining_floats_),
- has_orthogonal_flow_roots_(builder->has_orthogonal_flow_roots_),
- may_have_descendant_above_block_start_(
- builder->may_have_descendant_above_block_start_),
- depends_on_percentage_block_size_(DependsOnPercentageBlockSize(*builder)),
- status_(kSuccess) {}
+ is_initial_block_size_indefinite_(false),
+ has_descendant_that_depends_on_percentage_block_size_(
+ builder->has_descendant_that_depends_on_percentage_block_size_),
+ status_(kSuccess) {
+#if DCHECK_IS_ON()
+ if (is_self_collapsing_ && physical_fragment_) {
+ // A new formatting-context shouldn't be self-collapsing.
+ DCHECK(!physical_fragment_->IsBlockFormattingContextRoot());
+
+ // Self-collapsing children must have a block-size of zero.
+ NGFragment fragment(physical_fragment_->Style().GetWritingMode(),
+ *physical_fragment_);
+ DCHECK_EQ(LayoutUnit(), fragment.BlockSize());
+ }
+#endif
+}
// Define the destructor here, so that we can forward-declare more in the
// header.
NGLayoutResult::~NGLayoutResult() = default;
-bool NGLayoutResult::DependsOnPercentageBlockSize(
- const NGContainerFragmentBuilder& builder) {
- NGLayoutInputNode node = builder.node_;
-
- if (!node || node.IsInline())
- return builder.has_child_that_depends_on_percentage_block_size_;
-
- // NOTE: If an element is OOF positioned, and has top/bottom constraints
- // which are percentage based, this function will return false.
- //
- // This is fine as the top/bottom constraints are computed *before* layout,
- // and the result is set as a fixed-block-size constraint. (And the caching
- // logic will never check the result of this function).
- //
- // The result of this function still may be used for an OOF positioned
- // element if it has a percentage block-size however, but this will return
- // the correct result from below.
-
- if ((builder.has_child_that_depends_on_percentage_block_size_ ||
- builder.is_old_layout_root_) &&
- node.UseParentPercentageResolutionBlockSizeForChildren()) {
- // Quirks mode has different %-block-size behaviour, than standards mode.
- // An arbitrary descendant may depend on the percentage resolution
- // block-size given.
- // If this is also an anonymous block we need to mark ourselves dependent
- // if we have a dependent child.
- return true;
- }
-
- const ComputedStyle& style = builder.Style();
- if (style.LogicalHeight().IsPercentOrCalc() ||
- style.LogicalMinHeight().IsPercentOrCalc() ||
- style.LogicalMaxHeight().IsPercentOrCalc())
- return true;
-
- return false;
-}
-
NGExclusionSpace NGLayoutResult::MergeExclusionSpaces(
const NGLayoutResult& other,
const NGExclusionSpace& new_input_exclusion_space,
@@ -161,4 +140,44 @@ NGExclusionSpace NGLayoutResult::MergeExclusionSpaces(
/* new_input */ new_input_exclusion_space, offset_delta);
}
+#if DCHECK_IS_ON()
+void NGLayoutResult::CheckSameForSimplifiedLayout(
+ const NGLayoutResult& other,
+ bool check_same_block_size) const {
+ To<NGPhysicalBoxFragment>(*physical_fragment_)
+ .CheckSameForSimplifiedLayout(
+ To<NGPhysicalBoxFragment>(*other.physical_fragment_),
+ check_same_block_size);
+
+ DCHECK(unpositioned_list_marker_ == other.unpositioned_list_marker_);
+ exclusion_space_.CheckSameForSimplifiedLayout(other.exclusion_space_);
+
+ // We ignore bfc_block_offset_, and bfc_line_offset_ as "simplified" layout
+ // will move the layout result if required.
+
+ // We ignore the intrinsic_block_size_ as if a scrollbar gets added/removed
+ // this may change (even if the size of the fragment remains the same).
+
+ DCHECK(end_margin_strut_ == other.end_margin_strut_);
+ DCHECK_EQ(minimal_space_shortage_, other.minimal_space_shortage_);
+
+ DCHECK_EQ(initial_break_before_, other.initial_break_before_);
+ DCHECK_EQ(final_break_after_, other.final_break_after_);
+
+ DCHECK_EQ(has_valid_space_, other.has_valid_space_);
+ DCHECK_EQ(has_forced_break_, other.has_forced_break_);
+ DCHECK_EQ(is_self_collapsing_, other.is_self_collapsing_);
+ DCHECK_EQ(is_pushed_by_floats_, other.is_pushed_by_floats_);
+ DCHECK_EQ(adjoining_floats_, other.adjoining_floats_);
+
+ if (check_same_block_size) {
+ DCHECK_EQ(is_initial_block_size_indefinite_,
+ other.is_initial_block_size_indefinite_);
+ }
+ DCHECK_EQ(has_descendant_that_depends_on_percentage_block_size_,
+ other.has_descendant_that_depends_on_percentage_block_size_);
+ DCHECK_EQ(status_, other.status_);
+}
+#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 12a9d223b90..e9aeb01dbee 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
@@ -12,9 +12,9 @@
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
#include "third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -49,16 +49,13 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
base::Optional<LayoutUnit> bfc_block_offset);
~NGLayoutResult();
- const NGPhysicalFragment* PhysicalFragment() const {
- return physical_fragment_.get();
+ const NGPhysicalContainerFragment& PhysicalFragment() const {
+ DCHECK(physical_fragment_);
+ DCHECK_EQ(NGLayoutResultStatus::kSuccess, Status());
+ return *physical_fragment_;
}
- const Vector<NGOutOfFlowPositionedDescendant>&
- OutOfFlowPositionedDescendants() const {
- return oof_positioned_descendants_;
- }
-
- NGLogicalOffset OutOfFlowPositionedOffset() const {
+ LogicalOffset OutOfFlowPositionedOffset() const {
return oof_positioned_offset_;
}
@@ -99,6 +96,10 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
// Return true if the fragment broke because a forced break before a child.
bool HasForcedBreak() const { return has_forced_break_; }
+ // Returns true if the fragment should be considered empty for margin
+ // collapsing purposes (e.g. margins "collapse through").
+ bool IsSelfCollapsing() const { return is_self_collapsing_; }
+
// Return true if this fragment got its block offset increased by the presence
// of floats.
bool IsPushedByFloats() const { return is_pushed_by_floats_; }
@@ -113,18 +114,19 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
// the block, and the block will fail to clear).
NGFloatTypes AdjoiningFloatTypes() const { return adjoining_floats_; }
- bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
-
- // Returns true if we aren't able to re-use this layout result if the
- // PercentageResolutionBlockSize changes.
- bool DependsOnPercentageBlockSize() const {
- return depends_on_percentage_block_size_;
+ // Returns true if the initial (pre-layout) block-size of this fragment was
+ // indefinite. (e.g. it has "height: auto").
+ bool IsInitialBlockSizeIndefinite() const {
+ return is_initial_block_size_indefinite_;
}
- // Returns true if we have a descendant within this formatting context, which
- // is potentially above our block-start edge.
- bool MayHaveDescendantAboveBlockStart() const {
- return may_have_descendant_above_block_start_;
+ // Returns true if there is a descendant that depends on percentage
+ // resolution block-size changes.
+ // Some layout modes (flex-items, table-cells) have more complex child
+ // percentage sizing behaviour (typically when their parent layout forces a
+ // block-size on them).
+ bool HasDescendantThatDependsOnPercentageBlockSize() const {
+ return has_descendant_that_depends_on_percentage_block_size_;
}
// Returns true if the space stored with this layout result, is valid.
@@ -144,7 +146,7 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
protected:
friend class NGOutOfFlowLayoutPart;
- void SetOutOfFlowPositionedOffset(const NGLogicalOffset& offset) {
+ void SetOutOfFlowPositionedOffset(const LogicalOffset& offset) {
layout_result_->oof_positioned_offset_ = offset;
}
@@ -160,17 +162,24 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
return MutableForOutOfFlow(this);
}
+#if DCHECK_IS_ON()
+ void CheckSameForSimplifiedLayout(const NGLayoutResult&,
+ bool check_same_block_size = true) const;
+#endif
+
private:
friend class NGBoxFragmentBuilder;
friend class NGLineBoxFragmentBuilder;
friend class MutableForOutOfFlow;
// This constructor requires a non-null fragment and sets a success status.
- NGLayoutResult(scoped_refptr<const NGPhysicalFragment> physical_fragment,
- NGBoxFragmentBuilder*);
+ NGLayoutResult(
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
+ NGBoxFragmentBuilder*);
// This constructor requires a non-null fragment and sets a success status.
- NGLayoutResult(scoped_refptr<const NGPhysicalFragment> physical_fragment,
- NGLineBoxFragmentBuilder*);
+ NGLayoutResult(
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
+ NGLineBoxFragmentBuilder*);
// This constructor is for a non-success status.
NGLayoutResult(NGLayoutResultStatus, NGBoxFragmentBuilder*);
@@ -179,9 +188,10 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
NGLayoutResult(const NGLayoutResult&) = delete;
// Delegate constructor that sets up what it can, based on the builder.
- NGLayoutResult(NGContainerFragmentBuilder* builder, bool cache_space);
-
- static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);
+ NGLayoutResult(
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment,
+ NGContainerFragmentBuilder* builder,
+ bool cache_space);
static NGExclusionSpace MergeExclusionSpaces(
const NGLayoutResult& other,
@@ -193,14 +203,13 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
// as indicated by |has_valid_space_|.
const NGConstraintSpace space_;
- scoped_refptr<const NGPhysicalFragment> physical_fragment_;
- Vector<NGOutOfFlowPositionedDescendant> oof_positioned_descendants_;
+ scoped_refptr<const NGPhysicalContainerFragment> physical_fragment_;
// This is the final position of an OOF-positioned object in its parent's
// writing-mode. This is set by the |NGOutOfFlowLayoutPart| while generating
// this layout result.
// This field is unused for other objects.
- NGLogicalOffset oof_positioned_offset_;
+ LogicalOffset oof_positioned_offset_;
NGUnpositionedListMarker unpositioned_list_marker_;
const NGExclusionSpace exclusion_space_;
@@ -216,12 +225,12 @@ class CORE_EXPORT NGLayoutResult : public RefCounted<NGLayoutResult> {
unsigned has_valid_space_ : 1;
unsigned has_forced_break_ : 1;
+ unsigned is_self_collapsing_ : 1;
unsigned is_pushed_by_floats_ : 1;
- unsigned adjoining_floats_ : 2; // NGFloatTypes
+ unsigned adjoining_floats_ : 3; // NGFloatTypes
- unsigned has_orthogonal_flow_roots_ : 1;
- unsigned may_have_descendant_above_block_start_ : 1;
- unsigned depends_on_percentage_block_size_ : 1;
+ unsigned is_initial_block_size_indefinite_ : 1;
+ unsigned has_descendant_that_depends_on_percentage_block_size_ : 1;
unsigned status_ : 1;
};
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
index 1a98c061319..688755f9290 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_result_caching_test.cc
@@ -2,8 +2,10 @@
// 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/geometry/ng_fragment_geometry.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_layout_utils.h"
namespace blink {
namespace {
@@ -42,11 +44,14 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentExclusionSpace) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
EXPECT_NE(result.get(), nullptr);
EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(50));
EXPECT_EQ(result->BfcLineOffset(), LayoutUnit());
@@ -82,11 +87,14 @@ TEST_F(NGLayoutResultCachingTest, HitDifferentBFCOffset) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
EXPECT_NE(result.get(), nullptr);
EXPECT_EQ(result->BfcBlockOffset().value(), LayoutUnit(40));
EXPECT_EQ(result->BfcLineOffset(), LayoutUnit());
@@ -143,11 +151,14 @@ TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart1) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
@@ -180,14 +191,57 @@ TEST_F(NGLayoutResultCachingTest, MissDescendantAboveBlockStart2) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
+TEST_F(NGLayoutResultCachingTest, HitOOFDescendantAboveBlockStart) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Different BFC offset, same exclusion space, OOF-descendant above
+ // block start.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .float { float: left; width: 50px; }
+ </style>
+ <div class="bfc">
+ <div style="height: 50px;">
+ <div class="float" style="height: 20px;"></div>
+ </div>
+ <div id="test" style="position: relative; height: 20px; padding-top: 5px;">
+ <div style="position: absolute; height: 10px; top: -10px;"></div>
+ </div>
+ </div>
+ <div class="bfc">
+ <div style="height: 40px;">
+ <div class="float" style="height: 20px;"></div>
+ </div>
+ <div id="src" style="height: 20px;"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding1) {
ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
@@ -215,11 +269,14 @@ TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding1) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
@@ -250,11 +307,14 @@ TEST_F(NGLayoutResultCachingTest, MissFloatInitiallyIntruding2) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
@@ -284,11 +344,14 @@ TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude1) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
@@ -318,15 +381,18 @@ TEST_F(NGLayoutResultCachingTest, MissFloatWillIntrude2) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
EXPECT_EQ(result.get(), nullptr);
}
-TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) {
+TEST_F(NGLayoutResultCachingTest, HitPushedByFloats1) {
ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
// Same BFC offset, different exclusion space, pushed by floats.
@@ -343,7 +409,7 @@ TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) {
</div>
<div class="bfc">
<div style="height: 50px;">
- <div class="float" style="height: 40px;"></div>
+ <div class="float" style="height: 70px;"></div>
</div>
<div id="src" style="height: 20px; clear: left;"></div>
</div>
@@ -352,15 +418,18 @@ TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
- EXPECT_EQ(result.get(), nullptr);
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
}
-TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) {
+TEST_F(NGLayoutResultCachingTest, HitPushedByFloats2) {
ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
// Different BFC offset, same exclusion space, pushed by floats.
@@ -386,11 +455,438 @@ TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) {
auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, MissPushedByFloats1) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Same BFC offset, different exclusion space, pushed by floats.
+ // Miss due to shrinking offset.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .float { float: left; width: 50px; }
+ </style>
+ <div class="bfc">
+ <div style="height: 50px;">
+ <div class="float" style="height: 70px;"></div>
+ </div>
+ <div id="test" style="height: 20px; clear: left;"></div>
+ </div>
+ <div class="bfc">
+ <div style="height: 50px;">
+ <div class="float" style="height: 60px;"></div>
+ </div>
+ <div id="src" style="height: 20px; clear: left;"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
+ EXPECT_EQ(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, MissPushedByFloats2) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Different BFC offset, same exclusion space, pushed by floats.
+ // Miss due to shrinking offset.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .float { float: left; width: 50px; }
+ </style>
+ <div class="bfc">
+ <div style="height: 30px;">
+ <div class="float" style="height: 60px;"></div>
+ </div>
+ <div id="test" style="height: 20px; clear: left;"></div>
+ </div>
+ <div class="bfc">
+ <div style="height: 50px;">
+ <div class="float" style="height: 60px;"></div>
+ </div>
+ <div id="src" style="height: 20px; clear: left;"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
const NGConstraintSpace& space =
src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
- scoped_refptr<const NGLayoutResult> result =
- test->CachedLayoutResult(space, nullptr);
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
+ EXPECT_EQ(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitDifferentRareData) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Same absolute fixed constraints.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .container { position: relative; width: 100px; height: 100px; }
+ .abs { position: absolute; width: 100px; height: 100px; top: 0; left: 0; }
+ </style>
+ <div class="container">
+ <div id="test" class="abs"></div>
+ </div>
+ <div class="container" style="width: 200px; height: 200px;">
+ <div id="src" class="abs"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitPercentageMinWidth) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // min-width calculates to different values, but doesn't change size.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .inflow { width: 100px; min-width: 25%; }
+ </style>
+ <div class="bfc">
+ <div id="test" class="inflow"></div>
+ </div>
+ <div class="bfc" style="width: 200px; height: 200px;">
+ <div id="src" class="inflow"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitFixedMinWidth) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // min-width is always larger than the available size.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .inflow { min-width: 300px; }
+ </style>
+ <div class="bfc">
+ <div id="test" class="inflow"></div>
+ </div>
+ <div class="bfc" style="width: 200px; height: 200px;">
+ <div id="src" class="inflow"></div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitShrinkToFitSameIntrinsicSizes) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // We have a shrink-to-fit node, with the min, and max intrinsic sizes being
+ // equal (the available size doesn't affect the final size).
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .shrink { width: fit-content; }
+ .child { width: 250px; }
+ </style>
+ <div class="bfc">
+ <div id="test" class="shrink">
+ <div class="child"></div>
+ </div>
+ </div>
+ <div class="bfc" style="width: 200px; height: 200px;">
+ <div id="src" class="shrink">
+ <div class="child"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitShrinkToFitDifferentParent) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // The parent "bfc" node changes from shrink-to-fit, to a fixed width. But
+ // these calculate as the same available space to the "test" element.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; }
+ .child { width: 250px; }
+ </style>
+ <div class="bfc" style="width: fit-content; height: 100px;">
+ <div id="test">
+ <div class="child"></div>
+ </div>
+ </div>
+ <div class="bfc" style="width: 250px; height: 100px;">
+ <div id="src">
+ <div class="child"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, MissQuirksModePercentageBasedChild) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Quirks-mode %-block-size child.
+ GetDocument().SetCompatibilityMode(Document::kQuirksMode);
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .child { height: 50%; }
+ </style>
+ <div class="bfc">
+ <div id="test">
+ <div class="child"></div>
+ </div>
+ </div>
+ <div class="bfc" style="height: 200px;">
+ <div id="src">
+ <div class="child"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsLayout);
+ EXPECT_EQ(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitQuirksModePercentageBasedParentAndChild) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Quirks-mode %-block-size parent *and* child. Here we mark the parent as
+ // depending on %-block-size changes, however itself doesn't change in
+ // height.
+ // We are able to hit the cache as we detect that the height for the child
+ // *isn't* indefinite, and results in the same height as before.
+ GetDocument().SetCompatibilityMode(Document::kQuirksMode);
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .parent { height: 50%; min-height: 200px; }
+ .child { height: 50%; }
+ </style>
+ <div class="bfc">
+ <div id="test" class="parent">
+ <div class="child"></div>
+ </div>
+ </div>
+ <div class="bfc" style="height: 200px;">
+ <div id="src" class="parent">
+ <div class="child"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, HitStandardsModePercentageBasedChild) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ // Standards-mode %-block-size child.
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .bfc { display: flow-root; width: 300px; height: 300px; }
+ .child { height: 50%; }
+ </style>
+ <div class="bfc">
+ <div id="test">
+ <div class="child"></div>
+ </div>
+ </div>
+ <div class="bfc" style="height: 200px;">
+ <div id="src">
+ <div class="child"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test"));
+ auto* src = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ const NGConstraintSpace& space =
+ src->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+}
+
+TEST_F(NGLayoutResultCachingTest, ChangeTableCellBlockSizeConstrainedness) {
+ ScopedLayoutNGFragmentCachingForTest layout_ng_fragment_caching(true);
+
+ SetBodyInnerHTML(R"HTML(
+ <style>
+ .table { display: table; width: 300px; }
+ .cell { display: table-cell; }
+ .child1 { height: 100px; }
+ .child2, .child3 { overflow:auto; height:10%; }
+ </style>
+ <div class="table">
+ <div class="cell">
+ <div class="child1" id="test1"></div>
+ <div class="child2" id="test2">
+ <div style="height:30px;"></div>
+ </div>
+ <div class="child3" id="test3"></div>
+ </div>
+ </div>
+ <div class="table" style="height:300px;">
+ <div class="cell">
+ <div class="child1" id="src1"></div>
+ <div class="child2" id="src2">
+ <div style="height:30px;"></div>
+ </div>
+ <div class="child3" id="src3"></div>
+ </div>
+ </div>
+ )HTML");
+
+ auto* test1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test1"));
+ auto* test2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test2"));
+ auto* test3 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("test3"));
+ auto* src1 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src1"));
+ auto* src2 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src2"));
+ auto* src3 = To<LayoutBlockFlow>(GetLayoutObjectByElementId("src3"));
+
+ NGLayoutCacheStatus cache_status;
+ base::Optional<NGFragmentGeometry> fragment_geometry;
+ NGConstraintSpace space =
+ src1->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ scoped_refptr<const NGLayoutResult> result = test1->CachedLayoutResult(
+ space, nullptr, &fragment_geometry, &cache_status);
+ // The first child has a fixed height, and shouldn't be affected by the cell
+ // height.
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+
+ fragment_geometry.reset();
+ space = src2->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ result = test2->CachedLayoutResult(space, nullptr, &fragment_geometry,
+ &cache_status);
+ // The second child has overflow:auto and a percentage height, but its
+ // intrinsic height is identical to its extrinsic height (when the cell has a
+ // height). So it won't need layout, either.
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kHit);
+ EXPECT_NE(result.get(), nullptr);
+ fragment_geometry.reset();
+ space = src3->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
+ result = test3->CachedLayoutResult(space, nullptr, &fragment_geometry,
+ &cache_status);
+ // The third child has overflow:auto and a percentage height, and its
+ // intrinsic height is 0 (no children), so it matters whether the cell has a
+ // height or not. We're only going to need simplified layout, though, since no
+ // children will be affected by its height change.
+ EXPECT_EQ(cache_status, NGLayoutCacheStatus::kNeedsSimplifiedLayout);
EXPECT_EQ(result.get(), nullptr);
}
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 fce051a0d30..165908d0206 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
@@ -4,69 +4,22 @@
#include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
namespace blink {
namespace {
-bool ContentShrinkToFitMayChange(const ComputedStyle& style,
- const NGConstraintSpace& new_space,
- const NGConstraintSpace& old_space,
- const NGLayoutResult& layout_result) {
- if (old_space.AvailableSize().inline_size ==
- new_space.AvailableSize().inline_size)
- return false;
-
- NGBoxStrut margins = ComputeMarginsForSelf(new_space, style);
-
-#if DCHECK_IS_ON()
- // The margins must be the same, as this function won't be called if we have
- // percentage inline margins, and the percentage resolution size changes.
- NGBoxStrut old_margins = ComputeMarginsForSelf(old_space, style);
- DCHECK_EQ(margins.inline_start, old_margins.inline_start);
- DCHECK_EQ(margins.inline_end, old_margins.inline_end);
-#endif
-
- LayoutUnit old_available_inline_size =
- std::max(LayoutUnit(),
- old_space.AvailableSize().inline_size - margins.InlineSum());
- LayoutUnit new_available_inline_size =
- std::max(LayoutUnit(),
- new_space.AvailableSize().inline_size - margins.InlineSum());
-
- DCHECK(layout_result.PhysicalFragment());
- LayoutUnit inline_size =
- NGFragment(style.GetWritingMode(), *layout_result.PhysicalFragment())
- .InlineSize();
-
- // If the previous fragment was at its min-content size (indicated by the old
- // available size being smaller than the fragment), we may be able to skip
- // layout if the new available size is also smaller.
- bool unaffected_as_min_content_size =
- old_available_inline_size < inline_size &&
- new_available_inline_size <= inline_size;
-
- // If the previous fragment was at its max-content size (indicated by the old
- // available size being larger than the fragment), we may be able to skip
- // layout if the new available size is also larger.
- bool unaffected_as_max_content_size =
- old_available_inline_size > inline_size &&
- new_available_inline_size >= inline_size;
-
- // TODO(crbug.com/935634): There is an additional optimization where if we
- // detect (by setting a flag in the layout result) that the
- // min-content == max-content we can simply just skip layout, as the
- // available size won't have any effect.
-
- if (unaffected_as_min_content_size || unaffected_as_max_content_size)
- return false;
-
- return true;
-}
+// LengthResolveType indicates what type length the function is being passed
+// based on its CSS property. E.g.
+// kMinSize - min-width / min-height
+// kMaxSize - max-width / max-height
+// kMainSize - width / height
+enum class LengthResolveType { kMinSize, kMaxSize, kMainSize };
inline bool InlineLengthMayChange(const ComputedStyle& style,
const Length& length,
@@ -93,18 +46,6 @@ inline bool InlineLengthMayChange(const ComputedStyle& style,
old_space.PercentageResolutionInlineSize()))
return true;
- // For elements which shrink to fit, we can perform a specific optimization
- // where we can skip relayout if the element was sized to its min-content or
- // max-content size.
- bool is_content_shrink_to_fit =
- type == LengthResolveType::kMainSize &&
- (new_space.IsShrinkToFit() || length.IsFitContent());
-
- if (is_content_shrink_to_fit) {
- return ContentShrinkToFitMayChange(style, new_space, old_space,
- layout_result);
- }
-
if (is_unspecified) {
if (new_space.AvailableSize().inline_size !=
old_space.AvailableSize().inline_size)
@@ -127,18 +68,27 @@ inline bool BlockLengthMayChange(const Length& length,
old_space.AvailableSize().block_size)
return true;
}
+
return false;
}
// Return true if it's possible (but not necessarily guaranteed) that the new
// constraint space will give a different size compared to the old one, when
// computed style and child content remain unchanged.
-bool SizeMayChange(const ComputedStyle& style,
+bool SizeMayChange(const NGBlockNode& node,
const NGConstraintSpace& new_space,
const NGConstraintSpace& old_space,
const NGLayoutResult& layout_result) {
+ if (node.IsQuirkyAndFillsViewport())
+ return true;
+
DCHECK_EQ(new_space.IsFixedSizeInline(), old_space.IsFixedSizeInline());
DCHECK_EQ(new_space.IsFixedSizeBlock(), old_space.IsFixedSizeBlock());
+ DCHECK_EQ(new_space.IsShrinkToFit(), old_space.IsShrinkToFit());
+ DCHECK_EQ(new_space.TableCellChildLayoutPhase(),
+ old_space.TableCellChildLayoutPhase());
+
+ const ComputedStyle& style = node.Style();
// Go through all length properties, and, depending on length type
// (percentages, auto, etc.), check whether the constraint spaces differ in
@@ -178,7 +128,7 @@ bool SizeMayChange(const ComputedStyle& style,
return true;
// We only need to check if the PercentageResolutionBlockSizes match if the
// layout result has explicitly marked itself as dependent.
- if (layout_result.DependsOnPercentageBlockSize()) {
+ if (layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) {
if (new_space.PercentageResolutionBlockSize() !=
old_space.PercentageResolutionBlockSize())
return true;
@@ -203,11 +153,198 @@ bool SizeMayChange(const ComputedStyle& style,
return false;
}
+// Given the pre-computed |fragment_geometry| calcuates the
+// |NGLayoutCacheStatus| based on this sizing information. Returns:
+// - |NGLayoutCacheStatus::kNeedsLayout| if the |new_space| will produce a
+// different sized fragment, or if any %-block-size children will change
+// size.
+// - |NGLayoutCacheStatus::kNeedsSimplifiedLayout| if the block-size of the
+// fragment will change, *without* affecting any descendants (no descendants
+// have %-block-sizes).
+// - |NGLayoutCacheStatus::kHit| otherwise.
+NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatusWithGeometry(
+ const NGBlockNode& node,
+ const NGFragmentGeometry& fragment_geometry,
+ const NGLayoutResult& layout_result,
+ const NGConstraintSpace& new_space,
+ const NGConstraintSpace& old_space) {
+ const ComputedStyle& style = node.Style();
+ const NGPhysicalBoxFragment& physical_fragment =
+ To<NGPhysicalBoxFragment>(layout_result.PhysicalFragment());
+ NGBoxFragment fragment(style.GetWritingMode(), style.Direction(),
+ physical_fragment);
+
+ if (fragment_geometry.border_box_size.inline_size != fragment.InlineSize())
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ LayoutUnit block_size = fragment_geometry.border_box_size.block_size;
+ bool is_initial_block_size_indefinite = block_size == kIndefiniteSize;
+ if (is_initial_block_size_indefinite) {
+ // The intrinsic size of column flex-boxes can depend on the
+ // %-resolution-block-size. This occurs when a flex-box has "max-height:
+ // 100%" or similar on itself.
+ //
+ // Due to this we can't use cached |NGLayoutResult::IntrinsicBlockSize|
+ // value, as the following |block_size| calculation would be incorrect.
+ if (node.IsFlexibleBox() && style.IsColumnFlexDirection() &&
+ layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) {
+ if (new_space.PercentageResolutionBlockSize() !=
+ old_space.PercentageResolutionBlockSize())
+ return NGLayoutCacheStatus::kNeedsLayout;
+ }
+
+ block_size = ComputeBlockSizeForFragment(
+ new_space, node, fragment_geometry.border + fragment_geometry.padding,
+ layout_result.IntrinsicBlockSize());
+ }
+
+ bool is_block_size_equal = block_size == fragment.BlockSize();
+
+ if (!is_block_size_equal) {
+ // If we are the document or body element in quirks mode, changing our size
+ // means that a scrollbar was added/removed. Require full layout.
+ if (node.IsQuirkyAndFillsViewport())
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ // If a block (within a formatting-context) changes to/from an empty-block,
+ // margins may collapse through this node, requiring full layout. We
+ // approximate this check by checking if the block-size is/was zero.
+ if (!physical_fragment.IsBlockFormattingContextRoot() &&
+ !block_size != !fragment.BlockSize())
+ return NGLayoutCacheStatus::kNeedsLayout;
+ }
+
+ if (layout_result.HasDescendantThatDependsOnPercentageBlockSize()) {
+ // %-block-size children of flex-items sometimes don't resolve their
+ // percentages against a fixed block-size.
+ // We miss the cache if the %-resolution block-size changes from indefinite
+ // to definite (or visa-versa).
+ bool is_new_initial_block_size_indefinite =
+ new_space.IsFixedSizeBlock() ? !new_space.FixedSizeBlockIsDefinite()
+ : is_initial_block_size_indefinite;
+
+ bool is_old_initial_block_size_indefinite =
+ old_space.IsFixedSizeBlock()
+ ? !old_space.FixedSizeBlockIsDefinite()
+ : layout_result.IsInitialBlockSizeIndefinite();
+
+ if (is_old_initial_block_size_indefinite !=
+ is_new_initial_block_size_indefinite)
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ // %-block-size children of table-cells have different behaviour if they
+ // are in the "measure" or "layout" phase.
+ // Instead of trying to capture that logic here, we always miss the cache.
+ if (node.IsTableCell() &&
+ new_space.IsFixedSizeBlock() != old_space.IsFixedSizeBlock())
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ // If our initial block-size is definite, we know that if we change our
+ // block-size we'll affect any descendant that depends on the resulting
+ // percentage block-size.
+ if (!is_block_size_equal && !is_new_initial_block_size_indefinite)
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ DCHECK(is_block_size_equal || is_new_initial_block_size_indefinite);
+
+ // At this point we know that either we have the same block-size for our
+ // fragment, or our initial block-size was indefinite.
+ //
+ // The |NGPhysicalContainerFragment::DependsOnPercentageBlockSize| flag
+ // will returns true if we are in quirks mode, and have a descendant that
+ // depends on a percentage block-size, however it will also return true if
+ // the node itself depends on the %-block-size.
+ //
+ // As we only care about the quirks-mode %-block-size behaviour we remove
+ // this false-positive by checking if we have an initial indefinite
+ // block-size.
+ if (is_new_initial_block_size_indefinite &&
+ layout_result.PhysicalFragment().DependsOnPercentageBlockSize()) {
+ DCHECK(is_old_initial_block_size_indefinite);
+ if (new_space.PercentageResolutionBlockSize() !=
+ old_space.PercentageResolutionBlockSize())
+ return NGLayoutCacheStatus::kNeedsLayout;
+ if (new_space.ReplacedPercentageResolutionBlockSize() !=
+ old_space.ReplacedPercentageResolutionBlockSize())
+ return NGLayoutCacheStatus::kNeedsLayout;
+ }
+ }
+
+ if (style.MayHavePadding() && fragment_geometry.padding != fragment.Padding())
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ // If we've reached here we know that we can potentially "stretch"/"shrink"
+ // ourselves without affecting any of our children.
+ // In that case we may be able to perform "simplified" layout.
+ return is_block_size_equal ? NGLayoutCacheStatus::kHit
+ : NGLayoutCacheStatus::kNeedsSimplifiedLayout;
+}
+
+bool IntrinsicSizeWillChange(
+ const NGBlockNode& node,
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space,
+ base::Optional<NGFragmentGeometry>* fragment_geometry) {
+ const ComputedStyle& style = node.Style();
+ if (!new_space.IsShrinkToFit() && !NeedMinMaxSize(style))
+ return false;
+
+ if (!*fragment_geometry)
+ *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node);
+
+ LayoutUnit inline_size = NGFragment(style.GetWritingMode(),
+ cached_layout_result.PhysicalFragment())
+ .InlineSize();
+
+ if ((*fragment_geometry)->border_box_size.inline_size != inline_size)
+ return true;
+
+ return false;
+}
+
} // namespace
-bool MaySkipLayout(const NGBlockNode node,
- const NGLayoutResult& cached_layout_result,
- const NGConstraintSpace& new_space) {
+NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatus(
+ const NGBlockNode& node,
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space,
+ base::Optional<NGFragmentGeometry>* fragment_geometry) {
+ DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
+ DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
+
+ const NGConstraintSpace& old_space =
+ cached_layout_result.GetConstraintSpaceForCaching();
+
+ if (!new_space.MaySkipLayout(old_space))
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ if (new_space.AreSizeConstraintsEqual(old_space)) {
+ // It is possible that our intrinsic size has changed, check for that here.
+ // TODO(cbiesinger): Investigate why this check doesn't apply to
+ // |MaySkipLegacyLayout|.
+ if (IntrinsicSizeWillChange(node, cached_layout_result, new_space,
+ fragment_geometry))
+ return NGLayoutCacheStatus::kNeedsLayout;
+
+ // We don't have to check our style if we know the constraint space sizes
+ // will remain the same.
+ if (new_space.AreSizesEqual(old_space))
+ return NGLayoutCacheStatus::kHit;
+
+ if (!SizeMayChange(node, new_space, old_space, cached_layout_result))
+ return NGLayoutCacheStatus::kHit;
+ }
+
+ if (!*fragment_geometry)
+ *fragment_geometry = CalculateInitialFragmentGeometry(new_space, node);
+
+ return CalculateSizeBasedLayoutCacheStatusWithGeometry(
+ node, **fragment_geometry, cached_layout_result, new_space, old_space);
+}
+
+bool MaySkipLegacyLayout(const NGBlockNode& node,
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space) {
DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
@@ -216,15 +353,89 @@ bool MaySkipLayout(const NGBlockNode node,
if (!new_space.MaySkipLayout(old_space))
return false;
- if (!new_space.AreSizesEqual(old_space)) {
- // We need to descend all the way down into BODY if we're in quirks mode,
- // since it magically follows the viewport size.
- if (node.IsQuirkyAndFillsViewport())
+ if (!new_space.AreSizeConstraintsEqual(old_space))
+ return false;
+
+ if (new_space.AreSizesEqual(old_space))
+ return true;
+
+ if (SizeMayChange(node, new_space, old_space, cached_layout_result))
+ return false;
+
+ return true;
+}
+
+bool MaySkipLayoutWithinBlockFormattingContext(
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space,
+ base::Optional<LayoutUnit>* bfc_block_offset) {
+ DCHECK_EQ(cached_layout_result.Status(), NGLayoutResult::kSuccess);
+ DCHECK(cached_layout_result.HasValidConstraintSpaceForCaching());
+ DCHECK(bfc_block_offset);
+
+ const NGConstraintSpace& old_space =
+ cached_layout_result.GetConstraintSpaceForCaching();
+
+ LayoutUnit old_clearance_offset = old_space.ClearanceOffset();
+ LayoutUnit new_clearance_offset = new_space.ClearanceOffset();
+
+ // Determine if we can reuse a result if it was affected by clearance.
+ bool is_pushed_by_floats = cached_layout_result.IsPushedByFloats();
+ if (is_pushed_by_floats) {
+ DCHECK(old_space.HasFloats());
+
+ // We don't attempt to reuse the cached result if the clearance offset
+ // differs from the final BFC-block-offset.
+ //
+ // The |is_pushed_by_floats| flag is also used by nodes who have a *child*
+ // which was pushed by floats. In this case the node may not have a
+ // BFC-block-offset or one equal to the clearance offset.
+ if (!cached_layout_result.BfcBlockOffset() ||
+ *cached_layout_result.BfcBlockOffset() != old_space.ClearanceOffset())
return false;
- // If the available / percentage sizes have changed in a way that may affect
- // layout, we cannot re-use the previous result.
- if (SizeMayChange(node.Style(), new_space, old_space, cached_layout_result))
+ // We only reuse the cached result if the delta between the
+ // BFC-block-offset, and the clearance offset grows or remains the same. If
+ // it shrinks it may not be affected by clearance anymore as a margin may
+ // push the fragment below the clearance offset instead.
+ //
+ // TODO(layout-dev): If we track if any margins affected this calculation
+ // (with an additional bit on the layout result) we could potentially skip
+ // this check.
+ if (old_clearance_offset - old_space.BfcOffset().block_offset >
+ new_clearance_offset - new_space.BfcOffset().block_offset) {
+ return false;
+ }
+ }
+
+ // Check we have a descendant that *may* be positioned above the block-start
+ // edge. We abort if either the old or new space has floats, as we don't keep
+ // track of how far above the child could be. This case is relatively rare,
+ // and only occurs with negative margins.
+ if (cached_layout_result.PhysicalFragment()
+ .MayHaveDescendantAboveBlockStart() &&
+ (old_space.HasFloats() || new_space.HasFloats()))
+ return false;
+
+ // We can now try to adjust the BFC block-offset.
+ if (*bfc_block_offset) {
+ // Check if the previous position may intersect with any floats.
+ if (**bfc_block_offset <
+ old_space.ExclusionSpace().ClearanceOffset(EClear::kBoth))
+ return false;
+
+ if (is_pushed_by_floats) {
+ DCHECK_EQ(**bfc_block_offset, old_clearance_offset);
+ *bfc_block_offset = new_clearance_offset;
+ } else {
+ *bfc_block_offset = **bfc_block_offset -
+ old_space.BfcOffset().block_offset +
+ new_space.BfcOffset().block_offset;
+ }
+
+ // Check if the new position may intersect with any floats.
+ if (**bfc_block_offset <
+ new_space.ExclusionSpace().ClearanceOffset(EClear::kBoth))
return false;
}
@@ -238,8 +449,7 @@ bool IsBlockLayoutComplete(const NGConstraintSpace& space,
if (space.IsIntermediateLayout())
return false;
// Check that we're done positioning pending floats.
- return !result.AdjoiningFloatTypes() || result.BfcBlockOffset() ||
- space.FloatsBfcBlockOffset();
+ return !result.AdjoiningFloatTypes() || result.BfcBlockOffset();
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
index 1a880d95cb9..b37bdc7eaf9 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_layout_utils.h
@@ -12,12 +12,48 @@ namespace blink {
class NGConstraintSpace;
class NGLayoutResult;
-// Returns true if for a given |new_space|, the |node| will provide the same
-// |NGLayoutResult| as |cached_layout_result|, and therefore might be able to
-// skip layout.
-bool MaySkipLayout(const NGBlockNode node,
- const NGLayoutResult& cached_layout_result,
- const NGConstraintSpace& new_space);
+// NGLayoutCacheStatus indicates what type of cache hit/miss occurred. For
+// various types of misses we may be able to perform less work than a full
+// layout.
+//
+// See |NGSimplifiedLayoutAlgorithm| for details about the
+// |kNeedsSimplifiedLayout| cache miss type.
+enum class NGLayoutCacheStatus {
+ kHit, // Cache hit, no additional work required.
+ kNeedsLayout, // Cache miss, full layout required.
+ kNeedsSimplifiedLayout // Cache miss, simplified layout required.
+};
+
+// Calculates the |NGLayoutCacheStatus| based on sizing information. Returns:
+// - |NGLayoutCacheStatus::kHit| if the size will be the same as
+// |cached_layout_result|, and therefore might be able to skip layout.
+// - |NGLayoutCacheStatus::kNeedsSimplifiedLayout| if a simplified layout may
+// be possible (just based on the sizing information at this point).
+// - |NGLayoutCacheStatus::kNeedsLayout| if a full layout is required.
+//
+// May pre-compute the |fragment_geometry| while calculating this status.
+NGLayoutCacheStatus CalculateSizeBasedLayoutCacheStatus(
+ const NGBlockNode& node,
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space,
+ base::Optional<NGFragmentGeometry>* fragment_geometry);
+
+// Similar to |MaySkipLayout| but for legacy layout roots. Doesn't attempt to
+// pre-compute the geometry of the fragment.
+bool MaySkipLegacyLayout(const NGBlockNode& node,
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space);
+
+// Returns true if for a given |new_space|, the |cached_layout_result| won't be
+// affected by clearance, or floats, and therefore might be able to skip
+// layout.
+// Additionally (if this function returns true) it will calculate the new
+// |bfc_block_offset| for the layout result. This may still be |base::nullopt|
+// if not previously set.
+bool MaySkipLayoutWithinBlockFormattingContext(
+ const NGLayoutResult& cached_layout_result,
+ const NGConstraintSpace& new_space,
+ base::Optional<LayoutUnit>* bfc_block_offset);
// Return true if layout is considered complete. In some cases we require more
// than one layout pass.
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 8f167e9b57f..b3107708039 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
@@ -1,4 +1,4 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2016 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.
@@ -97,12 +97,12 @@ bool BlockLengthUnresolvable(
opt_percentage_resolution_block_size_for_min_max
? *opt_percentage_resolution_block_size_for_min_max
: constraint_space.PercentageResolutionBlockSize();
- return percentage_resolution_block_size == NGSizeIndefinite;
+ return percentage_resolution_block_size == kIndefiniteSize;
}
if (length.IsFillAvailable()) {
return phase == LengthResolvePhase::kIntrinsic ||
- constraint_space.AvailableSize().block_size == NGSizeIndefinite;
+ constraint_space.AvailableSize().block_size == kIndefiniteSize;
}
return false;
@@ -253,7 +253,7 @@ MinMaxSize ComputeMinAndMaxContentContribution(
.ToConstraintSpace();
LayoutUnit content_size =
- min_and_max ? min_and_max->max_size : NGSizeIndefinite;
+ min_and_max ? min_and_max->max_size : kIndefiniteSize;
MinMaxSize computed_sizes;
const Length& inline_size = parent_writing_mode == WritingMode::kHorizontalTb
@@ -380,17 +380,26 @@ MinMaxSize ComputeMinAndMaxContentSizeForOutOfFlow(
const NGBoxStrut& border_padding,
const MinMaxSizeInput& input) {
LayoutBox* box = node.GetLayoutBox();
- if (!box->PreferredLogicalWidthsDirty() &&
- !box->NeedsPreferredWidthsRecalculation()) {
+ // If we've already populated the legacy preferred logical widths cache for
+ // this node at the bottom of this function, use those cached results here.
+ // Or, if we're working on a table, use the legacy preferred widths code
+ // instead of ComputeMinAndMaxContentContribution below because
+ // ComputeMinAndMaxContentContribution assumes that if an element has a
+ // specified size, that's its final size, which tables don't follow.
+ if ((!box->PreferredLogicalWidthsDirty() &&
+ !box->NeedsPreferredWidthsRecalculation()) ||
+ box->IsTable()) {
return MinMaxSize{box->MinPreferredLogicalWidth(),
box->MaxPreferredLogicalWidth()};
}
+ // Compute the intrinsic sizes without regard to the specified sizes.
MinMaxSize result = node.ComputeMinMaxSize(node.Style().GetWritingMode(),
input, &constraint_space);
- // Cache these computed values.
+ // Apply the specified min, main, max sizes.
MinMaxSize contribution = ComputeMinAndMaxContentContribution(
node.Style().GetWritingMode(), node.Style(), border_padding, result);
+ // Cache these computed values.
box->SetPreferredLogicalWidthsFromNG(contribution);
return result;
}
@@ -472,23 +481,36 @@ LayoutUnit ComputeBlockSizeForFragmentInternal(
LayoutUnit content_size,
const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
nullptr) {
+ LayoutUnit min = ResolveMinBlockLength(
+ constraint_space, style, border_padding, style.LogicalMinHeight(),
+ content_size, LengthResolvePhase::kLayout,
+ opt_percentage_resolution_block_size_for_min_max);
const Length& logical_height = style.LogicalHeight();
// Scrollable percentage-sized children of table cells, in the table
// "measure" phase contribute nothing to the row height measurement.
// See: https://drafts.csswg.org/css-tables-3/#row-layout
+ // We only apply this rule if the block size of the containing table cell is
+ // considered to be restricted, though. Otherwise, especially if this is the
+ // only child of the cell, and that is the only cell in the row, we'd end up
+ // with zero block size. To match the legacy layout engine behavior in
+ // LayoutBox::ContainingBlockLogicalHeightForPercentageResolution(), we only
+ // check the block-size of the containing cell and its containing table. Other
+ // things to consider, would be checking the row and row-group, and also other
+ // properties, such as {min,max}-block-size.
if (logical_height.IsPercentOrCalc() &&
constraint_space.TableCellChildLayoutPhase() ==
NGTableCellChildLayoutPhase::kMeasure &&
(style.OverflowY() == EOverflow::kAuto ||
- style.OverflowY() == EOverflow::kScroll))
- return border_padding.BlockSum();
+ style.OverflowY() == EOverflow::kScroll) &&
+ constraint_space.IsInRestrictedBlockSizeTableCell())
+ return min;
LayoutUnit extent = ResolveMainBlockLength(
constraint_space, style, border_padding, logical_height, content_size,
LengthResolvePhase::kLayout,
opt_percentage_resolution_block_size_for_min_max);
- if (extent == NGSizeIndefinite) {
- DCHECK_EQ(content_size, NGSizeIndefinite);
+ if (extent == kIndefiniteSize) {
+ DCHECK_EQ(content_size, kIndefiniteSize);
return extent;
}
@@ -496,10 +518,6 @@ LayoutUnit ComputeBlockSizeForFragmentInternal(
constraint_space, style, border_padding, style.LogicalMaxHeight(),
content_size, LengthResolvePhase::kLayout,
opt_percentage_resolution_block_size_for_min_max);
- LayoutUnit min = ResolveMinBlockLength(
- constraint_space, style, border_padding, style.LogicalMinHeight(),
- content_size, LengthResolvePhase::kLayout,
- opt_percentage_resolution_block_size_for_min_max);
return ConstrainByMinMax(extent, min, max);
}
@@ -508,21 +526,25 @@ LayoutUnit ComputeBlockSizeForFragmentInternal(
LayoutUnit ComputeBlockSizeForFragment(
const NGConstraintSpace& constraint_space,
- const ComputedStyle& style,
+ const NGBlockNode& node,
const NGBoxStrut& border_padding,
LayoutUnit content_size) {
+ // The final block-size of a table-cell is always its intrinsic size.
+ if (node.IsTableCell() && content_size != kIndefiniteSize)
+ return content_size;
+
if (constraint_space.IsFixedSizeBlock())
return constraint_space.AvailableSize().block_size;
if (constraint_space.IsAnonymous())
return content_size;
- return ComputeBlockSizeForFragmentInternal(constraint_space, style,
+ return ComputeBlockSizeForFragmentInternal(constraint_space, node.Style(),
border_padding, content_size);
}
// Computes size for a replaced element.
-NGLogicalSize ComputeReplacedSize(
+LogicalSize ComputeReplacedSize(
const NGLayoutInputNode& node,
const NGConstraintSpace& space,
const base::Optional<MinMaxSize>& child_minmax) {
@@ -561,11 +583,11 @@ NGLogicalSize ComputeReplacedSize(
replaced_block = ConstrainByMinMax(*replaced_block, block_min, block_max);
}
if (replaced_inline && replaced_block)
- return NGLogicalSize(*replaced_inline, *replaced_block);
+ return LogicalSize(*replaced_inline, *replaced_block);
base::Optional<LayoutUnit> intrinsic_inline;
base::Optional<LayoutUnit> intrinsic_block;
- NGLogicalSize aspect_ratio;
+ LogicalSize aspect_ratio;
node.IntrinsicSize(&intrinsic_inline, &intrinsic_block, &aspect_ratio);
// Computing intrinsic size is complicated by the fact that
@@ -682,14 +704,14 @@ NGLogicalSize ComputeReplacedSize(
}
}
}
- return NGLogicalSize(*replaced_inline, *replaced_block);
+ return LogicalSize(*replaced_inline, *replaced_block);
}
int ResolveUsedColumnCount(int computed_count,
LayoutUnit computed_size,
LayoutUnit used_gap,
LayoutUnit available_size) {
- if (computed_size == NGSizeIndefinite) {
+ if (computed_size == kIndefiniteSize) {
DCHECK(computed_count);
return computed_count;
}
@@ -706,7 +728,7 @@ int ResolveUsedColumnCount(LayoutUnit available_size,
const ComputedStyle& style) {
LayoutUnit computed_column_inline_size =
style.HasAutoColumnWidth()
- ? NGSizeIndefinite
+ ? kIndefiniteSize
: std::max(LayoutUnit(1), LayoutUnit(style.ColumnWidth()));
LayoutUnit gap = ResolveUsedColumnGap(available_size, style);
int computed_count = style.ColumnCount();
@@ -731,7 +753,7 @@ LayoutUnit ResolveUsedColumnInlineSize(LayoutUnit available_size,
LayoutUnit computed_size =
style.HasAutoColumnWidth()
- ? NGSizeIndefinite
+ ? kIndefiniteSize
: std::max(LayoutUnit(1), LayoutUnit(style.ColumnWidth()));
int computed_count = style.HasAutoColumnCount() ? 0 : style.ColumnCount();
LayoutUnit used_gap = ResolveUsedColumnGap(available_size, style);
@@ -872,6 +894,22 @@ NGBoxStrut ComputePadding(const NGConstraintSpace& constraint_space,
return padding;
}
+NGBoxStrut ComputeScrollbars(const NGConstraintSpace& constraint_space,
+ const NGLayoutInputNode node) {
+ const ComputedStyle& style = node.Style();
+ if (constraint_space.IsAnonymous() || style.IsOverflowVisible())
+ return NGBoxStrut();
+ NGPhysicalBoxStrut sizes;
+ const LayoutBox* layout_box = node.GetLayoutBox();
+ LayoutUnit horizontal = LayoutUnit(layout_box->HorizontalScrollbarHeight());
+ sizes.bottom = horizontal;
+ LayoutUnit vertical = LayoutUnit(layout_box->VerticalScrollbarWidth());
+ if (layout_box->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
+ sizes.left = vertical;
+ else
+ sizes.right = vertical;
+ return sizes.ConvertToLogical(style.GetWritingMode(), style.Direction());
+}
bool NeedsInlineSizeToResolveLineLeft(const ComputedStyle& style,
const ComputedStyle& container_style) {
@@ -949,14 +987,30 @@ LayoutUnit LineOffsetForTextAlign(ETextAlign text_align,
}
}
-LayoutUnit InlineOffsetForTextAlign(const ComputedStyle& container_style,
- LayoutUnit space_left) {
- TextDirection direction = container_style.Direction();
- LayoutUnit line_offset = LineOffsetForTextAlign(
- container_style.GetTextAlign(), direction, space_left);
- return IsLtr(direction) ? line_offset : space_left - line_offset;
+namespace {
+
+// Calculates default content size for html and body elements in quirks mode.
+// Returns |kIndefiniteSize| in all other cases.
+LayoutUnit CalculateDefaultBlockSize(
+ const NGConstraintSpace& space,
+ const NGBlockNode& node,
+ const NGBoxStrut& border_scrollbar_padding) {
+ // In quirks mode, html and body elements will completely fill the ICB, block
+ // percentages should resolve against this size.
+ if (node.IsQuirkyAndFillsViewport()) {
+ LayoutUnit block_size = space.AvailableSize().block_size;
+ block_size -= ComputeMarginsForSelf(space, node.Style()).BlockSum();
+ return std::max(block_size.ClampNegativeToZero(),
+ border_scrollbar_padding.BlockSum());
+ }
+ return kIndefiniteSize;
}
+// Clamp the inline size of the scrollbar, unless it's larger than the inline
+// size of the content box, in which case we'll return that instead. Scrollbar
+// handling is quite bad in such situations, and this method here is just to
+// make sure that left-hand scrollbars don't mess up scrollWidth. For the full
+// story, visit http://crbug.com/724255.
bool ClampScrollbarToContentBox(NGBoxStrut* scrollbars,
LayoutUnit content_box_inline_size) {
DCHECK(scrollbars->InlineSum());
@@ -972,49 +1026,64 @@ bool ClampScrollbarToContentBox(NGBoxStrut* scrollbars,
return true;
}
-NGBoxStrut CalculateBorderScrollbarPadding(
+} // namespace
+
+NGFragmentGeometry CalculateInitialFragmentGeometry(
const NGConstraintSpace& constraint_space,
- const NGBlockNode node) {
- // If we are producing an anonymous fragment (e.g. a column), it has no
- // borders, padding or scrollbars. Using the ones from the container can only
- // cause trouble.
- if (constraint_space.IsAnonymous())
- return NGBoxStrut();
- return ComputeBorders(constraint_space, node) +
- ComputePadding(constraint_space, node.Style()) +
- ComputeIntrinsicPadding(constraint_space, node) +
- node.GetScrollbarSizes();
-}
+ const NGBlockNode& node) {
+ const ComputedStyle& style = node.Style();
+
+ NGBoxStrut border = ComputeBorders(constraint_space, node);
+ NGBoxStrut padding = ComputePadding(constraint_space, style);
+ NGBoxStrut scrollbar = ComputeScrollbars(constraint_space, node);
+ NGBoxStrut border_padding = border + padding;
+ NGBoxStrut border_scrollbar_padding = border_padding + scrollbar;
-NGLogicalSize CalculateBorderBoxSize(const NGConstraintSpace& constraint_space,
- const NGBlockNode& node,
- const NGBoxStrut& border_padding,
- LayoutUnit block_content_size) {
// If we have a percentage size, we need to set the
- // HasPercentHeightDescendants flag correctly so that flexboz knows it may
+ // HasPercentHeightDescendants flag correctly so that flexbox knows it may
// need to redo layout and can also do some performance optimizations.
- if (node.Style().LogicalHeight().IsPercentOrCalc() ||
- node.Style().LogicalMinHeight().IsPercentOrCalc() ||
- node.Style().LogicalMaxHeight().IsPercentOrCalc() ||
- (node.GetLayoutBox()->IsFlexItemIncludingNG() &&
- node.Style().FlexBasis().IsPercentOrCalc())) {
+ if (style.LogicalHeight().IsPercentOrCalc() ||
+ style.LogicalMinHeight().IsPercentOrCalc() ||
+ style.LogicalMaxHeight().IsPercentOrCalc() ||
+ (node.IsFlexItem() && style.FlexBasis().IsPercentOrCalc())) {
// This call has the side-effect of setting HasPercentHeightDescendants
// correctly.
node.GetLayoutBox()->ComputePercentageLogicalHeight(Length::Percent(0));
}
- return NGLogicalSize(
+ LayoutUnit default_block_size = CalculateDefaultBlockSize(
+ constraint_space, node, border_scrollbar_padding);
+ LogicalSize border_box_size(
ComputeInlineSizeForFragment(constraint_space, node, border_padding),
- ComputeBlockSizeForFragment(constraint_space, node.Style(),
- border_padding, block_content_size));
+ ComputeBlockSizeForFragment(constraint_space, node, border_padding,
+ default_block_size));
+
+ if (UNLIKELY(border_box_size.inline_size <
+ border_scrollbar_padding.InlineSum() &&
+ scrollbar.InlineSum() && !constraint_space.IsAnonymous())) {
+ ClampScrollbarToContentBox(
+ &scrollbar, border_box_size.inline_size - border_padding.InlineSum());
+ }
+
+ return {border_box_size, border, scrollbar, padding};
+}
+
+NGFragmentGeometry CalculateInitialMinMaxFragmentGeometry(
+ const NGConstraintSpace& constraint_space,
+ const NGBlockNode& node) {
+ NGBoxStrut border = ComputeBorders(constraint_space, node);
+ NGBoxStrut padding = ComputePadding(constraint_space, node.Style());
+ NGBoxStrut scrollbar = ComputeScrollbars(constraint_space, node);
+
+ return {/* border_box_size */ LogicalSize(), border, scrollbar, padding};
}
-NGLogicalSize ShrinkAvailableSize(NGLogicalSize size, const NGBoxStrut& inset) {
- DCHECK_NE(size.inline_size, NGSizeIndefinite);
+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());
- if (size.block_size != NGSizeIndefinite) {
+ if (size.block_size != kIndefiniteSize) {
size.block_size -= inset.BlockSum();
size.block_size = std::max(size.block_size, LayoutUnit());
}
@@ -1022,42 +1091,27 @@ NGLogicalSize ShrinkAvailableSize(NGLogicalSize size, const NGBoxStrut& inset) {
return size;
}
-LayoutUnit CalculateDefaultBlockSize(
- const NGConstraintSpace& space,
- const NGBlockNode& node,
- const NGBoxStrut& border_scrollbar_padding) {
- // In quirks mode, html and body elements will completely fill the ICB, block
- // percentages should resolve against this size.
- if (node.IsQuirkyAndFillsViewport()) {
- LayoutUnit block_size = space.AvailableSize().block_size;
- block_size -= ComputeMarginsForSelf(space, node.Style()).BlockSum();
- return std::max(block_size.ClampNegativeToZero(),
- border_scrollbar_padding.BlockSum());
- }
- return NGSizeIndefinite;
-}
-
namespace {
// Implements the common part of the child percentage size calculation. Deals
// with how percentages are propagated from parent to child in quirks mode.
-NGLogicalSize AdjustChildPercentageSizeForQuirksAndFlex(
+LogicalSize AdjustChildPercentageSizeForQuirksAndFlex(
const NGConstraintSpace& space,
const NGBlockNode node,
- NGLogicalSize child_percentage_size,
+ LogicalSize child_percentage_size,
LayoutUnit parent_percentage_block_size) {
// Flex items may have a fixed block-size, but children shouldn't resolve
// their percentages against this.
if (space.IsFixedSizeBlock() && !space.FixedSizeBlockIsDefinite()) {
DCHECK(node.IsFlexItem());
- child_percentage_size.block_size = NGSizeIndefinite;
+ child_percentage_size.block_size = kIndefiniteSize;
return child_percentage_size;
}
// In quirks mode the percentage resolution height is passed from parent to
// child.
// https://quirks.spec.whatwg.org/#the-percentage-height-calculation-quirk
- if (child_percentage_size.block_size == NGSizeIndefinite &&
+ if (child_percentage_size.block_size == kIndefiniteSize &&
node.UseParentPercentageResolutionBlockSizeForChildren())
child_percentage_size.block_size = parent_percentage_block_size;
@@ -1066,15 +1120,15 @@ NGLogicalSize AdjustChildPercentageSizeForQuirksAndFlex(
} // namespace
-NGLogicalSize CalculateChildPercentageSize(
+LogicalSize CalculateChildPercentageSize(
const NGConstraintSpace& space,
const NGBlockNode node,
- const NGLogicalSize& child_available_size) {
+ const LogicalSize& child_available_size) {
// Anonymous block or spaces should pass the percent size straight through.
if (space.IsAnonymous() || node.IsAnonymousBlock())
return space.PercentageResolutionSize();
- NGLogicalSize child_percentage_size = child_available_size;
+ LogicalSize child_percentage_size = child_available_size;
bool is_table_cell_in_measure_phase =
node.IsTableCell() && !space.IsFixedSizeBlock();
@@ -1082,7 +1136,7 @@ NGLogicalSize CalculateChildPercentageSize(
// Table cells which are measuring their content, force their children to
// have an indefinite percentage resolution size.
if (is_table_cell_in_measure_phase) {
- child_percentage_size.block_size = NGSizeIndefinite;
+ child_percentage_size.block_size = kIndefiniteSize;
return child_percentage_size;
}
@@ -1098,10 +1152,10 @@ NGLogicalSize CalculateChildPercentageSize(
space.PercentageResolutionBlockSize());
}
-NGLogicalSize CalculateReplacedChildPercentageSize(
+LogicalSize CalculateReplacedChildPercentageSize(
const NGConstraintSpace& space,
const NGBlockNode node,
- NGLogicalSize border_box_size,
+ LogicalSize border_box_size,
const NGBoxStrut& border_scrollbar_padding,
const NGBoxStrut& border_padding) {
// Anonymous block or spaces should pass the percent size straight through.
@@ -1121,12 +1175,12 @@ NGLogicalSize CalculateReplacedChildPercentageSize(
// To handle this we recalculate the border-box block-size, ignoring the
// fixed size constraint.
if (is_table_cell_in_layout_phase && has_resolvable_block_size) {
- border_box_size.block_size = ComputeBlockSizeForFragmentInternal(
- space, node.Style(), border_padding,
- /* content_size */ NGSizeIndefinite);
+ border_box_size.block_size =
+ ComputeBlockSizeForFragmentInternal(space, node.Style(), border_padding,
+ /* content_size */ kIndefiniteSize);
}
- NGLogicalSize child_percentage_size =
+ LogicalSize child_percentage_size =
ShrinkAvailableSize(border_box_size, border_scrollbar_padding);
return AdjustChildPercentageSizeForQuirksAndFlex(
@@ -1149,12 +1203,12 @@ LayoutUnit CalculateChildPercentageBlockSizeForMinMax(
&parent_percentage_block_size);
LayoutUnit child_percentage_block_size =
- block_size == NGSizeIndefinite
- ? NGSizeIndefinite
+ block_size == kIndefiniteSize
+ ? kIndefiniteSize
: (block_size - border_padding.BlockSum()).ClampNegativeToZero();
// For OOF-positioned nodes, use the parent (containing-block) size.
- if (child_percentage_block_size == NGSizeIndefinite &&
+ if (child_percentage_block_size == kIndefiniteSize &&
(node.UseParentPercentageResolutionBlockSizeForChildren() ||
node.IsOutOfFlowPositioned()))
child_percentage_block_size = parent_percentage_block_size;
@@ -1162,4 +1216,49 @@ LayoutUnit CalculateChildPercentageBlockSizeForMinMax(
return child_percentage_block_size;
}
+LayoutUnit ClampIntrinsicBlockSize(const NGBlockNode& node,
+ const NGBoxStrut& border_scrollbar_padding,
+ LayoutUnit current_intrinsic_block_size) {
+ // With contain: size, we ignore intrinsic sizing. If the block-size was
+ // specified as auto, its content-box size will become 0.
+ if (node.ShouldApplySizeContainment())
+ return border_scrollbar_padding.BlockSum();
+
+ // If display locking induces size containment, then we replace its content
+ // size with the locked content size.
+ if (node.DisplayLockInducesSizeContainment()) {
+ return node.GetDisplayLockContext().GetLockedContentLogicalHeight() +
+ border_scrollbar_padding.BlockSum();
+ }
+
+ return current_intrinsic_block_size;
+}
+
+base::Optional<MinMaxSize> CalculateMinMaxSizesIgnoringChildren(
+ const NGBlockNode& node,
+ const NGBoxStrut& border_scrollbar_padding,
+ NGMinMaxSizeType type) {
+ MinMaxSize sizes;
+ if (type == NGMinMaxSizeType::kBorderBoxSize)
+ sizes += border_scrollbar_padding.InlineSum();
+
+ // Size contained elements don't consider children for intrinsic sizing.
+ if (node.ShouldApplySizeContainment())
+ return sizes;
+
+ // Display locked elements override the content size, without considering
+ // children. Note that contain: size (above) takes precedence over display
+ // locking.
+ if (node.DisplayLockInducesSizeContainment()) {
+ sizes += node.GetDisplayLockContext().GetLockedContentLogicalWidth();
+ return sizes;
+ }
+
+ // If we don't have children, we can also determine the size immediately.
+ if (!node.FirstChild())
+ return sizes;
+
+ return base::nullopt;
+}
+
} // namespace blink
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 1d9be95fbf8..db3fcfa4b34 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
@@ -7,11 +7,13 @@
#include "base/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/layout/min_max_size.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
+#include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
@@ -30,13 +32,6 @@ class NGLayoutInputNode;
// intrinsic sizes pass, and kLayout must be used during the layout pass.
enum class LengthResolvePhase { kIntrinsic, kLayout };
-// LengthResolveType indicates what type length the function is being passed
-// based on its CSS property. E.g.
-// kMinSize - min-width / min-height
-// kMaxSize - max-width / max-height
-// kMainSize - width / height
-enum class LengthResolveType { kMinSize, kMaxSize, kMainSize };
-
inline bool NeedMinMaxSize(const ComputedStyle& style) {
// This check is technically too broad (fill-available does not need intrinsic
// size computation) but that's a rare case and only affects performance, not
@@ -150,7 +145,8 @@ inline LayoutUnit ResolveMinBlockLength(
LengthResolvePhase phase,
const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
nullptr) {
- if (LIKELY(length.IsAuto() ||
+ if (LIKELY(length.IsAuto() || length.IsMinContent() ||
+ length.IsMaxContent() || length.IsFitContent() ||
BlockLengthUnresolvable(
constraint_space, length, phase,
opt_percentage_resolution_block_size_for_min_max)))
@@ -171,7 +167,8 @@ inline LayoutUnit ResolveMaxBlockLength(
LengthResolvePhase phase,
const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
nullptr) {
- if (LIKELY(length.IsMaxSizeNone() ||
+ if (LIKELY(length.IsMaxSizeNone() || length.IsMinContent() ||
+ length.IsMaxContent() || length.IsFitContent() ||
BlockLengthUnresolvable(
constraint_space, length, phase,
opt_percentage_resolution_block_size_for_min_max)))
@@ -256,15 +253,14 @@ CORE_EXPORT LayoutUnit ComputeInlineSizeForFragment(
// Same as ComputeInlineSizeForFragment, but uses height instead of width.
CORE_EXPORT LayoutUnit
ComputeBlockSizeForFragment(const NGConstraintSpace&,
- const ComputedStyle&,
+ const NGBlockNode&,
const NGBoxStrut& border_padding,
LayoutUnit content_size);
// Computes intrinsic size for replaced elements.
-CORE_EXPORT NGLogicalSize
-ComputeReplacedSize(const NGLayoutInputNode&,
- const NGConstraintSpace&,
- const base::Optional<MinMaxSize>&);
+CORE_EXPORT LogicalSize ComputeReplacedSize(const NGLayoutInputNode&,
+ const NGConstraintSpace&,
+ const base::Optional<MinMaxSize>&);
// Based on available inline size, CSS computed column-width, CSS computed
// column-count and CSS used column-gap, return CSS used column-count.
@@ -384,6 +380,9 @@ inline NGLineBoxStrut ComputeLinePadding(
style.IsFlippedLinesWritingMode());
}
+CORE_EXPORT NGBoxStrut ComputeScrollbars(const NGConstraintSpace&,
+ const NGLayoutInputNode);
+
// 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,
// or when block alignment isn't line-left (e.g. with align!=left, and always in
@@ -411,62 +410,43 @@ CORE_EXPORT LayoutUnit LineOffsetForTextAlign(ETextAlign,
TextDirection,
LayoutUnit space_left);
-// Same as |LineOffsetForTextAlign| but returns the logical inline offset
-// instead of line-left offset.
-CORE_EXPORT LayoutUnit InlineOffsetForTextAlign(const ComputedStyle&,
- LayoutUnit space_left);
-
inline LayoutUnit ConstrainByMinMax(LayoutUnit length,
LayoutUnit min,
LayoutUnit max) {
return std::max(min, std::min(length, max));
}
-// Clamp the inline size of the scrollbar, unless it's larger than the inline
-// size of the content box, in which case we'll return that instead. Scrollbar
-// handling is quite bad in such situations, and this method here is just to
-// make sure that left-hand scrollbars don't mess up scrollWidth. For the full
-// story, visit http://crbug.com/724255.
-bool ClampScrollbarToContentBox(NGBoxStrut* scrollbars,
- LayoutUnit content_box_inline_size);
+// Calculates the initial (pre-layout) fragment geometry given a node, and a
+// constraint space.
+// The "pre-layout" block-size may be indefinite, as we'll only have enough
+// information to determine this post-layout.
+CORE_EXPORT NGFragmentGeometry
+CalculateInitialFragmentGeometry(const NGConstraintSpace&, const NGBlockNode&);
-NGBoxStrut CalculateBorderScrollbarPadding(
- const NGConstraintSpace& constraint_space,
- const NGBlockNode node);
-
-// border_padding can be passed in as an optimization; otherwise this function
-// will compute it itself.
-NGLogicalSize CalculateBorderBoxSize(
- const NGConstraintSpace& constraint_space,
- const NGBlockNode& node,
- const NGBoxStrut& border_padding,
- LayoutUnit block_content_size = NGSizeIndefinite);
+// Similar to |CalculateInitialFragmentGeometry| however will only calculate
+// the border, scrollbar, and padding (resolving percentages to zero).
+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.
-NGLogicalSize ShrinkAvailableSize(NGLogicalSize size, const NGBoxStrut& inset);
-
-// Calculates default content size for html and body elements in quirks mode.
-// Returns NGSizeIndefinite in all other cases.
-LayoutUnit CalculateDefaultBlockSize(
- const NGConstraintSpace&,
- const NGBlockNode&,
- const NGBoxStrut& border_scrollbar_padding);
+LogicalSize ShrinkAvailableSize(LogicalSize size, const NGBoxStrut& inset);
// Calculates the percentage resolution size that children of the node should
// use.
-NGLogicalSize CalculateChildPercentageSize(
+LogicalSize CalculateChildPercentageSize(
const NGConstraintSpace&,
const NGBlockNode node,
- const NGLogicalSize& child_available_size);
+ const LogicalSize& child_available_size);
// Calculates the percentage resolution size that replaced children of the node
// should use.
-NGLogicalSize CalculateReplacedChildPercentageSize(
+LogicalSize CalculateReplacedChildPercentageSize(
const NGConstraintSpace&,
const NGBlockNode node,
- NGLogicalSize border_box_size,
+ LogicalSize border_box_size,
const NGBoxStrut& border_scrollbar_padding,
const NGBoxStrut& border_padding);
@@ -476,6 +456,22 @@ LayoutUnit CalculateChildPercentageBlockSizeForMinMax(
const NGBoxStrut& border_padding,
LayoutUnit parent_percentage_block_size);
+// The following function clamps the calculated size based on the node
+// requirements. Specifically, this adjusts the size based on size containment
+// and display locking status.
+LayoutUnit ClampIntrinsicBlockSize(const NGBlockNode&,
+ const NGBoxStrut& border_scrollbar_padding,
+ LayoutUnit current_intrinsic_block_size);
+
+// This function checks if the inline size of this node has to be calculated
+// without considering children. If so, it returns the calculated size.
+// Otherwise, it returns base::nullopt and the caller has to compute the size
+// itself.
+base::Optional<MinMaxSize> CalculateMinMaxSizesIgnoringChildren(
+ const NGBlockNode&,
+ const NGBoxStrut& border_scrollbar_padding,
+ NGMinMaxSizeType);
+
} // namespace blink
#endif // NGLengthUtils_h
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc
index 9790ddd8857..7028dfc5241 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_length_utils_test.cc
@@ -24,7 +24,7 @@ static NGConstraintSpace ConstructConstraintSpace(
bool fixed_inline = false,
bool fixed_block = false,
WritingMode writing_mode = WritingMode::kHorizontalTb) {
- NGLogicalSize size = {LayoutUnit(inline_size), LayoutUnit(block_size)};
+ LogicalSize size = {LayoutUnit(inline_size), LayoutUnit(block_size)};
return NGConstraintSpaceBuilder(
writing_mode, writing_mode,
@@ -86,16 +86,6 @@ class NGLengthUtilsTest : public testing::Test {
LengthResolvePhase::kLayout);
}
- LayoutUnit ComputeBlockSizeForFragment(
- NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300),
- LayoutUnit content_size = LayoutUnit()) {
- NGBoxStrut border_padding = ComputeBordersForTest(*style_) +
- ComputePadding(constraint_space, *style_);
-
- return ::blink::ComputeBlockSizeForFragment(constraint_space, *style_,
- border_padding, content_size);
- }
-
scoped_refptr<ComputedStyle> style_;
};
@@ -120,6 +110,20 @@ class NGLengthUtilsTestWithNode : public NGLayoutTest {
border_padding, &sizes);
}
+ LayoutUnit ComputeBlockSizeForFragment(
+ NGConstraintSpace constraint_space = ConstructConstraintSpace(200, 300),
+ LayoutUnit content_size = LayoutUnit()) {
+ LayoutBox* body = ToLayoutBox(GetDocument().body()->GetLayoutObject());
+ body->SetStyle(style_);
+ body->SetPreferredLogicalWidthsDirty();
+ NGBlockNode node(body);
+
+ NGBoxStrut border_padding = ComputeBordersForTest(*style_) +
+ ComputePadding(constraint_space, *style_);
+ return ::blink::ComputeBlockSizeForFragment(constraint_space, node,
+ border_padding, content_size);
+ }
+
scoped_refptr<ComputedStyle> style_;
};
@@ -344,7 +348,7 @@ TEST_F(NGLengthUtilsTestWithNode, testComputeInlineSizeForFragment) {
ComputeInlineSizeForFragment(constraint_space, sizes));
}
-TEST_F(NGLengthUtilsTest, testComputeBlockSizeForFragment) {
+TEST_F(NGLengthUtilsTestWithNode, testComputeBlockSizeForFragment) {
style_->SetLogicalHeight(Length::Percent(30));
EXPECT_EQ(LayoutUnit(90), ComputeBlockSizeForFragment());
@@ -402,11 +406,11 @@ TEST_F(NGLengthUtilsTest, testComputeBlockSizeForFragment) {
// TODO(layout-ng): test {min,max}-content on max-height.
}
-TEST_F(NGLengthUtilsTest, testIndefinitePercentages) {
+TEST_F(NGLengthUtilsTestWithNode, testIndefinitePercentages) {
style_->SetMinHeight(Length::Fixed(20));
style_->SetHeight(Length::Percent(20));
- EXPECT_EQ(NGSizeIndefinite,
+ EXPECT_EQ(kIndefiniteSize,
ComputeBlockSizeForFragment(ConstructConstraintSpace(200, -1),
LayoutUnit(-1)));
EXPECT_EQ(LayoutUnit(20),
@@ -523,7 +527,7 @@ int GetUsedColumnWidth(int computed_column_count,
int available_inline_size) {
LayoutUnit column_width(computed_column_width);
if (!computed_column_width)
- column_width = LayoutUnit(NGSizeIndefinite);
+ column_width = LayoutUnit(kIndefiniteSize);
return ResolveUsedColumnInlineSize(computed_column_count, column_width,
LayoutUnit(used_column_gap),
LayoutUnit(available_inline_size))
@@ -535,7 +539,7 @@ int GetUsedColumnCount(int computed_column_count,
int available_inline_size) {
LayoutUnit column_width(computed_column_width);
if (!computed_column_width)
- column_width = LayoutUnit(NGSizeIndefinite);
+ column_width = LayoutUnit(kIndefiniteSize);
return ResolveUsedColumnCount(computed_column_count, column_width,
LayoutUnit(used_column_gap),
LayoutUnit(available_inline_size));
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_link.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_link.h
index a67a326192c..5d78813eecf 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_link.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_link.h
@@ -6,68 +6,38 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LINK_H_
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
-#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
namespace blink {
-// We use this struct to store NGLinks in a flexible array in fragments. We have
-// to use this struct instead of using NGLink because flexible array members
-// cannot have destructors, so we need to do manual refcounting.
-struct NGLinkStorage {
- NGPhysicalOffset Offset() const { return offset; }
- const NGPhysicalFragment* get() const { return fragment; }
-
- operator bool() const { return fragment; }
- const NGPhysicalFragment& operator*() const { return *fragment; }
- const NGPhysicalFragment* operator->() const { return fragment; }
-
- const NGPhysicalFragment* fragment;
- NGPhysicalOffset offset;
-};
-
// Class representing the offset of a child fragment relative to the
// parent fragment. Fragments themselves have no position information
// allowing entire fragment subtrees to be reused and cached regardless
// of placement.
-class CORE_EXPORT NGLink {
- DISALLOW_NEW();
-
- public:
- NGLink() = default;
- NGLink(scoped_refptr<const NGPhysicalFragment> fragment,
- NGPhysicalOffset offset)
- : fragment_(std::move(fragment)), offset_(offset) {}
- NGLink(NGLink&& o) noexcept
- : fragment_(std::move(o.fragment_)), offset_(o.offset_) {}
- NGLink(const NGLinkStorage& storage)
- : fragment_(storage.fragment), offset_(storage.offset) {}
- ~NGLink() = default;
- NGLink(const NGLink&) = default;
- NGLink& operator=(const NGLink&) = default;
- NGLink& operator=(NGLink&&) = default;
-
- // Returns the offset relative to the parent fragment's content-box.
- NGPhysicalOffset Offset() const { return offset_; }
+// This class is stored in a C-style regular array on
+// NGPhysicalContainerFragment. It cannot have destructors. Fragment reference
+// counting is done manually.
+struct CORE_EXPORT NGLink {
+ PhysicalOffset Offset() const { return offset; }
+ const NGPhysicalFragment* get() const { return fragment; }
- operator bool() const { return fragment_.get(); }
- const NGPhysicalFragment& operator*() const { return *fragment_.get(); }
- const NGPhysicalFragment* operator->() const { return fragment_.get(); }
- const NGPhysicalFragment* get() const { return fragment_.get(); }
+ operator bool() const { return fragment; }
+ const NGPhysicalFragment& operator*() const { return *fragment; }
+ const NGPhysicalFragment* operator->() const { return fragment; }
- private:
- scoped_refptr<const NGPhysicalFragment> fragment_;
- NGPhysicalOffset offset_;
+ // Returns a |NGLink| with newer generation if exists, or |this|. See
+ // |NGPhysicalFragment::PostLayout()| for more details.
+ const NGLink PostLayout() const {
+ if (const NGPhysicalFragment* new_fragment = fragment->PostLayout())
+ return {new_fragment, offset};
+ return *this;
+ }
- // The builder classes needs to set the offset_ field during
- // fragment construciton to allow the child vector to be moved
- // instead of reconstructed during fragment construction.
- friend class NGLayoutResult;
+ const NGPhysicalFragment* fragment;
+ PhysicalOffset offset;
};
} // namespace blink
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGLink)
-
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_LINK_H_
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 2da01999351..57d100f7344 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
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/layout_block.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h"
@@ -19,6 +20,7 @@
#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_descendant.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
@@ -36,7 +38,7 @@ bool IsAnonymousContainer(const LayoutObject* layout_object) {
// - NG uses the anonymous' parent block, that contains all the anonymous
// continuations.
// This function finds the correct anonymous parent block.
-LayoutBoxModelObject* GetOOFContainingBlockFromAnonymous(
+const LayoutInline* GetOOFContainingBlockFromAnonymous(
const LayoutObject* anonymous_block,
EPosition child_position) {
DCHECK(IsAnonymousContainer(anonymous_block));
@@ -59,8 +61,7 @@ LayoutBoxModelObject* GetOOFContainingBlockFromAnonymous(
}
DCHECK(absolute_containing_block->IsLayoutInline());
// Make absolute_containing_block continuation root.
- return ToLayoutBoxModelObject(
- absolute_containing_block->GetNode()->GetLayoutObject());
+ return ToLayoutInline(absolute_containing_block->ContinuationRoot());
}
} // namespace
@@ -84,7 +85,7 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
const NGConstraintSpace& container_space,
const NGBoxStrut& border_scrollbar,
NGBoxFragmentBuilder* container_builder,
- base::Optional<NGLogicalSize> initial_containing_block_fixed_size)
+ base::Optional<LogicalSize> initial_containing_block_fixed_size)
: container_builder_(container_builder),
contains_absolute_(contains_absolute),
contains_fixed_(contains_fixed) {
@@ -101,13 +102,13 @@ NGOutOfFlowLayoutPart::NGOutOfFlowLayoutPart(
? *initial_containing_block_fixed_size
: default_containing_block_.content_size_for_absolute;
- default_containing_block_.container_offset = NGLogicalOffset(
+ default_containing_block_.container_offset = LogicalOffset(
border_scrollbar.inline_start, border_scrollbar.block_start);
NGPhysicalBoxStrut physical_border_scrollbar =
border_scrollbar.ConvertToPhysical(container_style.GetWritingMode(),
container_style.Direction());
- default_containing_block_.physical_container_offset = NGPhysicalOffset(
+ default_containing_block_.physical_container_offset = PhysicalOffset(
physical_border_scrollbar.left, physical_border_scrollbar.top);
}
@@ -130,11 +131,11 @@ void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
// See "Special case: oof css container" comment for detailed description.
if (descendant_candidates.size() > 0 && current_container && !only_layout &&
IsAnonymousContainer(current_container)) {
- LayoutBoxModelObject* absolute_containing_block =
+ const LayoutInline* absolute_containing_block =
contains_absolute_ ? GetOOFContainingBlockFromAnonymous(
current_container, EPosition::kAbsolute)
: nullptr;
- LayoutBoxModelObject* fixed_containing_block =
+ const LayoutInline* fixed_containing_block =
contains_fixed_ ? GetOOFContainingBlockFromAnonymous(current_container,
EPosition::kFixed)
: nullptr;
@@ -160,6 +161,8 @@ void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
if (only_layout)
return;
+
+ wtf_size_t prev_placed_objects_size = placed_objects.size();
while (SweepLegacyDescendants(&placed_objects)) {
container_builder_->GetAndClearOutOfFlowDescendantCandidates(
&descendant_candidates, current_container);
@@ -170,6 +173,19 @@ void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
LayoutDescendantCandidates(&descendant_candidates, only_layout,
&placed_objects);
+
+ // Legacy currently has a bug where an OOF-positioned node is present
+ // within the current node's |LayoutBlock::PositionedObjects|, however it
+ // is not the containing-block for this node.
+ //
+ // This results in |LayoutDescendantCandidates| never performing layout on
+ // any additional objects.
+ wtf_size_t placed_objects_size = placed_objects.size();
+ if (prev_placed_objects_size == placed_objects_size) {
+ NOTREACHED();
+ break;
+ }
+ prev_placed_objects_size = placed_objects_size;
}
}
@@ -219,14 +235,14 @@ bool NGOutOfFlowLayoutPart::SweepLegacyDescendants(
LayoutBoxUtils::ComputeStaticPositionFromLegacy(*layout_box,
container_builder_);
- LayoutObject* css_container = layout_box->Container();
+ const LayoutObject* css_container = layout_box->Container();
if (IsAnonymousContainer(css_container)) {
css_container = GetOOFContainingBlockFromAnonymous(
css_container, layout_box->Style()->GetPosition());
}
container_builder_->AddOutOfFlowLegacyCandidate(
NGBlockNode(layout_box), static_position,
- css_container->IsBox() ? nullptr : css_container);
+ ToLayoutInlineOrNull(css_container));
}
return true;
}
@@ -236,9 +252,8 @@ NGOutOfFlowLayoutPart::GetContainingBlockInfo(
const NGOutOfFlowPositionedDescendant& descendant) const {
if (descendant.inline_container) {
const auto it = containing_blocks_map_.find(descendant.inline_container);
- // TODO(atotic): Make this if-condition a DCHECK.
- if (it != containing_blocks_map_.end())
- return it->value;
+ DCHECK(it != containing_blocks_map_.end());
+ return it->value;
}
return default_containing_block_;
}
@@ -258,139 +273,128 @@ void NGOutOfFlowLayoutPart::ComputeInlineContainingBlocks(
// Fetch start/end fragment info.
container_builder_->ComputeInlineContainerFragments(
&inline_container_fragments);
- NGLogicalSize container_builder_size = container_builder_->Size();
- NGPhysicalSize container_builder_physical_size =
- ToNGPhysicalSize(container_builder_size,
- default_containing_block_.style->GetWritingMode());
+ LogicalSize container_builder_size = container_builder_->Size();
+ PhysicalSize container_builder_physical_size =
+ ToPhysicalSize(container_builder_size,
+ default_containing_block_.style->GetWritingMode());
// Translate start/end fragments into ContainingBlockInfo.
for (auto& block_info : inline_container_fragments) {
// Variables needed to describe ContainingBlockInfo
const ComputedStyle* inline_cb_style = block_info.key->Style();
- NGLogicalSize inline_cb_size;
- NGLogicalOffset container_offset;
- NGPhysicalOffset physical_container_offset;
-
- if (!block_info.value.has_value()) {
- // This happens when Legacy block is the default container.
- // In this case, container builder does not have any fragments because
- // ng layout algorithm did not run.
- DCHECK(block_info.key->IsLayoutInline());
- NOTIMPLEMENTED()
- << "Inline containing block might need geometry information";
- // TODO(atotic) ContainingBlockInfo geometry
- // must be computed from Legacy algorithm
- } else {
- DCHECK(inline_cb_style);
- NGBoxStrut inline_cb_borders = ComputeBordersForInline(*inline_cb_style);
-
- // The calculation below determines the size of the inline containing
- // block rect.
- //
- // To perform this calculation we:
- // 1. Determine the start_offset "^", this is at the logical-start (wrt.
- // default containing block), of the start fragment rect.
- // 2. Determine the end_offset "$", this is at the logical-end (wrt.
- // default containing block), of the end fragment rect.
- // 3. Determine the logical rectangle defined by these two offsets.
- //
- // Case 1a: Same direction, overlapping fragments.
- // +---------------
- // ---> |^*****-------->
- // +*----*---------
- // * *
- // ------*----*+
- // ----> *****$| --->
- // ------------+
- //
- // Case 1b: Different direction, overlapping fragments.
- // +---------------
- // ---> ^******* <-----|
- // *------*--------
- // * *
- // -----*------*
- // |<-- *******$ --->
- // ------------+
- //
- // Case 2a: Same direction, non-overlapping fragments.
- // +--------
- // ---------> |^ ----->
- // +*-------
- // *
- // --------+ *
- // ------->| $ --->
- // --------+
- //
- // Case 2b: Same direction, non-overlapping fragments.
- // +--------
- // ---------> ^ <-----|
- // *--------
- // *
- // --------+ *
- // | <------ $ --->
- // --------+
- //
- // Note in cases [1a, 2a] we need to account for the inline borders of
- // the rectangles, where-as in [1b, 2b] we do not. This is handled by the
- // is_same_direction check(s).
- //
- // Note in cases [2a, 2b] we don't allow a "negative" containing block
- // size, we clamp negative sizes to zero.
- WritingMode container_writing_mode =
- default_containing_block_.style->GetWritingMode();
- TextDirection container_direction =
- default_containing_block_.style->Direction();
-
- bool is_same_direction =
- container_direction == inline_cb_style->Direction();
-
- // Step 1 - determine the start_offset.
- const NGPhysicalOffsetRect& start_rect =
- block_info.value->start_fragment_union_rect;
- NGLogicalOffset start_offset = start_rect.offset.ConvertToLogical(
- container_writing_mode, container_direction,
- container_builder_physical_size, start_rect.size);
-
- // Make sure we add the inline borders, we don't need to do this in the
- // inline direction if the blocks are in opposite directions.
- start_offset.block_offset += inline_cb_borders.block_start;
- if (is_same_direction)
- start_offset.inline_offset += inline_cb_borders.inline_start;
-
- // Step 2 - determine the end_offset.
- const NGPhysicalOffsetRect& end_rect =
- block_info.value->end_fragment_union_rect;
- NGLogicalOffset end_offset = end_rect.offset.ConvertToLogical(
- container_writing_mode, container_direction,
- container_builder_physical_size, end_rect.size);
-
- // Add in the size of the fragment to get the logical end of the fragment.
- end_offset += end_rect.size.ConvertToLogical(container_writing_mode);
-
- // Make sure we substract the inline borders, we don't need to do this in
- // the inline direction if the blocks are in opposite directions.
- end_offset.block_offset -= inline_cb_borders.block_end;
- if (is_same_direction)
- end_offset.inline_offset -= inline_cb_borders.inline_end;
-
- // Make sure we don't end up with a rectangle with "negative" size.
- end_offset.inline_offset =
- std::max(end_offset.inline_offset, start_offset.inline_offset);
-
- // Step 3 - determine the logical rectange.
-
- // Determine the logical size of the containing block.
- inline_cb_size = {end_offset.inline_offset - start_offset.inline_offset,
- end_offset.block_offset - start_offset.block_offset};
- DCHECK_GE(inline_cb_size.inline_size, LayoutUnit());
- DCHECK_GE(inline_cb_size.block_size, LayoutUnit());
-
- // Determine the container offsets.
- container_offset = start_offset;
- physical_container_offset = container_offset.ConvertToPhysical(
- container_writing_mode, container_direction,
- container_builder_physical_size,
- ToNGPhysicalSize(inline_cb_size, container_writing_mode));
- }
+ LogicalSize inline_cb_size;
+ LogicalOffset container_offset;
+ PhysicalOffset physical_container_offset;
+
+ DCHECK(block_info.value.has_value());
+ DCHECK(inline_cb_style);
+ NGBoxStrut inline_cb_borders = ComputeBordersForInline(*inline_cb_style);
+
+ // The calculation below determines the size of the inline containing block
+ // rect.
+ //
+ // To perform this calculation we:
+ // 1. Determine the start_offset "^", this is at the logical-start (wrt.
+ // default containing block), of the start fragment rect.
+ // 2. Determine the end_offset "$", this is at the logical-end (wrt.
+ // default containing block), of the end fragment rect.
+ // 3. Determine the logical rectangle defined by these two offsets.
+ //
+ // Case 1a: Same direction, overlapping fragments.
+ // +---------------
+ // ---> |^*****-------->
+ // +*----*---------
+ // * *
+ // ------*----*+
+ // ----> *****$| --->
+ // ------------+
+ //
+ // Case 1b: Different direction, overlapping fragments.
+ // +---------------
+ // ---> ^******* <-----|
+ // *------*--------
+ // * *
+ // -----*------*
+ // |<-- *******$ --->
+ // ------------+
+ //
+ // Case 2a: Same direction, non-overlapping fragments.
+ // +--------
+ // ---------> |^ ----->
+ // +*-------
+ // *
+ // --------+ *
+ // ------->| $ --->
+ // --------+
+ //
+ // Case 2b: Same direction, non-overlapping fragments.
+ // +--------
+ // ---------> ^ <-----|
+ // *--------
+ // *
+ // --------+ *
+ // | <------ $ --->
+ // --------+
+ //
+ // Note in cases [1a, 2a] we need to account for the inline borders of the
+ // rectangles, where-as in [1b, 2b] we do not. This is handled by the
+ // is_same_direction check(s).
+ //
+ // Note in cases [2a, 2b] we don't allow a "negative" containing block size,
+ // we clamp negative sizes to zero.
+ WritingMode container_writing_mode =
+ default_containing_block_.style->GetWritingMode();
+ TextDirection container_direction =
+ default_containing_block_.style->Direction();
+
+ bool is_same_direction =
+ container_direction == inline_cb_style->Direction();
+
+ // Step 1 - determine the start_offset.
+ const PhysicalRect& start_rect =
+ block_info.value->start_fragment_union_rect;
+ LogicalOffset start_offset = start_rect.offset.ConvertToLogical(
+ container_writing_mode, container_direction,
+ container_builder_physical_size, start_rect.size);
+
+ // Make sure we add the inline borders, we don't need to do this in the
+ // inline direction if the blocks are in opposite directions.
+ start_offset.block_offset += inline_cb_borders.block_start;
+ if (is_same_direction)
+ start_offset.inline_offset += inline_cb_borders.inline_start;
+
+ // Step 2 - determine the end_offset.
+ const PhysicalRect& end_rect = block_info.value->end_fragment_union_rect;
+ LogicalOffset end_offset = end_rect.offset.ConvertToLogical(
+ container_writing_mode, container_direction,
+ container_builder_physical_size, end_rect.size);
+
+ // Add in the size of the fragment to get the logical end of the fragment.
+ end_offset += end_rect.size.ConvertToLogical(container_writing_mode);
+
+ // Make sure we subtract the inline borders, we don't need to do this in the
+ // inline direction if the blocks are in opposite directions.
+ end_offset.block_offset -= inline_cb_borders.block_end;
+ if (is_same_direction)
+ end_offset.inline_offset -= inline_cb_borders.inline_end;
+
+ // Make sure we don't end up with a rectangle with "negative" size.
+ end_offset.inline_offset =
+ std::max(end_offset.inline_offset, start_offset.inline_offset);
+
+ // Step 3 - determine the logical rectangle.
+
+ // Determine the logical size of the containing block.
+ inline_cb_size = {end_offset.inline_offset - start_offset.inline_offset,
+ end_offset.block_offset - start_offset.block_offset};
+ DCHECK_GE(inline_cb_size.inline_size, LayoutUnit());
+ DCHECK_GE(inline_cb_size.block_size, LayoutUnit());
+
+ // Determine the container offsets.
+ container_offset = start_offset;
+ physical_container_offset = container_offset.ConvertToPhysical(
+ container_writing_mode, container_direction,
+ container_builder_physical_size,
+ ToPhysicalSize(inline_cb_size, container_writing_mode));
containing_blocks_map_.insert(
block_info.key,
ContainingBlockInfo{inline_cb_style, inline_cb_size, inline_cb_size,
@@ -409,11 +413,12 @@ void NGOutOfFlowLayoutPart::LayoutDescendantCandidates(
(!only_layout || candidate.node.GetLayoutBox() == only_layout)) {
scoped_refptr<const NGLayoutResult> result =
LayoutDescendant(candidate, only_layout);
- container_builder_->AddChild(*result,
- result->OutOfFlowPositionedOffset());
+ container_builder_->AddChild(result->PhysicalFragment(),
+ result->OutOfFlowPositionedOffset(),
+ candidate.inline_container);
placed_objects->insert(candidate.node.GetLayoutBox());
if (candidate.node.GetLayoutBox() != only_layout)
- candidate.node.UseOldOutOfFlowPositioning();
+ candidate.node.UseLegacyOutOfFlowPositioning();
} else {
container_builder_->AddOutOfFlowDescendant(candidate);
}
@@ -447,18 +452,24 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
WritingMode container_writing_mode = container_style.GetWritingMode();
WritingMode descendant_writing_mode = descendant_style.GetWritingMode();
- NGLogicalSize container_content_size =
+ LogicalSize container_content_size =
container_info.ContentSize(descendant_style.GetPosition());
- NGLogicalSize container_content_size_in_child_writing_mode =
- ToNGPhysicalSize(container_content_size, container_writing_mode)
+ LogicalSize container_content_size_in_child_writing_mode =
+ ToPhysicalSize(container_content_size, container_writing_mode)
.ConvertToLogical(descendant_writing_mode);
// Determine if we need to actually run the full OOF-positioned sizing, and
// positioning algorithm.
- if (scoped_refptr<const NGLayoutResult> cached_result =
- node.CachedLayoutResultForOutOfFlowPositioned(
- container_content_size_in_child_writing_mode))
- return cached_result;
+ //
+ // When this candidate has an inline container, the container may move without
+ // setting |NeedsLayout()| to the candidate and that there are cases where the
+ // cache validity cannot be determined.
+ if (!descendant.inline_container) {
+ if (scoped_refptr<const NGLayoutResult> cached_result =
+ node.CachedLayoutResultForOutOfFlowPositioned(
+ container_content_size_in_child_writing_mode))
+ return cached_result;
+ }
// Adjust the |static_position| (which is currently relative to the default
// container's border-box). ng_absolute_utils expects the static position to
@@ -474,6 +485,50 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
.SetPercentageResolutionSize(container_content_size)
.ToConstraintSpace();
+ base::Optional<PaintLayerScrollableArea::FreezeScrollbarsScope>
+ freeze_scrollbars;
+ do {
+ scoped_refptr<const NGLayoutResult> layout_result =
+ Layout(node, descendant_constraint_space, static_position,
+ container_content_size, container_info, only_layout);
+
+ if (!freeze_scrollbars.has_value()) {
+ // Since out-of-flow positioning sets up a constraint space with fixed
+ // inline-size, the regular layout code (|NGBlockNode::Layout()|) cannot
+ // re-layout if it discovers that a scrollbar was added or removed. Handle
+ // that situation here. The assumption is that if preferred logical widths
+ // are dirty after layout, AND its inline-size depends on preferred
+ // logical widths, it means that scrollbars appeared or disappeared. We
+ // have the same logic in legacy layout in
+ // |LayoutBlockFlow::UpdateBlockLayout()|.
+ if (node.GetLayoutBox()->PreferredLogicalWidthsDirty() &&
+ AbsoluteNeedsChildInlineSize(descendant_style)) {
+ // Freeze the scrollbars for this layout pass. We don't want them to
+ // change *again*.
+ freeze_scrollbars.emplace();
+ continue;
+ }
+ }
+
+ return layout_result;
+ } while (true);
+}
+
+scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
+ NGBlockNode node,
+ const NGConstraintSpace& descendant_constraint_space,
+ const NGStaticPosition& static_position,
+ LogicalSize container_content_size,
+ const ContainingBlockInfo& container_info,
+ const LayoutBox* only_layout) {
+ const ComputedStyle& container_style = *container_info.style;
+ const ComputedStyle& descendant_style = node.Style();
+ WritingMode container_writing_mode = container_style.GetWritingMode();
+ WritingMode descendant_writing_mode = descendant_style.GetWritingMode();
+
+ LogicalSize container_content_size_in_child_writing_mode =
+ ToPhysicalSize(container_content_size, container_writing_mode)
+ .ConvertToLogical(descendant_writing_mode);
NGBoxStrut border_padding =
ComputeBorders(descendant_constraint_space, node) +
ComputePadding(descendant_constraint_space, descendant_style);
@@ -495,15 +550,15 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
descendant_constraint_space, node, border_padding, input);
}
- base::Optional<NGLogicalSize> replaced_size;
+ base::Optional<LogicalSize> replaced_size;
if (is_replaced) {
replaced_size =
ComputeReplacedSize(node, descendant_constraint_space, min_max_size);
} else if (should_be_considered_as_replaced) {
- replaced_size = NGLogicalSize{
+ replaced_size = LogicalSize{
min_max_size->ShrinkToFit(
descendant_constraint_space.AvailableSize().inline_size),
- NGSizeIndefinite};
+ kIndefiniteSize};
}
NGAbsolutePhysicalPosition node_position =
ComputePartialAbsoluteWithChildInlineSize(
@@ -521,9 +576,8 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
GenerateFragment(node, container_content_size_in_child_writing_mode,
block_estimate, node_position);
- DCHECK(layout_result->PhysicalFragment());
NGFragment fragment(descendant_writing_mode,
- *layout_result->PhysicalFragment());
+ layout_result->PhysicalFragment());
block_estimate = fragment.BlockSize();
}
@@ -542,6 +596,8 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
block_estimate, node_position);
}
+ // TODO(mstensho): Move the rest of this method back into LayoutCandidate().
+
if (node.GetLayoutBox()->IsLayoutNGObject()) {
To<LayoutBlock>(node.GetLayoutBox())
->SetIsLegacyInitiatedOutOfFlowLayout(false);
@@ -558,12 +614,12 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
// |inset| is relative to the container's padding-box. Convert this to being
// relative to the default container's border-box.
- NGLogicalOffset offset = container_info.container_offset;
+ LogicalOffset offset = container_info.container_offset;
offset.inline_offset += inset.inline_start;
offset.block_offset += inset.block_start;
base::Optional<LayoutUnit> y = ComputeAbsoluteDialogYPosition(
- *node.GetLayoutBox(), layout_result->PhysicalFragment()->Size().height);
+ *node.GetLayoutBox(), layout_result->PhysicalFragment().Size().height);
if (y.has_value()) {
if (IsHorizontalWritingMode(container_writing_mode))
offset.block_offset = *y;
@@ -613,15 +669,15 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::LayoutDescendant(
// the legacy offset from being wrt #container to being wrt #anonymous2.
const LayoutObject* container = node.GetLayoutBox()->Container();
if (container->IsAnonymousBlock()) {
- NGLogicalOffset container_offset =
+ LogicalOffset container_offset =
container_builder_->GetChildOffset(container);
offset -= container_offset;
} else if (container->IsLayoutInline() &&
container->ContainingBlock()->IsAnonymousBlock()) {
// Location of OOF with inline container, and anonymous containing block
// is wrt container.
- NGLogicalOffset container_offset =
- container_builder_->GetChildOffset(container->Parent());
+ LogicalOffset container_offset =
+ container_builder_->GetChildOffset(container->ContainingBlock());
offset -= container_offset;
}
@@ -655,7 +711,7 @@ bool NGOutOfFlowLayoutPart::IsContainingBlockForDescendant(
// position calculation.
scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
NGBlockNode descendant,
- const NGLogicalSize& container_content_size_in_child_writing_mode,
+ const LogicalSize& container_content_size_in_child_writing_mode,
const base::Optional<LayoutUnit>& block_estimate,
const NGAbsolutePhysicalPosition& node_position) {
// As the |block_estimate| is always in the descendant's writing mode, we
@@ -668,7 +724,7 @@ scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::GenerateFragment(
block_estimate ? *block_estimate
: container_content_size_in_child_writing_mode.block_size;
- NGLogicalSize available_size(inline_size, block_size);
+ LogicalSize available_size(inline_size, block_size);
// TODO(atotic) will need to be adjusted for scrollbars.
NGConstraintSpaceBuilder builder(writing_mode, writing_mode,
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 0c04ae5c596..e414289366a 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
@@ -8,7 +8,7 @@
#include "third_party/blink/renderer/core/core_export.h"
#include "base/optional.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
#include "third_party/blink/renderer/core/layout/ng/ng_absolute_utils.h"
#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
@@ -51,7 +51,7 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
const NGConstraintSpace& container_space,
const NGBoxStrut& border_scrollbar,
NGBoxFragmentBuilder* container_builder,
- base::Optional<NGLogicalSize> initial_containing_block_fixed_size =
+ base::Optional<LogicalSize> initial_containing_block_fixed_size =
base::nullopt);
// Normally this function lays out and positions all out-of-flow objects from
@@ -80,16 +80,16 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
// Containing block style.
const ComputedStyle* style;
// Logical in containing block coordinates.
- NGLogicalSize content_size_for_absolute;
+ LogicalSize content_size_for_absolute;
// Content size for fixed is different for the ICB.
- NGLogicalSize content_size_for_fixed;
+ LogicalSize content_size_for_fixed;
// Offsets (both logical and physical) of the container's padding-box, wrt.
// the default container's border-box.
- NGLogicalOffset container_offset;
- NGPhysicalOffset physical_container_offset;
+ LogicalOffset container_offset;
+ PhysicalOffset physical_container_offset;
- NGLogicalSize ContentSize(EPosition position) const {
+ LogicalSize ContentSize(EPosition position) const {
return position == EPosition::kAbsolute ? content_size_for_absolute
: content_size_for_fixed;
}
@@ -112,12 +112,19 @@ class CORE_EXPORT NGOutOfFlowLayoutPart {
const NGOutOfFlowPositionedDescendant&,
const LayoutBox* only_layout);
+ scoped_refptr<const NGLayoutResult> Layout(NGBlockNode,
+ const NGConstraintSpace&,
+ const NGStaticPosition&,
+ LogicalSize container_content_size,
+ const ContainingBlockInfo&,
+ const LayoutBox* only_layout);
+
bool IsContainingBlockForDescendant(
const NGOutOfFlowPositionedDescendant& descendant);
scoped_refptr<const NGLayoutResult> GenerateFragment(
NGBlockNode node,
- const NGLogicalSize& container_content_size_in_child_writing_mode,
+ const LogicalSize& container_content_size_in_child_writing_mode,
const base::Optional<LayoutUnit>& block_estimate,
const NGAbsolutePhysicalPosition& node_position);
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 a373b888b99..c950e7f86e4 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
@@ -61,7 +61,8 @@ TEST_F(NGOutOfFlowLayoutPartTest, FixedInsideAbs) {
scoped_refptr<const NGLayoutResult> result =
block_flow->GetCachedLayoutResult();
EXPECT_TRUE(result);
- EXPECT_EQ(result->OutOfFlowPositionedDescendants().size(), (size_t)2);
+ EXPECT_EQ(result->PhysicalFragment().OutOfFlowPositionedDescendants().size(),
+ 2u);
// Test the final result.
Element* fixed_1 = GetDocument().getElementById("fixed1");
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h
index 69b0ff6c3fb..cab4bdc6ce5 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_descendant.h
@@ -7,6 +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/geometry/ng_static_position.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
@@ -25,14 +26,19 @@ namespace blink {
struct CORE_EXPORT NGOutOfFlowPositionedDescendant {
NGBlockNode node;
NGStaticPosition static_position;
- const LayoutObject* inline_container;
+ // Continuation root of the optional inline container.
+ const LayoutInline* inline_container;
+
NGOutOfFlowPositionedDescendant(
NGBlockNode node,
NGStaticPosition static_position,
- const LayoutObject* inline_container = nullptr)
+ const LayoutInline* inline_container = nullptr)
: node(node),
static_position(static_position),
- inline_container(inline_container) {}
+ inline_container(inline_container) {
+ DCHECK(!inline_container ||
+ inline_container == inline_container->ContinuationRoot());
+ }
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
index 3cdce969610..4e3ceb48b1e 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_outline_utils.cc
@@ -23,7 +23,7 @@ bool NGOutlineUtils::HasPaintedOutline(const ComputedStyle& style,
bool NGOutlineUtils::IsInlineOutlineNonpaintingFragment(
const NGPhysicalFragment& physical_fragment) {
- LayoutObject* layout_object = physical_fragment.GetLayoutObject();
+ const LayoutObject* layout_object = physical_fragment.GetLayoutObject();
if (!layout_object)
return false;
if (!layout_object->IsLayoutInline())
@@ -31,7 +31,7 @@ bool NGOutlineUtils::IsInlineOutlineNonpaintingFragment(
if (layout_object->IsElementContinuation()) {
// If continuation root did generate a fragment,
// this fragment should not paint.
- if (layout_object->GetNode()->GetLayoutObject()->FirstInlineFragment())
+ if (layout_object->ContinuationRoot()->FirstInlineFragment())
return true;
}
if (!layout_object->FirstInlineFragment())
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 107500346f3..bea1257ab78 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
@@ -16,63 +16,62 @@
namespace blink {
-NGPageLayoutAlgorithm::NGPageLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token)
- : NGLayoutAlgorithm(node, space, To<NGBlockBreakToken>(break_token)) {
- container_builder_.SetIsNewFormattingContext(space.IsNewFormattingContext());
+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) {
+ container_builder_.SetIsNewFormattingContext(
+ params.space.IsNewFormattingContext());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}
scoped_refptr<const NGLayoutResult> NGPageLayoutAlgorithm::Layout() {
- NGBoxStrut borders = ComputeBorders(ConstraintSpace(), Node());
- NGBoxStrut scrollbars = Node().GetScrollbarSizes();
- NGBoxStrut padding = ComputePadding(ConstraintSpace(), Style());
- NGBoxStrut border_scrollbar_padding = borders + scrollbars + padding;
- NGLogicalSize border_box_size =
- CalculateBorderBoxSize(ConstraintSpace(), Node(), borders + padding);
- NGLogicalSize content_box_size =
- ShrinkAvailableSize(border_box_size, border_scrollbar_padding);
- NGLogicalSize page_size = content_box_size;
+ LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
+ LogicalSize content_box_size =
+ ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
+ LogicalSize page_size = content_box_size;
NGConstraintSpace child_space = CreateConstraintSpaceForPages(page_size);
- container_builder_.SetInlineSize(border_box_size.inline_size);
WritingMode writing_mode = ConstraintSpace().GetWritingMode();
scoped_refptr<const NGBlockBreakToken> break_token = BreakToken();
LayoutUnit intrinsic_block_size;
- NGLogicalOffset page_offset(border_scrollbar_padding.StartOffset());
+ LogicalOffset page_offset(border_scrollbar_padding_.StartOffset());
// TODO(mstensho): Handle auto block size.
- NGLogicalOffset page_progression(LayoutUnit(), page_size.block_size);
+ LogicalOffset page_progression(LayoutUnit(), page_size.block_size);
do {
// Lay out one page. Each page will become a fragment.
- NGBlockLayoutAlgorithm child_algorithm(Node(), child_space,
- break_token.get());
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialFragmentGeometry(child_space, Node());
+ NGBlockLayoutAlgorithm child_algorithm(
+ {Node(), fragment_geometry, child_space, break_token.get()});
scoped_refptr<const NGLayoutResult> result = child_algorithm.Layout();
- const auto* page = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
+ const auto& page = result->PhysicalFragment();
- container_builder_.AddChild(*result, page_offset);
+ container_builder_.AddChild(page, page_offset);
- NGBoxFragment logical_fragment(writing_mode, ConstraintSpace().Direction(),
- *page);
- intrinsic_block_size =
- std::max(intrinsic_block_size,
- page_offset.block_offset + logical_fragment.BlockSize());
+ LayoutUnit page_block_size = NGFragment(writing_mode, page).BlockSize();
+ intrinsic_block_size = std::max(intrinsic_block_size,
+ page_offset.block_offset + page_block_size);
page_offset += page_progression;
- break_token = To<NGBlockBreakToken>(page->BreakToken());
+ break_token = To<NGBlockBreakToken>(page.BreakToken());
} while (break_token && !break_token->IsFinished());
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(), borders + padding, intrinsic_block_size);
+ ConstraintSpace(), Node(), border_padding_, intrinsic_block_size);
container_builder_.SetBlockSize(border_box_size.block_size);
- container_builder_.SetBorders(ComputeBorders(ConstraintSpace(), Node()));
- container_builder_.SetPadding(ComputePadding(ConstraintSpace(), Style()));
- NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), borders + scrollbars,
- &container_builder_)
+ NGOutOfFlowLayoutPart(
+ Node(), ConstraintSpace(),
+ container_builder_.Borders() + container_builder_.Scrollbar(),
+ &container_builder_)
.Run();
// TODO(mstensho): Propagate baselines.
@@ -82,12 +81,15 @@ scoped_refptr<const NGLayoutResult> NGPageLayoutAlgorithm::Layout() {
base::Optional<MinMaxSize> NGPageLayoutAlgorithm::ComputeMinMaxSize(
const MinMaxSizeInput& input) const {
- NGBlockLayoutAlgorithm algorithm(Node(), ConstraintSpace());
+ NGFragmentGeometry fragment_geometry =
+ CalculateInitialMinMaxFragmentGeometry(ConstraintSpace(), Node());
+ NGBlockLayoutAlgorithm algorithm(
+ {Node(), fragment_geometry, ConstraintSpace()});
return algorithm.ComputeMinMaxSize(input);
}
NGConstraintSpace NGPageLayoutAlgorithm::CreateConstraintSpaceForPages(
- const NGLogicalSize& page_size) const {
+ const LogicalSize& page_size) const {
NGConstraintSpaceBuilder space_builder(
ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
space_builder.SetAvailableSize(page_size);
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_page_layout_algorithm.h
index 62c49d87fb9..8491eaa198e 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
@@ -13,18 +13,15 @@ namespace blink {
class NGBlockNode;
class NGBlockBreakToken;
-class NGBreakToken;
class NGConstraintSpace;
-struct NGLogicalSize;
+struct LogicalSize;
class CORE_EXPORT NGPageLayoutAlgorithm
: public NGLayoutAlgorithm<NGBlockNode,
NGBoxFragmentBuilder,
NGBlockBreakToken> {
public:
- NGPageLayoutAlgorithm(NGBlockNode node,
- const NGConstraintSpace& space,
- const NGBreakToken* break_token = nullptr);
+ NGPageLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
scoped_refptr<const NGLayoutResult> Layout() override;
@@ -33,7 +30,10 @@ class CORE_EXPORT NGPageLayoutAlgorithm
private:
NGConstraintSpace CreateConstraintSpaceForPages(
- const NGLogicalSize& size) const;
+ 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 08a62f35cd8..5fd401f8e02 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
@@ -22,33 +22,53 @@ namespace {
struct SameSizeAsNGPhysicalBoxFragment : NGPhysicalContainerFragment {
NGBaselineList baselines;
- NGPhysicalBoxStrut box_struts[2];
+ NGLink children[];
};
static_assert(sizeof(NGPhysicalBoxFragment) ==
sizeof(SameSizeAsNGPhysicalBoxFragment),
"NGPhysicalBoxFragment should stay small");
+bool HasControlClip(const NGPhysicalBoxFragment& self) {
+ const LayoutBox* box = ToLayoutBoxOrNull(self.GetLayoutObject());
+ return box && box->HasControlClip();
+}
+
+LayoutUnit BorderWidth(unsigned edges, unsigned edge, float border_width) {
+ return (edges & edge) ? LayoutUnit(border_width) : LayoutUnit();
+}
+
} // namespace
scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create(
NGBoxFragmentBuilder* builder,
WritingMode block_or_line_writing_mode) {
+ const NGPhysicalBoxStrut borders =
+ builder->initial_fragment_geometry_->border.ConvertToPhysical(
+ builder->GetWritingMode(), builder->Direction());
+ const NGPhysicalBoxStrut padding =
+ builder->initial_fragment_geometry_->padding.ConvertToPhysical(
+ builder->GetWritingMode(), builder->Direction());
+ const size_t byte_size = sizeof(NGPhysicalBoxFragment) +
+ sizeof(NGLink) * builder->children_.size() +
+ (borders.IsZero() ? 0 : sizeof(borders)) +
+ (padding.IsZero() ? 0 : sizeof(padding));
// 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.
// The initialization of the array is done by NGPhysicalContainerFragment;
// we pass the buffer as a constructor argument.
void* data = ::WTF::Partitions::FastMalloc(
- sizeof(NGPhysicalBoxFragment) +
- builder->children_.size() * sizeof(NGLinkStorage),
- ::WTF::GetStringWithTypeName<NGPhysicalBoxFragment>());
- new (data) NGPhysicalBoxFragment(builder, block_or_line_writing_mode);
+ byte_size, ::WTF::GetStringWithTypeName<NGPhysicalBoxFragment>());
+ new (data) NGPhysicalBoxFragment(builder, borders, padding,
+ block_or_line_writing_mode);
return base::AdoptRef(static_cast<NGPhysicalBoxFragment*>(data));
}
NGPhysicalBoxFragment::NGPhysicalBoxFragment(
NGBoxFragmentBuilder* builder,
+ const NGPhysicalBoxStrut& borders,
+ const NGPhysicalBoxStrut& padding,
WritingMode block_or_line_writing_mode)
: NGPhysicalContainerFragment(
builder,
@@ -58,56 +78,53 @@ NGPhysicalBoxFragment::NGPhysicalBoxFragment(
? kFragmentRenderedLegend
: kFragmentBox,
builder->BoxType()),
- baselines_(builder->baselines_),
- borders_(builder->borders_.ConvertToPhysical(builder->GetWritingMode(),
- builder->Direction())),
- padding_(builder->padding_.ConvertToPhysical(builder->GetWritingMode(),
- builder->Direction())) {
+ baselines_(builder->baselines_) {
DCHECK(GetLayoutObject() && GetLayoutObject()->IsBoxModelObject());
+ has_borders_ = !borders.IsZero();
+ if (has_borders_)
+ *const_cast<NGPhysicalBoxStrut*>(ComputeBordersAddress()) = borders;
+ has_padding_ = !padding.IsZero();
+ if (has_padding_)
+ *const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress()) = padding;
is_fieldset_container_ = builder->is_fieldset_container_;
- is_old_layout_root_ = builder->is_old_layout_root_;
+ is_legacy_layout_root_ = builder->is_legacy_layout_root_;
border_edge_ = builder->border_edges_.ToPhysical(builder->GetWritingMode());
children_inline_ =
builder->layout_object_ && builder->layout_object_->ChildrenInline();
}
bool NGPhysicalBoxFragment::HasSelfPaintingLayer() const {
- return GetLayoutBoxModelObject().HasSelfPaintingLayer();
-}
-
-bool NGPhysicalBoxFragment::HasControlClip() const {
- const LayoutObject* layout_object = GetLayoutObject();
- DCHECK(layout_object);
- return layout_object->IsBox() && ToLayoutBox(layout_object)->HasControlClip();
+ SECURITY_DCHECK(GetLayoutObject() && GetLayoutObject()->IsBoxModelObject());
+ return (static_cast<const LayoutBoxModelObject*>(GetLayoutObject()))
+ ->HasSelfPaintingLayer();
}
-LayoutRect NGPhysicalBoxFragment::OverflowClipRect(
- const LayoutPoint& location,
+PhysicalRect NGPhysicalBoxFragment::OverflowClipRect(
+ const PhysicalOffset& location,
OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox());
const LayoutBox* box = ToLayoutBox(GetLayoutObject());
return box->OverflowClipRect(location, overlay_scrollbar_clip_behavior);
}
-NGPhysicalOffsetRect NGPhysicalBoxFragment::ScrollableOverflow() const {
+PhysicalRect NGPhysicalBoxFragment::ScrollableOverflow() const {
DCHECK(GetLayoutObject());
- LayoutObject* layout_object = GetLayoutObject();
+ const LayoutObject* layout_object = GetLayoutObject();
if (layout_object->IsBox()) {
if (HasOverflowClip())
- return NGPhysicalOffsetRect({}, Size());
+ return PhysicalRect({}, Size());
// Legacy is the source of truth for overflow
- return NGPhysicalOffsetRect(
- ToLayoutBox(layout_object)->LayoutOverflowRect());
+ return PhysicalRect(ToLayoutBox(layout_object)->LayoutOverflowRect());
} else if (layout_object->IsLayoutInline()) {
// Inline overflow is a union of child overflows.
- NGPhysicalOffsetRect overflow({}, Size());
+ PhysicalRect overflow({}, Size());
WritingMode container_writing_mode = Style().GetWritingMode();
TextDirection container_direction = Style().Direction();
for (const auto& child_fragment : Children()) {
- NGPhysicalOffsetRect child_overflow =
+ PhysicalRect child_overflow =
child_fragment->ScrollableOverflowForPropagation(layout_object);
if (child_fragment->Style() != Style()) {
- NGPhysicalOffset relative_offset = ComputeRelativeOffset(
+ PhysicalOffset relative_offset = ComputeRelativeOffset(
child_fragment->Style(), container_writing_mode,
container_direction, Size());
child_overflow.offset += relative_offset;
@@ -119,7 +136,7 @@ NGPhysicalOffsetRect NGPhysicalBoxFragment::ScrollableOverflow() const {
} else {
NOTREACHED();
}
- return NGPhysicalOffsetRect({}, Size());
+ return PhysicalRect({}, Size());
}
IntSize NGPhysicalBoxFragment::ScrolledContentOffset() const {
@@ -128,16 +145,16 @@ IntSize NGPhysicalBoxFragment::ScrolledContentOffset() const {
return box->ScrolledContentOffset();
}
-LayoutSize NGPhysicalBoxFragment::ScrollSize() const {
+PhysicalSize NGPhysicalBoxFragment::ScrollSize() const {
DCHECK(GetLayoutObject() && GetLayoutObject()->IsBox());
const LayoutBox* box = ToLayoutBox(GetLayoutObject());
- return LayoutSize(box->ScrollWidth(), box->ScrollHeight());
+ return {box->ScrollWidth(), box->ScrollHeight()};
}
-NGPhysicalOffsetRect NGPhysicalBoxFragment::ComputeSelfInkOverflow() const {
+PhysicalRect NGPhysicalBoxFragment::ComputeSelfInkOverflow() const {
CheckCanUpdateInkOverflow();
const ComputedStyle& style = Style();
- LayoutRect ink_overflow({}, Size().ToLayoutSize());
+ PhysicalRect ink_overflow({}, Size().ToLayoutSize());
DCHECK(GetLayoutObject());
if (style.HasVisualOverflowingEffect()) {
@@ -145,22 +162,22 @@ NGPhysicalOffsetRect NGPhysicalBoxFragment::ComputeSelfInkOverflow() const {
if (NGOutlineUtils::HasPaintedOutline(style,
GetLayoutObject()->GetNode()) &&
!NGOutlineUtils::IsInlineOutlineNonpaintingFragment(*this)) {
- Vector<LayoutRect> outline_rects;
+ Vector<PhysicalRect> outline_rects;
// The result rects are in coordinates of this object's border box.
AddSelfOutlineRects(
- &outline_rects, LayoutPoint(),
+ &outline_rects, PhysicalOffset(),
GetLayoutObject()->OutlineRectsShouldIncludeBlockVisualOverflow());
- LayoutRect rect = UnionRectEvenIfEmpty(outline_rects);
- rect.Inflate(style.OutlineOutsetExtent());
+ PhysicalRect rect = UnionRectEvenIfEmpty(outline_rects);
+ rect.Inflate(LayoutUnit(style.OutlineOutsetExtent()));
ink_overflow.Unite(rect);
}
}
- return NGPhysicalOffsetRect(ink_overflow);
+ return ink_overflow;
}
void NGPhysicalBoxFragment::AddSelfOutlineRects(
- Vector<LayoutRect>* outline_rects,
- const LayoutPoint& additional_offset,
+ Vector<PhysicalRect>* outline_rects,
+ const PhysicalOffset& additional_offset,
NGOutlineType outline_type) const {
// TODO(kojii): Needs inline_element_continuation logic from
// LayoutBlockFlow::AddOutlineRects?
@@ -168,17 +185,18 @@ void NGPhysicalBoxFragment::AddSelfOutlineRects(
const LayoutObject* layout_object = GetLayoutObject();
DCHECK(layout_object);
if (layout_object->IsLayoutInline()) {
- Vector<LayoutRect> blockflow_outline_rects =
- layout_object->PhysicalOutlineRects(LayoutPoint(), outline_type);
+ Vector<PhysicalRect> blockflow_outline_rects =
+ layout_object->OutlineRects(PhysicalOffset(), outline_type);
// The rectangles returned are offset from the containing block. We need the
// offset from this fragment.
if (blockflow_outline_rects.size() > 0) {
- LayoutPoint first_fragment_offset = blockflow_outline_rects[0].Location();
- LayoutSize corrected_offset = additional_offset - first_fragment_offset;
+ PhysicalOffset first_fragment_offset = blockflow_outline_rects[0].offset;
+ PhysicalOffset corrected_offset =
+ additional_offset - first_fragment_offset;
for (auto& outline : blockflow_outline_rects) {
- // Skip if both width and height are zero. Contaning blocks in empty
+ // Skip if both width and height are zero. Containing blocks in empty
// linebox is one such case.
- if (outline.Size().IsZero())
+ if (outline.size.IsZero())
continue;
outline.Move(corrected_offset);
outline_rects->push_back(outline);
@@ -192,16 +210,26 @@ void NGPhysicalBoxFragment::AddSelfOutlineRects(
if (!layout_object->IsAnonymous()) {
outline_rects->emplace_back(additional_offset, Size().ToLayoutSize());
}
-
if (outline_type == NGOutlineType::kIncludeBlockVisualOverflow &&
- !HasOverflowClip() && !HasControlClip()) {
- AddOutlineRectsForNormalChildren(outline_rects, additional_offset,
- outline_type);
-
- // TODO(kojii): LayoutBlock::AddOutlineRects handles positioned objects
- // here. Do we need it?
+ !HasOverflowClip() && !HasControlClip(*this)) {
+ // Tricky code ahead: we pass a 0,0 additional_offset to
+ // AddOutlineRectsForNormalChildren, and add it in after the call.
+ // This is necessary because AddOutlineRectsForNormalChildren expects
+ // additional_offset to be an offset from containing_block.
+ // Since containing_block is our layout object, offset must be 0,0.
+ // https://crbug.com/968019
+ Vector<PhysicalRect> children_rects;
+ AddOutlineRectsForNormalChildren(&children_rects, PhysicalOffset(),
+ outline_type,
+ ToLayoutBoxModelObject(GetLayoutObject()));
+ if (!additional_offset.IsZero()) {
+ for (auto& rect : children_rects)
+ rect.offset += additional_offset;
+ }
+ outline_rects->AppendVector(children_rects);
+ // LayoutBlock::AddOutlineRects also adds out of flow objects here.
+ // In LayoutNG out of flow objects are not part of the outline.
}
-
// TODO(kojii): Needs inline_element_continuation logic from
// LayoutBlockFlow::AddOutlineRects?
}
@@ -221,4 +249,58 @@ UBiDiLevel NGPhysicalBoxFragment::BidiLevel() const {
return self_item->BidiLevel();
}
+NGPixelSnappedPhysicalBoxStrut NGPhysicalBoxFragment::BorderWidths() const {
+ unsigned edges = BorderEdges();
+ NGPhysicalBoxStrut box_strut(
+ BorderWidth(edges, NGBorderEdges::kTop, Style().BorderTopWidth()),
+ BorderWidth(edges, NGBorderEdges::kRight, Style().BorderRightWidth()),
+ BorderWidth(edges, NGBorderEdges::kBottom, Style().BorderBottomWidth()),
+ BorderWidth(edges, NGBorderEdges::kLeft, Style().BorderLeftWidth()));
+ return box_strut.SnapToDevicePixels();
+}
+
+#if DCHECK_IS_ON()
+void NGPhysicalBoxFragment::CheckSameForSimplifiedLayout(
+ const NGPhysicalBoxFragment& other,
+ bool check_same_block_size) const {
+ DCHECK_EQ(layout_object_, other.layout_object_);
+
+ LogicalSize size = size_.ConvertToLogical(Style().GetWritingMode());
+ LogicalSize other_size =
+ other.size_.ConvertToLogical(Style().GetWritingMode());
+ DCHECK_EQ(size.inline_size, other_size.inline_size);
+ if (check_same_block_size)
+ DCHECK_EQ(size.block_size, other_size.block_size);
+
+ // "simplified" layout doesn't work within a fragmentation context.
+ DCHECK(!break_token_ && !other.break_token_);
+
+ DCHECK_EQ(type_, other.type_);
+ DCHECK_EQ(sub_type_, other.sub_type_);
+ DCHECK_EQ(style_variant_, other.style_variant_);
+
+ DCHECK_EQ(has_floating_descendants_, other.has_floating_descendants_);
+ DCHECK_EQ(has_orthogonal_flow_roots_, other.has_orthogonal_flow_roots_);
+ DCHECK_EQ(may_have_descendant_above_block_start_,
+ other.may_have_descendant_above_block_start_);
+ DCHECK_EQ(depends_on_percentage_block_size_,
+ other.depends_on_percentage_block_size_);
+
+ DCHECK_EQ(children_inline_, other.children_inline_);
+ DCHECK_EQ(is_fieldset_container_, other.is_fieldset_container_);
+ DCHECK_EQ(is_legacy_layout_root_, other.is_legacy_layout_root_);
+ DCHECK_EQ(border_edge_, other.border_edge_);
+
+ // The oof_positioned_descendants_ vector can change during "simplified"
+ // layout. This occurs when an OOF-descendant changes from "fixed" to
+ // "absolute" (or visa versa) changing its containing block.
+
+ // Legacy layout can (incorrectly) shift baseline position(s) during
+ // "simplified" layout.
+ DCHECK(IsLegacyLayoutRoot() || baselines_ == other.baselines_);
+ DCHECK(Borders() == other.Borders());
+ DCHECK(Padding() == other.Padding());
+}
+#endif
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index cd4ce4b02f9..ada1b07cd39 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
@@ -25,7 +25,7 @@ class CORE_EXPORT NGPhysicalBoxFragment final
WritingMode block_or_line_writing_mode);
~NGPhysicalBoxFragment() {
- for (const NGLinkStorage& child : Children())
+ for (const NGLink& child : Children())
child.fragment->Release();
}
@@ -33,56 +33,81 @@ class CORE_EXPORT NGPhysicalBoxFragment final
return baselines_.Offset(request);
}
- const NGPhysicalBoxStrut Borders() const { return borders_; }
+ const NGPhysicalBoxStrut Borders() const {
+ if (!has_borders_)
+ return NGPhysicalBoxStrut();
+ return *ComputeBordersAddress();
+ }
- const NGPhysicalBoxStrut Padding() const { return padding_; }
+ const NGPhysicalBoxStrut Padding() const {
+ if (!has_padding_)
+ return NGPhysicalBoxStrut();
+ return *ComputePaddingAddress();
+ }
NGPixelSnappedPhysicalBoxStrut PixelSnappedPadding() const {
- return padding_.SnapToDevicePixels();
+ if (!has_padding_)
+ return NGPixelSnappedPhysicalBoxStrut();
+ return ComputePaddingAddress()->SnapToDevicePixels();
}
bool HasSelfPaintingLayer() const;
bool ChildrenInline() const { return children_inline_; }
- bool HasControlClip() const;
-
- NGPhysicalOffsetRect ScrollableOverflow() const;
+ PhysicalRect ScrollableOverflow() const;
// TODO(layout-dev): These three methods delegate to legacy layout for now,
// update them to use LayoutNG based overflow information from the fragment
// and change them to use NG geometry types once LayoutNG supports overflow.
- LayoutRect OverflowClipRect(
- const LayoutPoint& location,
+ PhysicalRect OverflowClipRect(
+ const PhysicalOffset& location,
OverlayScrollbarClipBehavior = kIgnorePlatformOverlayScrollbarSize) const;
IntSize ScrolledContentOffset() const;
- LayoutSize ScrollSize() const;
+ PhysicalSize ScrollSize() const;
// Compute visual overflow of this box in the local coordinate.
- NGPhysicalOffsetRect ComputeSelfInkOverflow() const;
+ PhysicalRect ComputeSelfInkOverflow() const;
// Fragment offset is this fragment's offset from parent.
// Needed to compensate for LayoutInline Legacy code offsets.
- void AddSelfOutlineRects(Vector<LayoutRect>* outline_rects,
- const LayoutPoint& additional_offset,
+ void AddSelfOutlineRects(Vector<PhysicalRect>* outline_rects,
+ const PhysicalOffset& additional_offset,
NGOutlineType include_block_overflows) const;
UBiDiLevel BidiLevel() const;
- scoped_refptr<const NGPhysicalFragment> CloneWithoutOffset() const;
+ // Bitmask for border edges, see NGBorderEdges::Physical.
+ unsigned BorderEdges() const { return border_edge_; }
+ NGPixelSnappedPhysicalBoxStrut BorderWidths() const;
- LayoutBoxModelObject& GetLayoutBoxModelObject() const {
- SECURITY_DCHECK(GetLayoutObject() && GetLayoutObject()->IsBoxModelObject());
- return *static_cast<LayoutBoxModelObject*>(GetLayoutObject());
- }
+#if DCHECK_IS_ON()
+ void CheckSameForSimplifiedLayout(const NGPhysicalBoxFragment&,
+ bool check_same_block_size) const;
+#endif
private:
NGPhysicalBoxFragment(NGBoxFragmentBuilder* builder,
+ const NGPhysicalBoxStrut& borders,
+ const NGPhysicalBoxStrut& padding,
WritingMode block_or_line_writing_mode);
+ const NGPhysicalBoxStrut* ComputeBordersAddress() const {
+ DCHECK(has_borders_);
+ return reinterpret_cast<const NGPhysicalBoxStrut*>(children_ +
+ Children().size());
+ }
+
+ const NGPhysicalBoxStrut* ComputePaddingAddress() const {
+ DCHECK(has_padding_);
+ const NGPhysicalBoxStrut* address =
+ reinterpret_cast<const NGPhysicalBoxStrut*>(children_ +
+ Children().size());
+ return has_borders_ ? address + 1 : address;
+ }
+
NGBaselineList baselines_;
- NGPhysicalBoxStrut borders_;
- NGPhysicalBoxStrut padding_;
- NGLinkStorage children_[];
+ NGLink children_[];
+ // borders and padding come from after |children_| if they are not zero.
};
template <>
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
index e135731ed46..aee364305bf 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment_test.cc
@@ -89,35 +89,35 @@ TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsInlineBlock) {
// instead of using |contenteditable|.
// Tests that a normal old layout root box fragment has correct box type.
-TEST_F(NGPhysicalBoxFragmentTest, DISABLED_NormalOldLayoutRoot) {
+TEST_F(NGPhysicalBoxFragmentTest, DISABLED_NormalLegacyLayoutRoot) {
SetBodyInnerHTML("<div contenteditable>X</div>");
const NGPhysicalFragment* fragment =
GetBodyFragment().Children().front().get();
ASSERT_TRUE(fragment);
EXPECT_TRUE(fragment->IsBox());
EXPECT_EQ(NGPhysicalFragment::kNormalBox, fragment->BoxType());
- EXPECT_TRUE(fragment->IsOldLayoutRoot());
+ EXPECT_TRUE(fragment->IsLegacyLayoutRoot());
EXPECT_TRUE(fragment->IsBlockFormattingContextRoot());
}
// TODO(editing-dev): Once LayoutNG supports editing, we should change this
// test to use LayoutNG tree.
// Tests that a float old layout root box fragment has correct box type.
-TEST_F(NGPhysicalBoxFragmentTest, DISABLED_FloatOldLayoutRoot) {
+TEST_F(NGPhysicalBoxFragmentTest, DISABLED_FloatLegacyLayoutRoot) {
SetBodyInnerHTML("<span contenteditable style='float:left'>X</span>foo");
const NGPhysicalFragment* fragment =
GetBodyFragment().Children().front().get();
ASSERT_TRUE(fragment);
EXPECT_TRUE(fragment->IsBox());
EXPECT_EQ(NGPhysicalFragment::kFloating, fragment->BoxType());
- EXPECT_TRUE(fragment->IsOldLayoutRoot());
+ EXPECT_TRUE(fragment->IsLegacyLayoutRoot());
EXPECT_TRUE(fragment->IsBlockFormattingContextRoot());
}
// TODO(editing-dev): Once LayoutNG supports editing, we should change this
// test to use LayoutNG tree.
// Tests that an inline block old layout root box fragment has correct box type.
-TEST_F(NGPhysicalBoxFragmentTest, DISABLED_InlineBlockOldLayoutRoot) {
+TEST_F(NGPhysicalBoxFragmentTest, DISABLED_InlineBlockLegacyLayoutRoot) {
SetBodyInnerHTML(
"<span contenteditable style='display:inline-block'>X</span>foo");
const auto* line_box = To<NGPhysicalContainerFragment>(
@@ -126,7 +126,7 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_InlineBlockOldLayoutRoot) {
ASSERT_TRUE(fragment);
EXPECT_TRUE(fragment->IsBox());
EXPECT_EQ(NGPhysicalFragment::kAtomicInline, fragment->BoxType());
- EXPECT_TRUE(fragment->IsOldLayoutRoot());
+ EXPECT_TRUE(fragment->IsLegacyLayoutRoot());
EXPECT_TRUE(fragment->IsBlockFormattingContextRoot());
}
@@ -134,7 +134,8 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_InlineBlockOldLayoutRoot) {
// test to use LayoutNG tree.
// Tests that an out-of-flow positioned old layout root box fragment has correct
// box type.
-TEST_F(NGPhysicalBoxFragmentTest, DISABLED_OutOfFlowPositionedOldLayoutRoot) {
+TEST_F(NGPhysicalBoxFragmentTest,
+ DISABLED_OutOfFlowPositionedLegacyLayoutRoot) {
SetBodyInnerHTML(
"<style>body {position: absolute}</style>"
"<div contenteditable style='position: absolute'>X</div>");
@@ -143,7 +144,7 @@ TEST_F(NGPhysicalBoxFragmentTest, DISABLED_OutOfFlowPositionedOldLayoutRoot) {
ASSERT_TRUE(fragment);
EXPECT_TRUE(fragment->IsBox());
EXPECT_EQ(NGPhysicalFragment::kOutOfFlowPositioned, fragment->BoxType());
- EXPECT_TRUE(fragment->IsOldLayoutRoot());
+ EXPECT_TRUE(fragment->IsLegacyLayoutRoot());
EXPECT_TRUE(fragment->IsBlockFormattingContextRoot());
}
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 23dd88be76f..16af3f47c2f 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
@@ -17,6 +17,9 @@ namespace blink {
namespace {
struct SameSizeAsNGPhysicalContainerFragment : NGPhysicalFragment {
+ void* break_token;
+ std::unique_ptr<Vector<NGOutOfFlowPositionedDescendant>>
+ oof_positioned_descendants_;
void* pointer;
wtf_size_t size;
};
@@ -30,34 +33,47 @@ static_assert(sizeof(NGPhysicalContainerFragment) ==
NGPhysicalContainerFragment::NGPhysicalContainerFragment(
NGContainerFragmentBuilder* builder,
WritingMode block_or_line_writing_mode,
- NGLinkStorage* buffer,
+ NGLink* buffer,
NGFragmentType type,
unsigned sub_type)
: NGPhysicalFragment(builder, type, sub_type),
+ break_token_(std::move(builder->break_token_)),
+ oof_positioned_descendants_(
+ builder->oof_positioned_descendants_.IsEmpty()
+ ? nullptr
+ : new Vector<NGOutOfFlowPositionedDescendant>(
+ std::move(builder->oof_positioned_descendants_))),
buffer_(buffer),
num_children_(builder->children_.size()) {
- has_floating_descendants_ = builder->HasFloatingDescendants();
+ has_floating_descendants_ = builder->has_floating_descendants_;
+ has_orthogonal_flow_roots_ = builder->has_orthogonal_flow_roots_;
+ may_have_descendant_above_block_start_ =
+ builder->may_have_descendant_above_block_start_;
+ depends_on_percentage_block_size_ = DependsOnPercentageBlockSize(*builder);
- DCHECK_EQ(builder->children_.size(), builder->offsets_.size());
// 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.
wtf_size_t i = 0;
for (auto& child : builder->children_) {
- buffer[i].fragment = child.get();
+ buffer[i].fragment = child.fragment.get();
buffer[i].fragment->AddRef();
- buffer[i].offset = builder->offsets_[i].ConvertToPhysical(
+ buffer[i].offset = child.offset.ConvertToPhysical(
block_or_line_writing_mode, builder->Direction(), Size(),
- child->Size());
+ child.fragment->Size());
++i;
}
}
+NGPhysicalContainerFragment::~NGPhysicalContainerFragment() = default;
+
+// additional_offset must be offset from the containing_block.
void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
- Vector<LayoutRect>* outline_rects,
- const LayoutPoint& additional_offset,
- NGOutlineType outline_type) const {
- for (const auto& child : Children()) {
+ Vector<PhysicalRect>* outline_rects,
+ const PhysicalOffset& additional_offset,
+ NGOutlineType outline_type,
+ const LayoutBoxModelObject* containing_block) const {
+ for (const auto& child : PostLayoutChildren()) {
// Outlines of out-of-flow positioned descendants are handled in
// NGPhysicalBoxFragment::AddSelfOutlineRects().
if (child->IsOutOfFlowPositioned())
@@ -66,54 +82,61 @@ void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
// Outline of an element continuation or anonymous block continuation is
// added when we iterate the continuation chain.
// See NGPhysicalBoxFragment::AddSelfOutlineRects().
- if (LayoutObject* child_layout_object = child->GetLayoutObject()) {
- auto* child_layout_block_flow =
- DynamicTo<LayoutBlockFlow>(child_layout_object);
- if (child_layout_object->IsElementContinuation() ||
- (child_layout_block_flow &&
- child_layout_block_flow->IsAnonymousBlockContinuation()))
- continue;
+ if (!child->IsLineBox()) {
+ const LayoutObject* child_layout_object = child->GetLayoutObject();
+ if (auto* child_layout_block_flow =
+ DynamicTo<LayoutBlockFlow>(child_layout_object)) {
+ if (child_layout_object->IsElementContinuation() ||
+ child_layout_block_flow->IsAnonymousBlockContinuation())
+ continue;
+ }
}
-
AddOutlineRectsForDescendant(child, outline_rects, additional_offset,
- outline_type);
+ outline_type, containing_block);
}
}
+// additional_offset must be offset from the containing_block because
+// LocalToAncestorRect returns rects wrt containing_block.
void NGPhysicalContainerFragment::AddOutlineRectsForDescendant(
const NGLink& descendant,
- Vector<LayoutRect>* outline_rects,
- const LayoutPoint& additional_offset,
- NGOutlineType outline_type) const {
+ Vector<PhysicalRect>* outline_rects,
+ const PhysicalOffset& additional_offset,
+ NGOutlineType outline_type,
+ const LayoutBoxModelObject* containing_block) const {
if (descendant->IsText() || descendant->IsListMarker())
return;
if (const auto* descendant_box =
DynamicTo<NGPhysicalBoxFragment>(descendant.get())) {
- LayoutObject* descendant_layout_object = descendant_box->GetLayoutObject();
+ const LayoutObject* descendant_layout_object =
+ descendant_box->GetLayoutObject();
DCHECK(descendant_layout_object);
+ // TODO(layoutng): Explain this check. I assume we need it because layers
+ // may have transforms and so we have to go through LocalToAncestorRects?
if (descendant_box->HasLayer()) {
- Vector<LayoutRect> layer_outline_rects;
- descendant_box->AddSelfOutlineRects(&layer_outline_rects, LayoutPoint(),
- outline_type);
+ Vector<PhysicalRect> layer_outline_rects;
+ descendant_box->AddSelfOutlineRects(&layer_outline_rects,
+ PhysicalOffset(), outline_type);
+
+ // Don't pass additional_offset because LocalToAncestorRects will itself
+ // apply it.
descendant_layout_object->LocalToAncestorRects(
- layer_outline_rects, ToLayoutBoxModelObject(GetLayoutObject()),
- LayoutPoint(), additional_offset);
+ layer_outline_rects, containing_block, PhysicalOffset(),
+ PhysicalOffset());
outline_rects->AppendVector(layer_outline_rects);
return;
}
if (descendant_layout_object->IsBox()) {
descendant_box->AddSelfOutlineRects(
- outline_rects,
- additional_offset + descendant.Offset().ToLayoutPoint(),
- outline_type);
+ outline_rects, additional_offset + descendant.Offset(), outline_type);
return;
}
DCHECK(descendant_layout_object->IsLayoutInline());
- LayoutInline* descendant_layout_inline =
+ const LayoutInline* descendant_layout_inline =
ToLayoutInline(descendant_layout_object);
// As an optimization, an ancestor has added rects for its line boxes
// covering descendants' line boxes, so descendants don't need to add line
@@ -131,8 +154,8 @@ void NGPhysicalContainerFragment::AddOutlineRectsForDescendant(
if (const auto* descendant_line_box =
DynamicTo<NGPhysicalLineBoxFragment>(descendant.get())) {
descendant_line_box->AddOutlineRectsForNormalChildren(
- outline_rects, additional_offset + descendant.Offset().ToLayoutPoint(),
- outline_type);
+ outline_rects, additional_offset + descendant.Offset(), outline_type,
+ containing_block);
if (!descendant_line_box->Size().IsEmpty()) {
outline_rects->emplace_back(additional_offset,
@@ -155,4 +178,42 @@ void NGPhysicalContainerFragment::AddOutlineRectsForDescendant(
}
}
+bool NGPhysicalContainerFragment::DependsOnPercentageBlockSize(
+ const NGContainerFragmentBuilder& builder) {
+ NGLayoutInputNode node = builder.node_;
+
+ if (!node || node.IsInline())
+ return builder.has_descendant_that_depends_on_percentage_block_size_;
+
+ // NOTE: If an element is OOF positioned, and has top/bottom constraints
+ // which are percentage based, this function will return false.
+ //
+ // This is fine as the top/bottom constraints are computed *before* layout,
+ // and the result is set as a fixed-block-size constraint. (And the caching
+ // logic will never check the result of this function).
+ //
+ // The result of this function still may be used for an OOF positioned
+ // element if it has a percentage block-size however, but this will return
+ // the correct result from below.
+
+ if ((builder.has_descendant_that_depends_on_percentage_block_size_ ||
+ builder.is_legacy_layout_root_) &&
+ node.UseParentPercentageResolutionBlockSizeForChildren()) {
+ // Quirks mode has different %-block-size behaviour, than standards mode.
+ // An arbitrary descendant may depend on the percentage resolution
+ // block-size given.
+ // If this is also an anonymous block we need to mark ourselves dependent
+ // if we have a dependent child.
+ return true;
+ }
+
+ const ComputedStyle& style = builder.Style();
+ if (style.LogicalHeight().IsPercentOrCalc() ||
+ style.LogicalMinHeight().IsPercentOrCalc() ||
+ style.LogicalMaxHeight().IsPercentOrCalc())
+ return true;
+
+ return false;
+}
+
} // namespace blink
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 3da2322035a..8b5c9f878f6 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
@@ -5,8 +5,10 @@
#ifndef NGPhysicalContainerFragment_h
#define NGPhysicalContainerFragment_h
+#include "base/containers/span.h"
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -15,57 +17,166 @@
namespace blink {
class NGContainerFragmentBuilder;
+struct NGOutOfFlowPositionedDescendant;
enum class NGOutlineType;
class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
public:
- class ChildLinkList {
+ class ChildLinkListBase {
public:
- ChildLinkList(wtf_size_t count, const NGLinkStorage* buffer)
+ ChildLinkListBase(wtf_size_t count, const NGLink* buffer)
: count_(count), buffer_(buffer) {}
wtf_size_t size() const { return count_; }
- const NGLinkStorage& operator[](wtf_size_t idx) const {
- return buffer_[idx];
- }
- const NGLinkStorage& front() const { return buffer_[0]; }
- const NGLinkStorage& back() const { return buffer_[count_ - 1]; }
-
- const NGLinkStorage* begin() const { return buffer_; }
- const NGLinkStorage* end() const { return begin() + count_; }
-
bool IsEmpty() const { return count_ == 0; }
- private:
+ protected:
wtf_size_t count_;
- const NGLinkStorage* buffer_;
+ const NGLink* buffer_;
+ };
+
+ class ChildLinkList : public ChildLinkListBase {
+ public:
+ using ChildLinkListBase::ChildLinkListBase;
+
+ const NGLink& operator[](wtf_size_t idx) const { return buffer_[idx]; }
+ const NGLink& front() const { return buffer_[0]; }
+ const NGLink& back() const { return buffer_[count_ - 1]; }
+
+ const NGLink* begin() const { return buffer_; }
+ const NGLink* end() const { return begin() + count_; }
};
+ // Same as |ChildLinkList|, except that each |NGLink| has the latest
+ // generation of post-layout. See |NGPhysicalFragment::UpdatedFragment()| for
+ // more details.
+ class PostLayoutChildLinkList : public ChildLinkListBase {
+ public:
+ using ChildLinkListBase::ChildLinkListBase;
+
+ class ConstIterator {
+ STACK_ALLOCATED();
+
+ public:
+ ConstIterator(const NGLink* current) : current_(current) {}
+
+ const NGLink& operator*() const { return *PostLayoutOrCurrent(); }
+ const NGLink* operator->() const { return PostLayoutOrCurrent(); }
+
+ ConstIterator& operator++() {
+ ++current_;
+ return *this;
+ }
+ bool operator==(const ConstIterator& other) const {
+ return current_ == other.current_;
+ }
+ bool operator!=(const ConstIterator& other) const {
+ return current_ != other.current_;
+ }
+
+ private:
+ const NGLink* PostLayoutOrCurrent() const {
+ post_layout_.fragment = current_->fragment->PostLayout();
+ if (!post_layout_.fragment)
+ return current_;
+ post_layout_.offset = current_->offset;
+ return &post_layout_;
+ }
+
+ const NGLink* current_;
+ mutable NGLink post_layout_;
+ };
+ using const_iterator = ConstIterator;
+
+ const_iterator begin() const { return const_iterator(buffer_); }
+ const_iterator end() const { return const_iterator(buffer_ + count_); }
+
+ const NGLink operator[](wtf_size_t idx) const {
+ CHECK_LT(idx, count_);
+ return buffer_[idx].PostLayout();
+ }
+ const NGLink front() const { return (*this)[0]; }
+ const NGLink back() const { return (*this)[count_ - 1]; }
+ };
+
+ ~NGPhysicalContainerFragment();
+
+ NGBreakToken* BreakToken() const { return break_token_.get(); }
+
+ // Returns the children of |this|.
+ //
+ // Note, children in this collection maybe old generations. Items in this
+ // collection are safe, but their children (grandchildren of |this|) maybe
+ // from deleted nodes or LayoutObjects. Also see |PostLayoutChildren()|.
ChildLinkList Children() const {
return ChildLinkList(num_children_, buffer_);
}
- void AddOutlineRectsForNormalChildren(Vector<LayoutRect>* outline_rects,
- const LayoutPoint& additional_offset,
- NGOutlineType outline_type) const;
- void AddOutlineRectsForDescendant(const NGLink& descendant,
- Vector<LayoutRect>* rects,
- const LayoutPoint& additional_offset,
- NGOutlineType outline_type) const;
+ // Similar to |Children()| but all children are the latest generation of
+ // post-layout, and therefore all descendants are safe.
+ PostLayoutChildLinkList PostLayoutChildren() const {
+ return PostLayoutChildLinkList(num_children_, buffer_);
+ }
bool HasFloatingDescendants() const { return has_floating_descendants_; }
+ bool HasOrthogonalFlowRoots() const { return has_orthogonal_flow_roots_; }
+
+ // Returns true if we have a descendant within this formatting context, which
+ // is potentially above our block-start edge.
+ bool MayHaveDescendantAboveBlockStart() const {
+ return may_have_descendant_above_block_start_;
+ }
+
+ // Returns true if we aren't able to re-use this fragment if the
+ // |NGConstraintSpace::PercentageResolutionBlockSize| changes.
+ bool DependsOnPercentageBlockSize() const {
+ return depends_on_percentage_block_size_;
+ }
+
+ bool HasOutOfFlowPositionedDescendants() const {
+ DCHECK(!oof_positioned_descendants_ ||
+ !oof_positioned_descendants_->IsEmpty());
+ return oof_positioned_descendants_.get();
+ }
+
+ base::span<NGOutOfFlowPositionedDescendant> OutOfFlowPositionedDescendants()
+ const {
+ if (!HasOutOfFlowPositionedDescendants())
+ return base::span<NGOutOfFlowPositionedDescendant>();
+ return {oof_positioned_descendants_->data(),
+ oof_positioned_descendants_->size()};
+ }
+
protected:
// block_or_line_writing_mode is used for converting the child offsets.
NGPhysicalContainerFragment(NGContainerFragmentBuilder*,
WritingMode block_or_line_writing_mode,
- NGLinkStorage* buffer,
+ NGLink* buffer,
NGFragmentType,
unsigned sub_type);
+ void AddOutlineRectsForNormalChildren(
+ Vector<PhysicalRect>* outline_rects,
+ const PhysicalOffset& additional_offset,
+ NGOutlineType outline_type,
+ const LayoutBoxModelObject* containing_block) const;
+ void AddOutlineRectsForDescendant(
+ const NGLink& descendant,
+ Vector<PhysicalRect>* rects,
+ const PhysicalOffset& additional_offset,
+ NGOutlineType outline_type,
+ const LayoutBoxModelObject* containing_block) const;
+
+ static bool DependsOnPercentageBlockSize(const NGContainerFragmentBuilder&);
+
+ scoped_refptr<NGBreakToken> break_token_;
+ const std::unique_ptr<Vector<NGOutOfFlowPositionedDescendant>>
+ oof_positioned_descendants_;
+
// 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 NGLinkStorage* buffer_;
+ const NGLink* buffer_;
wtf_size_t num_children_;
};
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 456798a7648..b81af6f690c 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
@@ -12,7 +12,6 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
@@ -24,8 +23,8 @@ namespace {
struct SameSizeAsNGPhysicalFragment
: RefCounted<const NGPhysicalFragment, NGPhysicalFragmentTraits> {
- void* pointers[2];
- NGPhysicalSize size;
+ void* layout_object;
+ PhysicalSize size;
unsigned flags;
};
@@ -33,12 +32,11 @@ static_assert(sizeof(NGPhysicalFragment) ==
sizeof(SameSizeAsNGPhysicalFragment),
"NGPhysicalFragment should stay small");
-bool AppendFragmentOffsetAndSize(
- const NGPhysicalFragment* fragment,
- base::Optional<NGPhysicalOffset> fragment_offset,
- StringBuilder* builder,
- NGPhysicalFragment::DumpFlags flags,
- bool has_content) {
+bool AppendFragmentOffsetAndSize(const NGPhysicalFragment* fragment,
+ base::Optional<PhysicalOffset> fragment_offset,
+ StringBuilder* builder,
+ NGPhysicalFragment::DumpFlags flags,
+ bool has_content) {
if (flags & NGPhysicalFragment::DumpOffset) {
if (has_content)
builder->Append(" ");
@@ -83,10 +81,10 @@ String StringForBoxType(const NGPhysicalFragment& fragment) {
result.Append("block-flow-root");
break;
}
- if (fragment.IsOldLayoutRoot()) {
+ if (fragment.IsLegacyLayoutRoot()) {
if (result.length())
result.Append(" ");
- result.Append("old-layout-root");
+ result.Append("legacy-layout-root");
}
if (fragment.IsBlockFlow()) {
if (result.length())
@@ -114,7 +112,7 @@ String StringForBoxType(const NGPhysicalFragment& fragment) {
}
void AppendFragmentToString(const NGPhysicalFragment* fragment,
- base::Optional<NGPhysicalOffset> fragment_offset,
+ base::Optional<PhysicalOffset> fragment_offset,
StringBuilder* builder,
NGPhysicalFragment::DumpFlags flags,
unsigned indent = 2) {
@@ -210,10 +208,6 @@ void AppendFragmentToString(const NGPhysicalFragment* fragment,
builder->Append("\n");
}
-LayoutUnit BorderWidth(unsigned edges, unsigned edge, float border_width) {
- return (edges & edge) ? LayoutUnit(border_width) : LayoutUnit();
-}
-
} // namespace
// static
@@ -224,31 +218,32 @@ void NGPhysicalFragmentTraits::Destruct(const NGPhysicalFragment* fragment) {
NGPhysicalFragment::NGPhysicalFragment(NGFragmentBuilder* builder,
NGFragmentType type,
unsigned sub_type)
- : layout_object_(builder->layout_object_),
- size_(ToNGPhysicalSize(builder->size_, builder->GetWritingMode())),
- break_token_(std::move(builder->break_token_)),
+ : layout_object_(*builder->layout_object_),
+ size_(ToPhysicalSize(builder->size_, builder->GetWritingMode())),
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)builder->style_variant_),
has_floating_descendants_(false),
is_fieldset_container_(false),
- is_old_layout_root_(false) {}
+ is_legacy_layout_root_(false) {
+ DCHECK(builder->layout_object_);
+}
NGPhysicalFragment::NGPhysicalFragment(LayoutObject* layout_object,
NGStyleVariant style_variant,
- NGPhysicalSize size,
+ PhysicalSize size,
NGFragmentType type,
- unsigned sub_type,
- scoped_refptr<NGBreakToken> break_token)
- : layout_object_(layout_object),
+ unsigned sub_type)
+ : layout_object_(*layout_object),
size_(size),
- break_token_(std::move(break_token)),
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)style_variant),
has_floating_descendants_(false),
is_fieldset_container_(false),
- is_old_layout_root_(false) {}
+ is_legacy_layout_root_(false) {
+ DCHECK(layout_object);
+}
// Keep the implementation of the destructor here, to avoid dependencies on
// ComputedStyle in the header file.
@@ -272,29 +267,34 @@ void NGPhysicalFragment::Destroy() const {
}
}
-const ComputedStyle& NGPhysicalFragment::Style() const {
- if (auto* line_box = DynamicTo<NGPhysicalLineBoxFragment>(this))
- return line_box->Style();
+const ComputedStyle& NGPhysicalFragment::SlowEffectiveStyle() const {
switch (StyleVariant()) {
case NGStyleVariant::kStandard:
- DCHECK(GetLayoutObject());
- return *GetLayoutObject()->Style();
+ return layout_object_.StyleRef();
case NGStyleVariant::kFirstLine:
- DCHECK(GetLayoutObject());
- return *GetLayoutObject()->FirstLineStyle();
+ return layout_object_.FirstLineStyleRef();
case NGStyleVariant::kEllipsis:
- return To<NGPhysicalTextFragment>(this)->Style();
+ DCHECK_EQ(Type(), kFragmentText);
+ DCHECK_EQ(StyleVariant(), NGStyleVariant::kEllipsis);
+ // The ellipsis is styled according to the line style.
+ // https://drafts.csswg.org/css-ui/#ellipsing-details
+ // Use first-line style if exists since most cases it is the first line.
+ // TODO(kojii): Should determine if it's really in the first line.
+ DCHECK(layout_object_.IsInline());
+ if (LayoutObject* block = layout_object_.ContainingBlock())
+ return block->FirstLineStyleRef();
+ return layout_object_.FirstLineStyleRef();
}
NOTREACHED();
- return *GetLayoutObject()->Style();
+ return layout_object_.StyleRef();
}
Node* NGPhysicalFragment::GetNode() const {
- return layout_object_ ? layout_object_->GetNode() : nullptr;
+ return !IsLineBox() ? layout_object_.GetNode() : nullptr;
}
bool NGPhysicalFragment::HasLayer() const {
- return layout_object_ && layout_object_->HasLayer();
+ return !IsLineBox() && layout_object_.HasLayer();
}
PaintLayer* NGPhysicalFragment::Layer() const {
@@ -303,7 +303,7 @@ PaintLayer* NGPhysicalFragment::Layer() const {
// If the underlying LayoutObject has a layer it's guaranteed to be a
// LayoutBoxModelObject.
- return static_cast<LayoutBoxModelObject*>(layout_object_)->Layer();
+ return static_cast<LayoutBoxModelObject&>(layout_object_).Layer();
}
bool NGPhysicalFragment::HasSelfPaintingLayer() const {
@@ -312,45 +312,48 @@ bool NGPhysicalFragment::HasSelfPaintingLayer() const {
// If the underlying LayoutObject has a layer it's guaranteed to be a
// LayoutBoxModelObject.
- return static_cast<LayoutBoxModelObject*>(layout_object_)
- ->HasSelfPaintingLayer();
+ return static_cast<LayoutBoxModelObject&>(layout_object_)
+ .HasSelfPaintingLayer();
}
bool NGPhysicalFragment::HasOverflowClip() const {
- return layout_object_ && layout_object_->HasOverflowClip();
+ return !IsLineBox() && layout_object_.HasOverflowClip();
}
bool NGPhysicalFragment::ShouldClipOverflow() const {
- return layout_object_ && layout_object_->ShouldClipOverflow();
+ return !IsLineBox() && layout_object_.ShouldClipOverflow();
}
bool NGPhysicalFragment::IsBlockFlow() const {
- return layout_object_ && layout_object_->IsLayoutBlockFlow();
+ return !IsLineBox() && layout_object_.IsLayoutBlockFlow();
}
bool NGPhysicalFragment::IsListMarker() const {
- return layout_object_ && layout_object_->IsLayoutNGListMarker();
+ return !IsLineBox() && layout_object_.IsLayoutNGListMarker();
}
bool NGPhysicalFragment::IsPlacedByLayoutNG() const {
// TODO(kojii): Move this to a flag for |LayoutNGBlockFlow::UpdateBlockLayout|
// to set.
- if (!layout_object_)
+ if (IsLineBox())
return false;
- const LayoutBlock* container = layout_object_->ContainingBlock();
+ const LayoutBlock* container = layout_object_.ContainingBlock();
if (!container)
return false;
return container->IsLayoutNGMixin() || container->IsLayoutNGFlexibleBox();
}
-NGPixelSnappedPhysicalBoxStrut NGPhysicalFragment::BorderWidths() const {
- unsigned edges = BorderEdges();
- NGPhysicalBoxStrut box_strut(
- BorderWidth(edges, NGBorderEdges::kTop, Style().BorderTopWidth()),
- BorderWidth(edges, NGBorderEdges::kRight, Style().BorderRightWidth()),
- BorderWidth(edges, NGBorderEdges::kBottom, Style().BorderBottomWidth()),
- BorderWidth(edges, NGBorderEdges::kLeft, Style().BorderLeftWidth()));
- return box_strut.SnapToDevicePixels();
+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;
+ }
+ }
+ }
+ return nullptr;
}
#if DCHECK_IS_ON()
@@ -365,7 +368,7 @@ void NGPhysicalFragment::CheckCanUpdateInkOverflow() const {
}
#endif
-NGPhysicalOffsetRect NGPhysicalFragment::ScrollableOverflow() const {
+PhysicalRect NGPhysicalFragment::ScrollableOverflow() const {
switch (Type()) {
case kFragmentBox:
case kFragmentRenderedLegend:
@@ -381,16 +384,17 @@ NGPhysicalOffsetRect NGPhysicalFragment::ScrollableOverflow() const {
return {{}, Size()};
}
-NGPhysicalOffsetRect NGPhysicalFragment::ScrollableOverflowForPropagation(
+PhysicalRect NGPhysicalFragment::ScrollableOverflowForPropagation(
const LayoutObject* container) const {
DCHECK(container);
- NGPhysicalOffsetRect overflow = ScrollableOverflow();
+ PhysicalRect overflow = ScrollableOverflow();
if (GetLayoutObject() &&
GetLayoutObject()->ShouldUseTransformFromContainer(container)) {
TransformationMatrix transform;
- GetLayoutObject()->GetTransformFromContainer(container, LayoutSize(),
+ GetLayoutObject()->GetTransformFromContainer(container, PhysicalOffset(),
transform);
- overflow = NGPhysicalOffsetRect(transform.MapRect(overflow.ToLayoutRect()));
+ overflow =
+ PhysicalRect::EnclosingRect(transform.MapRect(FloatRect(overflow)));
}
return overflow;
}
@@ -447,6 +451,22 @@ TextDirection NGPhysicalFragment::ResolvedDirection() const {
return TextDirection::kLtr;
}
+bool NGPhysicalFragment::ShouldPaintCursorCaret() const {
+ // TODO(xiaochengh): Merge cursor caret painting functions from LayoutBlock to
+ // FrameSelection.
+ if (const auto* block = DynamicTo<LayoutBlock>(GetLayoutObject()))
+ return block->ShouldPaintCursorCaret();
+ return false;
+}
+
+bool NGPhysicalFragment::ShouldPaintDragCaret() const {
+ // TODO(xiaochengh): Merge drag caret painting functions from LayoutBlock to
+ // DragCaret.
+ if (const auto* block = DynamicTo<LayoutBlock>(GetLayoutObject()))
+ return block->ShouldPaintDragCaret();
+ return false;
+}
+
String NGPhysicalFragment::ToString() const {
StringBuilder output;
output.AppendFormat("Type: '%d' Size: '%s'", Type(),
@@ -473,7 +493,7 @@ String NGPhysicalFragment::ToString() const {
String NGPhysicalFragment::DumpFragmentTree(
DumpFlags flags,
- base::Optional<NGPhysicalOffset> fragment_offset,
+ base::Optional<PhysicalOffset> fragment_offset,
unsigned indent) const {
StringBuilder string_builder;
if (flags & DumpHeaderText)
@@ -482,14 +502,14 @@ String NGPhysicalFragment::DumpFragmentTree(
return string_builder.ToString();
}
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
void NGPhysicalFragment::ShowFragmentTree() const {
DumpFlags dump_flags = DumpAll;
LOG(INFO) << "\n" << DumpFragmentTree(dump_flags).Utf8().data();
}
-#endif // !NDEBUG
+#endif
-NGPhysicalOffsetRect NGPhysicalFragmentWithOffset::RectInContainerBox() const {
+PhysicalRect NGPhysicalFragmentWithOffset::RectInContainerBox() const {
return {offset_to_container_box, fragment->Size()};
}
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 d67ed752816..8ed0c068d70 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
@@ -8,10 +8,10 @@
#include "base/memory/scoped_refptr.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/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
-#include "third_party/blink/renderer/core/layout/ng/ng_break_token.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/ng/ng_style_variant.h"
#include "third_party/blink/renderer/platform/graphics/touch_action.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
@@ -21,12 +21,9 @@
namespace blink {
class ComputedStyle;
-class LayoutObject;
class Node;
class NGFragmentBuilder;
-class NGBreakToken;
class NGInlineItem;
-struct NGPixelSnappedPhysicalBoxStrut;
class PaintLayer;
class NGPhysicalFragment;
@@ -131,13 +128,13 @@ class CORE_EXPORT NGPhysicalFragment
// inside the fieldset except the rendered legend).
bool IsFieldsetContainer() const { return is_fieldset_container_; }
- // Returns whether the fragment is old layout root.
- bool IsOldLayoutRoot() const { return is_old_layout_root_; }
+ // Returns whether the fragment is legacy layout root.
+ bool IsLegacyLayoutRoot() const { return is_legacy_layout_root_; }
bool IsBlockFormattingContextRoot() const {
return (IsBox() &&
BoxType() >= NGBoxType::kMinimumBlockFormattingContextRoot) ||
- IsOldLayoutRoot();
+ IsLegacyLayoutRoot();
}
// |Offset()| is reliable only when this fragment was placed by LayoutNG
@@ -151,24 +148,30 @@ class CORE_EXPORT NGPhysicalFragment
// exist for paint, hit-testing, etc.
// Returns the border-box size.
- NGPhysicalSize Size() const { return size_; }
+ PhysicalSize Size() const { return size_; }
// Returns the rect in the local coordinate of this fragment; i.e., offset is
// (0, 0).
- NGPhysicalOffsetRect LocalRect() const { return {{}, size_}; }
+ PhysicalRect LocalRect() const { return {{}, size_}; }
- // Bitmask for border edges, see NGBorderEdges::Physical.
- unsigned BorderEdges() const { return border_edge_; }
- NGPixelSnappedPhysicalBoxStrut BorderWidths() const;
-
- NGBreakToken* BreakToken() const { return break_token_.get(); }
NGStyleVariant StyleVariant() const {
return static_cast<NGStyleVariant>(style_variant_);
}
bool UsesFirstLineStyle() const {
return StyleVariant() == NGStyleVariant::kFirstLine;
}
- const ComputedStyle& Style() const;
+
+ // Returns the style for this fragment.
+ //
+ // For a line box, this returns the style of the containing block. This mostly
+ // represents the style for the line box, except 1) |style.Direction()| maybe
+ // incorrect, use |BaseDirection()| instead, and 2) margin/border/padding,
+ // background etc. do not apply to the line box.
+ const ComputedStyle& Style() const {
+ return StyleVariant() == NGStyleVariant::kStandard
+ ? layout_object_.StyleRef()
+ : SlowEffectiveStyle();
+ }
Node* GetNode() const;
// Whether there is a PaintLayer associated with the fragment.
@@ -187,13 +190,32 @@ class CORE_EXPORT NGPhysicalFragment
// GetLayoutObject should only be used when necessary for compatibility
// with LegacyLayout.
- LayoutObject* GetLayoutObject() const { return layout_object_; }
+ //
+ // For a line box, |layout_object_| has its containing block but this function
+ // returns |nullptr| for the historical reasons. TODO(kojii): We may change
+ // this in future. Use |IsLineBox()| instead of testing this is |nullptr|.
+ const LayoutObject* GetLayoutObject() const {
+ return !IsLineBox() ? &layout_object_ : nullptr;
+ }
+ // TODO(kojii): We should not have mutable version at all, the use of this
+ // function should be eliminiated over time.
+ LayoutObject* GetMutableLayoutObject() const {
+ return !IsLineBox() ? &layout_object_ : nullptr;
+ }
+
+ // Returns the latest generation of the post-layout fragment. Returns
+ // |nullptr| if |this| is the one.
+ //
+ // When subtree relayout occurs at the relayout boundary, its containing block
+ // may keep the reference to old generations of this fragment. Callers can
+ // check if there were newer generations.
+ const NGPhysicalFragment* PostLayout() const;
// Scrollable overflow. including contents, in the local coordinate.
- NGPhysicalOffsetRect ScrollableOverflow() const;
+ PhysicalRect ScrollableOverflow() const;
// ScrollableOverflow(), with transforms applied wrt container if needed.
- NGPhysicalOffsetRect ScrollableOverflowForPropagation(
+ PhysicalRect ScrollableOverflowForPropagation(
const LayoutObject* container) const;
// The allowed touch action is the union of the effective touch action
@@ -207,6 +229,14 @@ class CORE_EXPORT NGPhysicalFragment
// be confused with the CSS 'direction' property.
TextDirection ResolvedDirection() const;
+ // Utility functions for caret painting. Note that carets are painted as part
+ // of the containing block's foreground.
+ bool ShouldPaintCursorCaret() const;
+ bool ShouldPaintDragCaret() const;
+ bool ShouldPaintCarets() const {
+ return ShouldPaintCursorCaret() || ShouldPaintDragCaret();
+ }
+
String ToString() const;
void CheckCanUpdateInkOverflow() const;
@@ -226,10 +256,10 @@ class CORE_EXPORT NGPhysicalFragment
typedef int DumpFlags;
String DumpFragmentTree(DumpFlags,
- base::Optional<NGPhysicalOffset> = base::nullopt,
+ base::Optional<PhysicalOffset> = base::nullopt,
unsigned indent = 2) const;
-#ifndef NDEBUG
+#if DCHECK_IS_ON()
void ShowFragmentTree() const;
#endif
@@ -240,40 +270,51 @@ class CORE_EXPORT NGPhysicalFragment
NGPhysicalFragment(LayoutObject* layout_object,
NGStyleVariant,
- NGPhysicalSize size,
+ PhysicalSize size,
NGFragmentType type,
- unsigned sub_type,
- scoped_refptr<NGBreakToken> break_token = nullptr);
+ unsigned sub_type);
+
+ const ComputedStyle& SlowEffectiveStyle() const;
const Vector<NGInlineItem>& InlineItemsOfContainingBlock() const;
- LayoutObject* const layout_object_;
- const NGPhysicalSize size_;
- scoped_refptr<NGBreakToken> break_token_;
+ 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
- // The following bitfield is only to be used by NGPhysicalContainerFragment
+ // 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_ : 1;
+ unsigned has_orthogonal_flow_roots_ : 1;
+ unsigned may_have_descendant_above_block_start_ : 1;
+ unsigned depends_on_percentage_block_size_ : 1;
- // The following bitfield is only to be used by NGPhysicalLineBoxFragment
+ // The following bitfields are only to be used by NGPhysicalLineBoxFragment
// (it's defined here to save memory, since that class has no bitfields).
+ unsigned has_propagated_descendants_ : 1;
unsigned base_direction_ : 1; // TextDirection
+ unsigned has_hanging_ : 1;
- // The following bitfield is only to be used by NGPhysicalBoxFragment (it's
- // defined here to save memory, since that class has no bitfields).
+ // The following bitfields are only to be used by NGPhysicalBoxFragment
+ // (it's defined here to save memory, since that class has no bitfields).
unsigned children_inline_ : 1;
- unsigned is_fieldset_container_ : 1;
- unsigned is_old_layout_root_ : 1;
unsigned border_edge_ : 4; // NGBorderEdges::Physical
+ unsigned has_borders_ : 1;
+ unsigned has_padding_ : 1;
- // The following bitfield is only to be used by NGPhysicalTextFragment (it's
- // defined here to save memory, since that class has no bitfields).
+ // The following are only used by NGPhysicalBoxFragment but are initialized
+ // for all types to allow methods using them to be inlined.
+ unsigned is_fieldset_container_ : 1;
+ unsigned is_legacy_layout_root_ : 1;
+
+ // The following bitfields are only to be used by NGPhysicalTextFragment
+ // (it's defined here to save memory, since that class has no bitfields).
unsigned line_orientation_ : 2; // NGLineOrientation
- unsigned is_anonymous_text_ : 1;
+ unsigned is_generated_text_ : 1;
+ mutable unsigned ink_overflow_computed_ : 1;
private:
friend struct NGPhysicalFragmentTraits;
@@ -285,9 +326,9 @@ struct CORE_EXPORT NGPhysicalFragmentWithOffset {
DISALLOW_NEW();
scoped_refptr<const NGPhysicalFragment> fragment;
- NGPhysicalOffset offset_to_container_box;
+ PhysicalOffset offset_to_container_box;
- NGPhysicalOffsetRect RectInContainerBox() const;
+ PhysicalRect RectInContainerBox() const;
};
#if !DCHECK_IS_ON()
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc
index a8ce3dbe009..e9837e027cd 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.cc
@@ -5,19 +5,19 @@
#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
#include "base/optional.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
namespace blink {
// Returns the child's relative position wrt the containing fragment.
-NGPhysicalOffset ComputeRelativeOffset(const ComputedStyle& child_style,
- WritingMode container_writing_mode,
- TextDirection container_direction,
- NGPhysicalSize container_size) {
- NGPhysicalOffset offset;
+PhysicalOffset ComputeRelativeOffset(const ComputedStyle& child_style,
+ WritingMode container_writing_mode,
+ TextDirection container_direction,
+ PhysicalSize container_size) {
+ PhysicalOffset offset;
if (child_style.GetPosition() != EPosition::kRelative)
return offset;
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.h
index 1291a8e2f87..8dc07e63815 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.h
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils.h
@@ -6,22 +6,22 @@
#define NGRelativeUtils_h
#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_logical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
#include "third_party/blink/renderer/platform/text/text_direction.h"
namespace blink {
class ComputedStyle;
-struct NGPhysicalOffset;
+struct PhysicalOffset;
// Implements relative positioning spec:
// https://www.w3.org/TR/css-position-3/#rel-pos
// Return relative position offset as defined by style.
-CORE_EXPORT NGPhysicalOffset
+CORE_EXPORT PhysicalOffset
ComputeRelativeOffset(const ComputedStyle& child_style,
WritingMode container_writing_mode,
TextDirection container_direction,
- NGPhysicalSize container_size);
+ PhysicalSize container_size);
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils_test.cc
index 216d0fa7744..763839f8f9b 100644
--- a/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils_test.cc
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_relative_utils_test.cc
@@ -5,8 +5,8 @@
#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h"
-#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_size.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_size.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
@@ -26,7 +26,7 @@ class NGRelativeUtilsTest : public testing::Test {
void SetUp() override {
style_ = ComputedStyle::Create();
style_->SetPosition(EPosition::kRelative);
- container_size_ = NGPhysicalSize{kHorizontalSize, kVerticalSize};
+ container_size_ = PhysicalSize{kHorizontalSize, kVerticalSize};
}
void SetTRBL(LayoutUnit top,
@@ -43,11 +43,11 @@ class NGRelativeUtilsTest : public testing::Test {
}
scoped_refptr<ComputedStyle> style_;
- NGPhysicalSize container_size_;
+ PhysicalSize container_size_;
};
TEST_F(NGRelativeUtilsTest, HorizontalTB) {
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
// Everything auto defaults to kZero,kZero
SetTRBL(kAuto, kAuto, kAuto, kAuto);
@@ -80,7 +80,7 @@ TEST_F(NGRelativeUtilsTest, HorizontalTB) {
}
TEST_F(NGRelativeUtilsTest, VerticalRightLeft) {
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
// Set all sides
SetTRBL(kTop, kRight, kBottom, kLeft);
@@ -106,7 +106,7 @@ TEST_F(NGRelativeUtilsTest, VerticalRightLeft) {
}
TEST_F(NGRelativeUtilsTest, VerticalLeftRight) {
- NGPhysicalOffset offset;
+ PhysicalOffset offset;
// Set all sides
SetTRBL(kTop, kRight, kBottom, kLeft);
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
new file mode 100644
index 00000000000..ed81dd98250
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.cc
@@ -0,0 +1,213 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h"
+
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
+
+namespace blink {
+
+NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm(
+ const NGLayoutAlgorithmParams& params,
+ const NGLayoutResult& result)
+ : NGLayoutAlgorithm(params),
+ previous_result_(result),
+ border_scrollbar_padding_(params.fragment_geometry.border +
+ params.fragment_geometry.scrollbar +
+ params.fragment_geometry.padding),
+ writing_mode_(Style().GetWritingMode()),
+ direction_(Style().Direction()),
+ exclusion_space_(ConstraintSpace().ExclusionSpace()) {
+ // 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.
+ DCHECK(Node().IsBlockFlow());
+ const NGPhysicalBoxFragment& physical_fragment =
+ To<NGPhysicalBoxFragment>(result.PhysicalFragment());
+
+ container_builder_.SetStyleVariant(physical_fragment.StyleVariant());
+ container_builder_.SetIsNewFormattingContext(
+ physical_fragment.IsBlockFormattingContextRoot());
+ container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
+
+ NGExclusionSpace exclusion_space = result.ExclusionSpace();
+ container_builder_.SetExclusionSpace(std::move(exclusion_space));
+
+ // Ensure that the parent layout hasn't asked us to move our BFC position.
+ DCHECK_EQ(ConstraintSpace().BfcOffset(),
+ previous_result_.GetConstraintSpaceForCaching().BfcOffset());
+
+ container_builder_.SetBfcLineOffset(result.BfcLineOffset());
+ if (result.BfcBlockOffset())
+ container_builder_.SetBfcBlockOffset(*result.BfcBlockOffset());
+
+ container_builder_.SetEndMarginStrut(result.EndMarginStrut());
+ container_builder_.SetIntrinsicBlockSize(result.IntrinsicBlockSize());
+ container_builder_.SetUnpositionedListMarker(result.UnpositionedListMarker());
+
+ if (result.IsSelfCollapsing())
+ container_builder_.SetIsSelfCollapsing();
+ if (result.IsPushedByFloats())
+ container_builder_.SetIsPushedByFloats();
+ container_builder_.AddAdjoiningFloatTypes(result.AdjoiningFloatTypes());
+
+ for (const auto& request : ConstraintSpace().BaselineRequests()) {
+ base::Optional<LayoutUnit> baseline = physical_fragment.Baseline(request);
+ if (baseline)
+ container_builder_.AddBaseline(request, *baseline);
+ }
+
+ container_builder_.SetBlockSize(ComputeBlockSizeForFragment(
+ ConstraintSpace(), Node(),
+ container_builder_.Borders() + container_builder_.Padding(),
+ result.IntrinsicBlockSize()));
+
+ child_available_inline_size_ =
+ ShrinkAvailableSize(container_builder_.InitialBorderBoxSize(),
+ border_scrollbar_padding_)
+ .inline_size;
+
+ // We need the previous physical container size to calculate the position of
+ // any child fragments.
+ previous_physical_container_size_ = physical_fragment.Size();
+}
+
+scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() {
+ const auto previous_child_fragments =
+ To<NGPhysicalBoxFragment>(previous_result_.PhysicalFragment()).Children();
+
+ const auto* it = previous_child_fragments.begin();
+ const auto* end = previous_child_fragments.end();
+
+ // We may have a list-marker as our first child. This may have been
+ // propagated up to this container by an arbitrary child. As we don't know
+ // where it came from initially add it as the first child again.
+ if (it != end && (*it)->IsListMarker()) {
+ AddChildFragment(*it, *To<NGPhysicalContainerFragment>(it->get()));
+ ++it;
+ }
+
+ // Initialize the static block-offset for any OOF-positioned children.
+ static_block_offset_ = border_scrollbar_padding_.block_start;
+
+ for (NGLayoutInputNode child = Node().FirstChild(); child;
+ child = child.NextSibling()) {
+ // We've already dealt with any list-markers, so just skip this node.
+ if (child.IsListMarker())
+ continue;
+
+ if (child.IsOutOfFlowPositioned()) {
+ HandleOutOfFlowPositioned(To<NGBlockNode>(child));
+ continue;
+ }
+
+ DCHECK(it != end);
+
+ if (child.IsInline()) {
+ // Simplified layout will only run if none of the lineboxes are dirty.
+ while (it != end && (*it)->IsLineBox()) {
+ // NOTE: When we remove continuations it'll be necessary for lineboxes
+ // to keep track of any exclusions they've added (and update the
+ // exclusion space).
+ AddChildFragment(*it, *To<NGPhysicalContainerFragment>(it->get()));
+ ++it;
+ }
+ continue;
+ }
+
+ DCHECK_EQ((*it)->GetLayoutObject(), child.GetLayoutBox());
+
+ // Add the (potentially updated) layout result.
+ scoped_refptr<const NGLayoutResult> result =
+ To<NGBlockNode>(child).SimplifiedLayout();
+
+ // The child may have failed "simplified" layout! (Due to adding/removing
+ // scrollbars). In this case we also return a nullptr, indicating a full
+ // layout is required.
+ if (!result)
+ return nullptr;
+
+ const NGPhysicalContainerFragment& fragment = result->PhysicalFragment();
+ AddChildFragment(*it, fragment);
+
+ const ComputedStyle& child_style = child.Style();
+
+ // Calculate the static block-offset for any OOF-positioned children.
+ NGMarginStrut margin_strut = result->EndMarginStrut();
+ NGBoxStrut child_margins = ComputeMarginsFor(
+ child_style, child_available_inline_size_, writing_mode_, direction_);
+ margin_strut.Append(child_margins.block_end,
+ child_style.HasMarginBeforeQuirk());
+
+ static_block_offset_ += margin_strut.Sum();
+
+ // Only take exclusion spaces from children which don't establish their own
+ // formatting context.
+ if (!fragment.IsBlockFormattingContextRoot())
+ exclusion_space_ = result->ExclusionSpace();
+ ++it;
+ }
+
+ NGOutOfFlowLayoutPart(
+ Node(), ConstraintSpace(),
+ container_builder_.Borders() + container_builder_.Scrollbar(),
+ &container_builder_)
+ .Run();
+
+ return container_builder_.ToBoxFragment();
+}
+
+void NGSimplifiedLayoutAlgorithm::HandleOutOfFlowPositioned(
+ const NGBlockNode& child) {
+ LogicalOffset static_offset = {border_scrollbar_padding_.inline_start,
+ static_block_offset_};
+
+ if (child.Style().IsOriginalDisplayInlineType()) {
+ NGBfcOffset origin_bfc_offset = {
+ container_builder_.BfcLineOffset() +
+ border_scrollbar_padding_.LineLeft(direction_),
+ (container_builder_.BfcBlockOffset()
+ ? *container_builder_.BfcBlockOffset()
+ : ConstraintSpace().BfcOffset().block_offset) +
+ static_block_offset_};
+
+ static_offset.inline_offset += CalculateOutOfFlowStaticInlineLevelOffset(
+ Style(), origin_bfc_offset, exclusion_space_,
+ child_available_inline_size_);
+ }
+
+ container_builder_.AddOutOfFlowChildCandidate(child, static_offset);
+}
+
+void NGSimplifiedLayoutAlgorithm::AddChildFragment(
+ const NGLink& old_fragment,
+ const NGPhysicalContainerFragment& new_fragment) {
+ DCHECK_EQ(old_fragment->Size(), new_fragment.Size());
+
+ PhysicalSize physical_child_size = new_fragment.Size();
+ LogicalSize child_size = physical_child_size.ConvertToLogical(writing_mode_);
+
+ // Determine the previous position in the logical coordinate system.
+ LogicalOffset child_offset = old_fragment.Offset().ConvertToLogical(
+ writing_mode_, direction_, previous_physical_container_size_,
+ physical_child_size);
+
+ // Add the new fragemnt to the builder.
+ container_builder_.AddChild(new_fragment, child_offset);
+
+ // Update the static block-offset for any OOF-positioned children.
+ static_block_offset_ = child_offset.block_offset + child_size.block_size;
+}
+
+} // namespace blink
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
new file mode 100644
index 00000000000..29dc6351982
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_simplified_layout_algorithm.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_SIMPLIFIED_LAYOUT_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_SIMPLIFIED_LAYOUT_ALGORITHM_H_
+
+#include "third_party/blink/renderer/core/layout/ng/ng_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"
+
+namespace blink {
+
+class NGBlockBreakToken;
+struct NGLink;
+class NGPhysicalContainerFragment;
+
+// The "simplified" layout algorithm will run in the following circumstances:
+// - An OOF-positioned descendant of this node (this node is its containing
+// block) has its constraints changed.
+// - A child requires "simplified" layout, i.e. an indirect-descendant
+// OOF-positioned child has its constraints changed.
+// - The block-size of the fragment has changed, and we know that it won't
+// affect any inflow children (no %-block-size descendants).
+//
+// This algorithm effectively performs a (convoluted) "copy" of the previous
+// layout result. It will:
+// 1. Copy data from the previous |NGLayoutResult| into the
+// |NGBoxFragmentBuilder|, (e.g. flags, end margin strut, etc).
+// 2. Iterate through all the children and:
+// a. If OOF-positioned determine the static-position and add it as an
+// OOF-positioned candidate.
+// b. Otherwise perform layout on the inflow child (which may trigger
+// "simplified" layout on its children).
+// 3. Run the |NGOutOfFlowLayoutPart|.
+class CORE_EXPORT NGSimplifiedLayoutAlgorithm
+ : public NGLayoutAlgorithm<NGBlockNode,
+ NGBoxFragmentBuilder,
+ NGBlockBreakToken> {
+ public:
+ NGSimplifiedLayoutAlgorithm(const NGLayoutAlgorithmParams&,
+ const NGLayoutResult&);
+
+ scoped_refptr<const NGLayoutResult> Layout() override;
+
+ private:
+ void HandleOutOfFlowPositioned(const NGBlockNode&);
+
+ void AddChildFragment(const NGLink& old_fragment,
+ const NGPhysicalContainerFragment& new_fragment);
+
+ const NGLayoutResult& previous_result_;
+ const NGBoxStrut border_scrollbar_padding_;
+
+ const WritingMode writing_mode_;
+ const TextDirection direction_;
+
+ LayoutUnit child_available_inline_size_;
+ PhysicalSize previous_physical_container_size_;
+
+ LayoutUnit static_block_offset_;
+ NGExclusionSpace exclusion_space_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_SIMPLIFIED_LAYOUT_ALGORITHM_H_
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 8cb6b8f4d29..ba44dfdb772 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
@@ -30,7 +30,7 @@ NGConstraintSpace CreateIndefiniteConstraintSpaceForChild(
WritingMode child_writing_mode = child.Style().GetWritingMode();
DCHECK(!IsParallelWritingMode(parent_writing_mode, child_writing_mode));
- NGLogicalSize indefinite_size(NGSizeIndefinite, NGSizeIndefinite);
+ LogicalSize indefinite_size(kIndefiniteSize, kIndefiniteSize);
NGConstraintSpaceBuilder builder(parent_writing_mode, child_writing_mode,
child.CreatesNewFormattingContext());
SetOrthogonalFallbackInlineSizeIfNeeded(container_style, child, &builder);
@@ -39,7 +39,6 @@ NGConstraintSpace CreateIndefiniteConstraintSpaceForChild(
.SetPercentageResolutionSize(indefinite_size)
.SetReplacedPercentageResolutionSize(indefinite_size)
.SetIsIntermediateLayout(true)
- .SetFloatsBfcBlockOffset(LayoutUnit())
.ToConstraintSpace();
}
@@ -51,7 +50,7 @@ void SetOrthogonalFallbackInlineSizeIfNeeded(
child.Style().GetWritingMode()))
return;
- NGPhysicalSize orthogonal_children_containing_block_size =
+ PhysicalSize orthogonal_children_containing_block_size =
child.InitialContainingBlockSize();
LayoutUnit fallback_size;
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 9d7badb5119..559bef22a49 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
@@ -20,7 +20,6 @@ int NGTextDecorationOffset::ComputeUnderlineOffsetForUnder(
FontBaseline baseline_type = style.GetFontBaseline();
if (decorating_box_) {
- // TODO(eae): Replace with actual baseline once available.
NGBaselineRequest baseline_request = {
NGBaselineAlgorithmType::kAtomicInline,
FontBaseline::kIdeographicBaseline};